/*
 * 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.
 *
 */

#define _PROC

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <dirent.h>
#include <signal.h>
#include <ncurses.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "proc.h"
#include "colors.h"
#include "flags.h"

int LogSize;
long logpos;

int LOCKBase(int);
void invalidLine(char *, int);
int readLOCK(LOCKType *l, FILE *f);
void writeLOCK(LOCKType *l, FILE *f);
int parseswitch(int l, char *s, char *t, int *val);

extern void spezExt(char *);

typedef struct
{
	char name[20];
	int *value;
} SwitchType;

static int maxSwitches=12;

static SwitchType Switches[12]=
{
	{"beep",&BeepOn},
	{"autodelempty",&AutoDelEmpty},
	{"askfororigname",&AskForOrigName},
	{"keeppkt",&KeepPkt},
	{"keepnl",&KeepNl},
	{"keepbackups",&KeepBackups},
	{"verbosesubject",&ListMode},
	{"autonextfolder",&AutoNextFolder},
	{"skipdeleted",&SkipDeleted},
	{"replysubject",&ReplySubject},
	{"convertchars",&ConvertChars},
	{"clearmarks",&ClearMarks},
};

int loadrc(int lockbase)
{
	FILE *rcf, *f;
	int linenum=0;
	char line[4000], *d1, *d2, rcn[256], *LoginBuffer;
	short i, gu=0, error=1;
	UserAkaType *runaka;
	Addr4dType *runroute;
	char *Login;
	DIR *dir;
	struct stat st;
	struct sigaction sa;
	sa.sa_handler=SIG_IGN;
	sigfillset(&sa.sa_mask);
	sa.sa_flags=SA_RESTART;
	sigaction(SIGALRM,&sa,NULL);
	StandardFlags=256; /*Local*/
	AskForOrigName=SkipDeleted=AutoNextFolder=ListMode=ShowAllAddr=0;
	Mailer=1;
	MaxMsgLength=56;
	QuoteLength=70;
	BaseOrig=BaseAlias=ReplySubject=ConvertChars=ClearMarks=0;
	Aliases=Origlines=LogSize=0;
	Origchoose=-1;
	KeepPkt=KeepNl=KeepBackups=1;
	AutoDelEmpty=BeepOn=1;
	Alias=NULL;
	OrigLine=NULL;
	Aka=runaka=NULL;
	runroute=NULL;
	printERR("FEddi V%s, Copyright (C) 1993-95 by Oliver Graf\n",VERSION);
	LoginBuffer=(char *)malloc(256);
	Login=cuserid(LoginBuffer);
	strcpy(HomePath,getenv("HOME"));
	if (ConfigName[0])
	{
		strcpy(rcn,ConfigName[0]=='~'?HomePath:"");
		strcat(rcn,ConfigName+(ConfigName[0]=='~'?1:0));
		printERR("Reading config from %s\n",rcn);
	} else
	{
		strcpy(rcn,HomePath);
		strcat(rcn,"/.feddirc");
	}
	rcf=fopen(rcn,"rt");
	if (!rcf)
	{
		printERR("\nCan't stat %s !\n\n",rcn);
		return 0;
	}
	strcpy(UtilityPath,HomePath);
	mksl(UtilityPath);
	while (fgets(line,80,rcf))
	{
		linenum++;
		if (line[0]!=0 && line[0]!=';')
		{
			for (d1=line; *d1; d1++)
				if(*d1==9) *d1=32;
			d1=strchr(line,0xa);
			if (d1) *d1=0;
			if (gu)
			{
				if (strcasecmp(line,"end")==0)
					gu--;
				else
				{
					if (runaka)
					{
						runaka->next=(UserAkaType *)malloc(UserAkaSize);
						runaka=runaka->next;
					} else
					{
						Profile.Aka=(UserAkaType *)malloc(UserAkaSize);
						runaka=Profile.Aka;
					}
					runaka->next=NULL;
					d2=strchr(line,32);
					*d2=0;
					d1=line;
					for (runaka->UserZone=0; *d1>='0' && *d1<='9'; d1++)
						runaka->UserZone=runaka->UserZone*10+*d1-'0';
					for (d1++, runaka->UserNet=0; *d1>='0' && *d1<='9'; d1++)
						runaka->UserNet=runaka->UserNet*10+*d1-'0';
					for (d1++, runaka->UserNode=0; *d1>='0' && *d1<='9'; d1++)
						runaka->UserNode=runaka->UserNode*10+*d1-'0';
					for (d1++, runaka->UserPoint=0; *d1>='0' && *d1<='9'; d1++)
						runaka->UserPoint=runaka->UserPoint*10+*d1-'0';
					d2++;
					while (*d2==32) d2++;
					d1=strchr(d2,32);
					*d1=0;
					runaka->NetName=(char *)malloc(strlen(d2)+1);
					strcpy(runaka->NetName,d2);
					d1++;
					while (*d1==32) d1++;
					d2=strchr(d1,32);
					*d2=0;
					strcpy(runaka->Password,d1);
					d2++;
					while (*d2==32) d2++;
					d1=strchr(d2,32);
					*d1=0;
					strcpy(runaka->Outbound,d2);
					d1++;
					while (*d1==32) d1++;
					runaka->Route=NULL;
					for (i=0; !i;)
					{
						d2=strchr(d1,32);
						if (d2) *d2=0; else i++;
						if (runaka->Route)
						{
							runroute->next=(Addr4dType *)malloc(Addr4dSize);
							runroute=runroute->next;
						} else
						{
							runaka->Route=(Addr4dType *)malloc(Addr4dSize);
							runroute=runaka->Route;
						}
						runroute->next=NULL;
						for (runroute->Zone=0; *d1>='0' && *d1<='9'; d1++)
							runroute->Zone=runroute->Zone*10+*d1-'0';
						for (d1++, runroute->Net=0; *d1>='0' && *d1<='9'; d1++)
							runroute->Net=runroute->Net*10+*d1-'0';
						d1++;
						if (!*d1) d1-=2;
						for (runroute->Node=0; *d1>='0' && *d1<='9'; d1++)
							runroute->Node=runroute->Node*10+*d1-'0';
						d1++;
						if (!*d1) d1-=2;
						for (runroute->Point=0; *d1>='0' && *d1<='9'; d1++)
							runroute->Point=runroute->Point*10+*d1-'0';
						d1=d2+1;
					}
				}
				continue;
			}
			if (strncasecmp(line,"msgbasepath",11)==0)
			{
				d1=strchr(line,32);
				if (!d1) 
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(BasePath,d1);
				extendhome(BasePath);
				mksl(BasePath);
				continue;
			}
			if (strncasecmp(line,"inboundpath",11)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(InboundPath,d1);
				extendhome(InboundPath);
				mksl(InboundPath);
				continue;
			}
			if (strncasecmp(line,"log",3)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				d2=strchr(d1,32);
				if (d2)
				{
					*d2++=0;
					while (*d2==32) d2++;
					LogSize=atoi(d2);
				} else
				{
					invalidLine(line,linenum);
					continue;
				}
				strcpy(LogFile,d1);
				extendhome(LogFile);
				continue;
			}
			if (strncasecmp(line,"copypath",8)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(CopyPath,d1);
				extendhome(CopyPath);
				mksl(CopyPath);
				continue;
			}
			if (strncasecmp(line,"nodelistpath",12)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(NodelistPath,d1);
				extendhome(NodelistPath);
				mksl(NodelistPath);
				continue;
			}
			if (strncasecmp(line,"outboundpath",12)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(OutboundPath,d1);
				extendhome(OutboundPath);
				mksl(OutboundPath);
				continue;
			}
			if (strncasecmp(line,"editor",6)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(Editor,d1);
				continue;
			}
			if (strncasecmp(line,"packer",6)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(Packer,d1);
				continue;
			}
			if (strncasecmp(line,"unpacker",8)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(Unpacker,d1);
				continue;
			}
			if (strncasecmp(line,"printmsg",8)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(PrintMsg,d1);
				continue;
			}
			if (strncasecmp(line,"exportmsg",9)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(ExportMsg,d1);
				continue;
			}
			if (strncasecmp(line,"importtext",9)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(ImportText,d1);
				continue;
			}
			if (strncasecmp(line,"quotelength",11)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				QuoteLength=atoi(d1);
				continue;
			}
			if (strncasecmp(line,"maxmsglength",12)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				MaxMsgLength=strtol(d1,&d2,10);
				if (d2)
					switch (tolower(*d2))
					{
						case 'k':
							MaxMsgLength*=4;
							break;
						case 'b':
							MaxMsgLength=(MaxMsgLength/256)+1;
							break;
						case 'f':
							break;
						default:
							if (MaxMsgLength<=64)
							{
								MaxMsgLength*=4;
								printERR("\007MaxMsgLength: Guessed KBytes\n");
								Log("!MaxMsgLength: Guessed KBytes");
							} else
								if (MaxMsgLength>255)
								{
									MaxMsgLength=(MaxMsgLength/256)+1;
									printERR("\007MaxMsgLength: Guessed Bytes\n");
									Log("!MaxMsgLength: Guessed Bytes");
								} else
								{
									printERR("\007MaxMsgLength: Guessed FEddi-Blocks\n");
									Log("!MaxMsgLength: Guessed FEddi-Blocks");
								}
					}
				continue;
			}
			if (strncasecmp(line,"showalladdr",11)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				if (strcasecmp(d1,"no")==0) ShowAllAddr=0;
				if (strcasecmp(d1,"yes")==0) ShowAllAddr=1;
				if (strcasecmp(d1,"name")==0) ShowAllAddr=2;
				continue;
			}
			if (strncasecmp(line,"utilitypath",10)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				strcpy(UtilityPath,d1);
				extendhome(UtilityPath);
				mksl(UtilityPath);
				continue;
			}
			if (strncasecmp(line,"mailer",6)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				if (strcasecmp(d1,"binkley")==0) Mailer=1;
					else if (strcasecmp(d1,"ifcico")==0) Mailer=0;
				continue;
			}
			if (strncasecmp(line,"standardflags",13)==0)
			{
				d1=strchr(line,32);
				if (!d1)
				{
					invalidLine(line,linenum);
					continue;
				}
				while (*d1==32) d1++;
				for (StandardFlags=0; *d1; d1+=2)
					for (i=0; i<16; i++)
						if (strncasecmp(d1,NAMEFLAG[i],2)==0)
						{
							StandardFlags|=(1<<i);
							break;
						}
				continue;
			}
			if (strncasecmp(line,"profile",7)==0)
			{
				if (runaka==NULL)
				{
					gu++;
					d1=strchr(line,32);
					if (!d1)
					{
						invalidLine(line,linenum);
						continue;
					}
					while (*d1==32) d1++;
					Profile.Aka=NULL;
					runaka=NULL;
					Profile.UserLogin=(char *)malloc(strlen(Login)+1);
					strcpy(Profile.UserLogin,Login);
					Profile.UserName=(char *)malloc(strlen(d1)+1);
					strcpy(Profile.UserName,d1);
					if (strlen(Profile.UserName)>35)
					{
						printERR("\nUserName to long!\n");
						error=0;
					}
				} else Log("!Double Profile in ~/.feddirc");
				continue;
			}
			for (i=0; i<maxSwitches; i++)
				if (parseswitch(linenum,line,Switches[i].name,Switches[i].value))
					break;
			if (i!=maxSwitches) continue;
			spezExt(line);
		}
	}
	fclose(rcf);
	if ((f=fopen(LogFile,"at"))==NULL)
	{
		printERR("\nUnable to open Log-File\n");
		error=0;
	}	
	fclose(f);
	if (!Profile.Aka)
	{
		printERR("\nYou have no Address in any Net!\n"\
									"\nPlease check %s for user entry\n",rcn);
		error=0;
	}
	if ((dir=opendir(BasePath))==NULL)
	{
		printERR("\nCan't stat MsgBasePath\n");
		error=0;
	}
	closedir(dir);
	if ((dir=opendir(InboundPath))==NULL)
	{
		printERR("\nCan't stat InboundPath\n");
		error=0;
	}
	closedir(dir);
	if ((dir=opendir(OutboundPath))==NULL)
	{
		printERR("\nCan't stat OutboundPath\n");
		error=0;
	}
	closedir(dir);
	if ((dir=opendir(NodelistPath))==NULL)
	{
		printERR("\nCan't stat NodelistPath\n");
		error=0;
	}
	closedir(dir);
	if ((dir=opendir(CopyPath))==NULL)
	{
		printERR("\nCan't stat CopyPath\n");
		error=0;
	}
	closedir(dir);
	d1=strchr(Packer,32);
	if (d1) *d1=0;
	if (stat(Packer,&st)==-1)
	{
		printERR("\nCan't stat Packer\n");
		error=0;
	}
	if (d1) *d1=32;
	if (Unpacker[0])
	{
		if (!findfile(Unpacker,line))
		{
			printERR("\nCan't stat Unpacker\n");
			error=0;
		}
		if (d1) *d1=32;
	}
	if (Editor[0]!=0)
	{
		d1=strchr(Editor,32);
		if (d1) *d1=0;
		if (stat(Editor,&st)==-1)
		{
			printERR("\nCan't stat Editor\n");
			error=0;
		}
		if (d1) *d1=32;
	}
	if (lockbase)
	{
		if (LOCKBase(1))
			exit(1);
		atexit(UNLOCKBase);
	}
	return error;
}

