/* Those who hate GOTOs (like me) B E W A R E............
   To save code space, extensive use of GOTOs was used to prevent
   repetition of code. Could SURELY be cleaner, but it works!
   KO4KS	*/

#include <stdio.h>
#include <time.h>
#include <ctype.h>
#ifdef MSDOS
#include <dos.h>
#endif
#include "config.h"
#include "bm.h"
#include "ftpserv.h"
#include "mailutil.h"
#include "dirutil.h"
#include "files.h"
#include "smtp.h"

static void rmailit __ARGS((FILE *fp,char *from, char *to, char *subject,long startat));
static int handleError __ARGS((FILE *fp,char *from, char *str));
void parseheader __ARGS((FILE *fp,char *from, char *subject, char *buf,long *startat));
extern int mailuser __ARGS((FILE *data,char *from,char *to,char *origto));
extern int groupcommand __ARGS((char *action,char *group,char *user,int local, FILE **out, FILE *in));
extern char DAEMONSTR[], Version[];
extern char *skipwhite __ARGS((char *));
extern char *skipnonwhite __ARGS((char *));
extern char *Mbfwdinfo;
extern char shortversion[], SysMessage[];

static char DAEMONSTR[] = "%sREQSVR@%s (Mail Delivery Subsystem)\n";

int
rdaemon(data, from, to, msg, msgtype, mode)
FILE *data;		/* pointer to rewound data file */
char *from, *to, *msg, msgtype;
int mode;
{
time_t t;
FILE *tfile;
char buf[LINELEN], *newaddr, *orgto;
long mid;

	if((tfile = tmpfile()) == NULLFILE)
		return -1;
/*	if (to == NULLCHAR)
		mode = 1;		*/
	to = strdup ((to == NULLCHAR) ? "sysop" : to);
		
	orgto = strdup (to);
	if((newaddr = rewrite_address(to)) != NULLCHAR)	{
		free (to);
		to = newaddr;
	}
	time(&t);
	fprintf(tfile,Hdrs[RECEIVED]);
	fprintf(tfile,"from %s ",Hostname);
	mid = get_msgid(0);
#ifdef MBFWD
	fprintf(tfile,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
		Hostname, (Mbfwdinfo != NULLCHAR) ? Mbfwdinfo : shortversion, \
		mid, ptime(&t));
#else
	fprintf(tfile,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
		Hostname, shortversion, mid, ptime(&t));
#endif
	fprintf(tfile,"%s%c\n",Hdrs[BBSTYPE], msgtype);
	fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
	mid = get_msgid(1);
	fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],mid,Hostname);
	if (from == NULLCHAR)
		fprintf(tfile,DAEMONSTR,Hdrs[FROM],Hostname);
	else
		fprintf(tfile,"%s%s\n",Hdrs[FROM],from);
	fprintf(tfile,"%s%s\n",Hdrs[TO], orgto);
	fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT], msg);
	pwait (NULL);
	
	if (data != NULLFILE)	{
		if (mode)
			fprintf(tfile,"  ===== Message %sfollows ====\n", (msg == SysMessage) ? "" : "header ");
		while(fgets(buf,sizeof(buf),data) != NULLCHAR)	{
			if(mode && buf[0] == '\n')
				break;
			pwait (NULL);
			fputs(buf,tfile);
		}
/*		if (mode && (msg != SysMessage))
			fprintf(tfile,"  ===== Message Stored ====\n");		*/
	} else
		fputc ('\n', tfile);
	rewind(tfile);

	sprintf (buf, "REQSVR@%s", Hostname);
	(void) mailuser(tfile, (from) ? from : buf, to, orgto);
	free (to);
	free (orgto);
	fclose(tfile);
	pwait (NULL);
	return 0;
}
#if defined(REQSVR) || defined(RMAIL) || defined(DELEGATE)

