/*
 * Reaper for POP files. Goes through and strips out mail
 * where the recipient isn't named explicitly. 
 * This fails for Bcc's.
 *
 *
 * Usage: grim <file1> <file2>...
 *
 * When invoked as gstat, it will print mail drop stats.
 *
 * Tom Coppeto
 * 1/16/90
 *
 * $Source: /afs/.net.mit.edu/tools/src/grim/RCS/popreap.c,v $
 * $Author: tom $
 * $Log:	popreap.c,v $
 * Revision 1.7  93/06/15  00:09:30  tom
 * don't exit for silly error if processing whole directory
 * 
 * Revision 1.6  93/01/01  14:09:40  nocuser
 * loop through /usr/spool/pop if no args given
 * 
 * Revision 1.5  91/06/07  16:46:30  tom
 * fixed date bug
 * 
 * Revision 1.4  91/06/07  15:36:54  tom
 * fixed arg 0 bug
 * 
 * Revision 1.3  91/06/07  13:55:08  tom
 * added gstat option
 * 
 * Revision 1.2  90/01/16  15:56:10  tjcoppet
 * hello
 * 
 */

#ifndef lint
static char rcsid[]="$Header: /afs/.net.mit.edu/tools/src/grim/RCS/popreap.c,v 1.7 93/06/15 00:09:30 tom Exp Locker: tom $";
#endif


#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/dir.h>
#include <netdb.h>
#include <teatime.h>

/*
 * Each Mail file is separted by this
 */

#define POP_SEP "\001\001\001\001"
#define SOH '\001'

char *tformat = "%2nmon/%02mday/%year";
char *ourhost = (char *) NULL;
char *phrase = (char *) NULL;
char *dir = "/usr/spool/pop";
char *prog;

char hostname[HLEN];

struct gstat
{
  unsigned long l1;             /* time of first bulk message */
  unsigned long l2;             /* time of last bulk message */    
  unsigned long p1;             /* time of first personal message */
  unsigned long p2;             /* time of last personal message */  
  unsigned long t;              /* time of last message */
  unsigned int  s;              /* size of last message */
  unsigned int  nlist;          /* number of bulk messages */
  unsigned int  npersonal;      /* number of personal messages */
  unsigned int  slist;          /* total size of bulk mail */
  unsigned int  spersonal;      /* total size of personal mail */
};

  
/*
 * mango is true when inside a header
 */
 
int mango = 0;
int report = 0;
int eof = 0;

void reap();
void copy_message();
char *get_recipient();
char *get_next_word();


main(argc, argv)
     int argc;
     char *argv[];
{
  DIR *dirp;
  struct direct *dp;
  struct hostent *hp;
  char *c;
  int i;

  if(!(prog = (char *) rindex(argv[0], '/')))
    prog = argv[0];
  else
    ++prog;

  ++argv;

  while(*argv[0] == '-')
    {
      if(strncmp(*argv, "-h") == 0)
	{
	  if(*++argv)
	    ourhost = *argv;
	  else
	    {
	      fprintf(stderr, "%s: missing hostname after -h.\n", prog);
	      exit(1);
	    }
	  argc -= 2;
	}
      
      
      if(strncmp(*argv, "-d") == 0)
	{
	  if(*++argv)
	    dir = *argv;
	  else
	    {
	      fprintf(stderr, "%s: missing directory after -d.\n", prog);
	      exit(1);
	    }
	  argc -= 2;
	}
      
      if(strncmp(*argv, "-p") == 0)
	{
	  if(*++argv)
	    phrase = *argv;
	  else
	    {
	      fprintf(stderr, "%s: missing phrase after -p.\n", prog);
	      exit(1);
	    }
	  argc -= 2;
	}
    }
      

  if(!strcmp(prog, "gstat"))
    {
      report = 1;
      printf("             drop size      number messages              time stamps\n");
      printf("drop       total    bulk      total  bulk     first       first bulk  last\n");
    }

  if(report && (argc == 1))
    {
      dirp = opendir("/usr/spool/pop");
      for (dp = readdir(dirp); dp; dp = readdir(dirp))
	{
	  if((c = (char *) rindex(dp->d_name, '.')) && !strncmp(c+1, "map", 3))
	    continue;
	  reap(dp->d_name);
	}
      closedir(dirp); 
      exit(0);
    }

  while(*argv++)
    reap(*argv);

  exit(0);
}



