#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#ifndef unix
#include <direct.h>
#endif

#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif

char *get_group(char *xto);
int article_read(void);
int article_debug(void);
int article_munge(void);
int article_save(void);
int article_send(void);
int emsg(char * arg_list, ...);
int dolog(char *s);

#define BFSZ 600
static char tosite[BFSZ];
static char spool[BFSZ];
static char togroup[BFSZ];
static char infile[BFSZ];
static char approved[BFSZ];
static char admin[BFSZ];
static char fromlist[BFSZ];
static char xto[BFSZ];
static char xsubject[BFSZ];
static char xfrom[BFSZ];
static char logname[BFSZ];
static char gatename[BFSZ];
static int anygroup;
static int remove_reply=FALSE;
static char *content;
#ifdef WIN32
static int dotstuff = TRUE;
#else
static int dotstuff = FALSE;
#endif

void bf_grow(int sz);
static int mungeid = FALSE;
void bf_add(char *s);
static int bf_max;
static int bf_cur;
int strncmpnc(const char *s1, const char *s2, int len);
static char d_subject[BFSZ];
static char tfile[BFSZ];
static int fixcross;
static int ignore_inreplyto = FALSE;
int main(int argc, char *argv[])
{
	char bf[300];
	static char bf2[300];
	int article_ok;
	char *s;
	int i;

	strcpy(d_subject,"(No Subject)");
	if (argc<2) {
	 	printf("Usage: drobot -dir c:\\ntmail\\news -group comp.os.vms -admin you@this.site -from list@here -subject \"(blank)\" -trace trace.file \n");
		goto ret2;
	}

	for (i=1; i<argc; i++) {
		if (strncmp(argv[i],"-dir",2)==0) {strcpy(spool,argv[++i]);}
		else if (strncmp(argv[i],"-ap",2)==0) {strcpy(approved,argv[++i]);}
		else if (strcmp(argv[i],"-to")==0) {strcpy(tosite,argv[++i]);}
		else if (strncmp(argv[i],"-file",5)==0) {strcpy(infile,argv[++i]);}
		else if (strncmp(argv[i],"-g",2)==0) {strcpy(togroup,argv[++i]);}
		else if (strncmp(argv[i],"-name",5)==0) {strcpy(gatename,argv[++i]);}
		else if (strncmp(argv[i],"-admin",3)==0) {strcpy(admin,argv[++i]);}
		else if (strncmp(argv[i],"-from",3)==0) {strcpy(fromlist,argv[++i]);}
		else if (strncmp(argv[i],"-subject",3)==0) {strcpy(d_subject,argv[++i]);}
		else if (strncmp(argv[i],"-any",4)==0) {anygroup = TRUE;}
		else if (strncmp(argv[i],"-mungeid",4)==0) {mungeid = TRUE;}
		else if (strncmp(argv[i],"-fixcross",9)==0) {fixcross = TRUE;}
		else if (strncmp(argv[i],"-remove_reply",13)==0) {remove_reply = TRUE;}
		else if (strncmp(argv[i],"-nodotstuff",11)==0) {dotstuff = FALSE;}
		else if (strncmp(argv[i],"-dotstuff",9)==0) {dotstuff = TRUE;}
		else if (strncmp(argv[i],"-ignore_inreplyto",17)==0) {ignore_inreplyto = TRUE;}
		else if (strncmp(argv[i],"-trace",6)==0) {strcpy(tfile,argv[++i]);}
		else printf("Unknown arg (%s)\n",argv[i]);
	}

	if (anygroup) strcpy(togroup,"anygroup");

	/* Open the input file if it isn't stdin*/
	strcpy(bf,togroup);
	for (s=bf;*s!=0; s++) {
		if (*s=='.') *s = '_';
		if (*s=='+') *s = '_';
		if (*s=='-') *s = '_';
	}
	sprintf(logname,"%s/%s.log",spool,bf);
	if (strlen(spool)==0) {printf("Usage wrong, must give -dir d:/xxx \n"); return 0;}
	s = getenv("FROM"); if (s!=NULL) strcpy(xfrom,s);
	s = getenv("TO"); if (s!=NULL) strcpy(xto,s);
	s = getenv("SUBJECT"); if (s!=NULL) strcpy(xsubject,s);
	if (anygroup) strcpy(togroup,get_group(xto));
#ifdef WIN32
	_mkdir(spool);
#endif
	emsg("Processing message\n");
	if (strlen(fromlist)==0) {strcpy(fromlist,xfrom);}
	if (strlen(fromlist)==0) {strcpy(fromlist,"add_from_switch_to_drobot");}

 	article_ok = article_read();
	/*article_debug();*/
	emsg("Read article ok, got %d/%d bytes\n",bf_cur,bf_max);
	if (bf_cur==0) goto ret; 	 
	if (!article_ok) {emsg("Couldn't read article\n"); goto ret;}
	if (!article_munge()) {article_save(); goto ret;}
	if (!article_send()) {article_save();}
	emsg("Message processed successfully\n");
	return 0;
 ret: 	
	emsg("drobot exiting due to fault\n");
ret2:
	return 0;
}
int emsg(char * arg_list, ...)
{
 	va_list arg_ptr;
 	char *format, output[1000];

 	va_start(arg_ptr, arg_list);
 	format = arg_list;
	vsprintf(output, format, arg_ptr);
	dolog(output);
	return TRUE;
}
int dolog(char *s)
{
	FILE *f;
	f = fopen(logname,"a");
	if (f!=NULL) {
		fprintf(f,"%s",s);
		fclose(f);
	}
 	return TRUE;
}