void
parseheader (fp, from, subject, buf, startat)
FILE *fp;
char *from, *subject, *buf;
long *startat;
{
char *cp, *temp;

	rewind (fp);
	subject[0] = from[0] = 0;
	while(fgets(buf,128,fp) != NULLCHAR)	{
		if(buf[0] == '\n')
			break;
		rip (buf);
		if (htype(buf) == FROM)		{
			cp = &buf[strlen(Hdrs[FROM])];
			if (*cp == '"')	{
				cp++;
				if ((temp = strchr (cp, '"')) != NULLCHAR)
					cp = temp+1;
			}
			if ((temp = strchr (cp, '<')) != NULLCHAR)	{
				cp = temp+1;
				if ((temp = strchr (cp, '>')) != NULLCHAR)
					*temp = 0;
			}
			if ((temp = strpbrk (cp, " \t")) != NULLCHAR)
				*temp = 0;
			strcpy (from, cp);
		}
		if (htype(buf) == SUBJECT)	{
			cp = &buf[strlen(Hdrs[SUBJECT])];
			cp = skipwhite(cp);
			if (*cp == '"')	{
				cp++;
				if ((temp = strchr (cp, '"')) != NULLCHAR)
					*temp = 0;
			}
			strcpy (subject, cp);
		}
	}
	*startat = ftell (fp);
	if (fgets(buf,128,fp) != NULLCHAR)	{
		if(!strnicmp (buf, "R:", 2))	{
			while(fgets(buf,128,fp) != NULLCHAR)	{
				if(buf[0] == '\n')
					break;
			}
		*startat = ftell (fp);
		}
	}
	pwait (NULL);
}


static int
handleError (fp, from, str)
FILE *fp;
char *from, *str;
{
	rewind (fp);
	rdaemon (fp, NULLCHAR, from, str, 'P', 1);
	rewind (fp);
	rdaemon (fp, NULLCHAR, NULLCHAR, str, 'P', 1);
	return 0;	/* pass the message back */
}


#endif


#ifdef RMAIL
static char rmailerror[] = "Rmail Error";

static void
rmailit (fp, from, to, subject, startat)
FILE *fp;
char *from, *to, *subject;
long startat;
{
char *thisto;

	while (*to && to)	{
		thisto = to;
		to = strchr (to, ',');
		if (to)	{
			*to++ = 0;
			to = skipwhite (to);
		}
		if (!strnicmp (thisto, "rmail@", 6))
			continue;
		pwait (NULL);
		fseek (fp, startat, SEEK_SET);
		rdaemon (fp, from, thisto, subject, 'P', 0);
	}
}


int
rmail (fp, from)
FILE *fp;
char *from;
{
char buf[512], subject[256], realfrom[128], *to = NULLCHAR;
long startat;

	parseheader (fp, realfrom, subject, buf, &startat);
	if (realfrom[0] && subject[0])		{
		while(fgets(buf,512,fp) != NULLCHAR)	{
			if(buf[0] == '\n')
				break;
			if (htype(buf) == TO)	{
				if (to != NULLCHAR)	{	/* more than 1 To: - ERROR */
					free (to);
					to = NULLCHAR;
					break;
				} else	{
					rip(buf);
					to = strdup (&buf[4]);
				}
			}
		}
		if (to != NULLCHAR)	{
			rmailit (fp, realfrom, to, subject, startat);
			free (to);
			return 1;
		}
	}
	return (handleError (fp, realfrom, rmailerror));
}

#endif