/*
 * Function:    reap()
 * Description: Loops through each message in the file
 */

void
reap(file)
     char *file;
{
  FILE *fp;
  FILE *fsave;
  FILE *fdel;
  int scount = 0;
  int dcount = 0;
  struct gstat gs;
  struct tm ts, *tm;
  unsigned long t;
  char file[BUFSIZ];

  if(!(fp = fopen(file, "r")))
    {
      perror(prog);
      fprintf(stderr, "%s: unable to open file %s.\n", prog, file);
      return;
    }
  
  if(!report)
    {
      (void) sprintf(buf, "%s.save", file);
      if(!(fsave = fopen(file, "a")))
	{
	  perror(prog);
	  fprintf(stderr, "%s: unable to open save file %s.\n", prog, file);
	  return;
	}
      
      (void) sprintf(buf, "%s.del", file);
      if(!(fdel = fopen(file, "a")))
	{
	  perror(prog);
	  fprintf(stderr, "%s: unable to open save file %s.\n", prog, file);
	  return;
	}
    }

  eof = 0;

  bzero(&gs, sizeof(gs));
  while(get_next_message(fp))
    {      
      if(!stat_message(fp, file, &gs))
	 {
	   if(!report)
	     copy_message(fp, fsave);
	  ++scount;
	}
      else
	{
	  if(!report)
	    copy_message(fp, fdel);
	  ++dcount;
	}

      if(report)
	get_next_message(fp);

      if(eof)
	break;
    }

  if(report)
    {
      printf("%-10s %-8d %-8d  %-6d %-6d   ", 
	     file, gs.slist + gs.spersonal, gs.slist, gs.nlist + gs.npersonal,
	     gs.nlist);
      if(gs.l1 && (gs.l1 < gs.p1))
	t = gs.l1;
      else
	t = gs.p1;

      if(t)
	{
	  tm = localtime(&t);
	  ts = *tm;
	  printf("%s    ", tt_format(&ts, tformat));
	}
      else
	printf("****        ");

      if(gs.l1)
	{
	  tm = localtime(&gs.l1);
	  ts = *tm;
	  printf("%s    ", tt_format(&ts, tformat));
	}
      else
	printf("****        ");

      if(gs.t)
	{
	  tm = localtime(&gs.t);
	  ts = *tm;
	  printf("%s\n", tt_format(&ts, tformat));
	}
      else
	printf("**** \n");
    }
  else
    printf("%s: deleted %d out of %d in %s.\n", prog, dcount, dcount + scount,
	   file);

  (void) fclose(fp);
  if(!report)
    {
      if(fclose(fsave) != 0)
	fprintf(stderr, "%s: error closing %s.save!\n", prog, file);
      if(fclose(fdel) != 0)
	fprintf(stderr, "%s: error closing %s.del.\n", prog, file);
    }
  
  return;
}



/*
 * Function:    get_next_message()
 * Decsription: Find the start of next message by looking for POP_SEP
 * Returns:     1: if there is a next message
 *              0: EOF
 */

int
get_next_message(fp)
     FILE *fp;
{
  char buf[BUFSIZ];
  char c;

  while(fgets(buf, BUFSIZ-1, fp) != (char *) NULL)
    if(!strncmp(buf, POP_SEP, 4))
      return(1);
  
  return(0);
}


            

/*
 * Function:    copy_message()
 * Description: Copy the current message into the named file
 */

void
copy_message(from, to)
     FILE *from;
     FILE *to;
{
  char buf[BUFSIZ];
  char *cc;

  fprintf(to,"%4s\n", POP_SEP);
  while((cc = fgets(buf, BUFSIZ-1, from)) != (char *) NULL)
    {
      if(!strncmp(buf, POP_SEP, 4))
	{
	  fprintf(to,"%4s\n", POP_SEP);
	  break;
	}
      else
	fprintf(to, "%s", buf);
    }

  fflush(to);

  if(!cc)
    eof = 1;

  return;
}




/*
 * Function:    indirect_message()
 * Description: Decides if message was not sent directly to the user.
 * Returns:     0: message was sent directly
 *              1: otherwise
 */

int
stat_message(fp, file, g)
     FILE *fp;
     char *file;
     struct gstat *g;
{
  int pos;
  char *recipient;
  int status = 1;

  if((pos = ftell(fp)) < 0)
    {
      perror(prog);
      exit(1);
    }

  mango = 0;