void DateTime(char *s)
{
	time_t timer;
	struct tm *tblock;
	timer=time(NULL);
	tblock=localtime(&timer);
	strftime(s,20,"%d %b %y  %H:%M:%S",tblock);
}

dword MsgId(char *d, int msgnum)
{
	dword id=0;
	int m=0;
	char date[25], *d1, *d2;
	strcpy(date,d);
	d1=strchr(date,32);
	*d1++=0;
	id=atoi(date);
	d2=strchr(d1,32);
	*d2++=0;
	if (strcmp(d1,"Jan")==0) m=1;
	if (strcmp(d1,"Feb")==0) m=2;
	if (strcmp(d1,"Mar")==0) m=3;
	if (strcmp(d1,"Apr")==0) m=4;
	if (strcmp(d1,"May")==0) m=5;
	if (strcmp(d1,"Jun")==0) m=6;
	if (strcmp(d1,"Jul")==0) m=7;
	if (strcmp(d1,"Aug")==0) m=8;
	if (strcmp(d1,"Sep")==0) m=9;
	if (strcmp(d1,"Oct")==0) m=10;
	if (strcmp(d1,"Nov")==0) m=11;
	if (strcmp(d1,"Dec")==0) m=12;
	id=id|(m<<5);
	d1=strchr(d2,32);
	*d1++=0;
	d1++;
	id=id|(atoi(d2)<<9);
	d2=strchr(d1,':');
	*d2++=0;
	id=id|(atoi(d1)<<16);
	d1=strchr(d2,':');
	*d1++=0;
	id=id|(atoi(d2)<<21);
	id=id|(atoi(d1)<<26);
	id=id^TotalMsgs;
	id=id^(TotalMsgs<<16);
	id=id^msgnum;
	id=id^(msgnum<<16);
	return id;
}