#ifdef REQSVR
int
reqsvr (fp, from)
FILE *fp;
char *from;
{
char buf[128];
char name[128], path[128], realfrom[128];
char subject[LINELEN], *file, cmd, *cp = path, action = 'I', *ptr, *ptr2;
int anony = 1, mode = RETR_CMD, found = 0, first = 0;
FILE *out, *out2;
long prev, msgdata, privs;
unsigned attr;
#ifdef SAMCALLB
int err;
#endif
	parseheader (fp, realfrom, subject, buf, &prev);
	sprintf(buf,"%s/reqsvr.log",Spool);
	if((out = fopen(buf,APPEND_TEXT)) != NULLFILE)	{
		time_t t;
		time(&t);
		fprintf (out, "Received from %s (%s) on %s", realfrom, subject, ptime(&t));
		fclose (out);
	}
	if (!*subject)
		return 0;	/* we are konfuzd! No subject!?!? */
	pwait (NULL);
	do	{
		if (fgets(buf,128,fp) == NULLCHAR)
			break;
		if (!first && *buf == '\n')
			strcpy (buf, "R:");
		if (!strnicmp (buf, "R:", 2))
			found = 1;
		first = 1;
	} while (*buf != '\n');
	if (!found)
		fseek (fp, prev, SEEK_SET);
/*	else
		fgets(buf,128,fp);	*/	/* strip off blank line */

	/* now the fix for FBB/PRMBS/TNOS adding RFC 822-type lines to messages */
	prev = ftell (fp);
	if (fgets (buf, 128, fp) != NULLCHAR)	{
		if (strnicmp (buf, "From:", 5) && strnicmp (buf, "Date:", 5) && strnicmp (buf, "Message-Id:", 11))
			fseek (fp, prev, SEEK_SET);
		else	do	{
				if (fgets (buf, 128,fp) == NULLCHAR)
					break;
			} while (*buf != '\n');
	}

	msgdata = ftell (fp);
	strncpy (name, realfrom, 40);
	file = strchr (name, '@');
	if (file)
		*file = 0;
	cmd = tolower (*subject);
	/* get senders permissions and default path */
	privs = userlogin (name, NULLCHAR, &cp, 128, &anony);
	
	file = skipnonwhite (&subject[1]);
	file = skipwhite (file);
	file = pathname (path, file);
#ifdef DOS_GETFILEATTR
	_dos_getfileattr (file, &attr);
#endif
	if (!strnicmp (subject, "infor", 5))
		cmd = 'f';	/* inform */
	if (!strnicmp (subject, "uni", 3))
		cmd = 'n';	/* uninform */
	if (!strnicmp (subject, "uns", 3))
		cmd = 'c';	/* unsubscribe */

	switch (cmd)	{
		case 'i':	/* info */
		case 'h':	/* help */
				free (file);
				file = pathname (Helpdir, "reqsvr.hlp");
				if ((out = fopen (file, READ_TEXT)) != NULLFILE)
					goto okay;
				sprintf (buf, "Not Found %s", subject);
				goto error;
		case 'g':	/* group commands */
				ptr = skipnonwhite (&subject[1]);
				ptr = skipwhite (ptr);
				cmd = tolower (*ptr);
				if (cmd != 's' && cmd != 'u' && cmd != 'l')
					if (privs == -1 || !(privs & SYSOP_CMD))
						goto denied;

				ptr2 = skipnonwhite (ptr);
				action = *ptr2;
				*ptr2++ = 0;
				ptr2 = skipwhite (ptr2);
				switch (cmd)	{
					case 's':
					case 'u':
					case 'a':
					case 'd':
					case 'i':
					case 'l':	out = NULLFILE;
							found = groupcommand (ptr, (action) ? ptr2 : NULLCHAR, realfrom, 0, &out, fp);
							*(--ptr2) = action;
							if (found)	{
								if (cmd == 'l')
									goto done;
								else
									goto okay;
							} else
								goto denied;
					default:	*(--ptr2) = action;
							goto confuzed;
				}
		case 'q':	/* return callbook server data */
#ifdef SAMCALLB
				ptr = skipnonwhite (&subject[1]);
				ptr = skipwhite (ptr);
				if((out = tmpfile()) != NULLFILE){
					if ((err = cb_lookup (0, ptr, out)) != 0)	{
						if (err == 1)
							goto confuzed;
						else
							fprintf (out, "Amateur callsign '%s' not found or invalid\n", ptr);
					}
					goto okay;
				}
#endif
				goto ioerror;
		case 'v':	/* return version info */
				if((out = tmpfile()) != NULLFILE){
					fprintf (out, "Currently running %s at %s\n", Version, Hostname);
					goto okay;
				}
				goto ioerror;
		case 'u':	/* upload a file */
				mode = STOR_CMD;
		case 's':	/* subscribe to a file */
		case 'c':	/* unsubscribe to a file */
		case 'f':	/* inform of changes to a file */
		case 'n':	/* uninform of changes to a file */
		case 'w':	/* send a directory */
		case 'd':	/* download a file */
				if ((privs == -1) || (!permcheck(path, privs, mode, file)))	{
denied:					sprintf (buf, "Denied %s", subject);
					goto error;
				}
				switch (cmd)	{
					case 'c':	/* unsubscribe */
					case 'n':	/* uninform */
					case 's':	/* subscribe */
					case 'f':	/* inform */
						if (cmd == 'c' || cmd == 's')	/* subscribe functions */
							action = 'S';
						if(attr & FA_DIREC)	/* whole dir */
							cp = "/";
						else	{
							cp = strrchr (file, '/');
							*cp++ = 0;
						}
						sprintf(buf,"%s/reqsvr.dat",file);
						if (cmd == 's' || cmd == 'f')	{	/* adding functions */
							if((out = fopen(buf,APPEND_TEXT)) != NULLFILE)	{
								fprintf (out, "%s %s %c\n", cp, realfrom, action);
								goto complete;
							}
							goto ioerror;
						} else	{		/* removing functions */
							if((out = fopen(buf,UPDATE_TEXT)) != NULLFILE)	{
								while(fgets(buf,128,out) != NULLCHAR)	{
									pwait (NULL);
									rip (buf);
									if (!strnicmp (buf, cp, strlen (cp)))
										if (!strnicmp ((strchr (buf, ' ') + 1), realfrom, strlen (realfrom)))
											if (buf[strlen(buf) - 1] == action)	{
												fseek (out, (long) (ftell (out) - 3), SEEK_SET);
												fputc ('N', out);
												break;
											}
								}
							}
							goto complete;
						}
					case 'w':	/* what (dir) */
						if((out = dir(file, 1)) == NULLFILE)
							goto ioerror;
						goto okay;
					case 'u':	/* upload */
						if ((out = fopen (file, WRITE_TEXT)) == NULLFILE)	{
ioerror:						sprintf (buf, "IO Error %s", subject);
							goto error;
						}
						while(fgets(buf,128,fp) != NULLCHAR)	{
							pwait (NULL);
							fputs(buf,out);
						}
						fclose (out);
						cp = strrchr (file, '/');
						*cp++ = 0;
						sprintf(buf,"%s/reqsvr.dat",file);
						if((out = fopen(buf,READ_TEXT)) != NULLFILE)	{
							if((out2 = tmpfile()) != NULLFILE)
								fprintf (out2, "File changed by %s\n", realfrom);
							while(fgets(name,128,out) != NULLCHAR)	{
								pwait (NULL);
								if (*name == '/' || !strnicmp (name, cp, strlen (cp)))	{
									ptr = strrchr (name, ' ');
									*ptr = 0;
									action = ptr[1];
									ptr = (strchr (name, ' ') + 1);
									switch (action)	{
									 	case 'S':
									 		sprintf (buf, "Subscribe %s/%s", file, cp);
									 		fseek (fp, msgdata, SEEK_SET);
									 		rdaemon (fp, NULLCHAR, ptr, buf, 'P', 0);
									 		break;
									 	case 'I':
									 		sprintf (buf, "Inform %s/%s", file, cp);
											if (out2 != NULLFILE)
												rewind (out2);
									 		rdaemon (out2, NULLCHAR, ptr, buf, 'P', 0);
									 		break;
									}
								}
							}
							if (out2 != NULLFILE)
								fclose (out2);
						}
complete:					if (out != NULLFILE)	{
							fclose (out);
							out = NULLFILE;
						}
						goto okay;
					case 'd':	/* download */
						if ((out = fopen (file, READ_TEXT)) == NULLFILE)
							goto ioerror;
okay:						sprintf (buf, "Accepted %s", subject);
						if (out != NULLFILE)
							rewind (out);
						rdaemon (out, NULLCHAR, realfrom, buf, 'P', 0);
						if (out != NULLFILE)
							fclose (out);
				}
				break;
confuzed:
		default:	sprintf (buf, "Unknown %s", subject);
				goto error;
	}
done:	free (file);
	return 1;	/* we handled it, here! */

error:	free (file);
	return (handleError (fp, realfrom, buf));
}

#endif
