/* rsmtp.c */

#define	RSMTPMASTER

/*
 *  Includes
 */
#include <stdio.h>
#include <string.h>

#include "os.h"
#include "vrsion.h"
#include "vrsmtp.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "scmnd.h"
#include "cliutl.h"
#include "cticks.h"
#include "tsxutl.h"
#include "kbdutl.h"
#include "rtfile.h"
#include "datime.h"
#include "inicli.h"
#include "suspnd.h"
#include "usrblk.h"
#include "rcdlck.h"
#include "mailbx.h"

/*
 * Internal routines
 */

/*
 * Due to DECUS C aborts from lack of space
 * all functions/subroutines of type 'int'
 * are no longer explicitly defined.
 *
 * DECUS C defaults to 'int', VOID == int
 */

#if 0
extern	VOID	printtxt();		/* (txt) print text */
extern	int	dosess();		/* session loop */
extern	int	setsmtp();		/* setup SMTP session */
extern	VOID	rsmtpresp();		/* (s) response to client */
extern	VOID	lsterr();		/* error lister */
extern	int	response();		/* dumby routine for TSXUTL.C */
extern	VOID	fromproc();		/* (skt) 'From:' processing */
extern	VOID	toproc();		/* (skt) 'To:' processing */
extern	VOID	rsmtpd();		/* (code) process SMTP commands */
extern	VOID	transfer();		/* (fp,str) transfer text lines */
extern	int	pindex();		/* (i) get mail index number */
extern	int	strmatch();		/* (s,t) nocase string compare */
extern	int	sindex();		/* (i) get mail index number */
extern	int	getword();		/* (string,word) extract a word */
extern	VOID	strlwr();		/* (st) convert to lower case */
#endif

extern	char	*putmail();		/* place mail in users mailboxes */
extern	char	*sndmail();		/* place mail in user mailbox */
extern	char	*stptok();		/* (p,t,l,d) stop on token */
extern	char	*stpblk();		/* (ch) stop on block */
extern	char	*checkuser();		/* (us) check if this is a user */

/*
 *	Global Variables For RSMTP Control Channel
 */

static int
	aflag = 0,		/* Attach to Service Flag */
	rflag = 0,		/* RSMTP restart flag */
	rsmtpcskt = -1,		/* socket for incoming smtp */
	mflag = 0,		/* monitor processing */
	mode = 0,		/* processing mode */
	tofound = 0,		/* 'To:' found */
	waitpos = 0,		/* marker for gathering strings from net */
	rfstate = 0,		/* state of the control channel */
	retstate = 0,		/* to emulate a subroutine call */
	rcptcnt = 0,		/* number of recipients */
	rcnt = 0;		/* number of characters from control */

int	zflag = 0;		/* inactivity timeout flag */

long	izaptime = 0L;		/* inactivity zap time in ticks */

/*
 *	Global Buffers / Pointers
 */

#define	COMMAND		(1026)
#define	RSPSTRING	(514)
#define	RCPTNUM		(32)
#define GENBUFR		(128)
#define	BUFFERS		(512)

static char
	command[COMMAND],			/* command string */
	rsp[RSPSTRING],				/* response string */
	rcptname[RCPTNUM][GENBUFR+1],		/* recipients */
	rcptpath[RCPTNUM][DIRPATHLEN+1],	/* mailboxes */
	lclname[USERPASSLEN+1],			/* local user */
	lclpath[DIRPATHLEN+1],			/* local users path */
	pathname[GENBUFR],			/* space to keep path name */
	scratch[GENBUFR];			/* scratch string */

static int
	rcptmode[RCPTNUM];			/* mail mode */

struct userblock
	user;			/* usernames / passwords / mailboxes */

char	xs[BUFFERS];		/* dumby buffer required by TSXUTC.C */
				/* used by file copy routines */

static int			/* mailer task number */
	mailer = 0;
static char *			/* mailbox file */
	mailfl = "wf:mailbx.___";
static FILE *
	mailfh = NULL;

static char *			/* help file */
	helpfl = "TCP:rsmtp.hlp";
static FILE *
	helpfh = NULL;

static char *			/* password file */
	passfl = "PAS:paswrd.fil";
static FILE *
	passfh = NULL;


/*
 * Debugging options
 *
 *	bit 0	(0x02)	internal events
 *	bit 1	(0x01)	dosess() events
 *	bit 2	(0x04)	rsmtpd()
 *	bit 3	(0x08)	putmail() / pindex()
 *	bit 4	(0x10)	ckeckuser()
 */

#if ts$sys
static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  RSMTP [?] [-d level] [-hp filespec] [-t n] [-z s] [-aerv]",
       "	?               List the Help Text and Exit RSMTP",
       "	d  level	Debug Level",
       "		1 -	enable event printing",
       "		2 -	enable dosess() event printing",
       "		4 -	enable rsmtp() printing",
       "		8 -	enable putmail() / pindex() printing",
       "	       16 -	enable sndmail() / sindex() printing",
       "	       32 -	enable checkuser() printing",