int Str2Addr(char *s, Addr4dType *a)
{
	char *d1, *d2;
	int mode=0, value;
	if (Aka)
	{
		a->Zone=Aka->UserZone;
		a->Net=Aka->UserNet;
		a->Node=Aka->UserNode;
		a->Point=0;
	} else
		a->Zone=a->Net=a->Node=a->Point=0;
	if ((d2=strchr(s,'@'))!=NULL) *d2=0;
	d2=s;
	while (*d2 && !strloc(*d2,"0123456789:/. ")) d2++;
	if (*d2=='.')
	{
		point:
		value=strtol(d2+1,&d1,10);
		if (*d1==0)
		{
			a->Point=value;
			return 0;
		} else
			return 1;
	} else
	{
		for (d1=s; *d1; d1=d2+1)
		{
			value=strtol(d1,&d2,10);
			if (d1==d2) return 1;
			if (mode==0 && *d2==':')
			{
				a->Zone=value;
				mode=1;
				continue;
			}
			if ((mode==0 || mode==1) && *d2=='/')
			{
				a->Net=value;
				mode=2;
				continue;
			}
			if ((mode==0 || mode==2) && *d2==0)
			{
				a->Node=value;
				return 0;
			}
			if ((mode==0 || mode==2) && *d2=='.')
			{
				a->Node=value;
				goto point;
			}
			return 1;
		}
	}
	return 1;
}

