/*
    Reply-o-Matic - Automatic message replying system
    Copyright (C) 2006 - Rodrigo Barbosa <rodrigob@darkover.org>

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// #define DEBUG 1

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <pwd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/syslog.h>
#include <libgen.h>
#include <string.h>
#include <sys/file.h>

#include "rom.h"

/* Globals */
char usedflags[256];
int pfd[2], dfd[2], cpfd[2], cdfd[2];

/* Paranoid mode */
paranoid_t paranoid;

/* Rate limiting */
char ratefile[256];
unsigned long int replyrate = 0;
char needrateopen = 0;
FILE *ratefd = NULL;
char ratefn[256];
mode_t oldmask;
/**/

/* Ignores */
char igfile[256];
FILE *igfd = NULL;
char ighomefile[256];

char dochroot = 0, didchroot = 0;

FILE *logfd = NULL;

#define addflag(newflag) strncat(usedflags,newflag,255)

/* Taken from mutt */
#define SKIPWS(x) while(isspace(*x))x++
#define NONULL(x) x?x:""
/**/

/* We need this so we can be sure the children will exit correctly */
void diedie ( unsigned int exsig ) {
	if ( cpfd[1] > 2 ) 
		write(cpfd[1],"X",1);
	if ( cdfd[1] > 2 )
		write(cdfd[1],"X",1);
	if ( igfd )
		fclose(igfd);
#ifdef DEBUG
	if ( logfd )
		fclose(logfd);
#endif
	umask(oldmask);
	closelog();
	exit(exsig);
}

void p_log(char *fmt, ...)
{
        va_list vaa;
        char logmessage[2049];
	time_t tdata;

	va_start(vaa,fmt);
	vsprintf(logmessage,fmt,vaa);
	syslog(LOG_INFO,"%s",logmessage);
	tdata=time(NULL);
#ifdef DEBUG
	if ( logfd )
		fprintf(logfd,"%s [%d]: %s\n",ctime(&tdata),getpid(),logmessage);
#endif
	va_end(vaa);
}

void p_err(char *fmt, ...)
{
        va_list vaa;
        char logmessage[2049];
	time_t tdata;

        va_start(vaa,fmt);
        vsprintf(logmessage,fmt,vaa);
        syslog(LOG_ERR,"%s",logmessage);
	tdata=time(NULL);
#ifdef DEBUG
	if ( logfd )
		fprintf(logfd,"%s [%d]: %s\n",ctime(&tdata),getpid(),logmessage);
#endif
        va_end(vaa);
}

/* Functions taken from mutt, and adapted where (and when) needed */

int ascii_isupper (int c)
{
  return (c >= 'A') && (c <= 'Z');
}

int ascii_islower (int c)
{
  return (c >= 'a') && (c <= 'z');
}

int ascii_toupper (int c)
{
  if (ascii_islower (c))
    return c & ~32;

  return c;
}

int ascii_tolower (int c)
{
  if (ascii_isupper (c))
    return c | 32;

  return c;
}

int ascii_strcasecmp (const char *a, const char *b)
{
  int i;
  
  if (a == b)
    return 0;
  if (a == NULL && b)
    return -1;
  if (b == NULL && a)
    return 1;
  
  for (; *a || *b; a++, b++)
  {
    if ((i = ascii_tolower (*a) - ascii_tolower (*b)))
      return i;
  }

  return 0;
}

int mutt_strcasecmp(const char *a, const char *b)
{
  return strcasecmp(NONULL(a), NONULL(b));
}

size_t mutt_strlen(const char *a)
{
	  return a ? strlen (a) : 0;
}

char *
mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
{
  size_t len;

  len = end - beg;
  if (len > destlen - 1) 
    len = destlen - 1;
  memcpy (dest, beg, len);
  dest[len] = 0;
  return dest;
} 

/**/