#else
       "  RSMTP [?] [-hp filespec] [-t n] [-z s] [-aerv]",
       "	?               List the Help Text and Exit RSMTP",
#endif
       "	a		Attach to Service",
       "	e		Enable Monitoring of all RSMTP Transactions",
       "	h		Specify the Help Filespec",
       "	p		Specify the Password Filespec",
       "	r		Restart RSMTP after Disconnect",
       "	t  n		MAILER Delivery Task Number",
       "	v		Verbose Mode",
       "	z  s		Inactivity Timeout in Seconds",
       "",
       0
       };
#endif

#if rt$sys
static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  RSMTP [?] [-d level] [-hp filespec] [-z s] [-erv]",
       "	?               List the Help Text and Exit RSMTP",
       "	d  level	Debug Level",
       "		1 -	enable event printing",
       "		2 -	enable dosess() event printing",
       "		4 -	enable rsmtp() printing",
       "		8 -	enable putmail() / pindex() printing",
       "	       16 -	enable sndmail() / sindex() printing",
       "	       32 -	enable checkuser() printing",
#else
       "  RSMTP [?] [-hp filespec] [-z s] [-erv]",
       "	?               List the Help Text and Exit RSMTP",
#endif
       "	e		Enable Monitoring of all RSMTP Transactions",
       "	h		Specify the Help Filespec",
       "	p		Specify the Password Filespec",
       "	r		Restart RSMTP after Disconnect",
       "	v		Verbose Mode",
       "	z  s		Inactivity Timeout in Seconds",
       "",
       0
       };
#endif

/*
 *  printout text
 */
VOID printtxt(txt)
char **txt;
{
	register char **dp;

	for (dp = txt; *dp; dp++) {
		if (**dp == '_') {
			kb_puts(*dp + 1);
		} else {
			kb_puts(*dp);
			kb_nline();
		}
	}
}


main(argc,argv)
int argc;
char *argv[];
{
	int ev,i,j,k;
	register char c;
	register struct socket *skt;

	/*
	 * Initialize for TSX+ clients
	 */
	inicli();

	/*
	 * Initialize kb handler
	 * Set the break character to ^C
	 */
	kb_init(0x03);

	/*
	 * Verbose Mode Off
	 */
	kb_prnt = 0;

	/*
	 * parse arguments
	 */
	for(i=1; i<argc; i++) {
		if(argv[i][0] == '?') {
		    kb_prnt = 1;
		    printtxt(usetxt);
		    exit(0);
		} else
		if(argv[i][0] == '-') {
		    j = i;
		    k = 1;
		    while((c = argv[j][k]) != '\0') {
			switch(tolower(c)) {
#if ts$sys
			case 'a':	/* attach to service */
				aflag = 1;
				break;
#endif
#ifdef	DEBUGOPTION
			case 'd':	/* debug, optional level */
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = -1;
				break;
#endif
			case 'e':	/* enable RSMTP monitoring */
				mflag = 1;
				break;

			case 'h':	/* help file */
				helpfl = argv[++i];
				break;

			case 'p':	/* password file */
				passfl = argv[++i];
				break;

			case 'r':	/* enable RSMTP restarting */
				rflag = 1;
				break;
#if ts$sys
			/*
			 * This is a 'hidden' option for setting the
			 * the default subdirectory base 'ld' unit
			 * and setting the maximum subdirectory nesting
			 *	note:	subdbase + subdnest <= 8
			 */
			case 's':
				sscanf(argv[++i],"%d",&subdbase);
				sscanf(argv[++i],"%d",&subdnest);
				break;
#endif
#if ts$sys
			case 't':	/* MAILER Task Number */
				if(sscanf(argv[++i],"%d",&mailer) <= 0)
					mailer = 0;
				break;
#endif
			case 'v':	/* verbose mode on */
				kb_prnt = 1;
				break;

			case 'z':	/* inactivity timeout flag */
				sscanf(argv[++i],"%d",&zflag);
				izaptime = 60L * zflag;
				break;

			default:	/* unknown option */
				printf(
				"Unrecognized option -%c ignored\r\n", c);
				break;
			}
			k++;
		    }
		}
	}

#ifdef	DEBUGOPTION
	if (debug)
		kb_prnt = 1;
#endif

	/*
	 * identify self
	 */
	printtxt(vrstxt);

	/*
	 *  Start listening to the SMTP port
	 */
	while(((rsmtpcskt = setsmtp()) < 0) && !fndbrk) {
		/*
		 * Could not get a socket, wait 5 seconds
		 */
		suspnd(300);
	}

	while(!fndbrk && ev != -1) {
		ev = dosess();
		if(ev==0)
			suspnd(0);
	}

	if(fndbrk) 
		kb_puts("\r\n^C\r\n");

	skclose(rsmtpcskt);
	skrelease(rsmtpcskt,1);

	suspnd(0);
	errhandle();

	cd("dk:");

	kb_puts("\r\nEscaping from RSMTP\r\n");
	return(0);
}

