/*
 * This file is part of the FEddi package
 *
 * Personal use allowed under the terms of the
 *
 *              GNU GENERAL PUBLIC LICENSE Version 2
 *              (see LICENSE for the complete text)
 *
 *-------------------------------------------------------------------
 *
 *    ENTER AT YOUR OWN RISK !!
 *
 * This source is without any documentation and can drive you mad.
 * In case of sudden epileptic seizures please call your doctor.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include "regex.h"
#include <ctype.h>
#include "proc.h"
#include "charset.h"

void fgetns(char *s, FILE *f);
void fgetds(char *s, int l, FILE *f);
byte gettext(MsgHdrType *mh, FILE *f, char *old);
void check(char *s, int ml);
int getpkt(const struct dirent *);
int datesort(const struct dirent * const *, const struct dirent * const *);
void loadtosspath();
int matchPath(char *, char *, char *, char *);

typedef struct
{
	int type; /* 0==from 1==to 2==subj */
	struct re_pattern_buffer regex;
	int mode; /* 0==move 1==copy */
	char *toarea;
	int fromarea; /* 0==no -1==negate 1==yes */
	struct re_pattern_buffer refrom;
} PathType;

#define PathSize sizeof(PathType)

PathType *TossPath=NULL;
int numTossPath=0;

int Set;