/* Mostly copied from mutt */
void getmimetype (const char *path, char *mtype, size_t mtlen)
{
  FILE *f;
  char *p, *q, *ct;
  char buf[2049];
  char subtype[256], xtype[256];
  int count;
  int szf, sze, cur_sze;
  int type;

  *subtype = '\0';
  *xtype   = '\0';
  type     = 0;
  cur_sze  = 0;

  szf      = mutt_strlen (path);

  for (count = 0 ; count < 3 ; count++)
  {
    /*
     * can't use strtok() because we use it in an inner loop below, so use
     * a switch statement here instead.
     */
    switch (count)
    {
      case 0:
        snprintf (buf, sizeof (buf), ROMDIR"/mime.types");
        break;
      case 1:
        strncpy (buf, "/etc/mime.types", sizeof(buf));
        break;
      case 2:
	strncpy (buf, "/mime.types", sizeof(buf));
	break;
      default:
        goto bye;       /* shouldn't happen */
    }

    if ((f = fopen (buf, "r")) != NULL)
    {
      while (fgets (buf, sizeof (buf) - 1, f) != NULL)
      {
        /* weed out any comments */
        if ((p = (char *) strchr (buf, '#')))
          *p = 0;

        /* remove any leading space. */
        ct = buf;
        SKIPWS (ct);

        /* position on the next field in this line */
        if ((p = (char *) strpbrk (ct, " \t")) == NULL)
          continue;
        *p++ = 0;
        SKIPWS (p);

        /* cycle through the file extensions */
	while ((p = (char *) strtok (p, " \t\n")))
        {
          sze = mutt_strlen (p);
          if ((sze > cur_sze) && (szf >= sze) &&
              (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
              (szf == sze || path[szf - sze - 1] == '.'))
          {
            /* get the content-type */

            if ((p = (char *) strchr (ct, '/')) == NULL)
            {
              /* malformed line, just skip it. */
              break;
            }
            *p++ = 0;

            for (q = p; *q && !isspace (*q); q++)
              ;

            mutt_substrcpy (subtype, p, q, sizeof (subtype));

            strncpy (xtype, ct, sizeof (xtype));

            cur_sze = sze;
          }
          p = NULL;
        }
      }
      fclose (f);
    }
  }

bye:

  if (*xtype != '\0')
  {
	  snprintf(mtype,mtlen,"%s/%s",xtype,subtype);
  }
}


/* Used to create the MIME delimiter */
int getrandom ( int num, char *randout ) {
	int rfd;
	struct stat rstatbuf;
	char rchar;
	int rcount = 0;

	rfd=open(RANDOM_DEV,O_RDONLY);
	if ( !rfd ) {
		p_err("Unable to open %s.",RANDOM_DEV);
		close(rfd);
		return(0);
	}
	fstat(rfd,&rstatbuf);
	if ( ! S_ISCHR(rstatbuf.st_mode) ) {
		p_err("%s is no a character device. No random value will be gathered",RANDOM_DEV);
		close(rfd);
		return(0);
	}
	randout[num]=0;
	while ( rcount < num ) {
		if ( read(rfd,&rchar,1) < 1 ) {
			randout[rcount+1]=0;
			close(rfd);
			return(rcount+1);
		}
		if ( isalnum(rchar) || ( rchar == '+' ) || ( rchar == '_' ) ) {
			randout[rcount]=rchar;
			rcount++;
		}
	}
	close(rfd);
	return(rcount);
}


/* My strcasecmp function */
int rom_strcasecmp ( char * str1, char * str2 ) {
	if ( strlen(str1) > strlen(str2) )
		return(1);
	if ( strlen(str2) > strlen(str1) )
		return(-1);
	return(strcasecmp(str1,str2));
}

void help ( void ) {
	printf( \
"Reply-o-Matic v%s\n" \
"Copyright 2006 - Rodrigo Barbosa <rodrigob@darkover.org\n" \
"Reply-o-Matic cames with ABSOLUTELY NO WARRANTY\n" \
"This is free software, and licensed under the terms of the\n" \
"GNU General Public License v2\n" \
"\n" \
"Usage: rom [ -h[n] ] [ -f <from> ] [ -b <message body file> ]\n" \
"           [ -s <message subject>  || -S ] [ -r <reply-to> ] [ -v ]\n" \
"           [ -c <copy response to> ] [ -d <deliver-to> ]\n" \
"           [ -a <file to attach> ] [ -t <attachment mime type > ]\n" \
"           [ -C [<chroot dir>] [ -u <uid> ] [ -g <gid> ]\n"\
"           [ -U <uid> ] [ -G <gid> ] [ -R [<hours>]\n" \
"\n" \
"Parameters:\n" \
"\n" \
"       -h[n]     If n is 0, nothing in included on the reply\n" \
"                 If n is 1, the headers of the original message are included\n" \
"                    inlined on the reply message (default)\n" \
"                 If n is 2, the reply message goes as a mime multipart message\n" \
"                    with the full original message attached\n" \
"\n" \
"       -f        Set the From: field of the reply message\n" \
"       -b        A file that contains the text to be included on\n" \
"                 the reply message (MUST BE A REGULAR FILE!)\n" \
"       -s        Set the Subject: of the reply message\n" \
"       -S        Set the Subject line as a reply (RE: ) of the original one\n" \
"	(NOTE: -S taked precedence over -s. When both are set, the string in\n" \
"              -s is only used if there is not subject line on the original\n" \
"              message\n" \
"       -r        Set the Reply-To: field of the reply message\n" \
"       -v        Show this page\n" \
"       -c        Send a copy (blind carbon copy) the reply message\n" \
"                 to this address too\n" \
"       -d        Deliver the original message to this e-mail address\n" \
"                 (useful when you want an autoreply, but still need to\n" \
"                  get the original message)\n" \
"       -a        Attach a file on the reply. The file will be send as a \n" \
"                 MIME attachment, trying to guess the correct mimetype by\n" \
"                 the file extension. See manpage for more details.\n" \
"       -t        Specify the Content-Type of the attachment. No effect if\n" \
"                 used without the -a parameter\n" \
"       -C        Make ROM run in a chrooted environment. Default chroot dir\n" \
"                 is /etc/rom/\n" \
"       -u -g     Make ROM drop it's privileges to the given uid and gid,\n" \
"                 instead of using it's defaults of 65534\n" \
"       -U -G     Make ROM run the MTA/MDA with the given uid and gid,\n" \
"                 instead of not dropping privileges to do it\n"\
"       NOTICE:   -u, -g, -U and -G will only take effect if the calling user\n" \
"                 is root (real uid = 0). Otherwhise, ROM will drop it's\n" \
"                 privileges to the uid and gid of the calling user\n" \
"       -R        Number of hours to wait before replying to the same\n" \
"                 originator address. The given value or 1 hour, is -R\n" \
"                 is used without a value. (Default: don't wait)\n" \
"\n"\
"              SEE THE MANPAGE FOR MORE DETAILS\n"\
"\n"\
"This program is distributed in the hope that it will be useful,\n" \
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" \
"GNU General Public License for more details.\n" \
"\n" \
	      , rom_version);
	diedie(-1);
}

char *getbody ( char *fn ) {
	FILE *fd;
	static char *outbody;
	struct stat statbuf;

	fd=fopen ( (const char *) fn, "r" );
	if ( fd <= 0 ) {
		printf("unable to open %s\n",fn);
		help();
	}
	fstat(fileno(fd),&statbuf);
	if ( !S_ISREG(statbuf.st_mode) ) {
		p_err("Bodyfile must be a regular file\n");
		help();
	}
	outbody=calloc(statbuf.st_size + 1,1);
	fread(outbody,1,statbuf.st_size,fd);
	fclose(fd);
	return (outbody);
}

void raw_to_base64 (unsigned char *, const unsigned char *, size_t, size_t);

/* Read the attachment file, and return it's content */
unsigned char *getattach ( char *fn ) {
	int fd;
	static unsigned char *outbody;
	unsigned char *out64;
	struct stat statbuf;
	size_t olen;
	unsigned filesize;

	fd=open ( (const char *) fn, O_RDONLY );
	if ( fd <= 0 ) {
		p_err("Unable to open attachment\n");
		return(NULL);
	}
	fstat(fd,&statbuf);
	if ( !S_ISREG(statbuf.st_mode) ) {
		p_err("Attachments must be regular files\n");
		return(NULL);
	}
	filesize=statbuf.st_size;
	outbody=calloc(filesize + 1,1);
	read(fd,outbody,filesize);
	close(fd);
	out64=calloc((filesize * 4) + 1,1);
	olen=filesize * 4;
	raw_to_base64(out64,outbody,filesize,olen);
#ifdef DEBUG
	p_log("getattach data: %u %u %u %u\n",strlen(out64),statbuf.st_size,filesize,olen);
#endif
	return(out64);
}

/* Header unfolding (RFC2822) */
int unfold ( char *data, char *newdata, unsigned int datalen ) {
	unsigned long int i;
	unsigned long int ndi = 0;

	memset(newdata,0,datalen);
	for(i=0;i<datalen;i++) {
		if ( i==datalen-1 ) {
			newdata[ndi]=data[i];
			break;
		}
		if ( ( data[i]=='\r' ) &&
		     ( data[i+1]=='\n' ) &&
		     ( ( data[i+2]==' ' ) ||
		       ( data[i+2]=='\t' ) ) )
			i++;
		if ( ( data[i]=='\n' ) &&
		     ( ( data[i+1]==' ') ||
		       ( data[i+1]=='\t' ) ) )
			continue;
		newdata[ndi++]=data[i];
	}
	return(ndi);
}

void getparanoid ( void ) {
	struct stat statbuf;

	memset(&paranoid,0,sizeof(paranoid_t));
	if ( getuid() == 0 )
		return;
	if ( stat(ROMDIR"/paranoid",&statbuf) == 0 ) {
		if ( getuid() != statbuf.st_uid ) {
			paranoid.chroot = statbuf.st_gid & PARANOID_CHROOT ? 1 : 0;
			paranoid.lockuid = statbuf.st_gid & PARANOID_LOCKUID ? 1 : 0;
			paranoid.lockgid = statbuf.st_gid & PARANOID_LOCKGID ? 1 : 0;
			paranoid.noattach = statbuf.st_gid & PARANOID_NOATTACH ? 1 : 0;
			return;
		}
	}
}

int validinclude ( char *filename ) {
	char *bname;
	char *tname;

	tname=strdup(filename);
	bname=strdup(basename(tname));
	free(tname);
	if ( bname[0] == '.' ) {
		p_log("Files included (-a or -b) must not start with a dot: \"%s\"",filename);
		free(bname);
		return(0);
	}
	free(bname);
	return(1);
}

int valid_emailaddress ( char *email ) {
	int i;

	if ( email == NULL )
		return 0;
	if ( email[0] == 0 )
		return 0;
	for (i=0;i<strlen(email);i++) {
		if ( !isalnum(email[i]) &&
		     !strchr("@!#%+=_-",email[i]) )
			return 0;
	}
	return 1;
}

char * extract_addr ( char *xinaddr ) {
        static char * outaddr;
        char *loc = NULL;
        char *inaddr;
        int i;

        outaddr=calloc(strlen(xinaddr),1);
        loc=strchr((const char *) xinaddr,'<');
        if (loc) {
                inaddr=++loc;
        } else
                inaddr=xinaddr;
        for(i=0;i<strlen(inaddr);i++) { 
                if ( ( isblank(inaddr[i]) && strlen(outaddr) ) ||
                     ( inaddr[i] == '>' ) )
                        break;
                if ( isblank(inaddr[i]) )
                        continue;
                outaddr[strlen(outaddr)]=inaddr[i];
        }
        return(outaddr);
}

void split_user_data ( char *uname, char *uaddr, char *data ) {
	int i,field=0;

	uname[0]=uaddr[0]=0;

	for(i=0;i<strlen(data);i++) {
		if ( data[i]=='@' )
			field++;
		if ( !field ) {
			uname[strlen(uname)+1]=0;
			uname[strlen(uname)]=data[i];
		} else {
			uaddr[strlen(uaddr)+1]=0;
			uaddr[strlen(uaddr)]=data[i];
		}
	}
}
			
int check_ignores ( char *userdata ) {
	char data[256];
	char in_user[256];
	char in_addr[256];
	char udata[256];
	char adata[256];
	int field,i;

	split_user_data(in_user,in_addr,userdata);

	if ( !igfd )
		return(0);

	while(!feof(igfd)) {
		memset(data,0,256);
		fgets(data,255,igfd);
		if ( strlen(data) == 0 )
			continue;
		if ( data[strlen(data)-1] == '\n' ) 
			data[strlen(data)-1] = 0;
		if ( data[strlen(data)-1] == '\r' ) 
			data[strlen(data)-1] = 0;
		field=0;
		memset(udata,0,255);
		memset(adata,0,255);
		for (i=0;i<strlen(data);i++) {
			if ( isblank(data[i]))
				continue;
			if ( data[i]=='@' ) {
				field++;
			}
			if ( field == 0 )
				udata[strlen(udata)]=data[i];
			if ( field == 1 )
				adata[strlen(adata)]=data[i];
		}
		if ( !field )
			continue;
		if ( data[0] == '@' ) {
			if ( !rom_strcasecmp(adata,in_addr)) {
				return 1;
			} else {
				continue;
			}
		}
		if ( !rom_strcasecmp(data,userdata) ) {
			return 1;
		}
	}
	return(0);
}

int check_and_update_rate ( char *userdata ) {
	char data[256];
	char udata[256];
	char tm[256];
	int field,i;
	char *ratedata;
	size_t ratelen = 0;
	char found = 1;
	struct flock lock;

	ratedata=calloc(1,1);
	p_log("ratefd = %p, needrateopen = %d\n",ratefd,needrateopen);
	if ( !ratefd && !needrateopen ) {
		if (didchroot) 
			snprintf(ratefn,255,"/.rates/%s",ratefile);
		else
			snprintf(ratefn,255,"%s/.rates/%s",ROMDIR,ratefile);
		ratefd=fopen(ratefn,"a+");
		rewind(ratefd);
		needrateopen=1;
	}
	p_log("ratefd = %p, needrateopen = %d\n",ratefd,needrateopen);
	if ( ratefd && ( needrateopen == 1 )) {
		lock.l_type=(F_RDLCK);
		fcntl(fileno(ratefd),F_SETLKW,&lock);
		while(!feof(ratefd)) {
			memset(data,0,256);
			fgets(data,255,ratefd);
			if ( strlen(data) == 0 )
				continue;
			field=0;
			memset(udata,0,255);
			memset(tm,0,255);
			for (i=0;i<strlen(data);i++) {
				if ( isblank(data[i]) ) {
					field++;
					continue;
				}
				if ( field == 0 )
					udata[strlen(udata)]=data[i];
				if ( field == 1 )
					tm[strlen(tm)]=data[i];
			}
			if ( !rom_strcasecmp(udata,userdata) ) {
				if ( time(NULL) < ( atol(tm) + (3600*replyrate) ) ) {
					found=0;
				} else {
					continue;
				}
			}
			ratelen += strlen(data);
			ratedata=realloc(ratedata,ratelen+1);
			strcat(ratedata,data);
			ratedata[ratelen]=0;

		}
		memset(&lock,0,sizeof(struct flock));
		lock.l_type=(F_RDLCK|F_WRLCK);
		fcntl(fileno(ratefd),F_SETLKW,&lock);
		ftruncate(fileno(ratefd),0L);
		fseek(ratefd,0L,SEEK_END);
	} else {
		if ( needrateopen < 2 )
			ratefd=fopen(ratefn,"w");
	}
	if ( !ratefd ) {
		p_log("Unable to open ratefile (dir does not exist?) \"%s\"",ratefn);
		return(1);
	}
	lock.l_type=(F_RDLCK|F_WRLCK);
	fcntl(fileno(ratefd),F_SETLKW,&lock);
	if ( strlen(ratedata) > 2 )  {
		write(fileno(ratefd),ratedata,strlen(ratedata));
		if ( ratedata[strlen(ratedata)-1] != '\n' ) 
			write(fileno(ratefd),"\n",1);
	}
	if ( found )
		fprintf(ratefd,"%s %lu",userdata, (unsigned long) time(NULL));
	lock.l_type=F_UNLCK;
	fcntl(fileno(ratefd),F_SETLKW,&lock);
	fclose(ratefd);
	return(found);
}

int main ( int argc, char **argv ) {
	char instr[2049];
	int i,fni,fdi;
	char *fname = NULL;
	char *fdata = NULL;
	int isfield;
	char *tmpbuf;
	const char *optstr = "f:h::b:s:r:vd:c:Sa:t:C::u:g:U:G:R::";
	char c;
	char includeorig = 1;
	char rom_from[2049] = default_from;
	char rom_subject[2049] = default_subject;
	char dosubject = 0;
	char *rom_body = NULL;
	char isbody = 0;
	char boundary[256];
	char doattach = 0;
	char attachtype[256];
	char attachpn[2049];
	char dotype = 0;
	char *attachment = NULL;
	char *attachfn = NULL;
	int cpid, dpid = 0;
	char from[2049],sender[2049],replyto[2049],dest[2049],inreply[2049],replyaddr[2049],forwardto[2049],ccto[2049],subject[2049],precedence[2049];
	int romuid = 65534, romgid = 65534;
	int mailuid = 0, mailgid = 0;
	int rread = 0;

	/* Needed for ignorefile copying */
	struct passwd *pwent;
	struct stat cstatbuf;
	char cinfn[256];
	char coutfn[256];
	/**/

	/* Rates */
	char etcratefile[256];

	/* Needed for chroot */
	char chrootdir[256] = ROMDIR;
	char dogetbody = 0;
	char bodyfile[256];
	/**/

	/* Incoming message buffer */
	char *header = NULL, *uheader = NULL, *body = NULL, *hdr = NULL;
	unsigned long int headerlen = 0, uheaderlen = 0, bodylen = 0;
	/**/

	/* Open connection to syslogd */
	openlog("reply-o-matic",LOG_PID,LOG_MAIL);
#ifdef DEBUG
	logfd=fopen("/tmp/rom.log","a");
#endif

	/* Clearing buffers */
	memset(from,0,2049);
	memset(sender,0,2049);
	memset(replyto,0,2049);
	memset(inreply,0,2049);
	memset(replyaddr,0,2049);
	memset(forwardto,0,2049);
	memset(ccto,0,2049);
	memset(dest,0,2049);
	memset(attachpn,0,2049);
	memset(subject,0,2049);
	memset(precedence,0,2049);
	memset(usedflags,0,256);
	memset(boundary,0,256);
	memset(attachtype,0,256);
	memset(bodyfile,0,256);
	memset(ratefile,0,256);
	memset(etcratefile,0,256);
	memset(igfile,0,256);
	memset(cinfn,0,256);
	memset(coutfn,0,256);
	memset(ratefn,0,256);

	/* Getting paranoid */
	oldmask=umask(077);
	getparanoid();
	dochroot=paranoid.chroot ? 1 : 0;
	if ( dochroot )
		addflag("C");

	/* Need to define a boundary for mime multipart */
	if ( getrandom(15,boundary) < 15 ) {
		snprintf(boundary,255,"=_ReplyOMatic_Default_Boundary_QAPLWSOKEDIJRFUHTYG");
	} else {
		tmpbuf=calloc(255,1);
		snprintf(tmpbuf,255,"=_ReplyOMatic_%s_Boundary",boundary);
		strncpy(boundary,tmpbuf,255);
		free(tmpbuf);
	}

	if ( argc > 1 )
		while ( (c = getopt(argc,argv,optstr)) ) {
			if ( c != -1 ) 
				switch ( c ) {
					case 'h' : if ( optarg ) {
						   	if ( strlen(optarg) > 1 ) 
						   		help();
							switch ( optarg[0] ) {
								case '0': includeorig = atoi(optarg);
									  addflag("h0");
									  break;
								case '1': includeorig = atoi(optarg);
									  addflag("h1");
									  break;
								case '2': includeorig = atoi(optarg);
									  addflag("h2");
									  break;
								default:
									  help();
							}
						   } else {
							   includeorig=1;
							   addflag("h1");
						   }
						   break;
					case 'f' : strncpy(rom_from,optarg,2048);
						   addflag("f");
						   break;
					case 'S' : dosubject |= 2;
						   addflag("S");
						   break;
					case 's' : strncpy(rom_subject,optarg,2048);
						   dosubject |= 1;
						   addflag("s");
						   break;
					case 'b' : dogetbody++;
						   strncpy(bodyfile,optarg,255);
						   addflag("b");
						   break;
					case 'r' : strncpy(replyaddr,optarg,2048);
						   addflag("r");
						   break;
					case 'c' : strncpy(ccto,optarg,2048);
						   addflag("c");
						   break;
					case 'd' : strncpy(forwardto,optarg,2048);
						   if (!valid_emailaddress(forwardto)) {
							p_err("-d provided with an invalid e-mail address\n");
							exit(0);
						   }
						   addflag("d");
						   break;
					case 'v' : help();
						   break;
					case 't' : if ( dotype ) {
							   p_err("Only one mimetype can be specified. Only the first will take effect.");
							   break;
						   }
						   strncpy(attachtype,optarg,255);
						   addflag("t");
						   dotype++;
						   break;
					case 'a' : if ( paranoid.noattach )
							   break;
						   if ( attachment ) {
							   p_err("Only one attachment can be specified. Only the first will take effect.");
							   break;
						   }
						   doattach++;
						   strncpy(attachpn,optarg,2048);
						   addflag("a");
						   break;
					case 'u' : if ( paranoid.lockuid ) 
							  break; 
						   romuid = atoi (optarg);
						   if ( romuid == 0 )
							   romuid = 65534;
						   break;
					case 'U' : if ( paranoid.lockuid )
							   break;
						   mailuid = atoi (optarg);
						   break;
					case 'g' : if ( paranoid.lockgid )
							   break;
						   romgid = atoi (optarg);
						   if ( romgid == 0 )
							   romgid = 65534;
						   break;
					case 'G' : if ( paranoid.lockgid )
							   break;
						   mailgid = atoi (optarg);
						   break;
					case 'C' : if ( paranoid.chroot )
							   break;
						   if ( geteuid() != 0 ) {
							   p_err("Need to be root to use chroot()\nRunning without chroot()\n");
							   break;
						   }
						   if ( optarg ) {
							   strncpy(chrootdir,optarg,255);
						   }
						   dochroot=1;
						   addflag("C");
						   break;
					case 'R' : if ( !replyrate ) {
							   if ( optarg )
								   replyrate=atol(optarg);
							   else
								   replyrate=REPLYRATE;
							   addflag("R");
						   }
						   break;
					default:
						   help();
				}
			else
				break;
		}

	pipe(pfd);
	pipe(cpfd);
	switch ( cpid=fork() ) {
		case 0: close(0);
			close(pfd[1]);
			dup2(pfd[0],0);
			if ( getuid() == 0 ) {
				if ( mailgid )
					setgid(mailgid);
				if ( mailuid )
					setuid(mailuid);
			} else {
				setgid(getgid());
				setuid(getuid());
			}
			/* Stupid, but effective, why to prevent errors from
			 * the MTA
			 * It will only start when we tell it to.
			 */
			fcntl(cpfd[0],F_SETFL, O_NONBLOCK);
			tmpbuf=calloc(2,1);
			while ( read(cpfd[0],tmpbuf,1) < 1 ) {
				sleep(1);
			} 
			if ( tmpbuf[0] == 'X' ) {
				exit(0);
			}
			free(tmpbuf);
			execl("/usr/sbin/sendmail","/usr/sbin/sendmail","-bm","-t",NULL);
			printf("Exec error on child: %d\n",errno);
			break;
		case -1: 
			 diedie(2);
			 break;
		default:
			break;
	}
	if ( forwardto[0] != 0 ) {
		pipe(dfd);
		pipe(cdfd);
		switch ( dpid=fork() ) {
			case 0: close(0);
				close(dfd[1]);
				dup2(dfd[0],0);
				if ( getuid() == 0 ) {
					if ( mailgid )
						setgid(mailgid);
					if ( mailuid )
						setuid(mailuid);
				} else {
					setgid(getgid());
					setuid(getuid());
				}
			/* Stupid, but effective, why to prevent errors from
			 * the MTA
			 * It will only start when we tell it to.
			 */
				fcntl(cdfd[0],F_SETFL, O_NONBLOCK);
				tmpbuf=calloc(2,1);
				while ( (rread = read(cdfd[0],tmpbuf,1)) < 1 ) {
					if ( (rread < 0) &&
					    (errno == EINTR ||
					    errno == EAGAIN ||
					    errno == EWOULDBLOCK)
					    )
						sleep(1);
					else
						exit(0);
				}
				if ( tmpbuf[0] == 'X' ) {
					exit(0);
				}
				free(tmpbuf);
				execl("/usr/sbin/sendmail","/usr/sbin/sendmail","-bm",forwardto,NULL);
				printf("Exec error on child: %d\n",errno);
				break;
			case -1: 
				 diedie(2);
				 break;
			default:
				break;
		}
	}

	pwent=getpwuid(getuid());

	/* Define ratefile basename */
	sprintf(ratefile,".uid-%u",getuid());

	/* Try opening it on the users homedir */
	snprintf(ratefn,255,"%s/.rom_rates",pwent->pw_dir);
	snprintf(etcratefile,255,"%s/.rates/%s",ROMDIR,ratefile);
	if (!stat(ratefn,&cstatbuf)) {
		/* Only accept if it's a regular file */
		if ( S_ISREG(cstatbuf.st_mode) ) {
			ratefd=fopen(ratefn,"a+");
			rewind(ratefd);
			needrateopen=1;
		}
	}
	if ( !ratefd && stat(etcratefile,&cstatbuf) ) {
		ratefd=fopen(ratefn,"w");
		needrateopen=2;
		if ( geteuid() == 0 )
			fchown(fileno(ratefd),getuid(),getgid());
	}

	/* Let's copy the rates file from the user homedir, if it exists */
	sprintf(igfile,".uid-%u",getuid());
	snprintf(cinfn,255,"%s/.rom_ignores",pwent->pw_dir);

	/* Opening the .rom_ignores file on homedir */
        if (!stat(cinfn,&cstatbuf)) {
        	/* Only accept if it's a regular file */
		if ( S_ISREG(cstatbuf.st_mode) ) {
			igfd=fopen(cinfn,"r");
		}
	}
	/**/

	if ( dochroot )
		if ( chroot(chrootdir) == 0 ) {
			chdir("/");
			didchroot=1;
		}

	/* Dropping privileges is root */
	if ( geteuid() == 0 ) {
		if ( getuid() == 0 ) {
			setgid(romgid);
			setuid(romuid);
		} else {
			setgid(getgid());
			setuid(getuid());
		}
	}
	header=calloc(1,1);
	body=calloc(1,1);
	while (!feof(stdin)) {
		memset(instr,0,2049);
		fgets(instr,2048,stdin);
		if ( feof(stdin) )
			break;
		if ( strlen(instr) == 1 ) {
			isbody=1;
		} else {
			if ( ( strlen(instr) == 2 ) &&
			     ( instr[0] == '\r' ) &&
			     ( instr[1] == '\n' ) )
				isbody=1;
		}
		if (!isbody) {
			header=realloc(header,headerlen+strlen(instr)+1);
			headerlen += strlen(instr);
			strcat(header,instr);
			header[headerlen]=0;
		} else {
			body=realloc(body,bodylen+strlen(instr)+1);
			bodylen += strlen(instr);
			strcat(body,instr);
			body[bodylen]=0;
		}
	}
	uheader=calloc(headerlen+1,1);
	uheaderlen=unfold(header,uheader,headerlen);

	hdr=uheader;
	while ( (unsigned long int) hdr < ( (unsigned long int) uheader + uheaderlen ) ) {
		unsigned int idx = 0;

		memset(instr,0,2049);
		while ( 1 ) {
			if ( (unsigned long int) hdr > ( (unsigned long int) uheader + uheaderlen ) )
				break;
			instr[idx++]=hdr[0];
			if ( hdr[0] == '\n' ) {
				hdr++;
				break;
			}
			hdr++;
		}
		isfield=1;
		fname=calloc(strlen(instr) + 2,1);
		fdata=calloc(strlen(instr) + 2,1);
		fni=fdi=0;
		for (i=0;i<strlen(instr)-1;i++) {
			if ( instr[i] == 0 )  {
				break;
			}
			if   ( ( instr[i] == ' ' ) ||
			     ( instr[i] == '	' ) ) {
				if ( isfield == 1 ) {
					isfield=0;
				continue;
				}
			}
			if ( isfield ) 
				fname[fni++]=instr[i];
			else
				fdata[fdi++]=instr[i];
		}
		if ( !rom_strcasecmp ( fname, "SENDER:" ) )
			if ( sender[0] == 0 )
				strncpy(sender,fdata,sizeof(sender)-1);
		if ( !rom_strcasecmp ( fname, "FROM:" ) )
			if ( from[0] == 0 ) 
				strncpy(from,fdata,sizeof(from)-1);
		if ( !rom_strcasecmp ( fname, "REPLY-TO:" ) )
			if ( replyto[0] == 0 )
				strncpy(replyto,fdata,sizeof(replyto)-1);
		if ( !rom_strcasecmp (fname, "MESSAGE-ID:" ) )
			if ( inreply[0] == 0 )
				strncpy(inreply,fdata,sizeof(inreply)-1);
		if ( !rom_strcasecmp (fname, "SUBJECT:" ) )
			if ( subject[0] == 0 )
				strncpy(subject,fdata,sizeof(subject)-1);
		if ( !rom_strcasecmp (fname, "PRECEDENCE:" ) )
			if ( subject[0] == 0 )
				strncpy(precedence,fdata,sizeof(precedence)-1);
	}
	if ( fname ) {
		free(fname);
		fname=0;
	}
	if ( fdata ) { 
		free(fdata);
		fdata=0;
	}
	if ( ( strlen(sender) + strlen(from) + strlen(replyto) ) == 0 ) {
		diedie(1);
	}
	if ( strlen(replyto) ) 
		strncpy(dest,replyto,2048);
	else if ( strlen(from) )
		strncpy(dest,from,2048);
	else
		strncpy(dest,sender,2048);
	if ( !rom_strcasecmp(precedence,"list") ||
	     !rom_strcasecmp(precedence,"bulk") ) {
		/* We should not reply to list or bulk messages */
		/* But we should still deliver them if asked to */
		p_log("Not replying to \"%s\": list or bulk message",dest);
	}
	if ( replyrate ) {
		if ( !check_and_update_rate(extract_addr(dest)) ) {
			/* Did not pass the rate test */
			diedie(0);
		}
	}
	if (!valid_emailaddress(dest)) {
		/* We should not reply to invalid e-mail addresses */
		/* But lets deliver it is asked to */
		p_log("Not replying to invalid e-mail address \"%s\"\n",dest);
	}
	/* If we are ignoring, we should not reply, but should deliver
	 * the original message to -d if asked to
	 * The same is valid for list and bulk messages
	 */
	if ( !check_ignores(extract_addr(dest)) &&
	     rom_strcasecmp(precedence,"list") &&
	     rom_strcasecmp(precedence,"bulk") &&
	     valid_emailaddress(dest)
	     ) {
		p_log("Auto-reply to \"%s\" (flags: \"%s\")",dest,usedflags);
		write(cpfd[1],"1",1);
		tmpbuf=calloc(strlen(rom_from)+28,1);
		sprintf(tmpbuf,"MIME-Version: 1.0\nFrom: %s\n",rom_from);
		write(pfd[1],tmpbuf,strlen(tmpbuf));
		free(tmpbuf);
		tmpbuf=calloc(strlen(dest)+20,1);
		sprintf(tmpbuf,"To: %s\n",dest);
		write(pfd[1],tmpbuf,strlen(tmpbuf));
		free(tmpbuf);
		tmpbuf=calloc(strlen(rom_version)+50,1);
		sprintf(tmpbuf,"X-Mailer: Reply-o-Matic v%s\nPrecedence: bulk\n",rom_version);
		write(pfd[1],tmpbuf,strlen(tmpbuf));
		free(tmpbuf);
		if ( inreply[0] != 0 ) {
			tmpbuf=calloc((strlen(inreply)*2)+50,1);
			sprintf(tmpbuf,"References: %s\nIn-Reply-To: %s\n",inreply,inreply);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		}
		if ( replyaddr[0] != 0 ) {
			tmpbuf=calloc(strlen(replyaddr)+20,1);
			sprintf(tmpbuf,"Reply-To: %s\n",replyaddr);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		}
		if ( ccto[0] != 0 ) {
			p_log("Sending copy (-c) to \"%s\"",ccto);
			tmpbuf=calloc(strlen(ccto)+20,1);
			sprintf(tmpbuf,"Bcc: %s\n",ccto);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		}
		if ( doattach && validinclude(attachpn) ) {
			attachment=getattach(attachpn);
			if ( attachment ) {
				char *tname;

				tname=strdup(attachpn);
				attachfn=(char *) strdup(basename(tname));
				free(tname);
			} else 
				doattach=0;
		}
		if ( (includeorig == 2) || doattach) {
			tmpbuf=calloc(strlen(boundary)+50,1);
			sprintf(tmpbuf,"Content-Type: multipart/mixed; boundary=\"%s\"\n",boundary);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		}
		if ( !( dosubject & 2 ) || ( subject[0] == 0 ) ) {
			tmpbuf=calloc(strlen(rom_subject)+20,1);
			sprintf(tmpbuf,"Subject: %s\n\n",rom_subject);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		} else {
			tmpbuf=calloc(strlen(subject)+20,1);
			sprintf(tmpbuf,"Subject: RE: %s\n\n",subject);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		}

		if ( includeorig == 2 || doattach ) {
			tmpbuf=calloc(strlen(boundary)+20,1);
			sprintf(tmpbuf,"\n--%s\n",boundary);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			write(pfd[1],"\n",1);
			free(tmpbuf);
		}
		if ( dogetbody && validinclude(bodyfile) )
			rom_body=getbody(bodyfile);
		if ( !rom_body ) 
			write(pfd[1],default_body,strlen(default_body));
		else
			write(pfd[1],rom_body,strlen(rom_body));
		if ( includeorig == 1 ) {
			write(pfd[1],data_follows,strlen(data_follows));
			write(pfd[1],header,strlen(header));
		}
		if ( doattach && attachment ) {
			tmpbuf=calloc(strlen(boundary)+20,1);
			sprintf(tmpbuf,"\n--%s\n",boundary);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
			if ( !attachfn ) 
				attachfn=(char *) strdup("NO_NAME");
			p_log("Sending attachment \"%s\"",attachfn);
			if ( attachtype[0] == 0 )
				getmimetype(attachpn,attachtype,2048);
			if ( attachtype[0] == 0 )
				sprintf(attachtype,"application/octet-stream");
			tmpbuf=calloc(strlen(attachtype)+(strlen(attachfn) * 2)+160,1);
			sprintf(tmpbuf,"Content-Type: %s; name=\"%s\"\nContent-Disposition: attachment; filename=\"%s\"\nContent-Transfer-Encoding: base64\n\n",attachtype,attachfn,attachfn);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
			write(pfd[1],attachment,strlen(attachment));
			tmpbuf=calloc(strlen(boundary)+20,1);
			sprintf(tmpbuf,"\n\n--%s--\n",boundary);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
			free(attachfn);
		}

		if ( includeorig == 2 ) {
			tmpbuf=calloc(strlen(boundary)+20,1);
			sprintf(tmpbuf,"\n--%s\n",boundary);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			write(pfd[1],"Content-Type: message/rfc822\n\n",30);
			write(pfd[1],header,strlen(header));
			write(pfd[1],body,strlen(body));
			sprintf(tmpbuf,"\n\n--%s--\n",boundary);
			write(pfd[1],tmpbuf,strlen(tmpbuf));
			free(tmpbuf);
		}
		write(pfd[1],".\n",2);
		waitpid(cpid,NULL,0);
	}
	if ( forwardto[0] != 0 ) {
		p_log("Delivering original message to \"%s\"",forwardto);
		/* fork() */
		write(cdfd[1],"1",1);
		write(dfd[1],header,strlen(header));
#define tmpmsg "X-Note: Delivered by Reply-o-Matic after being replied\n"
		write(dfd[1],tmpmsg,strlen(tmpmsg));
		tmpbuf=calloc(256,1);
		snprintf(tmpbuf,255,"X-Delivery-Agent: Reply-O-Matic v%s\n",rom_version);
		write(dfd[1],tmpbuf,strlen(tmpbuf));
		write(dfd[1],body,strlen(body));
		free(tmpbuf);
		write(dfd[1],".\n",2);
		waitpid(dpid,NULL,0);
	}
	diedie(0);
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1