/*
 *  dosess
 *
 *  dosess is an infinite loop serving message events
 */
int dosess()
{
	int  cl,ev,dat;

	/*
	 * Check for any relevant messages
	 * that need to be handled by me
	 */
	ev = Sgetevent(SCKTCLASS | CONCLASS | USERCLASS |
			ERRCLASS | MSGCLASS | ABORTCLASS, &cl, &dat);
	if(ev!=0) {

#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("dosess(): cl = %d, ev = %d, dat = %u\r\n", cl,ev,dat);
}
#endif

	  switch(cl) {
	    case CONCLASS:
	      /*
	       * RSMTP Connection socket
	       */
	      rsmtpd(ev);
	      break;

	    case MSGCLASS:	/* messages */
	    case ERRCLASS:	/* error message */
	      kb_puts(skerrstring(ev));
	      kb_nline();
	      break;

	    case ABORTCLASS:	/* abort */
	      switch(ev) {
		case CLIENT:
		  /*
		   * disable automatic restart
		   */
		  rflag = 0;
		  kb_puts("\r\nRSMTP session aborted by TCPIP\r\n");
		  Stmrset(ABORTCLASS,EXITABORT,dat,5);

		case USERABORT:
		  if(skrelease(dat,0) < 0) {
		    return(-1);
		  } else {
		    skclose(dat);
		    Stmrset(ABORTCLASS,USERABORT,dat,1);
		  }
		  break;

		case EXITABORT:
		  return(-1);
		  break;

		default:
		  break;
	      }
	      break;

	    default:
	      break;
	  }
	} else {
	  rsmtpd(0);
	}

	return(ev);
}

/*
 *	set up to receive a connection for smtp commands
 */
int setsmtp()
{
	int sknum;

	rfstate = 0;		/* state of the control channel */
	retstate = 0;		/* to emulate a subroutine call */
	rcnt = 0;		/* number of characters from control */
	waitpos = 0;		/* character position */

	/*
	 * Set the default directory
	 */
	cd("dk:");

	/*
	 * Initialize various things
	 */
	Snetinit();

	/*
	 * aflag = 1	indicates attach mode
	 * aflag = 0	indicates listen mode
	 */
	if(aflag) {
		/*
		 * Attach job to connection opened by TCPIP
		 */
		sknum = attach(0,HSMTP);
		if(sknum==-1) {
			fndbrk++;
			return(-1);
		} else {
			Stmrset(ABORTCLASS,CLIENT,sknum,CONNWAITTIME);
		}
	} else {
		/*
		 * Create socket for control connection
		 */
		sknum = socket(0);
		if(sknum==-1)
			return(-1);
		/*
		 * default port
		 */
		listen(sknum,HSMTP);
	}
	return(sknum);

}

/*
 * Dump response to client
 */
VOID rsmtpresp(s)
register char *s;
{
	skwrite(rsmtpcskt, s, strlen(s));
	skwrite(rsmtpcskt, "\r\n", 2);
	skenque(rsmtpcskt, 1);
	if(mflag) {
		kb_puts(s);
		kbprintf("  {%d}\r\n", strlen(s)+2);
	}
}

/*
 * transfer
 *
 * Transfer Message File
 */
transfer(fp,str)
FILE *fp;
char *str;
{
	int fini,i;

	fini = FALSE;
	strcpy(rsp,str);
	i = strlen(str);
	do {
		if(skroom(rsmtpcskt) > LOWWATER) {
			if(fgetss(rsp+i,RSPSTRING-1-i,fp) != NULL) {
				rsmtpresp(rsp);
			} else {
				fini = TRUE;
			}
		} else {
			suspnd(6);
		}
	} while(fini != TRUE);
}

/*
 * This is the error print routine for TSXUTL.C
 * It will be called only if there is an internal error
 */
VOID lsterr()
{
	if(*errstr) {
		kb_puts(errstr);
		kb_nline();
		errstr[0] = '\0';
	}
}

/*
 * This routine is a dumby routine for TSXUTL.C
 * It should never be called, but returns a 1 [ABORT]
 */
int rtresp()
{
	kb_puts("rtresp: This dumby routine should never be called");
	return(1);
}

/*
 *	Remote SMTP Server
 */
