/* xnewbiff  (by Avrim Blum).  Some parts swiped from checkmail  */
/* Minor modifications by rig (Ronald Greenberg) 08/08/91. */
/* this version will also check a file for timed messages and print them
   out if realtime > time stamp. Format is: mm/dd hour:min \n <message> \n
   Currently, just prints out first line of message.
 */
/* kills all windows when you read your mail */

/* optional arguments: 
   -f <filename>, where <filename> holds the timed messages.
   -l <number of lines>, #lines to diplay on middle button press (default 20).
   -t, display the "To:" line in the mail message.
   -m <bell mag>,  how loud the bell should be (default 25).
   -x <xcoord>, give x coordinate for center of mail window.
   -y <ycoord>, give y coordinate for upper left corner of mail window.
   -d <delay>, check mail file every <delay> seconds. (default 10).
   -rv, for reverse video.
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <X11/Xlib.h>
#include <time.h>
#include <strings.h>
#include <ctype.h>

#define CHECKDELAY 3  /* check timed messages every DELAY*CHECKDELAY seconds */
#define DEFAULT_DISPLAY NULL
#define SAME 0
#define max(x,y) ((x) > (y) ? x : y)

char *maildir = "/usr/spool/mail";  /* system dependent */
	/* can override with environment variable MAILDIR */
char *temp; /* used for above purpose, added by rig; *userdir removed */
char *tolookfor[3];
char *nullist[2] = {"",""};
int tolooklength = 2;

 /* window stuff */
  Window w; Display *d; int screen;
  XSetWindowAttributes xswa;
  XEvent *rep;
  GC gc;  /* graphics context */
  long bg, fg;
  int rvflag = 0;
 
/* global v'bles for putting stuff in the window */
  int winstx,winsty,stx,sty,width,height,numlines,totlines,bell_mag;
  int maxwidth, maxheight, xval = 550, yval = 0;
  char **line;
  char testln[100];
  int maxlines = 23;
  int mail_bell_mag = 25;
  int delay = 10; /* number of seconds between checks of mail file */

  int window_is_up_flag = 0, doingtime = 0;