int main(int argc, char **argv)
{
	FILE *pktf;
	PktHdrType Header;
	PktMsgHdrType PktHdr;
	MsgHdrType MsgHdr;
	DIR *dir;
	struct dirent **entlist, *ent;
	long int pktpos;
	int packs=0, net=0, echo=0, netmail, entries, i, first=0, quiet=0, j, mails;
	int tosspath, tosscopy=0, tossmove=0, EL=0;
	char log[80], file[256], AreaName[80], *d1, name[256], sys[256], oarea[80];
	char OldArea[80], *CopyTxt;
	UserAkaType *ra=NULL;
	time_t timer;
	struct tm *tblock;
	timer=time(NULL);
	tblock=localtime(&timer);
	strcpy(oarea,"");
	strcpy(ProgName,"ftoss");
	for (i=1; i<argc; i++)
	{
		if (strcmp(argv[i],"--config")==0 || strcmp(argv[i],"-c")==0)
		{
			i++;
			if (i==argc)
			{
				printERR("No config-file specified!\n");
				exit(1);
			}
			strcpy(ConfigName,argv[i]);
			continue;
		}
		if (strcmp(argv[i],"--quiet")==0 || strcmp(argv[i],"-q")==0)
		{
			QUIET=1;
			continue;
		}
		printERR("Unknown param '%s'!\n",argv[i]);
		exit(1);
	}
	if (loadrc(1))
	{
		Log("*FEddi running in TossMode");
		loadtosspath();
		sync();
		if ((dir=opendir(InboundPath))!=NULL)
		{
			while ((ent=readdir(dir))!=NULL)
				if ((strstr(ent->d_name,".mo") ||
						strstr(ent->d_name,".tu") ||
						strstr(ent->d_name,".we") ||
						strstr(ent->d_name,".th") ||
						strstr(ent->d_name,".fr") ||
						strstr(ent->d_name,".sa") ||
						strstr(ent->d_name,".su")) &&
						strlen(ent->d_name)==12 &&
						ent->d_name[11]>='0' && ent->d_name[11]<='9')
				{
					if (!first) Log("+Unpacking archived Mail");
					first=1;
					Log("-Expanding Packet %s",ent->d_name);
					printERR("Expanding Packet %s\n",ent->d_name);
					unpack(1,ent->d_name,quiet);
				}
			closedir(dir);
		} else Log("!Can't stat InboundPath");
		sync();
		if ((entries=scandir(InboundPath,&entlist,getpkt,datesort))>0)
		{
			Log("+Searching for Mail");
			for (i=0; i<entries; i++)
			{
				strcpy(file,InboundPath);
				strcat(file,entlist[i]->d_name);
				if ((pktf=fopen(file,"rb"))!=NULL)
				{
					if (fread(&Header,58,1,pktf)!=0)
					{
						if (Header.Version==2)
						{
							for (ra=Profile.Aka; ra; ra=ra->next)
								if (Header.DestNet==ra->UserNet &&
										Header.DestNode==ra->UserNode &&
										(!Header.Use4dAddressing ||
										(Header.DestZone==ra->UserZone &&
										Header.DestPoint==ra->UserPoint))) break;
							if (ra)
							{
								EL=100;
								mails=0;
								sprintf(name,"%s -> %u:%u/%u.%u (%cD)",
										entlist[i]->d_name,
										Header.Use4dAddressing?Header.DestZone:ra->UserZone,
										Header.DestNet,Header.DestNode,
										Header.Use4dAddressing?Header.DestPoint:ra->UserPoint,
										Header.Use4dAddressing?'4':'2');
								while (fread(&PktHdr,14,1,pktf)!=0)
								{
									fread(&MsgHdr.DateTime,20,1,pktf);
									MsgHdr.DateTime[20]=0;
	
									fgetns(MsgHdr.WhoTo,pktf);
									fgetns(MsgHdr.WhoFrom,pktf);
									fgetns(MsgHdr.Subj,pktf);
									
									pktpos=ftell(pktf);
									fgetds(log,80,pktf);
									if (strncmp(log,"AREA:",5)==0)
									{
		                d1=strchr(log,0xd);
		                if (d1) *d1=0;
		                d1=strchr(log,':');
		                d1++;
		                strcpy(AreaName,d1);
		                upperstr(AreaName);
		                netmail=0;
		                echo++;
		  						} else
		  						{
		  							strcpy(AreaName,"NETMAIL");
		  							fseek(pktf,pktpos,SEEK_SET);
		  							netmail=1;
		  							net++;
		  						}

									OldArea[0]=0;
									tosspath=matchPath(MsgHdr.WhoFrom,MsgHdr.WhoTo,MsgHdr.Subj,
											AreaName);
									if (tosspath!=-1)
									{
										if (TossPath[tosspath].mode==0)
										{
											if (strcmp(AreaName,"NETMAIL")==0)
												net--;
											else
												echo--;
											strcpy(OldArea,AreaName);
											strcpy(AreaName,TossPath[tosspath].toarea);
											if (strcmp(AreaName,"NETMAIL")==0)
												net++;
											else
												echo++;
											tossmove++;
										}
									}

									mails++;
									printERR("\r%s [%u]",name,mails);
									
									if (strcmp(oarea,AreaName)==0)
										MsgHdr.StartRec+=MsgHdr.NumRecs;
									else
									{
										if ((MsgHdr.StartRec=openarea(AreaName,NULL))==0 &&
												!netmail)
										{
										  MsgData.RouteZone=Header.DestZone;
										  MsgData.RouteNet=Header.DestNet;
										  MsgData.RouteNode=Header.DestNode;
										}
										loadalias(AreaName);
									}
									
									fseek(mtf,MsgHdr.StartRec*256,SEEK_SET);
									
									MsgHdr.OrigZone=Header.OrigZone;
									MsgHdr.OrigNet=netmail?PktHdr.OrigNet:Header.OrigNet;
									MsgHdr.OrigNode=netmail?PktHdr.OrigNode:Header.OrigNode;
									MsgHdr.OrigPoint=0;
									MsgHdr.DestZone=netmail?\
											(Header.DestZone?Header.DestZone:Header.qDestZone):0;
									MsgHdr.DestNet=netmail?PktHdr.DestNet:0;
									MsgHdr.DestNode=netmail?PktHdr.DestNode:0;
									MsgHdr.DestPoint=0;
	
									MsgHdr.NumRecs=gettext(&MsgHdr,pktf,OldArea);
									transstr(MsgHdr.WhoTo,Set);
									transstr(MsgHdr.WhoFrom,Set);
									transstr(MsgHdr.Subj,Set);
								
									MsgHdr.Marked=MsgHdr.Deleted=MsgHdr.Direct=\
									MsgHdr.TruncSent=MsgHdr.DeleteSent=MsgHdr.Protected=0;
									MsgHdr.Mother=MsgHdr.Child=\
									MsgHdr.nextBrother=MsgHdr.prevBrother=\
									MsgHdr.nextSubject=MsgHdr.prevSubject=65535;
									MsgHdr.New=1;
									MsgData.New++;
									MsgData.Linked=0;
									MsgHdr.Personal=MsgHdr.NewPersonal=0;
									if (MsgHdr.WhoTo[0])
									{
										if (strcasecmp(Profile.UserName,MsgHdr.WhoTo)==0)
										{
											MsgHdr.Personal=MsgHdr.NewPersonal=1;
											MsgData.NewPersonal++;
										} else
										{
											for (j=0; j<Aliases; j++)
												if (strcasecmp(Alias[j],MsgHdr.WhoTo)==0) break;
											if (j<Aliases)
											{
												MsgHdr.Personal=MsgHdr.NewPersonal=1;
												MsgData.NewPersonal++;
											}
										}
									}
									MsgHdr.Attribute=netmail?(PktHdr.Attribute|4):
											(PktHdr.Attribute&1);
									DateTime(MsgHdr.Recvdt);
									
									fseek(mhf,TotalMsgs*MsgHdrSize,SEEK_SET);
									fwrite(&MsgHdr,MsgHdrSize,1,mhf);
									
									if (tosspath!=-1)
									{
										if (TossPath[tosspath].mode==1)
										{
											CopyTxt=(char *)malloc(MsgHdr.NumRecs*256+256);
											sprintf(CopyTxt,"\r*** ftoss: copied from %s\r\r",
													AreaName);
											fseek(mtf,MsgHdr.StartRec*256,SEEK_SET);
											fread(&(CopyTxt[strlen(CopyTxt)]),256,
													MsgHdr.NumRecs,mtf);
											MsgHdr.NumRecs=(strlen(CopyTxt)+1)/256+1;
											closearea();
											MsgHdr.StartRec=openarea(TossPath[tosspath].toarea,NULL);
											fseek(mtf,MsgHdr.StartRec*256,SEEK_SET);
											fwrite(CopyTxt,256,MsgHdr.NumRecs,mtf);
											fseek(mhf,TotalMsgs*MsgHdrSize,SEEK_SET);
											fwrite(&MsgHdr,MsgHdrSize,1,mhf);
											MsgData.New++;
											MsgData.Linked=0;
											if (strcasecmp(Profile.UserName,MsgHdr.WhoTo)==0)
												MsgData.NewPersonal++;
											else
											{
												for (j=0; j<Aliases; j++)
													if (strcasecmp(Alias[j],MsgHdr.WhoTo)==0) break;
												if (j<Aliases)
													MsgData.NewPersonal++;
											}
											free(CopyTxt);
											tosscopy++;
										}
									}
								}
								Log("-%s -> %u:%u/%u.%u (%cD) [%u]",
										entlist[i]->d_name,
										Header.Use4dAddressing?Header.DestZone:ra->UserZone,
										Header.DestNet,Header.DestNode,
										Header.Use4dAddressing?Header.DestPoint:ra->UserPoint,
										Header.Use4dAddressing?'4':'2',
										mails);
								if (mails) printERR("\n");
								packs++;
							} else
							{
								Log("!%s -> %u:%u/%u.%u ALIEN - not tossed",
										entlist[i]->d_name,
										Header.DestZone,Header.DestNet,
										Header.DestNode,Header.DestPoint);
								printERR("%s -> %u:%u/%u.%u ALIEN - not tossed\n",
										entlist[i]->d_name,
										Header.DestZone,Header.DestNet,
										Header.DestNode,Header.DestPoint);
							}
						} else
						{
							Log("!%s PKT-VERSION(%u) != 2",entlist[i]->d_name,
									Header.Version);
							printERR("%s PKT-VERSION(%u) != 2\n",entlist[i]->d_name,
									Header.Version);
						}
					}
	        fclose(pktf);
				}
				if (ra)
					sprintf(sys,"%s%s",CopyPath,entlist[i]->d_name);
				else
					sprintf(sys,"%s%s.%u.%u.%u.%u",CopyPath,entlist[i]->d_name,\
							Header.DestZone,Header.DestNet,\
							Header.DestNode,Header.DestPoint);
				if (rename(file,sys))
					Log("!rename: %s",strerror(errno));
			}
			closearea();
			Log("-Processed %u Packets",packs);
			printERR("Processed %u Packets\n",packs);
			Log("-tossed %u Net and %u Echo mails",net,echo);
			printERR("tossed %u Net and %u Echo mails\n",net,echo);
			if (tossmove || tosscopy)
			{
				Log("!copied %u and moved %u mails",tosscopy,tossmove);
				printERR("copied %u and moved %u mails\n",tosscopy,tossmove);
			}
			if (CopyPath[0] && KeepPkt)
			{
				sprintf(file,"%02u%02u%02u%02u.tgz",tblock->tm_mday,tblock->tm_hour,\
						tblock->tm_min,tblock->tm_sec);
				Log("+Packing Pkt to %s%s",CopyPath,file);
				printERR("Packing Pkt to %s%s\n",CopyPath,file);
				timer=time(NULL);
				tblock=localtime(&timer);
				sprintf(sys,"cd %s ; tar czf %s *.pkt *.PKT",CopyPath,file);
				system(sys);
			}
			sprintf(sys,"rm -f %s*.pkt %s*.PKT",CopyPath,CopyPath);
			system(sys);
			Log("+Mail processed");
		} else 
			if (entries==-1)
			{
				Log("!Can't stat InboundPath");
				EL=2;
			}
			else
			{
				Log("+Nothing to toss");
				printERR("Nothing to toss!\n");
			}
		Log("*Leaving TossMode\n");
	} else
	{
		printERR("\nError while parsing ~/.feddirc\n");
		return 1;
	}
	return EL;
}