int article_debug(void)
{
/*	FILE *f;
	f = fopen("debug.dat","wb");
	if (f==NULL) {perror("debug.dat"); return FALSE;}
	fprintf(f,"Article length %d \\015\012",xlen);
	fwrite(content,1,strlen(content),f);
	fclose(f);
	*/
	return TRUE;
}
#define MAX_LINE 10000
int article_read(void)
{
	char line[MAX_LINE];
	FILE *fin;
	FILE *tf=NULL;
	char *s,*s2,saved;

	int done=FALSE;


	if (strlen(infile)>0) {
		fin = fopen(infile,"r");
		if (fin==NULL) {
			perror(infile); 
			emsg("Failed to read input file \n");
			abort();
		}
	} else {
		fin = stdin;		
	}
/*emsg("Wait for input\n");*/
	if (strlen(tfile)>0) {
		tf = fopen(tfile,"ab");
	}
	while ( !feof(fin) ) {
		if (fgets( line, MAX_LINE, fin )==NULL) break;
		if (tf!=NULL) fputs(line,tf);
		while ( *(line + strlen( line ) - 1) == '\r' || 
			    *(line + strlen( line ) - 1) == '\n' )
			*(line + strlen( line ) - 1) = '\0';

		s = line;
again:		s2 = strchr(s,'\r');
		if (s2!=NULL) {
			*s2 = '\n'; saved = s2[1];
			s2[1] = 0;
			bf_add(s);
			s2[1] = saved;
			s = s2+1;
			goto again;
		} 
		if (dotstuff) if (strlen(infile)==0) if ( strcmp( line, "." ) == 0 ) break;
		strcat(s,"\n");
/*emsg("Got line %s\n",line);*/
		bf_add(s);
	}
	if (tf!=NULL) fclose(tf);
	if (strlen(infile)>0) fclose(fin);

	return TRUE;
}		
void bf_add(char *s)
{
	if ((int) (strlen(s) + bf_cur +10 ) > bf_max) bf_grow(bf_cur*2 + strlen(s) + 100);
	strcpy(content+bf_cur,s);
	bf_cur += strlen(s);
}
void bf_grow(int sz)
{
	char *p;
	p = malloc(sz);
	if (p==NULL) {emsg("Malloc failed %d\n",sz); exit(1);}
	if (content!=NULL) {
		memcpy(p,content,bf_max);
		free(content);
	}
	content = p;
	bf_max = sz;
}

