/*
** wmymail.c
** email indicator tool designed as dockapp for windowmaker
** (c) 2000 josh
** 
*/

/*
** wmymail version 0.1
*/

///////////////////////////////////////////////////////////////////////////////
// includes

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <utime.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <dockapp.h>

#include "xpm/main.xpm"
#include "xpm/numbers.xpm"
#include "xpm/unumbers.xpm"
#include "xpm/mbox_1.xpm"
#include "xpm/mbox_2.xpm"
#include "xpm/mbox_3.xpm"

///////////////////////////////////////////////////////////////////////////////
// defines

#define CHECKINTERVAL  1           // default mail check interval in seconds

#define NAME      "wmymail"
#define VERSION   "wmymail v0.1  June 6, 2001"

///////////////////////////////////////////////////////////////////////////////
// global data

char *displayName = "";
char *mailPath;
char *fontColor = "";
char *background = "";
char *clickcommand = "";
char *newcommand = "";
int numMessages = 0;
int lastnumMessages = 0;
int numRead = 0;
int numUnread = 0;
int lastnumUnread = 0;
int buttonpressed = 0;

  /*
     usefetchmail means to run "fetchmail -c" and parse the output, rather than
     counting up messages in an mbox file.

     It will change the interval from seconds to minutes!
  */

int usefetchmail = 0;
int flip = 1;
int checkInterval = CHECKINTERVAL;
time_t lastModifySeconds = 0;
off_t lastSize = 0;

Pixmap mainPixmap;
Pixmap numbersPixmap;
Pixmap unumbersPixmap;
Pixmap mboxonePixmap;
Pixmap mboxtwoPixmap;
Pixmap mboxthreePixmap;
Pixmap outPixmap1;
Pixmap outPixmap2;
GC defaultGC;

static DAProgramOption options[] = {

    {"-display", NULL, "display to use",
        DOString, False, {&displayName}},

    {"-i", "--interval", "seconds between mailbox checks (default 1)",
        DONatural, False, {&checkInterval} },

    {"-fc", "--fontcolor", "custom font color",
        DOString, False, {&fontColor} },

    {"-bg", "--background", "custom background color for non-shaped window",
        DOString, False, {&background} },

    {"-ns", "--noshape", "make the dock app non-shaped (windowed)",
        DONone, False, {NULL} },

    {"-F", "--fetchmail", "check with fetchmail -c instead of the mbox",
        DONone, False, {NULL} },

    {"-c", "--command", "command to run when clicked",
        DOString, False, {&clickcommand} },

    {"-n", "--newcommand", "command to run when new mail is received",
        DOString, False, {&newcommand} }

};

///////////////////////////////////////////////////////////////////////////////
// prototypes

void checkForNewMail(int dummy);
void updatePixmap(void);
void parseMailFile( struct stat *fileStat );
char *getHexColorString( char *colorName );
void putnumber (int number, Pixmap pixmap, Pixmap numbers,
  int destx, int desty);
void buttonpress(int button, int state, int x, int y);
void buttonrelease(int button, int state, int x, int y);
void checkfetchmail (void);
void checkmbox (void);
void launch (const char *command);

///////////////////////////////////////////////////////////////////////////////
// functions