char OpenArea[80]="";
char OpenAreaPath[PATH_MAX]="";

dword openarea(char *an, char *path1)
{
	char name[PATH_MAX], path[PATH_MAX];
	struct stat st;
	dword ts;
	FILE *mdf;
	closearea();
	strcpy(OpenArea,an);
	strcpy(OpenAreaPath,path1?path1:BasePath);
	sprintf(name,"%s+%s/header",path1?path1:BasePath,an);
	mhf=fopen(name,"r+b");
	if (!mhf)
	{
		sprintf(path,"%s+%s",path1?path1:BasePath,an);
		mkdir(path,FIDO_MOD+73);
		chown(path,-1,FIDO_GID);
		mhf=fopen(name,"w+b");
		chmod(name,FIDO_MOD);
		chown(name,-1,FIDO_GID);
	}
	stat(name,&st);
	TotalMsgs=st.st_size/MsgHdrSize;
	sprintf(name,"%s+%s/text",path1?path1:BasePath,an);
	mtf=fopen(name,"r+b");
	if (!mtf)
	{
		mtf=fopen(name,"w+b");
		chmod(name,FIDO_MOD);
		chown(name,-1,FIDO_GID);
	}
	stat(name,&st);
	ts=st.st_size/256;
	sprintf(name,"%s+%s/data",path1?path1:BasePath,an);
	mdf=fopen(name,"rb");
	if (!mdf)
	{
		mdf=fopen(name,"w+b");
		MsgData.ID[0]=PCODE[0];
		MsgData.ID[1]=PCODE[1];
		MsgData.ID[2]=PCODE[2];
		MsgData.VMAJOR=MBVERhi;
		MsgData.VMINOR=MBVERlo;
		MsgData.VPATCH=MBVERpl;
		MsgData.LastRead=0;
		MsgData.ReadOnly=MsgData.NetMail=0;
		MsgData.RouteZone=MsgData.RouteNet=MsgData.RouteNode=0;
		MsgData.New=MsgData.Deleted=MsgData.NewPersonal=MsgData.Linked=0;
		MsgData.Marked=0;
		MsgData.CharSet=0;
		if (strcmp(an,"NETMAIL")==0) MsgData.NetMail=1;
		strcpy(MsgData.Owner,Profile.UserName);
		strcpy(MsgData.RealName,an);
		fwrite(&MsgData,MsgDataSize,1,mdf);
		chmod(name,FIDO_MOD);
		chown(name,-1,FIDO_GID);
	} else
	{
		fread(&MsgData,MsgDataSize,1,mdf);
		if (memcmp(&PCODE,&MsgData.ID,6)!=0)
		{
			printERR("\nWrong MsgBase-Version!\n");
			if (MsgData.ID[0] != PCODE[0] ||
					MsgData.ID[1] != PCODE[1] ||
					MsgData.ID[2] != PCODE[2])
				printERR("V0.6pl3 or lower\n");
			else
				printERR("V%u.%u\n",MsgData.VMAJOR,MsgData.VMINOR);
			printERR("\nPlease call 'futility update'\n\n");
			exit(EXIT_FAILURE);
		}
	}
	fclose(mdf);
	if (MsgData.LastRead>=TotalMsgs) MsgData.LastRead=TotalMsgs-1;
	if (!TotalMsgs) MsgData.LastRead=0;
	return ts;
}

int closearea()
{
	fclose(mhf);
	fclose(mtf);
	savemsgdata();
	OpenArea[0]=0;
	OpenAreaPath[0]=0;
	return 1;
}

int AddrMatch(Addr4dType *a, MsgHdrType *mh)
{
	int cmp=0;
	if (a->Zone)
		cmp=a->Zone==mh->DestZone;
	if (a->Net)
		cmp&=(a->Net==mh->DestNet);
	if (a->Node)
		cmp&=(a->Node==mh->DestNode);
	if (a->Point)
		cmp&=(a->Node==mh->DestPoint);
	return cmp;
}

void MatchAddr(MsgHdrType *mh, int nm)
{
	Addr4dType *runRoute;
	UserAkaType *runAka;
	if (MsgData.NetMail || nm)
	{
		for (runAka=Profile.Aka; runAka; runAka=runAka->next)
			for (runRoute=runAka->Route; runRoute; runRoute=runRoute->next)
				if (AddrMatch(runRoute,mh))
				{
					mh->OrigZone=runAka->UserZone;
					mh->OrigNet=runAka->UserNet;
					mh->OrigNode=runAka->UserNode;
					mh->OrigPoint=runAka->UserPoint;
					return;
				}
	} else
	{
		for (runAka=Profile.Aka; runAka; runAka=runAka->next)
			if (MsgData.RouteZone==runAka->UserZone &&
					MsgData.RouteNet==runAka->UserNet &&
					MsgData.RouteNode==runAka->UserNode)
			{
				mh->OrigZone=runAka->UserZone;
				mh->OrigNet=runAka->UserNet;
				mh->OrigNode=runAka->UserNode;
				mh->OrigPoint=runAka->UserPoint;
				return;
			}
	}
}