VOID rsmtpd(code)
int code;
{
	register struct socket *skt;
	register char *p,*s;
	char word[80];
	int i,icnt,rsmtpcmnd;

	if(rsmtpcskt < 0)
		return;

	skt = (struct socket *) mapskt(rsmtpcskt);

	switch(rfstate) {
		case 0:
/*S*/		switch(code) {
		case CONOPEN:
			Stmrunset(ABORTCLASS,CLIENT,rsmtpcskt);

			/*
			 * Enable Timeout
			 */
			cticks(&skquetime);

			sprintf(rsp,
	/*Net*/
	"220 %s Simple Mail Transfer Service Ready.", skt->hostname
	/*Net*/
				);
			rsmtpresp(rsp);

			retstate = 1;
			rfstate = 50;
			waitpos = 0; 
			break;

		case CONCLOSE:
			kb_puts("Connection Closed by Remote Client\r\n");
			rcnt = -1;
			break;

		case CONFAIL:
			kb_puts("Connection Failed\r\n");
			rcnt = -1;
			break;

		default:
			break;
/*S*/		}
			break;

		case 1:
			/*
			 * interpret commands that are
			 * received from the other side
			 * set to a safe state to handle recursion
			 * wait for another command line from client
			 */
			retstate = 1;
			rfstate = 50; 
			waitpos = 0;

			i = strlen(command);
			while(i && (command[--i] < 33)) {
				command[i] = '\0';
			}

			if(getword(command,word) == FALSE)
				break;

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("rsmtpd(%d):", code);
	printf("%s %s\r\n", word, command);
}
#endif

			rsmtpcmnd = scmnd(word);
			switch(rsmtpcmnd) {

			case F_DATA:
				if(!rcptcnt) {
					rsmtpresp(
	/*Net*/
	"554 No recipients."
	/*Net*/
						);
					break;
				}
				/*
				 * Create a unique file name for mail data
				 */
				sprintf(mailfl,"wf:mailbx.%03d", tljobn);
				/*
				 * Open mail file
				 */
				if((mailfh = fopen(mailfl, "wn")) == NULL) {
					rsmtpresp(
	/*Net*/
	"552 Unable to open data file for mail."
	/*Net*/
						);
				} else {
					rsmtpresp(
	/*Net*/
	"354 Start mail, end with '.\\r\\n' ."
	/*Net*/
						);
					fprintf(mailfh,
	/*Mail*/
	"Received: by %s  (%s%s)\r\n", skt->hostname, vrstxt[1]+1, vrstxt[2]
	/*Mail*/
					);
					fprintf(mailfh,
	/*Mail*/
	"      %s\r\n", datime()
	/*Mail*/
					);
					rfstate = 50;
					retstate = 60;
				}
				break;

			case F_EHLO:
			case F_HELO:
				sprintf(rsp,
	/*Net*/
	"250 %s", skt->hostname
	/*Net*/
					);
				rsmtpresp(rsp);
				/*
				 * Log Session
				 */
				sprintf(scratch,
	/*Log*/
	"RSMTP: %s", command
	/*Log*/
					);
				logsession(rsmtpcskt,scratch);

				break;

			case F_HELP:
	/*Net*/
rsmtpresp("214-Begin Help");
sprintf(rsp,"    %s%s", vrstxt[1]+1, vrstxt[2]);
rsmtpresp(rsp);
	p = skt->tcpout.i.ipsource;
sprintf(rsp,"    Remote SMTP Server %s [%d.%d.%d.%d]", skt->hostname,
		*(p+0)&0xFF, *(p+1)&0xFF, *(p+2)&0xFF, *(p+3)&0xFF);
rsmtpresp(rsp);
	p = skt->tcpout.i.ipdest;
sprintf(rsp,"    Initiated from host: %d.%d.%d.%d",
		*(p+0)&0xFF, *(p+1)&0xFF, *(p+2)&0xFF, *(p+3)&0xFF);
rsmtpresp(rsp);
rsmtpresp("214-");
rsmtpresp("214-RSMTP-11 server, commands:");
rsmtpresp("214-DATA    EHLO    EXPN*   HELO    HELP");
rsmtpresp("214-MAIL    NOOP    QUIT    RCPT    RSET");
rsmtpresp("214-SAML    SEND    SOML    TURN*   VRFY*");
rsmtpresp("214-[*] - unsupported commands");
rsmtpresp("214-This implementation treats SAML == SEND == SOML == MAIL");
	if((helpfh = rtopen(helpfl,"r",0,0)) != NULL) {
rsmtpresp("214-");
		transfer(helpfh,"214-");
		rtclose(helpfl);
	}
rsmtpresp("214 End Help");
	/*Net*/
				break;

			case F_MAIL:
			case F_SAML:
			case F_SEND:
			case F_SOML:
				/*
				 * Initialize
				 */
				rcptcnt = 0;
				mode = 0;

				fromproc(skt);
				break;

			case F_NOOP:
				rsmtpresp(
	/*Net*/
	"200 Ok."
	/*Net*/
					);
				break;

			case F_QUIT:
				sprintf(scratch,
	/*Net*/
	"221 %s Service closing transmission channel."
	/*Net*/
					);
				rsmtpresp(scratch);
				rcnt = -1;
				break;

			case F_RCPT:
				toproc(skt);
				break;
				
			case F_RSET:
				/*
				 * Initialize
				 */
				rcptcnt = 0;
				mode = 0;

				rsmtpresp(
	/*Net*/
	"250 Ok."
	/*Net*/
					);
				break;

			case F_EXPN:
			case F_TURN:
			case F_VRFY:
			default:
				if(rsmtpcmnd != F_NULL) {
					/*
					 * unimplemented commands
					 */
					sprintf(rsp,
	/*Net*/
	"502 Command '%s' not implemented.", s_clist[rsmtpcmnd]
	/*Net*/
						);
				} else {
					/*
					 * command not understood
					 */
					sprintf(rsp,
	/*Net*/
	"500 Command '%s %s' not understood.", word,command
	/*Net*/
						);
				}
				rsmtpresp(rsp);
				break;
			}
			break;

		case 50:
			/*
			 * subroutine to wait for end of line
			 */
			icnt = 0;
			while(	0 <
			     (rcnt = skread(rsmtpcskt,&command[waitpos],1))) {
				icnt = 1;
				if(command[waitpos] == '\n') {
					/*
					 * put in terminator
					 */
					command[++waitpos] = '\0';
					rfstate = retstate;
					break;
				} else {
					if(waitpos < COMMAND-1)
						waitpos += rcnt;
				}
			}
			if (icnt) {
				skdeque(rsmtpcskt);
				suspnd(-1);
			}
			break;

		case 60:
			/*
			 * subroutine to write out mail data
			 */
			rfstate = 50;
			retstate = 60;
			/*
			 * check for end of data
			 */
			if(waitpos==3 && strneq(command,".\r\n",3)) {
				if(mailfh != NULL) {
					if(fclose(mailfh)) {
						p =
	/*Net*/
	"552 Disk write error, probably disk full."
	/*Net*/
							;
					} else {
						p =
	/*Net*/
	"250 Ok."
	/*Net*/
							;
						p = sndmail(p);
						p = putmail(p);
						delete(mailfl);
					}
					mailfh = NULL;
				} else {
					p =
	/*Net*/
	"552 Temporary mail file not open for output."
	/*Net*/
						;
				}
				rsmtpresp(p);
				retstate = 1;
				waitpos = 0;
				break;
			}
			/*
			 * skip . if two .'s
			 * at beginning of line
			 */
			if(strneq(command,"..",2)) {
				s = command+1;
				i = waitpos-1;
			} else {
				s = command;
				i = waitpos;
			}
			/*
			 * write mail data to file
			 */
			if(mailfh != NULL && !fwrite(s,1,i,mailfh)) {
				/*
				 * disk full err
				 */
				fclose(mailfh);
				mailfh = NULL;
				rsmtpresp(
	/*Net*/
	"552 Disk write error, probably disk full."
	/*Net*/
					);
			}
			waitpos = 0;
			suspnd(-1);
			break;

		default:
			break;
	}

	if(retstate >= 1) {
		/*
		 * Process Inactivity Timeout
		 */
		if((zflag != 0) &&
		   (skquetime != 0L) &&
		   (elapsed(skquetime) > izaptime)) {
			rsmtpresp(
	/*Net*/
	"221 Inactivity Timeout"
	/*Net*/
				);
			rcnt = -1;
		}
	}

	if(rcnt < 0) {

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("rsmtpd(%d): rfstate = %d, rcnt = %d\r\n",
		code,rfstate,rcnt);
}
#endif
		rcnt = 0;
		if(mailfh != NULL) {
			fclose(mailfh);
			mailfh = NULL;
			sndmail(NULL);
			putmail(NULL);
			delete(mailfl);
		}
		if(rsmtpcskt >= 0) {
			sksendwait(rsmtpcskt,(long) SENDWAIT);
			skclose(rsmtpcskt);
			rsmtpcskt = skrelease(rsmtpcskt,1);
		}
		if(rflag && !aflag) {
			/*
			 *  Start listening to the SMTP port
			 */
			while((rsmtpcskt = setsmtp()) < 0 && !fndbrk) {
				/*
				 * Could not get a socket, wait 5 seconds
				 */
				suspnd(300);
			}
		} else {
			fndbrk = 1;
		}
	}
}