int main(int argc, char **argv) {
  Pixmap mainPixmap_mask;

  unsigned width, height;

  DACallbacks callbacks = { NULL, &buttonpress, &buttonrelease,
                            NULL, NULL, NULL, NULL };

  DAParseArguments(argc, argv, options,
                   sizeof(options) / sizeof(DAProgramOption),
                   NAME, VERSION );

  DAInitialize(displayName, "Mail checker", 64, 64, argc, argv);

  // simple recoloring of the raw xpms befor creating Pixmaps of them
  // this works as long as you don't "touch" the images...

  if (options[2].used) { // custom font color ?
    char *colorLine = strdup("+      c #");

    strcat(colorLine, getHexColorString(fontColor));

    colorLine = strdup("+      c #");
    strcat(colorLine, getHexColorString(fontColor));
     numbers_xpm[3] = colorLine;
  }

  if (options[3].used && options[8].used) { // custom window background ?
    char *colorLine = strdup("       c #");

    strcat(colorLine, getHexColorString(background));
    main_xpm[1] = colorLine;
  }

  DAMakePixmapFromData(main_xpm, &mainPixmap, &mainPixmap_mask, &width, &height);
  DAMakePixmapFromData(numbers_xpm, &numbersPixmap, NULL, &width, &height);
  DAMakePixmapFromData(unumbers_xpm, &unumbersPixmap, NULL, &width, &height);

  DAMakePixmapFromData(mbox_1_xpm, &mboxonePixmap, NULL, &width, &height);
  DAMakePixmapFromData(mbox_2_xpm, &mboxtwoPixmap, NULL, &width, &height);
  DAMakePixmapFromData(mbox_3_xpm, &mboxthreePixmap, NULL, &width, &height);

  if (!options[4].used) // no shape to install
    DASetShape(mainPixmap_mask);

  if (options[5].used)  // use fetchmail
    usefetchmail = 1;
  else if ((mailPath = getenv("MAIL")) == NULL) {
    perror("Please define your $MAIL environment-variable!\n");
    exit(1);
  }

  DASetCallbacks( &callbacks );
  DASetTimeout(-1);

  outPixmap1 = DAMakePixmap();
  outPixmap2 = DAMakePixmap();
  defaultGC = XDefaultGC(DADisplay, 0);

  signal(SIGALRM, checkForNewMail);

  updatePixmap();

  DAShow();
    
  checkForNewMail(0);

  DAEventLoop();

  return 0;
}

char *getHexColorString(char *colorName) {
  XColor color;
  char *hexColorString;

  if (!XParseColor(DADisplay,
      DefaultColormap(DADisplay, DefaultScreen( DADisplay)),
        colorName, &color))
    {
      printf("unknown colorname: \"%s\"\n", colorName);
      exit(1);
    }

  hexColorString = (char *)malloc(7);
  sprintf(hexColorString, "%02X%02X%02X", color.red>>8, color.green>>8,
        color.blue>>8);

  return hexColorString;
}

  /*
   *
   * checkForNewMail
   *
   */

void checkForNewMail(int dummy) {
  struct itimerval timerVal;

  if (usefetchmail) {
    checkfetchmail();
  } else {
    checkmbox();
  }

  if (numMessages != lastnumMessages ||
      numUnread != lastnumUnread) {
    updatePixmap();
    if (numUnread > lastnumUnread && strlen(newcommand) > 0)
        launch(newcommand);
    lastnumMessages = numMessages;
    lastnumUnread = numUnread;
  }

  memset(&timerVal, 0, sizeof(timerVal));

  if (usefetchmail) {
    timerVal.it_value.tv_sec = checkInterval * 60;
  } else {
    timerVal.it_value.tv_sec = checkInterval;
  }

  setitimer(ITIMER_REAL, &timerVal, NULL);
}

  /*
   * 
   *  checkfetchmail
   *
   */

void checkfetchmail (void) {
  int msgtotal = 0;
  int msgseen = 0;
  int snpret;
  char tmpfile[20] = "wmymail.XXXXXX";
  char syscmd[120];
  char line[1024];
  char *s, *t;
  int fd;
  FILE *f;


  fd = mkstemp(tmpfile);
  if (fd == -1) {
      perror("wmymail: cannot get a temporay file");
      return;
  }

  snpret = snprintf(syscmd, 120, "fetchmail -c > %s", tmpfile);
  if (snpret < 0) {
    perror("wmymail: error in snprintf() call (should not happen)");
    return;
  }

  if (system(syscmd) < 0) {
    perror("wmymail: error when using system() to run fetchmail -c");
    return;
  }

  f = fdopen(fd, "r");
  if (f == NULL) {
      perror("wmymail: can't reread tempfile\n");
      return;
  }
  while (fgets(line, 1024, f) != NULL) {
    if (line[0] >= '0' && line[0] <= '9') {
      msgtotal += atoi(line);
      s = (char *)strstr(line, " ");
      if (s != NULL) {
        s++;
        t = (char *)strstr(s, " ");
        if (t != NULL && t[1] == '(' && t[2] >= '0' && t[2] <= '9') {
          t += 2;
          msgseen += atoi(t);
        }
      }
    }
  }
  fclose(f);
  remove(tmpfile);

  /* Now that that's been gotten through without major errors,
     move the values to the global variables */

  numMessages = msgtotal;
  numUnread = msgtotal - msgseen;
}
  /*
   * 
   *  checkmbox
   *
   */

