/* 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 "global.h"
#include "ctype.h"
#include "commands.h"
#include "bm.h"
#ifdef MSDOS
#undef FA_DIREC
#define FA_DIREC 0x10
#else
#include "dirutil.h"
#endif
#include "ftpserv.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: reqsvr.c,v 1.26 1997/08/19 01:19:22 root Exp $";
#endif

extern int mailuser (FILE *data,const char *from,const char *to,const char *origto);
extern int groupcommand (char *action,char *group,char *user,int local, FILE **out, FILE *in);
extern char *Mbfwdinfo;
extern char shortversion[], SysMessage[];
extern char *Callserver;
extern short SecureTelnet;
#ifdef RMAIL
static void rmailit (FILE *fp,char *from, char *to, char *subject,long startat);
#endif
#if defined(REQSVR) || defined(RMAIL) || defined(MAILFILTER)
static int handleError (FILE *fp,char *from, char *str);
#endif
#if defined(REQSVR) || defined(RMAIL) || defined(DELEGATE) || defined(MAILFILTER)
static void getfield (char *into, char *from, int fieldtype);
#endif
static void rdaemon_str (char *str,const char *replyto, const char *from, const char *to, const char *msg, const char msgtype, int mode);

char *reqsvr_filter_subject = NULLCHAR;
int reqsvr_bypass_permissions = 0;

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

static void
rdaemon_str(str, replyto, from, to, msg, msgtype, mode)
char *str;
const char *replyto;
const char *from, *to;
const char *msg, msgtype;
int mode;
{
FILE *fp;

	fp = tmpfile();
	if (fp)	{
		fputs (str, fp);
		rewind (fp);
		(void) rdaemon (fp, replyto, from, to, msg, msgtype, mode);
		(void) fclose (fp);
	}
}


int
rdaemon(data, replyto, from, to, msg, msgtype, mode)
FILE *data;		/* pointer to rewound data file */
const char *replyto;
const char *from, *to;
const char *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, 0)) != NULLCHAR)	{
		free (to);
		to = newaddr;
	}
	(void) 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], strlwr(orgto));
	if (replyto != NULLCHAR)
		fprintf(tfile, "%s%s\n", Hdrs[REPLYTO], replyto);
	fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT], msg);
	kwait (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;
			kwait (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);
	(void) fclose(tfile);
	kwait (NULL);
	return 0;
}

int
dosendmail (argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
FILE *fp;
char *from, *to, *data, *subj;
char buf[LINELEN];

	to = argv[1];
	if (argc > 4) {
		from = argv[2];
		subj = argv[3];
		data = argv[4];
	} else {
		sprintf (buf, "sysop@%s", Hostname);
		from = buf;
		subj = argv[2];
		data = argv[3];
	}
	if (*data == '<')	{
		/* filename given */
		if ((fp = fopen (&data[1], "r")) == NULL) {
			tprintf ("SendMail: Error opening file '%s'!", &data[1]);
			return 0;
		}
		(void) rdaemon (fp, NULLCHAR, from, to, subj, 'P', 0);
		(void) fclose (fp);
	} else 		/* string data given */
		rdaemon_str (data, NULLCHAR, from, to, subj, 'P', 0);
	smtptick (NULL);	/* wake SMTP to send that mail */
	return 0;
}



#if defined(REQSVR) || defined(RMAIL) || defined(DELEGATE) || defined(MAILFILTER) || defined(WPAGES) || defined(TUTOR)