/*
 * fromproc
 *
 * 'From:' processing
 */
VOID fromproc(skt)
struct socket *skt;
{
	char *q,*p;
	char *s,*t,*u;
	int cmnd;

	p = skt->tcpout.i.ipsource;
	sprintf(scratch,"%d.%d.%d.%d",
		*(p+0)&0xFF, *(p+1)&0xFF,
		*(p+2)&0xFF, *(p+3)&0xFF);
	p = scratch;
	q = skt->hostname;

	/*
	 * Extract FROM:<...>
	 */
	strlwr(command);

	cmnd = strneq(command,"from:",5);
	s = strchr(command,'<');
	t = strchr(command,'@');
	u = strchr(command,'>');

	if(	cmnd &&
		(s != NULL) && (t == NULL) && (u != NULL) &&
		(s < u)
		) {
		/*
		 * Assume this is an Automated
		 * Mail Delivery Error Message.
		 * Probably of the form "from:<>".
		 */
		rsmtpresp(
	/*Net*/
	"250 Ok."
	/*Net*/
			);
	} else
	if(	!cmnd ||
		(s == NULL) || (t == NULL) || (u == NULL) ||
		(s > t) || (t > u)
		) {
		rsmtpresp(
	/*Net*/
	"501 Syntax error (no From:<...@...>)."
	/*Net*/
			);
	} else
	if(	!strneq(t+1,p,strlen(p)) &&
		strncmp(t+1,q,u-(t+1))
		) {
		/*
		 * This is normal incoming mail
		 */
		rsmtpresp(
	/*Net*/
	"250 Ok."
	/*Net*/
			);
	} else {
		/*
		 * This should be outgoing mail from a local user
		 * [generated by a POP client]
		 */
		*t = '\0';
		strncpy(lclname,s+1,USERPASSLEN);
		lclname[USERPASSLEN] = '\0';

		if((u = checkuser(lclname))==NULL) {
			rsmtpresp(
	/*Net*/
	"550 Unknown local user."
	/*Net*/
				);
		} else {
			strncpy(lclpath,u,DIRPATHLEN);
			lclpath[DIRPATHLEN] = '\0';
			mode = 1;
			rsmtpresp(
	/*Net*/
	"250 Ok."
	/*Net*/
				);
		}
	}
}