void checkmbox (void) {
  struct stat fileStat;

  if (stat(mailPath, &fileStat) == -1 || fileStat.st_size == 0) {
    numMessages = 0;
    numUnread = 0;
  } else if (lastModifySeconds != fileStat.st_mtime ||
             lastSize != fileStat.st_size) {

    parseMailFile(&fileStat);

    lastModifySeconds = fileStat.st_mtime;
    lastSize = fileStat.st_size;
  }
}

  /*
   *
   * updatePixmap
   *
   */

void updatePixmap(void) {
  Pixmap outPixmap = flip ? outPixmap1 : outPixmap2;
    
  flip = !flip;

  XCopyArea(DADisplay, mainPixmap, outPixmap, defaultGC,
            0, 0, 64, 64, 0, 0);

  if (numMessages > 998) {
    putnumber(999, outPixmap, numbersPixmap, 40, 49);
  } else {
    putnumber(numMessages, outPixmap, numbersPixmap, 40, 49);
  }

  if (numUnread > 998) {
    putnumber(999, outPixmap, unumbersPixmap, 6, 49);
  } else if (!numUnread) {
    putnumber(0, outPixmap, numbersPixmap, 6, 49);
  } else {
    putnumber(numUnread, outPixmap, unumbersPixmap, 6, 49);
  }

  if (numUnread == 0) {
    // do nothing.
  } else if (numUnread == 1) {
    XCopyArea(DADisplay, mboxonePixmap, outPixmap, defaultGC,
      0, 0, 40, 34, 14, 6);
  } else if (numUnread == 2) {
    XCopyArea(DADisplay, mboxtwoPixmap, outPixmap, defaultGC,
      0, 0, 40, 34, 14, 6);
  } else {
    XCopyArea(DADisplay, mboxthreePixmap, outPixmap, defaultGC,
      0, 0, 40, 34, 14, 6);
  } 
    
  DASetPixmap(outPixmap);
}

  /*
   *
   *  putnumber
   *
   */

void putnumber (int number, Pixmap pixmap, Pixmap numbers,
  int destx, int desty) {

  int digit1, digit2, digit3;

  digit1 = number / 100;
  digit2 = (number % 100) / 10;
  digit3 = number % 10;

  if (digit1) XCopyArea(DADisplay, numbers, pixmap, defaultGC,
    digit1 * 5, 0, 5, 9, destx, desty);

  if (digit2 || digit1) XCopyArea(DADisplay, numbers, pixmap, defaultGC,
    digit2 * 5, 0, 5, 9, destx + 6, desty);

  XCopyArea(DADisplay, numbers, pixmap, defaultGC,
    digit3 * 5, 0, 5, 9, destx + 12, desty);
}

  /*
   *
   *  parseMailFile
   *
   */

void parseMailFile(struct stat *fileStat) {
  char buf[1024];
  int inHeader = 0;
  int statusRead = 0;
  int longline = 0;
  FILE *f = fopen(mailPath, "r");

  numMessages = 0;
  numRead = 0;

  while (fgets(buf, 1024, f) != NULL) {

    if (longline) {
      longline = index(buf, '\n') != NULL;
    } else {
      if(!strncmp(buf, "From ", 5)) {
        inHeader = 1;
        numMessages++;
      } else if (inHeader) {
  
        if (!strcmp(buf, "\n")) {
          inHeader = 0;
          if (statusRead) {
            numRead++;
            statusRead = 0;
          }
        } else if (!strncmp(buf, "Status: ", 8) && strchr(buf, 'R')) {
          statusRead = 1;
        }
      }
  
      longline = index(buf, '\n') == NULL;
    }
  }

  fclose(f);
  numUnread = numMessages - numRead;
}

void buttonpress (int button, int state, int x, int y) {
    buttonpressed = 1;
}

void buttonrelease (int button, int state, int x, int y) {
    if (buttonpressed && x > 0 && x < 64 && y > 0 && y < 64 &&
            strlen(clickcommand) > 0) {
        launch(clickcommand);
    }
    buttonpressed = 0;
}

void launch (const char *command) {
    int cpid;

    cpid = fork();
    if (cpid == -1) {
        perror("can't fork");
    } else if (cpid == 0) {
        system(command);
        exit(0);
    }
}



syntax highlighted by Code2HTML, v. 0.9.1