bigint getkey(char *address)
{
	char *end;
	bigint i=0;
	int z;
	end=address;
	do
	{
		z = strtol(end, &end, 10);
		i = (i<<16)|z;
	} while (*end++);
	if (strchr(address, '.')==NULL) i<<=16;
	return i;
}

void savemsgdata()
{
	char name[PATH_MAX];
	FILE *mdf;
	if (OpenArea[0])
	{
		sprintf(name,"%s+%s/data",OpenAreaPath,OpenArea);
		mdf=fopen(name,"wb");
		fwrite(&MsgData,MsgDataSize,1,mdf);
		fclose(mdf);
	}
}

void loadmsghdr(MsgHdrType **amh)
{
	if (OpenArea[0] && mhf)
	{
		if (*amh) free(*amh);
		*amh=(MsgHdrType *)malloc(MsgHdrSize*TotalMsgs);
		fread(*amh,MsgHdrSize,TotalMsgs,mhf);
	}
}

void savemsghdr(MsgHdrType **amh)
{
	if (OpenArea[0] && mhf)
	{
		fseek(mhf,0,SEEK_SET);
		fwrite(*amh,MsgHdrSize,TotalMsgs,mhf);
		free(*amh);
		*amh=NULL;
	}
}

int strmatch(char *s1, char *s2)
{
	while (*s1)
	{
		if (*s1=='#')
		{
			if (*s2<'0' || *s2>'9')
				return 1;
		} else
			if (*s1!=*s2)
				return 1;
		s1++;
		s2++;
	}
	return 0;
}

int unpack(int pkt, char *fname, int quiet)
{
	char sys[PATH_MAX];
	chdir(InboundPath);
	if (findfile(Unpacker,sys))
	{
		strcat(sys,Unpacker);
		strcat(sys," ");
		strcat(sys,fname);
		if (quiet)
			strcat(sys," >&/dev/null");
		switch (system(sys))
		{
			case 0:
				sprintf(sys,"rm -f %s",fname);
				system(sys);
				return 0;
			case 1 ... 99:
				sprintf(sys,"mv -f %s %s%s.unpackerr",fname,CopyPath,fname);
				system(sys);
				Log("!Error while unpacking %s",fname);
				break;
			case 100:
				Log("-%s is not packed",fname);
				return 100;
			default:
				Log("!General unpack error");
				break;
		}
	}
	return 1;
}

int strloc(char c, char *s)
{
	for (; *s; s++)
		if (c==*s) return 1;
	return 0;
}

void lowerstr(char *str)
{
	for (; *str; str++) *str=tolower(*str);
}

void upperstr(char *str)
{
	for (; *str; str++) *str=toupper(*str);
}

char *strcasestr(char *s1, char *s2)
{
	upperstr(s1);
	upperstr(s2);
	return (strstr(s1,s2));
}

void Log(char *template, ...)
{
	va_list params;
	FILE *log;
	char dt[21];
	log=fopen(LogFile,"at");
	if (log)
	{
		va_start(params,template);
	  DateTime(dt);
		fprintf(log,"%c [%s %s(%u)] ",*template,dt,ProgName,getpid());
		vfprintf(log,template+1,params);
		fputc('\n',log);
		fclose(log);
		chmod(LogFile,FIDO_MOD);
		chown(LogFile,-1,FIDO_GID);
		va_end(params);
	}
}

void EndLog()
{
	struct stat st;
	char line[256], name[256];
	FILE *ol, *nl;
	int i=0;
	stat(LogFile,&st);
	if (st.st_size>LogSize*1024 && LogSize)
	{
		if ((ol=fopen(LogFile,"rt"))!=NULL)
		{
			strcpy(name,TEMPPATH"log.feddi");
			if ((nl=fopen(name,"wt"))!=NULL)
			{
				while (line[0]!='\n')
					while (fgets(line,256,ol) && st.st_size-i>LogSize*1024)
						i+=strlen(line);
				while (fgets(line,256,ol))
					fputs(line,nl);
				fclose(nl);
				fclose(ol);
				nl=fopen(LogFile,"wt");
				ol=fopen(name,"rt");
				while (fgets(line,256,ol))
					fputs(line,nl);
				fclose(ol);
				fclose(nl);
				remove(name);
				chmod(LogFile,FIDO_MOD);
				chown(LogFile,-1,FIDO_GID);
			} else fclose(ol);
		}
	}
}

void savelogpos()
{
	struct stat st;
	sync();
	if (!stat(LogFile,&st))
		logpos=st.st_size;
	else
		logpos=0;
}

int logposchanged()
{
	struct stat st;
	sync();
	if (!stat(LogFile,&st))
		return (logpos!=st.st_size);
	return 0;
}

void backslash(char *str)
{
	char *dummy;
	int i, j, num, k;
	dummy=(char *)malloc(strlen(str)+1);
	for (i=0, j=0; i<strlen(str);)
		if (str[i]=='\\')
		{
			for (num=0, i++, k=0; \
					str[i]>='0' && str[i]<='9' && k<3; \
					num=(num*10)+str[i++]-'0', k++);
			dummy[j++]=num;
		}
		else
			dummy[j++]=str[i++];
	dummy[j]=0;
	strcpy(str,dummy);
}