/*
 * toproc
 *
 * 'To:' processing
 */
VOID toproc(skt)
struct socket *skt;
{
	char *q,*p;
	char *s,*t,*u;

	p = skt->tcpout.i.ipsource;
	sprintf(scratch,"%d.%d.%d.%d",
		*(p+0)&0xFF, *(p+1)&0xFF,
		*(p+2)&0xFF, *(p+3)&0xFF);
	p = scratch;
	q = skt->hostname;

	strlwr(command);
	/*
	 * Process POP3 client Local Destination
	 */
	if(	mode &&
		strneq(command,"to:",3) &&
		(s = strchr(command,'<')) != NULL &&
		(t = strchr(command,'@')) == NULL &&
		(u = strchr(command,'>')) != NULL
		) {
		sprintf(u,"@%s>",q);
	}
	/*
	 * Extract TO:<...>
	 */
	if(	!strneq(command,"to:",3) ||
		(s = strchr(command,'<')) == NULL ||
		(t = strchr(command,'@')) == NULL ||
		(u = strchr(command,'>')) == NULL ||
		(s > t) || (t > u)
		) {
		rsmtpresp(
	/*Net*/
	"501 Syntax error (no To:<...@...>)."
	/*Net*/
			);
	} else
	if(rcptcnt == RCPTNUM) {
		rsmtpresp(
	/*Net*/
	"552 Exceeded storage allocation."
	/*Net*/
			);
	} else
	if(!strneq(t+1,p,strlen(p)) && strncmp(t+1,q,u-(t+1))) {
		if(mode) {
			*u = '\0';
			strncpy(rcptname[rcptcnt],s+1,GENBUFR);
			rcptname[rcptcnt][GENBUFR] = '\0';
			rcptmode[rcptcnt] = 1;
			rcptcnt++;
			rsmtpresp(
	/*Net*/
	"250 Ok."
	/*Net*/
				);
		} else {
			rsmtpresp(
	/*Net*/
	"550 Not a local user, forwarding not implemented."
	/*Net*/
				);
		}
	} else {
		*t = '\0';
		strncpy(rcptname[rcptcnt],s+1,USERPASSLEN);
		rcptname[rcptcnt][USERPASSLEN] = '\0';
		rcptmode[rcptcnt] = 0;

		if((u = checkuser(rcptname[rcptcnt]))==NULL) {
			rsmtpresp(
	/*Net*/
	"550 Unknown user."
	/*Net*/
				);
		} else
		if(!(user.userflag[1] & RSMTP_PRIV)) {
			rsmtpresp(
	/*Net*/
	"550 User privilege insufficient to accept mail."
	/*Net*/
				);
		} else {
			strncpy(rcptpath[rcptcnt],u,DIRPATHLEN);
			rcptpath[rcptcnt][DIRPATHLEN] = '\0';
			rcptcnt++;
			rsmtpresp(
	/*Net*/
	"250 Ok."
	/*Net*/
				);
		}
	}
}

/*
 * putmail
 *
 * put the mail into each recipients mailbox directory
 */