void fgetns(char *s, FILE *f)
{
	char in;
	while ((in=fgetc(f))!=0)
	{
		if (in==0xa || in==0xd) break; /* extension 28.12.94 due to internet */
		*s=in;
		s++;
	}
	*s=0;
}

void fgetds(char *s, int l, FILE *f)
{
	char in, i;
	for (i=0; (in=fgetc(f))!=0xd && i<l-1; i++)
	{
		*s=in;
		s++;
	}
	*s=0;
}

byte gettext(MsgHdrType *mh, FILE *f, char *old)
{
	char *MsgTxt=NULL, *d1, *d2;
	int cs, i, j, origin=0;
	byte ein;
	Addr4dType Addr;
	MsgTxt=(char *)malloc(257);
	MsgTxt[0]=0xd;
	if (old[0])
	{ 
		sprintf(MsgTxt+1,"\r*** ftoss: moved from %s\r\r",old);
		cs=strlen(MsgTxt+1);
	} else
		cs=0;
	ein=fgetc(f);
	for (; ein!=0; ein=fgetc(f))
	{
		if (ein!=0xa && ein!=0x8d)
		{
	    MsgTxt[1+cs++]=ein;
		  if (!(cs%256))
				MsgTxt=(char *)realloc(MsgTxt,((cs/256)+1)*256+1);
		}
	}
	MsgTxt[1+cs++]=0;
	Set=STDCHRS;
	if ((d1=strstr(MsgTxt,"\015\001CHRS:"))!=NULL)
	{
		d2=strchr(d1,32);
		while (*d2==32) d2++;
		d1=strchr(d2,0xd);
		if (d1) *d1=0;
		for (i=0; i<MAXCHRS; i++)
		{
			for (j=0; CHRSETS[i][j][0]; j++)
				if (strcasecmp(d2,CHRSETS[i][j])==0) break;
			if (CHRSETS[i][j][0]) break;
		}
		if (i<MAXCHRS) Set=i;
		if (d1) *d1=0xd;
	}
	if ((d1=strstr(MsgTxt,"\015 * Origin:"))!=NULL)
	{
		d2=strrchr(d1,'(');
		if (d2)
		{
			d2++;
			d1=strchr(d2,')');
			if (d1) *d1=0;
			Str2Addr(d2,&Addr);
			if (d1) *d1=')';
			mh->OrigZone=Addr.Zone;
			mh->OrigNet=Addr.Net;
			mh->OrigNode=Addr.Node;
			mh->OrigPoint=Addr.Point;
			origin=1;
		}
	}
	if ((d1=strstr(MsgTxt,"\015\001INTL"))!=NULL)
	{
		d2=strchr(d1,32);
		d2++;
		d1=strchr(d2,32);
		if (d1) *d1=0;
		Str2Addr(d2,&Addr);
		if (d1) *d1=32;
		mh->DestZone=Addr.Zone;
		mh->DestNet=Addr.Net;
		mh->DestNode=Addr.Node;
		d1++;
		d2=strchr(d1,0xd);
		if (d2) *d2=0;
		Str2Addr(d1,&Addr);
		if (d2) *d2=0xd;
		mh->OrigZone=Addr.Zone;
		mh->OrigNet=Addr.Net;
		mh->OrigNode=Addr.Node;
	}
	if ((d1=strstr(MsgTxt,"\015\001FMPT"))!=NULL)
	{
		d2=strchr(d1,32);
		while (*d2==32) d2++;
		for (mh->OrigPoint=0; *d2!=0xd; d2++)
			mh->OrigPoint=mh->OrigPoint*10+*d2-'0';
	}
	if ((d1=strstr(MsgTxt,"\015\001TOPT"))!=NULL) 
	{
		d2=strchr(d1,32);
		while (*d2==32) d2++;
		for (mh->DestPoint=0; *d2!=0xd; d2++)
			mh->DestPoint=mh->DestPoint*10+*d2-'0';
	}
	if (!MsgData.NetMail && !origin)
		mh->OrigZone=mh->OrigNet=mh->OrigNode=mh->OrigPoint=0;
	transstr(MsgTxt,Set);
	fwrite(MsgTxt+1,256,(cs/256)+1,mtf);
	free(MsgTxt);
	
	return (cs/256)+1;
}

