/***************************************************************************
                          main.c  -  description
                             -------------------
    begin                : Sat Jan 17 14:41:50 CST 2004
    copyright            : (C) 2004 by Eric Bambach
    email                : eric@cisu.net
 ***************************************************************************/ 
  
  
/***************************************************************************
 *                                                                         
 *   This program is free software; you can redistribute it and/or modify  
 *   it under the terms of the GNU General Public License as published by  
 *   the Free Software Foundation; either version 2 of the License, or     
 *   (at your option) any later version.                                   
 *                         
 ***************************************************************************/ 
  
  /*
     Compile this program like this
     gcc -O2 -o mailparser mailparser.c
   */ 
  
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
  
#define EXIT_NOMATCH 0
#define EXIT_MATCH 1
  
//Should be large enough to scan most mail messages in one or two passes.
//Performance with 4K buffer is .8s for a 73M message.
//Increasing to 32K only trims it to .6s with an unjustified increase in memory use.
//DO NOT SET THIS TOO SMALL. It probably won't catch the spam flag then since it only scans the
//first BUFFERSIZE characters read in then fast copies the rest to stdout.
#define BUFFERSIZE 6144
  
//What we are checking for. must be EXACT. Leave the newline in because it protects the offchance that it is in the message body somewhere.
//This way it will only match if its at the beginning of the line.
#define CHECKSTRING "\nX-Spam-Flag: YES"

//Prototypes
int main (void);
int dump_full_message ();
int write_message (char *buffer, int len, int fd);
int read_message (char *buffer);
int scan_message (char *buffer, int len);

int main (void)
{
  
  //Our faithful buffer
  char buffer[BUFFERSIZE];
  
  //What file should we write to if its spam.
  const char *spampipe = "/dev/null";
  int len, fd = STDOUT_FILENO, exit_status = EXIT_NOMATCH;
  
  //We only want to scan our message once. Its unlikely there are more than 4K(BUFFERSIZE)
  //of headers. By scanning once, this lets us trash the rest of the output if its spam. Also
  //prevents scanning a huge non-matching mail-message-attachment.
  len = read_message (buffer);
  if (len) {
      if (scan_message (buffer, len) == EXIT_MATCH) {
	  if ((fd = open (spampipe, O_WRONLY)) == -1) {
	      perror ("Cannot open spam pipe...will write to stdout");
	      fd = STDOUT_FILENO;
	  }
	  exit_status = EXIT_MATCH;
      }
  }
  write_message (buffer, len, fd);
  //Tight read/write for just piping the data. After the first BUFFERSIZE characters
  //we should already have what we need and just pass it on in the queue.
  do {
        len = read_message (buffer);
        if (len){
	  write_message (buffer, len, fd);
	}
  }
  while (len > 0);
  close (fd);
  return exit_status;
}
int scan_message (char *buffer, int len)
{
  char *spamptr, *bufptr;
  int count = 0;
  char *spamflag = CHECKSTRING;
  spamptr = spamflag;
  bufptr = buffer;
  for (count = 0; count < len; count++, bufptr++) {
      
      //Test it
      if (*bufptr != *spamptr)
	spamptr = spamflag;
      if (*bufptr == *spamptr)
	spamptr++;
      
	//We've hit a match
	if (*spamptr == '\0')
	{
	  return EXIT_MATCH;
	}
    }
  return EXIT_NOMATCH;
}
int read_message (char *buffer)
{
  int len;
  len = read (STDIN_FILENO, buffer, BUFFERSIZE - 1);
  if (len < 0){
      perror ("Read Error");
      exit (EXIT_NOMATCH);
  }
  return len;
}

//Works almost like write() except checks for errors.
int write_message (char *buffer, int len, int fd)
{
  int ret;
  ret = write (fd, buffer, len);
  if (ret < 0){
      perror ("Write Error");
      exit (EXIT_NOMATCH);
  }
  return ret;
}