char *putmail(p)
char *p;
{
	FILE *inp,*oup;
	int i,j;
	char str[16];

	if((inp = fopen(mailfl,"r")) != NULL) {
	    for(i=0; i<rcptcnt; i++) {
		/*
		 * process only local mail
		 */
		if(rcptmode[i] != 0)
			continue;
		/*
		 * set mailbox directory path
		 * and get mail index for recipient
		 */
		if(j = pindex(i)) {
			sprintf(str,"%-.6s.%03d", rcptname[i], j);
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("putmail(): index = %d, mailbox = %s\r\n", j,str);
}
#endif
			if((oup = rtopen(str,"wn",0,0)) != NULL) {
				/*
				 * copy mail to recipient
				 */
				while(fgetss(xs,BUFFERS,inp)) {
					xs[BUFFERS-1] = '\0';
					fprintf(oup,"%s\r\n",xs);
				}		
				rtclose(oup);
				rewind(inp);
			} else {
				p =
	/*Net*/
	"552 Unable to open user file for mail."
	/*Net*/
					;
				break;
			}
		} else {
			p =
	/*Net*/
	"552 Unable to update user index file for mail."
	/*Net*/
				;
			break;
		}
	    }
	    fclose(inp);
	} else {
	    p =
	/*Net*/
	"552 Unable to open temporary mail file."
	/*Net*/
		;
	}
	return(p);
}

/*
 * pindex
 *
 * set the mailbox directory path and return
 * the next mail index number for user i
 *
 * an inaccessable directory or failure to update
 * the index file returns a 0
 */
int pindex(i)
register int i;
{
	register int j;

	/*
	 * set the mailbox directory path
	 */
	if(cd(rcptpath[i])) {
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("pindex(): %s\r\n", errstr);
}
#endif
		errstr[0] = '\0';
		return(0);
	}
	/*
	 * The mailidx file must be locked to ensure
	 * no other process has simultaneous access to
	 * the file while the index is being updated.
	 * If TSX+ is not sysgened for shared-file
	 * record locking then record locking does not occur.
	 */
	j = modidx(rcptname[i],0,INC_MLID|SET_RCVD|CLR_READ);
	if(!j) {
		/*
		 * Create index file if not opened
		 */
		if(!makidx(rcptname[i])) {
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("pindex(): Failed to create index file\r\n");
}
#endif
		} else {
			j = modidx(rcptname[i],1,SET_MLID|SET_RCVD|CLR_READ);
			if(!j) {
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("pindex(): Failed to open index file");
}
#endif
			}
		}
	}
	/*
	 * return the next mail index
	 */
	return(j);
}

/*
 * sndmail
 *
 * put the outgoing mail into the local users' mailbox directory
 */
char *sndmail(p)
char *p;
{
	FILE *inp,*oup;
	int i,j;
	char str[16];

	if((inp = fopen(mailfl,"r")) != NULL) {
	    for(i=0; i<rcptcnt; i++) {
		/*
		 * process only nonlocal mail
		 */
		if(rcptmode[i] == 0)
			continue;
		/*
		 * set mailbox directory path
		 * and get mail index for local user
		 */
		if(j = sindex(i)) {
			sprintf(str,"ml%-.4s.%03d", lclname, j);
#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("sndmail(): index = %d, mailbox = %s\r\n", j,str);
}
#endif
			if((oup = rtopen(str,"wn",0,0)) != NULL) {
				/*
				 * copy mail to local user directory
				 */
				tofound = 0;
				while(fgetss(xs,BUFFERS,inp)) {
					xs[BUFFERS-1] = '\0';
					if(!tofound && strmatch("To:",xs,3)) {
					    /*
					     * Replace 'To:' Address
					     */
					    fprintf(oup,"To: %s\r\n",
						rcptname[i]);
					    /*
					     * Change 'To:' to 'Cc:'
					     */
					    if(i) {
						strncpy(xs,"Cc:",3);
						fprintf(oup,"%s\r\n",xs);
					    }
					    tofound = 1;
					} else {
					    fprintf(oup,"%s\r\n",xs);
					}
				}		
				rtclose(oup);
				rewind(inp);
			} else {
				p =
	/*Net*/
	"552 Unable to open user file for mail."
	/*Net*/
					;
				break;
			}
		} else {
			p =
	/*Net*/
	"552 Unable to update user index file for mail."
	/*Net*/
				;
			break;
		}
	    }
	    fclose(inp);
	} else {
	    p =
	/*Net*/
	"552 Unable to open temporary mail file."
	/*Net*/
		;
	}

	/*
	 * Activate MAILER
	 */
	cl_xmit(TASKCLASS,STARTJOB,mailer);

	return(p);
}

/*
 * sindex
 *
 * set the mailbox directory path and return
 * the next mail index number for the local user
 *
 * an inaccessable directory or failure to update
 * the index file returns a 0
 */