#define matches(a,b) (strncmpnc(a,b,strlen(b))==0)
#define EOL "\015\012"
#define addline(xx) {strcpy(xout,xx); strcat(xout,EOL); xout += strlen(xx)+2;}
static char subject[BFSZ];
static char xxfrom[BFSZ];

static char *hdrs[1000];
static int nhdrs = 0;
void hdrs_clear(void)
{
	int i;
	for (i=0; i<nhdrs; i++) free(hdrs[i]);
	nhdrs = 0;
}
char *mystrlwr(char *s)
{
	char *xx=s;
	for (;*s!=0;s++) {
		if (isupper(*s)) tolower(*s);
	}
	return xx;
}
int hdrs_isdup(char *hdr)
{
	int i;
	char bf[BFSZ];
	char *s;
	strncpy(bf,hdr,BFSZ-1); bf[200] = 0;
	mystrlwr(bf);
	s = strchr(bf,':'); if (s!=NULL) *s = 0; else return FALSE;
	for (i=0; i<nhdrs; i++) {
		if (strcmp(bf,hdrs[i])==0) {
			emsg("Dropping duplicate header (%s)\n",bf);
			return TRUE;
		}
	}
	hdrs[nhdrs++] = strdup(bf);
	return FALSE;
}
int article_munge(void)
{
	char *s,*s2;
	char *out;
	char *xout;
	int inhead = TRUE;
	int r = TRUE;
	int donehead = FALSE;
	int done_extras = FALSE;
	int gotreply = FALSE;
	int gotref = FALSE;
	char ref[BFSZ];
	int dropit = FALSE;
	char *v;
	char tmp[BFSZ];
	char mid[BFSZ];
	char bf[30000];
	char lbf[30000];
	int skip_cont = FALSE;
	char realref[10000];
	int inref = FALSE;
	strcpy(mid,"");
	hdrs_clear();
	strcpy(ref,"");
	out = malloc(strlen(content)+30000);
	strcpy(subject,"");
	xout = out;
	strcpy(mid,"");
	s = content;
	s2 = strchr(s,'\012');
	for (;s2!=NULL;) {
		*s2 = 0;
		strcpy(bf,s);
		s = strchr(bf,'\015'); if (s!=NULL) *s = 0;
		s = strchr(bf,'\012'); if (s!=NULL) *s = 0;
		/*if (dotstuff) if (strcmp(bf,".")==0) strcpy(bf,"..");*/
		if (!dotstuff) {
			if (bf[0]=='.') { /* Always write result dot stuffed */
				memmove(bf+1,bf,strlen(bf)+1);
				bf[0] = '.';
			}

		}
		/* convert single .'s */
		if (inref) {
			if (isspace(bf[0])) {
				strcat(realref,bf+1);
				if (strlen(realref)>400)  inref = FALSE;
				goto cont;
			} else {
				inref = FALSE;
				goto normal_line;
			}
		}
		if (skip_cont) {
			if (isspace(bf[0])) {
				goto cont;
			} else skip_cont = FALSE;
		}
normal_line:
		if (inhead) {
		if (matches(bf,"Control:")) {emsg("Rejecting control message\n"); return FALSE; }
		if (matches(bf,"Newsgroups:")) if (strlen(togroup)>0) goto cont;
		if (matches(bf,"Approved:")) if (strlen(approved)>0) goto cont;
		if (matches(bf,"Subject:")) {strcpy(subject,bf+8);}
		if (matches(bf,"Message-ID:")) 	{
			strcpy(tmp,bf+12);
			emsg("Processing MID (%s)\n",tmp);
			if ((strchr(tmp,'<')==NULL)||(strchr(tmp,'@')==NULL)) {
				v = strtok(tmp," <>\r\n\t\"");
				if (v==NULL) goto cont;
				sprintf(mid,"<%s>",v);
				if (strchr(mid,'@')==NULL) {
					sprintf(mid,"<%s@dposter>",v);
				}
				goto cont;
			}
			if (mungeid) {
			  if (strstr(tmp,"dposter")==NULL) {
				v = strtok(tmp," <>\r\n\t\"");
				if (v==NULL) goto cont;
				sprintf(mid,"<%s.drobot>",v);
				emsg("New mid is (%s)\n",mid);
				goto cont;
			  }
			}
			if (fixcross) {
			  if (strstr(tmp,togroup)==NULL) {
				v = strtok(tmp," <>\r\n\t\"");
				if (v==NULL) goto cont;
				sprintf(mid,"<%s.%s>",v,togroup);
				emsg("New mid is (%s)\n",mid);
				goto cont;
			  }
			}
		}
		if (!ignore_inreplyto) if (matches(bf,"In-Reply-To:")) {strcpy(ref,bf+13);}
		if (matches(bf,"From:")) {strcpy(xxfrom,bf+6); goto cont;}
		if (matches(bf,"References:")) {
			strcpy(realref,bf+12);
			inref = TRUE;
			gotref = TRUE;
			goto cont;
		}

		if (matches(bf,"Posting-Version:")) goto cont; /* Illegal news headers */
		if (matches(bf,"Relay-Version:")) goto cont; /* Illegal news headers */
		if (matches(bf,"Posted:")) goto cont; /* Illegal news headers */
		if (matches(bf,"Received:")) {skip_cont = TRUE; goto cont;} /* Illegal news headers */
		if (matches(bf,"Date-Received:")) goto cont; /* Illegal news headers */
		if (matches(bf,"To:")) goto cont; /* Illegal news headers */
		if (remove_reply) if (matches(bf,"Reply-To:")) goto cont; /* Illegal news headers */
		if (matches(bf,"Cc:")) goto cont; /* Illegal news headers */
		if (matches(bf,"From ")) goto cont; /* Illegal news headers */
		if (hdrs_isdup(bf)) goto cont; /* Ignore duplicate headers */
		if (matches(bf,"X-Gateway-From:")) {
			if (strlen(gatename)==0) dropit = TRUE;
			else {
				strncpy(lbf,bf,200); lbf[200] = 0;
				mystrlwr(lbf);
				mystrlwr(gatename);
				if (strstr(lbf,gatename)!=NULL) {
					dropit = TRUE;
					emsg("Gateway name matched (%s)\n",gatename);
				} else {
					emsg("Gateway name didn't match (%s)\n",gatename);
				}
			}
		}

		if (strlen(bf)==0) if (!done_extras) {
			done_extras = TRUE;
			inhead = FALSE;
			if (strlen(xxfrom)>0) {
				sprintf(bf,"From: %s",xxfrom);
				addline(bf);
			}
			if (strlen(mid)>0) {
				sprintf(bf,"Message-ID: %s",mid);
				addline(bf);
			}
			if (strlen(approved)>0) {
				sprintf(bf,"Approved: %s",approved);
				addline(bf);
			}
			if (strlen(togroup)>0) {
				sprintf(bf,"Newsgroups: %s",togroup); 
				addline(bf);
			}
			if (strlen(subject)==0) if (strlen(d_subject)>0) {
				sprintf(bf,"Subject: %s",d_subject); 
				addline(bf);
			}
			if (strlen(admin)!=0) {sprintf(bf,"X-Gateway-Administrator: %s",admin);  addline(bf);}
			if (strlen(fromlist)!=0) {sprintf(bf,"X-Gateway-From: %s",fromlist);  addline(bf);}
			if (!gotref) {
				if (strlen(ref)>0) {sprintf(bf,"References: %s",ref);  addline(bf);} 
			} else {
				if (strstr(realref,ref)==NULL) {
					strcat(realref," ");
					strcat(realref,ref);
				}
				sprintf(bf,"References: %s",realref);  
				addline(bf); 
emsg("References {%s} {%s} \n",ref,realref);
			}
			strcpy(bf,"");
		}
		}
		addline(bf);
		s = s2 + 1;
		s2 = strchr(s,'\012');
		continue;

cont:		s = s2 + 1;
		s2 = strchr(s,'\012');
		if (((*s==' ') || (*s=='\t'))  && (s2!=NULL)) {
			*s2 = 0;
			strcpy(bf,s);
			s = strchr(bf,'\015'); if (s!=NULL) *s = 0;
			s = strchr(bf,'\012'); if (s!=NULL) *s = 0;

			goto cont;
		}
	}
	free(content);
	content = out;
	emsg("Article from {%s} subject {%s} \n",xxfrom,subject);
	if (dropit) {
		emsg("Article dropped as already gone thru gateway (X-Gateway-From found)\n");
		return FALSE;
	}
	return TRUE;	
}