static void
getfield (into, from, fieldtype)
char *into, *from;
int fieldtype;
{
char *cp, *temp;

	cp = &from[strlen(Hdrs[fieldtype])];
	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 (into, cp);
}


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

	rewind (fp);
	subject[0] = from[0] = 0;
	while(fgets(buf,128,fp) != NULLCHAR)	{
		if(buf[0] == '\n')
			break;
		rip (buf);
		fieldtype = htype(buf);
		if (fieldtype == FROM)	{
			trimright (buf);
			getfield (from, buf, fieldtype);
		}
		if (fieldtype == SUBJECT)	{
			trimright (buf);
			cp = &buf[strlen(Hdrs[SUBJECT])];
			cp = skipwhite(cp);
			if (*cp == '"')	{
				cp++;
				if ((temp = strchr (cp, '"')) != NULLCHAR)
					*temp = 0;
			}
			strcpy (subject, cp);
		}
		if ((to != NULLCHAR) && (fieldtype == TO))	{
			trimright (buf);
			getfield (to, buf, fieldtype);
		}
		if ((bid != NULLCHAR) && (fieldtype == MSGID))	{
			trimright (buf);
			getfield (bid, buf, fieldtype);
		}
	}
	*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);
		}
	}
	kwait (NULL);
}
#endif



#if defined(REQSVR) || defined(RMAIL) || defined(MAILFILTER)

static int
handleError (fp, from, str)
FILE *fp;
char *from, *str;
{
	rewind (fp);
	(void) rdaemon (fp, NULLCHAR, NULLCHAR, from, str, 'P', 1);
	rewind (fp);
	(void) rdaemon (fp, NULLCHAR, 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;
		kwait (NULL);
		fseek (fp, startat, SEEK_SET);
		(void) rdaemon (fp, NULLCHAR, from, thisto, subject, 'P', 0);
	}
}


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

	parseheader (fp, realfrom, subject, NULLCHAR, NULLCHAR, buf, &startat);
	if (realfrom[0] && subject[0])		{
		fseek (fp, startat, 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



#if defined(REQSVR) || defined(MAILFILTER)
int
reqsvr (fp, from)
FILE *fp;
const char *from OPTIONAL;
{
char buf[128], smallbuf[2];
char name[128], path[128], realfrom[128];
char subject[LINELEN], *file, cmd, *cp = path, action = 'I', *ptr, *ptr2;
char *ptr3, *cp1, *cp2;
int anony = 1, mode = RETR_CMD, found = 0 /*, first = 0*/ ;
FILE *out, *out2;
long prev, msgdata, privs;
unsigned attr = 0;
int overrideperm = 0;
#ifdef SAMCALLB
int err;
#endif
#if defined(SAMCALLB) || defined(CALLCLI)
struct mbx *mb;
#endif

	parseheader (fp, realfrom, subject, NULLCHAR, NULLCHAR, buf, &prev);
	if (reqsvr_filter_subject)	{
		strcpy (subject, reqsvr_filter_subject);
		reqsvr_filter_subject = NULLCHAR;
	}
	if (reqsvr_bypass_permissions)	{
		overrideperm = 1;
		reqsvr_bypass_permissions = 0;
	}
	sprintf(buf,"%s/reqsvr.log", LOGdir);
	if((out = fopen(buf,APPEND_TEXT)) != NULLFILE)	{
		time_t t;
		(void) time(&t);
		fprintf (out, "Received from %s (%s) on %s", realfrom, subject, ptime(&t));
		(void) fclose (out);
	}
	if (!*subject)
		return 0;	/* we are konfuzd! No subject!?!? */
	kwait (NULL);

#if 0
	/*
	** This code to detect R: headers is a problem.
	** parseheader() already seeks past R: Headers 
	** so the first line seen here is a blank line.
	** This causes any non blank lines to be skipped
	** until another blank line is encountered.
	** (sometimes the entire message !) -- N0EIR
	*/
	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);
#endif

/*	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 = (char) tolower (*subject);
	/* get senders permissions and default path */
	privs = userlogin (name, NULLCHAR, &cp, 128, &anony);
	
	file = skipnonwhite (&subject[1]);
	file = skipwhite (file);
	cp1 = strdup(cp);
	if ((cp2 = strchr(cp1, ';')) != NULLCHAR)
		*cp2 = '\0';
	file = pathname (cp1, file);
	free (cp1);
#ifdef DOS_GETFILEATTR
	(void) _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 = (char) 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);
				ptr3 = strdup (ptr2);
				ptr3 = strcat (ptr3, ".hlp");
				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 == 's')	{
									file = pathname (Helpdir, ptr3);
									if ((out = fopen (file, READ_TEXT)) != NULLFILE)
										goto okay;
								}
								if (cmd == 'l')
									goto done;
								else 
									goto okay;
							} else
								goto denied;
					default:	*(--ptr2) = action;
							goto confuzed;
				}
		case 'q':	/* return callbook server data */