MsgDataType getmsgdata(char *an, char *path)
{
	char name[PATH_MAX];
	FILE *mdf;
	MsgDataType md;
	memset(&md,0,MsgDataSize);
	sprintf(name,"%s+%s/data",path?path:BasePath,an);
	if ((mdf=fopen(name,"rb"))!=NULL)
	{
		fread(&md,MsgDataSize,1,mdf);
		fclose(mdf);
	}
	return md;
}

void newarea(char *an, UserAkaType *aka)
{
	char name[PATH_MAX], path[PATH_MAX];
	FILE *mdf;
	MsgDataType md;
	sprintf(path,"%s+%s",BasePath,an);
	mkdir(path,FIDO_MOD+73);
	chown(path,-1,FIDO_GID);
	sprintf(name,"%s+%s/header",BasePath,an);
	mhf=fopen(name,"w+b");
	fclose(mhf);
	chmod(name,FIDO_MOD);
	chown(name,-1,FIDO_GID);
	sprintf(name,"%s+%s/text",BasePath,an);
	mtf=fopen(name,"w+b");
	fclose(mtf);
	chmod(name,FIDO_MOD);
	chown(name,-1,FIDO_GID);
	sprintf(name,"%s+%s/data",BasePath,an);
	mdf=fopen(name,"w+b");
	md.ID[0]=PCODE[0];
	md.ID[1]=PCODE[1];
	md.ID[2]=PCODE[2];
	md.VMAJOR=MBVERhi;
	md.VMINOR=MBVERlo;
	md.VPATCH=MBVERpl;
	md.LastRead=0;
	md.ReadOnly=md.NetMail=0;
	md.RouteZone=aka->UserZone;
	md.RouteNet=aka->UserNet;
	md.RouteNode=aka->UserNode;
	md.New=md.Deleted=md.NewPersonal=md.Linked=0;
	md.Marked=0;
	md.CharSet=0;
	strcpy(md.Owner,Profile.UserName);
	strcpy(md.RealName,an);
	fwrite(&md,MsgDataSize,1,mdf);
	fclose(mdf);
	chmod(name,FIDO_MOD);
	chown(name,-1,FIDO_GID);
}

void getsizes(char *an, char *path, dword *tm, dword *tr)
{
	char name[PATH_MAX];
	struct stat st;
	sprintf(name,"%s+%s/header",path?path:BasePath,an);
	if (!stat(name,&st))
		*tm=st.st_size/MsgHdrSize;
	else
		*tm=0;
	sprintf(name,"%s+%s/text",path?path:BasePath,an);
	if (!stat(name,&st))
		*tr=st.st_size/256;
	else
		*tr=0;
}

void loadorig(char *area)
{
	char line[PATH_MAX], *d1;
	FILE *f;
	int x;
	sprintf(line,"%s+%s/origin",BasePath,area);
	if ((f=fopen(line,"rt"))==NULL)
	{
		if (BaseOrig) return;
		sprintf(line,"%sorigin",BasePath);
		if ((f=fopen(line,"rt"))==NULL)
		{
			for (x=0; x<Origlines; x++) free(OrigLine[x].line);
			free(OrigLine);
			OrigLine=NULL;
			Origlines=0;
			Origchoose=-1;
			return;
		}
		BaseOrig=1;
	} else
		BaseOrig=0;
	for (x=0; x<Origlines; x++) free(OrigLine[x].line);
	free(OrigLine);
	OrigLine=NULL;
	Origlines=0;
	Origchoose=-1;
	while (fgets(line,PATH_MAX,f))
	{
		d1=strrchr(line,'\n');
		if (d1) *d1=0;
		Origlines++;
		OrigLine=(LineListType *)realloc(OrigLine,Origlines*LineListSize);
		OrigLine[Origlines-1].line=strdup(line);
	}
	fclose(f);
}

void loadalias(char *area)
{
	char line[PATH_MAX], *d1;
	FILE *f;
	int x;
	sprintf(line,"%s+%s/alias",BasePath,area);
	if ((f=fopen(line,"rt"))==NULL)
	{
		if (BaseAlias) return;
		sprintf(line,"%salias",BasePath);
		if ((f=fopen(line,"rt"))==NULL)
		{
			for (x=0; x<Aliases; x++) free(Alias[x]);
			free(Alias);
			Aliases=2;
			Alias=(char **)malloc(2*sizeof(char *));
			Alias[0]=(char *)malloc(40);
			Alias[1]=(char *)malloc(strlen(Profile.UserName)+1);
			strcpy(Alias[1],Profile.UserName);
			return;
		}
		BaseAlias=1;
	} else
		BaseAlias=0;
	for (x=0; x<Aliases; x++) free(Alias[x]);
	free(Alias);
	Aliases=1;
	Alias=(char **)malloc(sizeof(char *));
	Alias[0]=(char *)malloc(40);
	while (fgets(line,PATH_MAX,f))
	{
		d1=strrchr(line,'\n');
		if (d1) *d1=0;
		Aliases++;
		Alias=(char **)realloc(Alias,Aliases*sizeof(char *));
		Alias[Aliases-1]=(char *)malloc(strlen(line)+1);
		strcpy(Alias[Aliases-1],line);
	}
	fclose(f);
	Aliases++;
	Alias=(char **)realloc(Alias,Aliases*sizeof(char *));
	Alias[Aliases-1]=(char *)malloc(strlen(Profile.UserName)+1);
	strcpy(Alias[Aliases-1],Profile.UserName);
}