int article_save(void)
{
	FILE *f;
	char bf[BFSZ];
	char fname[BFSZ];
	static int upto;

	sprintf(fname,"%s/upto.cnt",spool);
 	f = fopen(fname,"r");
	if (f!=NULL) {
		fgets(bf,BFSZ-10,f);
		fclose(f);
	}
	upto = atoi(bf);
	upto++;
	f = fopen(fname,"w");
	if (f!=NULL) {
		fprintf(f,"%d\n",upto);
		fclose(f);
	}
	sprintf(fname,"%s/fail_%d.dud",spool,upto);
	emsg("Saving failed article to file {%s} \n",fname);
	f = fopen(fname,"wb");
	if (f==NULL) {perror(fname); return FALSE;}
	fwrite(content,1,strlen(content),f);
	fclose(f);
	return TRUE;

}
int article_send(void)
{
	FILE *f;
	char bf[BFSZ];
	char fname[BFSZ];
	char cname[BFSZ];
	static int upto;

	sprintf(cname,"%s/upto.cnt",spool);
 	f = fopen(cname,"r");
	if (f!=NULL) {
		fgets(bf,BFSZ-10,f);
		fclose(f);
	}
	upto = atoi(bf);
next_file:
	upto++;
	f = fopen(cname,"w");
	if (f!=NULL) {
		fprintf(f,"%d\n",upto);
		fclose(f);
	}
	sprintf(fname,"%s/news_%d.itm",spool,upto);
	emsg("Saving article to file {%s} \n",fname);
	f = fopen(fname,"r");
	if (f!=NULL) {fclose(f); goto next_file;}
	f = fopen(fname,"wb");
	if (f==NULL) {emsg("Couldn't create %s\n",fname); return FALSE;}
	fwrite(content,1,strlen(content),f);
	fclose(f);
	return TRUE;

}
char *get_group(char *xto)
{
	static char bf[BFSZ];
	char *s;
	strncpy(bf,xto,BFSZ-10);
	s = strchr(bf,'@');
	if (s!=NULL) *s = 0;
	return bf;
}
static const unsigned char charmap[] = {
	0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
	0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
	0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
	0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
	0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
	0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
	0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
	0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
	0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
	0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
	0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
	0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
	0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
	0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
	0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
	0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
	0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
	0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
	0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
	0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
	0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
	0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
	0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
	0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
	0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

int strcmpnc(char *s1, char *s2) {
	const unsigned char *cm = charmap,
	     *us1 = (const unsigned char *)s1,
	     *us2 = (const unsigned char *)s2;

	while (cm[*us1] == cm[*us2++])
		if (*us1++ == '\0')
			return (0);
	return (cm[*us1] - cm[*--us2]);
}



int strncmpnc(const char *s1, const char *s2, int len)
{
        int i;
        for (i=0;i<len;i++,s1++,s2++) {
                if (tolower(*s1)<tolower(*s2)) return -1;
                if (tolower(*s1)>tolower(*s2)) return 1;
                if (*s1 == 0) return -1;
                if (*s2 == 0) return 1;
        }
        return 0;
}