#if defined(SAMCALLB) || defined(CALLCLI)
				ptr = skipnonwhite (&subject[1]);
				ptr = skipwhite (ptr);
				if((out = tmpfile()) != NULLFILE){
#ifdef SAMCALLB
					if ((err = cb_lookup (0, ptr, out)) != 0)	{
						if (err == 1)	{
#endif
							if (Callserver && *Callserver)	{
								char *newargv[3], bufport[8], cmdbuf[4];

								mb = (struct mbx *)callocw(1,sizeof(struct mbx));
								mb->quickfile = out;
								strcpy (cmdbuf, "C");
								newargv[0] = cmdbuf;
								newargv[1] = Callserver;
								sprintf(bufport,"%d",IPPORT_CALLDB);
								newargv[2] = bufport;
								mb->startmsg = mallocw(80);  /* is freed each time by gw_connect() */
								sprintf(mb->startmsg,"%s\n", ptr);
								SecureTelnet = 0;
								strcpy (mb->name, "reqsvr");
								mb->stype = '+';
								(void) dombtelnet(3,newargv,mb);
								free (mb);
							} else
								goto confuzed;
#ifdef SAMCALLB
						} else
							fprintf (out, "Amateur callsign '%s' not found or invalid\n", ptr);
					}
#endif
					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;	/*lint !e616 */
		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 ((!overrideperm && 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 */
							smallbuf[0] = '/';
							smallbuf[1] = 0;
							cp = smallbuf;
						} else	{
							cp = strrchr (file, '/');
							if (cp)
								*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)	{
									char *tmp1;
									kwait (NULL);
									rip (buf);
									if (cp && !strnicmp (buf, cp, strlen (cp)))	{
										tmp1 = strchr (buf, ' ');
										if (tmp1)
											if (!strnicmp (++tmp1, 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)	{
							kwait (NULL);
							fputs(buf,out);
						}
						(void) fclose (out);
						cp = strrchr (file, '/');
						if (cp)
							*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)	{
								kwait (NULL);
								if (!cp)
									goto confuzed;
								if (*name == '/' || !strnicmp (name, cp, strlen (cp)))	{
									ptr = strrchr (name, ' ');
									if (!ptr)
										goto confuzed;
									*ptr = 0;
									action = ptr[1];
									ptr = strchr (name, ' ');
									if (!ptr)
										goto confuzed;
									ptr++;
									switch (action)	{
									 	case 'S':
									 		sprintf (buf, "Subscribe %s/%s", file, cp);
									 		fseek (fp, msgdata, SEEK_SET);
									 		(void) rdaemon (fp, NULLCHAR, NULLCHAR, ptr, buf, 'P', 0);
									 		break;
									 	case 'I':
									 		sprintf (buf, "Inform %s/%s", file, cp);
											if (out2 != NULLFILE)
												rewind (out2);
									 		(void) rdaemon (out2, NULLCHAR, NULLCHAR, ptr, buf, 'P', 0);
									 		break;
									 	default:
									 		break;
									}
								}
							}
							if (out2 != NULLFILE)
								(void) fclose (out2);
						}
complete:					if (out != NULLFILE)	{
							(void) fclose (out);
							out = NULLFILE;
						}
						if (!strnicmp (subject, "uploadblind", 11) || !strnicmp (subject, "ub", 2))
							goto done;
						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);
						(void) rdaemon (out, NULLCHAR, NULLCHAR, realfrom, buf, 'P', 0);
						if (out != NULLFILE)
							(void) fclose (out);
						break;
					default:
						break;
				}
				break;
confuzed:
		default:	sprintf (buf, "Unknown %s", subject);	/*lint !e616 */
				goto error;
	}
done:	free (file);
	return 1;	/* we handled it, here! */

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

#endif