  while((recipient = get_recipient(fp, g)))
    if(lookup(file, recipient))
      {
	status = 0;
	break;
      }

  if(status)
    {
      g->nlist++;
      g->slist += g->s;
      g->l2 = g->t;
    }
  else
    {
      g->npersonal++;
      g->spersonal += g->s;
      g->p2 = g->t;
    }

  if(g->l1 == 0)
    g->l1 = g->l2;

  if(g->p1 == 0)
    g->p1 = g->p2;

  g->s = 0;

  if(fseek(fp, pos, 0) < 0)
    {
      perror(prog);
      exit(1);
    }

  return(status);
}



/* 
 * Function:    get_recpient()
 * Description: Consecutively return each member of the To: of Cc:
 *              field. 
 * Returns:     Pointer to name, NULL if no more names.
 */

char *
get_recipient(fp, g)
     FILE *fp;
     struct gstat *g;
{
  char name[BUFSIZ], *np = name;
  static char *lptr = (char *) NULL;
  static char line[BUFSIZ];
  char *cc;
  register char *cp;
  struct tm tm;
  
  if(lptr)
    {
      lptr = get_next_word(lptr, np);
      if(np)
	return(np);
    }

  while(cc = fgets(line, BUFSIZ-1, fp))
    {
      cp = line;
      while(*cp++ != '\n')
	g->s++;
      g->s++;

      if(!strncmp(line, POP_SEP, 4))
	return((char *) NULL);
	
      if(!strncmp(line, "Received: ", 10))
	{
	  cp = index(line, " ");    /* Received: */
	  if(cp)
	    cp = index(line, " ");  /* from */
	  if(cp) 
	    cp = index(line, " ");  /* mailer */
	  if(cp)
	    cp = index(line, " ");  /* by */
	  
	  if(cp)
	    if(!strncasecmp(cp, ourhost, strlen(ourhost)))
	      {
		if(cp)
		  cp = index(line, ";");  /* end of ID */
		if(cp)
		  {
		    tt_partime(++cp, &tm);
		    g->t = tt_tmtoseconds(&tm);
		    continue;
		  }
	      }
	}

#ifdef notdef

      if(!strncmp(line, "Date:", 5) && !g->t)
	{
	  tt_partime(&line[6], &tm);
	  g->t = tt_tmtoseconds(&tm);
	  continue;
	}
#endif

      if(!strncmp(line, "To:", 3) || !strncmp(line, "Cc:", 3) ||
	 (!strncmp(line, "Subject:", 3)))
	{
	  lptr = &line[0];
	  lptr = get_next_word(lptr+3, np);
	  mango = 1;
	  return(np);
	}

      /*
       * deal with multiple lines (use mango flag).
       */

      if(mango && ((*line == ' ') || (*line == '\t')))
	{
	  lptr = &line[0];
	  lptr = get_next_word(lptr+3, np);
	  return(np);
	}

      if(mango && whitespace(line))
	{
	  mango = 0;
	  return((char*) NULL);
	}
    }

  if(!cc)
    {
      fprintf(stderr, "%s: premature EOF in file.\n", prog);
      eof = 1;
      if(!report)
	exit(1);
    }

  return((char *) NULL);
}



/*
 * Function:    lookup()
 * Description: Decide if the given name is the same as the name of the
 *              list.
 */

int 
lookup(name, list)
     char *name;
     char *list;
{
  if(!strncmp(name, list, strlen(name)))
    return(1);

  return(0);
}



/*
 * Function:    get_next_word()
 * Description: Get next word in line.
 * Returns:     Pointer to resultant line.
 */

char *
get_next_word(line, buf)
     char *line;
     char *buf;
{
  char c;                 
  
  if(!line)
    return(line);

  while ((c = *line) == ' ' || c == '\t' || c == ',')
    line++;

  if (c == '\0' || c == '\n') 
    {
      *buf = '\0';
      return((char *) NULL);
    }
    
  while ((c = *line) != ' ' && c != '\t' && c != '\n' && c != '\0' && c!= ',') 
    {
      line++;
      *buf++ = c;
    }
  
  *buf = '\0';
  return(line);
}



/*
 * Function:    whitespace()
 * Description: Decide if there is anything on the line.
 */

int
whitespace(line)
     char *line;
{
  char *cc;

  for(cc = line; cc != '\0'; cc++)
    if((*cc != '\t') && (*cc != ' ') && (*cc != '\n'))
      return(0);

  return(1);
}