int getpkt(const struct dirent *ent)
{
	return ((strstr(ent->d_name,".pkt")!=NULL)||\
			(strstr(ent->d_name,".PKT")!=NULL));
}

int datesort(const struct dirent * const *e1, const struct dirent * const *e2)
{
	struct stat st1, st2;
	char file[256];
	strcpy(file,InboundPath);
	strcat(file,(*e1)->d_name);
	stat(file,&st1);
	strcpy(file,InboundPath);
	strcat(file,(*e2)->d_name);
	stat(file,&st2);
	if (st1.st_mtime<st2.st_mtime) return -1;
	if (st1.st_mtime==st2.st_mtime) return 0;
	return 1;
}

void spezExt(char *s)
{
}

void loadtosspath()
{
	FILE *f;
	char name[PATH_MAX], *d1, *d2, str[80], ar[80], far[80], trenn;
	int ty, mo, ig;
	sprintf(name,"%stosspath",BasePath);
	if ((f=fopen(name,"rt"))!=NULL)
	{
		while (fgets(name,PATH_MAX,f))
		{
			if (name[0]==';') continue;
			for (d1=name; *d1; d1++) if (*d1==9) *d1=32;
			if ((d1=strchr(name,'\n'))) *d1=0;
			if ((d1=strchr(name,'\r'))) *d1=0;
			if (strncasecmp(name,"move",4)==0)
				mo=0;
			else
				if (strncasecmp(name,"copy",4)==0)
					mo=1;
				else
					mo=-1;
			if (mo!=-1)
			{
				for (d1=name+4; *d1==32; d1++);
				switch (*d1)
				{
					case 'F':
					case 'O':
						ig=0;
						ty=0;
						break;
					case 'f':
					case 'o':
						ig=1;
						ty=0;
						break;
					case 'T':
					case 'D':
						ig=0;
						ty=1;
						break;
					case 't':
					case 'd':
						ig=1;
						ty=1;
						break;
					case 'S':
					case 'R':
						ig=0;
						ty=2;
						break;
					case 's':
					case 'r':
						ig=1;
						ty=2;
						break;
					default:
						ig=0;
						ty=-1;
						break;
				}
				if (ty!=-1)
				{
					d1++;
					trenn=*d1++;
					d2=strchr(d1,trenn);
					if (d2)
					{
						*d2=0;
						strcpy(str,d1);
						for (d2++; *d2==32; d2++);
						if ((d1=strchr(d2,32))) *d1=0;
						strcpy(ar,d2);
						upperstr(ar);
						if (d1)
						{
							d1++;
							while (isspace(*d1)) d1++;
							strcpy(far,d1);
						} else
							strcpy(far,"");
						upperstr(far);
						numTossPath++;
						TossPath=(PathType *)realloc(TossPath,PathSize*numTossPath);
						TossPath[numTossPath-1].type=ty;
						TossPath[numTossPath-1].mode=mo;
						TossPath[numTossPath-1].toarea=(char *)malloc(strlen(ar)+1);
						strcpy(TossPath[numTossPath-1].toarea,ar);
						TossPath[numTossPath-1].fromarea=(far[0]?((far[0]=='-')?-1:1):0);
						memset(&TossPath[numTossPath-1].regex,0,
								sizeof(struct re_pattern_buffer));
						if (ig)
							TossPath[numTossPath-1].regex.translate=
									(unsigned char *)RE_ICASE_TRANS_MAP;
						re_set_syntax(RE_SYNTAX_POSIX_EGREP);
						if (re_compile_pattern(str,strlen(str),
								&(TossPath[numTossPath-1].regex)))
						{
							numTossPath--;
							TossPath=(PathType *)realloc(TossPath,PathSize*numTossPath);
							printERR("\007Can't compile regex!\n");
							Log("!Can't compile toss-search regex");
						}
						memset(&TossPath[numTossPath-1].refrom,0,
								sizeof(struct re_pattern_buffer));
						if (ig)
							TossPath[numTossPath-1].refrom.translate=
									(unsigned char *)RE_ICASE_TRANS_MAP;
						re_set_syntax(RE_SYNTAX_POSIX_EGREP);
						if (re_compile_pattern((far[0]=='-')?far+1:far,
								strlen((far[0]=='-')?far+1:far),
								&(TossPath[numTossPath-1].refrom)))
						{
							numTossPath--;
							TossPath=(PathType *)realloc(TossPath,PathSize*numTossPath);
							printERR("\007Can't compile toss-area regex!\n");
							Log("!Can't compile regex");
						}
						continue;
					}
				}
			}
			printERR("Invalid tosspath line!\n");
			printERR("%s\n",name);
			Log("!Invalid tosspath line");
		}
	}
}

int matchPath(char *fr, char *to, char *sj, char *ar)
{
	int i, found;
	char match[120];
	struct re_registers regs;
	for (i=0; i<numTossPath; i++)
	{
		if (TossPath[i].fromarea)
		{
			regs.num_regs=0;
			regs.start=regs.end=NULL;
			found=
				(re_search(&(TossPath[i].refrom),ar,strlen(ar),0,strlen(ar),&regs)!=-1);
			free(regs.start);
			free(regs.end);
			if (TossPath[i].fromarea==1)
			{
				if (!found) continue;
			} else {
				if (found) continue;
			}
		}
		switch (TossPath[i].type)
		{
			case 0:
				strcpy(match,fr);
				break;
			case 1:
				strcpy(match,to);
				break;
			case 2:
				strcpy(match,sj);
				break;
		}
		regs.num_regs=0;
		regs.start=regs.end=NULL;
		if (re_search(&(TossPath[i].regex),match,strlen(match),
				0,strlen(match),&regs)!=-1)
		{
			free(regs.start);
			free(regs.end);
			return i;
		}
	}
	return -1;
}