dword openareasloppy(char *an)
{
	char name[PATH_MAX], path[PATH_MAX];
	struct stat st;
	dword ts;
	FILE *mdf;
	closearea();
	strcpy(OpenArea,an);
	sprintf(name,"%s+%s/header",BasePath,an);
	mhf=fopen(name,"r+b");
	if (!mhf)
	{
		sprintf(path,"%s+%s",BasePath,an);
		mkdir(path,FIDO_MOD+73);
		chown(name,-1,FIDO_GID);
		mhf=fopen(name,"w+b");
		chmod(name,FIDO_MOD);
		chown(name,-1,FIDO_GID);
	}
	stat(name,&st);
	TotalMsgs=st.st_size/MsgHdrSize;
	sprintf(name,"%s+%s/text",BasePath,an);
	mtf=fopen(name,"r+b");
	if (!mtf)
	{
		mtf=fopen(name,"w+b");
		chmod(name,FIDO_MOD);
		chown(name,-1,FIDO_GID);
	}
	stat(name,&st);
	ts=st.st_size/256;
	sprintf(name,"%s+%s/data",BasePath,an);
	mdf=fopen(name,"rb");
	if (!mdf)
	{
		mdf=fopen(name,"w+b");
		MsgData.ID[0]=PCODE[0];
		MsgData.ID[1]=PCODE[1];
		MsgData.ID[2]=PCODE[2];
		MsgData.VMAJOR=MBVERhi;
		MsgData.VMINOR=MBVERlo;
		MsgData.VPATCH=MBVERpl;
		MsgData.LastRead=0;
		MsgData.ReadOnly=MsgData.NetMail=0;
		MsgData.RouteZone=MsgData.RouteNet=MsgData.RouteNode=0;
		MsgData.New=MsgData.Deleted=MsgData.NewPersonal=MsgData.Linked=0;
		MsgData.Marked=0;
		MsgData.CharSet=0;
		if (strcmp(an,"NETMAIL")==0) MsgData.NetMail=1;
		strcpy(MsgData.Owner,Profile.UserName);
		strcpy(MsgData.RealName,an);
		fwrite(&MsgData,MsgDataSize,1,mdf);
		chmod(name,FIDO_MOD);
		chown(name,-1,FIDO_GID);
	} else
		fread(&MsgData,MsgDataSize,1,mdf);
	fclose(mdf);
	return ts;
}

void UNLOCKBase()
{
	char name[PATH_MAX];
	sprintf(name,"%sLOCK",BasePath);
	remove(name);
}

LOCKType FElock;

int LOCKBase(int startup)
{
	FILE *f;
	char name[PATH_MAX];
	int lockok=0;
	sprintf(name,"%sLOCK",BasePath);
	if ((f=fopen(name,"rb"))!=NULL)
	{
		if (readLOCK(&FElock,f))
		{
			fclose(f);
			if (kill(FElock.PID,SIGALRM)==-1)
				if (errno==ESRCH)
					if (remove(name)!=-1)
					{
						Log("&Removed expired LOCK '%s' [%u]",FElock.Prog,FElock.PID);
						lockok=1;
					}
		} else
		{
			fclose(f);
			if (remove(name)!=-1)
			{
				Log("&Removed expired LOCK '%s' [%u]",FElock.Prog,FElock.PID);
				lockok=1;
			}
		}
	} else
		lockok=1;
	if (lockok)
	{
		if ((f=fopen(name,"wt"))!=NULL)
		{
			strcpy(FElock.Prog,ProgName);
			FElock.PID=getpid();
			gethostname(FElock.Host,400);
			DateTime(FElock.Date);
			writeLOCK(&FElock,f);
			fclose(f);
			chmod(name,FIDO_MOD);
			chown(name,-1,FIDO_GID);
		} else
		{
			Log("!Can't lock Base");
			if (startup) printERR("\nCan't lock Base!!\n\n");
			return 1;
		}
	} else
	{
		Log("!MsgBase already locked by:");
		Log("!%s (PID %u from %s)",FElock.Prog,FElock.PID,FElock.Host);
		Log("!since %s",FElock.Date);
		if (startup)
			printERR("\nMsgBase already locked by:\n\n"\
					"%s (PID %u from %s) since %s\n\n",
					FElock.Prog,FElock.PID,FElock.Host,FElock.Date);
		return 1;
	}
	return 0;
}

void invalidLine(char *l, int n)
{
	printERR("\007Invalid Line in .feddirc!\n");
	printERR("%u: %s\n\n",n,l);
	Log("!.feddirc: error in line %u",n);
}

int extendhome(char *s)
{
	struct passwd *pwd=NULL;
	char d[PATH_MAX], *d1=NULL;
	if (s[0]=='~')
	{
		if (s[1]=='/')
		{
			strcpy(d,HomePath);
			strcat(d,s+1);
			strcpy(s,d);
			return 1;
		} else
			if ((d1=strchr(s,'/')))
			{
				*d1=0;
				if ((pwd=getpwnam(s+1)))
				{
					*d1='/';
					strcpy(d,pwd->pw_dir);
					strcat(d,d1);
					strcpy(s,d);
					return 1;
				}
				*d1='/';
			}
	}
	return 0;
}