main(argc, argv)
     int argc;
     char *argv[];
{
  struct stat buf;
  char *namep, *getlogin(), *getenv(); /* getenv added by rig */
  FILE *mailfp, *msgfp;
  char filename[50], msgfilename[50];
  int lastsize = 0, i, j, c, k=0, retval, ppid;
  long curposn;
  time_t clock;
  struct tm *tm;
  int day, month, hour, minute, fileflag = 0;

/*
  if ((namep = getlogin()) == NULL) {
    fprintf(stderr,"can't get login name.\n"); exit(1);
  }
 */

  if ((namep = (char *)getenv("USER")) == NULL) {
    fprintf(stderr,"can't get login name.\n");
    exit(1);
  }
  ppid = getppid();
  /* handle the arguments */
  for(i=1; i < argc; i = i + 1) {
    if (strcmp(argv[i],"-f") == SAME) {
      doingtime = 1;
      if (argc < i+2 || (sscanf(argv[i+1], "%s",msgfilename) < 1)) {
	printf("need file name for '-f' option.\n"); exit(1); }
      ++i;
    } else if (strcmp(argv[i],"-l") == SAME) {
      if (argc < i+2 || (sscanf(argv[i+1], "%d",&maxlines) < 1)) {
	printf("need file number of lines for '-l' option.\n"); exit(1); }
      maxlines += 3;
      ++i;
    } else if (strcmp(argv[i],"-t") == SAME) {
      tolooklength = 3;
    } else if (strcmp(argv[i],"-m") == SAME) {
      if (argc < i+2 || (sscanf(argv[i+1], "%d",&mail_bell_mag) < 1)) {
	printf("need number between 0 and 100 for '-m' option.\n"); exit(1); }
      ++i;
    } else if (strcmp(argv[i],"-x") == SAME) {
      if (argc < i+2 || (sscanf(argv[i+1], "%d",&xval) < 1)) {
	printf("need x coordinate for '-x' option.\n"); exit(1); }
      ++i;  
    } else if (strcmp(argv[i],"-y") == SAME) {
      if (argc < i+2 || (sscanf(argv[i+1], "%d",&yval) < 1)) {
	printf("need y coordinate for '-y' option.\n"); exit(1); }
      ++i;
    } else if (strcmp(argv[i],"-d") == SAME) {
      if (argc < i+2 || (sscanf(argv[i+1], "%d",&delay) < 1)) {
	printf("need delay amount for '-d' option.\n"); exit(1); }
      ++i;
    }  else if (strcmp(argv[i],"-rv") == SAME) {
      rvflag = 1;
    }
  }

  temp = getenv("MAILDIR");  /* this line up to next blank line added by rig */
  if (temp != NULL) maildir = temp;
  if (fopen(maildir,"r") == NULL) {
    fprintf(stderr,"xnewbiff: can't open mail dir %s\n",maildir); exit(1);
  }

  sprintf(filename,"%s/%s",maildir, namep);

  stat(filename, &buf);
  lastsize = buf.st_size;
  initvals();
  initwin();
  while(1) {
    if (stat(filename, &buf) == -1) /* no mailbox */
      buf.st_size = 0;
    if (buf.st_size > lastsize) { /* got something */
      if ((mailfp = fopen(filename,"r")) == NULL) { 
	fprintf(stderr,"major bug, can't open mailbox\n"); exit(1);
      }
      fseek(mailfp,lastsize,0);    /* start at last point */
      get_lines(mailfp, maxlines, tolookfor, tolooklength, 10, 0);
      fclose(mailfp);
      stx = 5; sty = 11;
      winstx = xval - width/2; winsty = yval;
      bell_mag = mail_bell_mag;
      manage_win(1);
      lastsize = buf.st_size;

/* check the timed messages.  If we've passed the time for any message, 
 * then put the message on the screen.  Only does one line.
 */
    } else if ((k == CHECKDELAY-1) && doingtime) {
      k = 0;
      if ((msgfp = fopen(msgfilename,"r+")) != NULL) {    /* got the file */

	/* get the real time into tm */
	clock = (time_t) time(0);
	tm = localtime(&clock);

	/* File format is: mm/dd hour:min \n <message> \n \n */
	curposn = ftell(msgfp);
	while ((retval = 
	    fscanf(msgfp,"%d/%d%d:%d",&month, &day, &hour, &minute)) != EOF) {
	  if (retval < 4) {
	    fseek(msgfp, curposn, 0); /* get exactly one line */
	    fgets(testln,100,msgfp); 
/*	    getc(msgfp); /* get the newline */  /* line commented out by rig */
	    curposn = ftell(msgfp);
	    continue;
	  }
	  
	  /* It's time to do the message if real time > time stamp.
	     Also, change the file so you won't do it again.   */
	  if (good_time(tm->tm_mon+1,tm->tm_mday, tm->tm_hour, tm->tm_min,
			month, day, hour, minute)) {

	    /* read the message and put it on the screen */
	    fseek(msgfp,curposn,0);  /* go to curposn, to put date on as well*/
	    get_lines(msgfp,2,nullist,2,20, 10);
	    winstx = (int) 512 - width/2; winsty = 400;
	    bell_mag = 75;
	    stx = 10; sty = 18;
	    
	    /* remove the message by putting a "**" in front of it */
	    fseek(msgfp, curposn, 0);  /* go to curposn */
	    fprintf(msgfp,"**");
	    
	    manage_win(1);
	    break;  /* only do one per cycle */
	  }
	}
	/* EOF reached */
	fclose(msgfp);
      } else {
	printf("can't access your message file.\n");
	doingtime = 0;
      }
    } else if (doingtime) ++k;
    if (buf.st_size < lastsize)  /* guy has read his mail */
      kill_win();
    lastsize = buf.st_size;
    if (window_is_up_flag) manage_win(0);
    else sleep(delay);
    if (getppid() != ppid) exit(0);  /* guy has logged out */
  }
}

/* Get lines. "to_get_length" (length of "to_get_list") used as the small 
   number.  Looks from start to get as much of the "to_get_list"
   as possible, then goes from the last one gotten to get the rest.

   The idea here is, for instance, if there's no subject line, you still
   want to get some of the message.
   The way mail works is there's a "To:" line, then an optional "Subject:" 
   line, then a blank line, and then the rest of the message.

   Sets values of numlines, width, maxwidth, etc. 
*/
get_lines(fp, maxnum, to_get_list, to_get_length, woffset, hoffset)
     int maxnum, to_get_length, woffset, hoffset;
     FILE *fp;
     char **to_get_list;
{
  int i,j, result, c;
  long curposn;
  curposn = ftell(fp);
  numlines = to_get_length;
  width = 0;
  for(i=0; i < to_get_length; ++i) { /*get into line array */
    while ((result = (int) fgets(testln,100,fp)) &&
	   strncmp(testln,to_get_list[i],strlen(to_get_list[i])) != SAME);
    if (!result) {  /* go back to top */
      fseek(fp, curposn,0);
      --numlines;
    } else {
      copy_and_kill_tabs(line[i],testln,100);
      curposn = ftell(fp);
      width = max(width, strlen(line[i]));
    }
  }
  fseek(fp, curposn, 0);
  maxwidth = width;
  totlines = numlines;
  for(i=numlines; (i < maxnum) && fgets(testln,100,fp); ++i) {
    copy_and_kill_tabs(line[i], testln,100);
    ++totlines;
    maxwidth = max(maxwidth, strlen(line[i]));
  }
  height = 15*numlines+hoffset; maxheight = 15*totlines+hoffset;
  width = width*8+woffset; maxwidth = maxwidth*8+woffset;
}