int sindex(i)
register int i;
{
	register int j;

	/*
	 * set the mailbox directory path
	 */
	if(cd(lclpath)) {
#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("sindex(): %s\r\n", errstr);
}
#endif
		errstr[0] = '\0';
		return(0);
	}
	/*
	 * The mailidx file must be locked to ensure
	 * no other process has simultaneous access to
	 * the file while the index is being updated.
	 * If TSX+ is not sysgened for shared-file
	 * record locking then record locking does not occur.
	 */
	j = modidx(lclname,0,INC_MLTMP);
	if(!j) {
		/*
		 * Create index file if not opened
		 */
		if(!makidx(lclname)) {
#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("sindex(): Failed to create index file\r\n");
}
#endif
		} else {
			j = modidx(lclname,1,SET_MLTMP);
			if(!j) {
#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("sindex(): Failed to open index file");
}
#endif
			}
		}
	}
	/*
	 * return the next mail index
	 */
	return(j);
}

/*
 * strmatch
 *
 * non case sensitive string compare
 */
int strmatch(s,t)
char *s,*t;
{
	int i,j;

	j = strlen(s);
	for(i=0; i<j; i++) {
		if(tolower(*s++) != tolower(*t++))
			return(0);
	}
	return(1);
}

/*
 * getword: remove a word from a string.  Things within quotes are
 * assumed to be one word.
 * return TRUE on success, FALSE on end of string
 */

int getword(string,word)
char *string;
register char *word;
{
	register char *p;
	register int i;
	char *q;

	i = 0;

	/*
	 * skip leading blanks
	 */
	p = stpblk(string);
	if(!(*p)) {
		/*
		 * no words in string
		 */
		word[0] = '\0';
		return(FALSE);
	}
	if(*p == '\"') {
		/*
		 * word delimited by quotes
		 */
		while(p[++i] && p[i] != '\"')
			word[i-1] = p[i];
		word[i-1] = '\0';
		if(!p[i]) {
			/*
			 * Missing \". Assumed at end of string.
			 */
		} else {
			i++;
		}
		q = p+i;
	} else {
		/*
		 * get word, max len 79
		 */
		q = stptok(p, word, 79, " \t\r\n");
	}
     	/*
	 * remove trailing blanks
	 */
	p = stpblk(q);
	/*
	 * remove extracted stuff
	 */
	strcpy(string,p);
	return(TRUE);
}

char *stptok( p, toword, ilen, delim)
register char *p;
char *toword;
int ilen;
char *delim;
{
	register char *adv;
	register int i;
	int j,end;
 
	adv = toword;
	end = 0;
	j = strlen(delim);

 	do { 
 		for(i=0; i<j; i++)
 			if(*p == delim[i] || (!*p))
				end++;
 		if(!end) {
 			if(adv >= (toword+ilen-1)) 
				end++;
 			*adv++=*p++;
 		}
 	} while(!end);
 	*adv='\0';
 	return(p);
}
 
char *stpblk(ch)
register char *ch;
{
 	while(*ch == ' ' || *ch == '\t') ch++;
 	return(ch);
}

VOID strlwr(st)
register char *st;
{
	while(*st) {
		*st = tolower(*st);
		*st++;
	}
}

/*
 *  checkuser (us)
 *
 *  Check the password file for the users' name.
 *  An inaccessable password file results in an invalid return.
 *
 *  If the user is found then
 *	(1)	the mailbox path is set, else
 *	(2)	the default path is set, else
 *	(3)	invalid return
 *
 *  An invalid return occurrs if the user is not found or the
 *  directory path is invalid.
 *
 *  Returns valid(char*)/invalid(NULL)
 */
char *checkuser(us)
char *us;
{
	if(NULL==(passfh = rtopen(passfl,"rn",0,0))) {
#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("checkuser(): no passfl: %s\r\n", passfl);
}
#endif
		return(NULL);
	}
	while (NULL != fread(&user,sizeof(struct userblock),1,passfh)) {
		/*
		 * does user exist ?
		 */
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("checkuser(): us = '%s', user = %s\r\n", us,user.username);
}
#endif
		if(!strncmp(us,&user.username,USERPASSLEN)) {
			/*
			 * User found
			 */
			rtclose(passfh);
			getpath(pathname);
			/*
			 * Set Users Mailbox Directory
			 */
			if(user.mailbox.path[0]!='\0' &&
				!cd(&user.mailbox.path)) {
				cd(pathname);
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("checkuser(): mailbox directory: %s\r\n", user.mailbox.path);
}
#endif
				return(user.mailbox.path);
			} else
			/*
			 * Set Users default directory
			 */
			if(user.defdir.path[0]!='\0' &&
				!cd(&user.defdir.path)) {
				cd(pathname);
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("checkuser(): default directory: %s\r\n", user.defdir.path);
}
#endif
				return(user.defdir.path);
			}
			cd(pathname);
			return(NULL);
		}
	}
	rtclose(passfh);
	return(NULL);
}

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     