int findfile(char *s, char *p)
{
	char path[PATH_MAX], file[PATH_MAX], *d1, *d2;
	struct stat st;
	strcpy(file,UtilityPath);
	strcat(file,s);
	if (!stat(file,&st))
		if (st.st_mode&0111 && st.st_mode&S_IFREG)
		{
			strcpy(p,UtilityPath);
			return 1;
		}
	if (*s=='/')
	{
		if (!stat(s,&st))
			if (st.st_mode&0111 && st.st_mode&S_IFREG)
			{
				strcpy(p,"");
				return 1;
			}
	}
	if (*s!='/')
	{
		if ((strcpy(path,getenv("PATH"))))
		{
			for (d1=path; d1; d1=d2)
			{
				if ((d2=strchr(d1,':'))) *d2++=0;
				strcpy(file,d1);
				mksl(file);
				strcat(file,s);
				if (!stat(file,&st))
					if (st.st_mode&0111 && st.st_mode&S_IFREG)
					{
						strcpy(p,d1);
						mksl(p);
						return 1;
					}
			}
		}
	}
	return 0;
}

int readLOCK(LOCKType *l, FILE *f)
{
	char str[4096], *d1, *d2;
	fgets(str,4095,f);
	l->PID=strtol(str,&d1,10);
	if (!d1) return 0;
	d1++;
	d2=strchr(d1,32);
	if (!d2) return 0;
	*d2++=0;
	strcpy(l->Prog,d1);
	d1=strchr(d2,32);
	if (!d1) return 0;
	*d1++=0;
	strcpy(l->Host,d2);
	strcpy(l->Date,d1);
	return 1;
}

void writeLOCK(LOCKType *l, FILE *f)
{
	fprintf(f,"%u %s %s %s",l->PID,l->Prog,l->Host,l->Date);
}

void printERR(char *template, ...)
{
	va_list params;
	if (!QUIET)
	{
		va_start(params,template);
		vfprintf(stderr,template,params);
		va_end(params);
	}
}

int parseswitch(int l, char *s, char *t, int *val)
{
	char *d1;
	if (strncasecmp(s,t,strlen(t))==0)
	{
		d1=strchr(s,32);
		if (!d1)
		{
			invalidLine(s,l);
			return 1;
		}
		while (*d1==32) d1++;
		if (strcasecmp(d1,"yes")==0) *val=1;
			else if (strcasecmp(d1,"no")==0) *val=0;
		return 1;
	}
	return 0;
}

int pipecall(char *command, char **args, FILE **readpipe, FILE **writepipe)
{
	int childpid, pipe1[2], pipe2[2];
	char path[PATH_MAX]="";
	if (!findfile(command,path))
	{
		Log("!Can't find %s in path",command);
		return 0;
	}
	strcat(path,command);
	if ((pipe(pipe1)<0) || (pipe(pipe2)<0))
	{
		Log("!Can't open pipes");
		return 0;
	}
	if ((childpid=vfork())<0)
	{
		Log("!Can't do vfork");
		return 0;
	} else
	{
		if (childpid>0)
		{
			close(pipe1[0]);
			close(pipe2[1]);
			*readpipe=fdopen(pipe2[0],"r");
			*writepipe=fdopen(pipe1[1],"w");
			setlinebuf(*writepipe);
			return childpid;
		} else
		{
			close(pipe1[1]);
			close(pipe2[0]);
			dup2(pipe1[0],0);
			dup2(pipe2[1],1);
			close(pipe1[0]);
			close(pipe2[1]);
			execvp(path,args);
			exit(EXIT_FAILURE);
		}
	}
	return 0;
}

int canwrite(FILE *wp)
{
	fd_set fds;
	struct timeval delay;
	memset(&delay,0,sizeof(struct timeval));
	FD_ZERO(&fds);
	FD_SET(fileno(wp),&fds);
	return (select(FD_SETSIZE,NULL,&fds,NULL,&delay)>0);
}

int canread(FILE *rp)
{
	fd_set fds;
	struct timeval delay;
	memset(&delay,0,sizeof(struct timeval));
	FD_ZERO(&fds);
	FD_SET(fileno(rp),&fds);
	return (select(FD_SETSIZE,&fds,NULL,NULL,&delay)>0);
}

void freedirentries(struct dirent ***ents, int num)
{
	int i;
	if (num)
	{
		for (i=0; i<num; i++) free((*ents)[i]);
		free((*ents));
	}
}

int strtoadr(char *s, Addr4dType *adr)
{
	memset(adr,0,Addr4dSize);
	for (; isdigit(*s); s++) adr->Zone=adr->Zone*10+(*s-'0');
	if (*s++!=':') return 1;
	if (*s=='*')
	{
		adr->Net=-1;
		s++;
	} else
		for (; isdigit(*s); s++) adr->Net=adr->Net*10+(*s-'0');
	if (*s==32 || *s==0)
	{
		adr->Node=adr->Point=-1;
		return 0;
	}
	if (*s++!='/') return 1;
	if (*s=='*')
	{
		adr->Node=-1;
		s++;
	} else
		for (; isdigit(*s); s++) adr->Node=adr->Node*10+(*s-'0');
	if (*s==32 || *s==0) return 0;
	if (*s++=='.')
	{
		if (*s=='*')
		{
			adr->Point=-1;
			s++;
		} else
			for (; isdigit(*s); s++) adr->Point=adr->Point*10+(*s-'0');
		if (*s!=32 && *s!=0) return 1;
	} else
		return 1;
	return 0;
}