/* copies one to the other, replacing tabs with 8 spaces */
copy_and_kill_tabs(toarr,fromarr,maxlen)
     char *toarr, *fromarr;
     int maxlen;
{
  int i, j=0,k, frlen;
  frlen = strlen(fromarr);
  for(i=0; i<frlen && j < maxlen-1; ++i) {
    if (fromarr[i] == '	')
      for(k=j; j < k+8; ++j) toarr[j] = ' ';
    else
      { toarr[j] = fromarr[i]; ++j; }
  }
  toarr[j] = '\0';
}


/* this puts up the window, puts the stuff into it and watches for events */
manage_win(start)
     int start;
{
  int i,j;
  if (start == 1) {
    XSetWindowBackground(d,w,bg);
    XSetWindowBorderWidth(d,w,1);
    XRaiseWindow(d,w);
    XMoveWindow(d, w, winstx, winsty);
    XClearWindow(d,w);
    write_to_win(width,height,numlines,1);
    XBell(d,bell_mag);
    window_is_up_flag = 1;
    XFlush(d);
  } else {
    for(j=0; j<delay*10; ++j) {
      usleep(100000);
      if (XPending(d) > 0) {   
	XNextEvent(d,rep);
	if ((rep->type == Expose)) {  /* sometimes will go when we're at
					 button 2 */
	  write_to_win(width,height,numlines,0);
	}
	else if (rep->type == ButtonPress) {
	  if (rep->xbutton.button == Button2) {
	    /* print out all the lines */
	    XRaiseWindow(d,w);  
	    write_to_win(maxwidth, maxheight,totlines,1);
	    XFlush(d);
	  } else {   /* button 1 or 3 */
	    kill_win();
	    break;
	  }
	} else if (rep->type == ButtonRelease) { 
	    /* button released, so restore old window */
	    write_to_win(width,height,numlines,1);
	  }
      }
    }
  }
}

kill_win()
{
  XSetWindowBackground(d,w,BlackPixel(d, screen));
  XMoveResizeWindow(d,w,1023,863,1,1);
  XSetWindowBorderWidth(d,w,0);
  window_is_up_flag = 0;
  XFlush(d);
}

write_to_win(wid,ht,nlines, resizep)
     int wid,ht,nlines, resizep;
{
  int i;
  if (resizep) XResizeWindow(d, w, wid, ht);
  for(i = 0; i < nlines; ++i)
       XDrawString(d,w,gc, stx, sty + 14*i, line[i], strlen(line[i]) - 1);
}

initvals()
{
  int i;
  line = (char **) malloc(maxlines*sizeof(char *));
  for(i=0; i<maxlines; ++i) line[i] = (char *) calloc(100,sizeof(char));
  tolookfor[0] = "From:";
  if (tolooklength == 2)
    tolookfor[1] = "Subject:";
  else {
    tolookfor[1] = "To:"; tolookfor[2] = "Subject:";
  }
}

initwin()
{
  rep = (XEvent *) malloc(sizeof(XEvent));

  if ((d = XOpenDisplay(DEFAULT_DISPLAY)) == NULL) {
    printf("can't open display.\n"); exit(1);
  }
  screen = DefaultScreen(d);
  if (!rvflag) { bg = WhitePixel(d,screen); fg = BlackPixel(d,screen);} 
  else         { fg = WhitePixel(d,screen); bg = BlackPixel(d,screen);}
  
  xswa.override_redirect = True;  /* don't let the guy move the window */
  w = XCreateSimpleWindow(d,DefaultRootWindow(d), 1023, 863, 1, 1,0,
		    fg,fg);
  XChangeWindowAttributes(d,w,CWOverrideRedirect, &xswa);
  XMapWindow(d,w);
  gc = DefaultGC(d, screen);
  XSetForeground(d, gc, fg);
  XSetFont(d,gc, XLoadFont(d,"8x13"));
  XSelectInput(d, w, ButtonPressMask | ButtonReleaseMask | ExposureMask);
}

/* Will print out a message if its time has passed, and at most 2 months ago.
   Ten months ahead is interpreted as two months ago.
   */
good_time(mon,day,hour,min,oldmon,oldday,oldhour,oldmin)
     int oldmon,oldday,oldhour,oldmin,mon,day,hour,min;
{
  if (oldmon > mon + 9) oldmon = oldmon - 12;
  if (((oldmon < mon) && (oldmon > mon - 3)) || 
      ((oldmon == mon) && 
       ((oldday < day) || 
	((oldday == day) &&
	 ((oldhour < hour) || 
	  (oldhour == hour) && (oldmin <= min))))))
    return(1);
  return(0);
}
