/* mailer.c */

#define	MAILERMASTER

/*
 *  Includes
 */
#include <stdio.h>
#include <string.h>

#include "os.h"
#include "vrsion.h"
#include "vmailr.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "cliutl.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"
#include "cticks.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	VOID	lsterr();		/* error lister */
extern	int	response();		/* dumby routine for TSXUTL.C */
extern	VOID	userscan();		/* scan user mail directories */
extern	int	getword();		/* (string,word) extract a word */
extern	VOID	strlwr();		/* (st) convert to lower case */
extern	int	getuser();		/* load user data */
extern	int	usercnt();		/* get number of users */
extern	int	maktmp();		/* (src,dst) create temp. file */
extern	int	cpytmp();		/* (src,dst,err) copy temp. file */
extern	VOID	smtpexit();		/* SMTP abort */
extern	int	strmatch();		/* caseless string compare */
extern	VOID	getinfo();		/* parse header info into structure */
extern	VOID	sendmail();		/* SMTP send mail */
extern	int	send();			/* subroutine of sendmail() */
extern	VOID	scrnview();		/* view net messages */
extern	VOID	smtprsp();		/* SMTP responses */
extern	int	smtpopen();		/* Open SMTP */
extern	int	smtpreplies();		/* SMTP responses */
extern	int	rgetline();		/* read a response line */
extern	int	checkevent();		/* event scanner */
#endif

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 */

/*
 *	Defines
 */

#define FALSE		 0
#define TRUE		 1
#define SUCCESS		 2
#define HAVEDATA	 4
#define ERROR	   	-1
#define NONE	   	-2
#define ABORT	   	-3
#define INCOMPLETE	-4
#define AMBIGUOUS	-5

#define	INFO_LEN	(128)
#define BUFFERS		(512)
#define	RSPSTRING	(128)
#define GENBUFR		(128)


/*
 *	Global Variables For SMTP Control Channel
 */

static int
	mflag = 0,		/* monitor processing */
	tflag = 0,		/* timed mail check flag */
	smtpcskt = -1;		/* socket for outgoing mail */

static int
	zflag = 0;		/* connection timeout flag */

static long
	izaptime = 0L;		/* connection zap time in ticks */

struct mesginfo {
	char	to[INFO_LEN];
	char	to_dest[INFO_LEN];
	char	to_user[INFO_LEN];
	char	to_mach[INFO_LEN];
	char	to_host[INFO_LEN];
};

static struct mesginfo
	header;			/* message header information */


/*
 *	Global Buffers / Pointers
 */

static char
	hostname[INFO_LEN],		/* local host name */
	hostip[INFO_LEN],		/* local host ip address */
	rsp[RSPSTRING],			/* response string */
	scratch[GENBUFR],		/* scratch string */
	pathname[DIRPATHLEN+2],		/* space to keep path name */
	frompath[DIRPATHLEN+2],		/* from mail path */
	fromuser[USERPASSLEN+2];	/* from username */

static struct userblock
	user;			/* usernames / passwords / mailboxes */

char	xs[BUFFERS];		/* buffer space for incoming data */
				/* and TSXUTL.C command files */

static char *			/* mailer wild card file */
	mailfspc = "ml____.___";

static char *			/* password file */
	passfl = "PAS:paswrd.fil";

static char
	tmpfspc[20];	/* temporary file */

static FILE *
	passfh = NULL;

char *	ipadd = NULL;		/* intermediate IP Address */


/*
 * Debugging options
 *
 *	bit 0	(0x01)	event printing
 *	bit 0	(0x02)	checkevent()
 *	bit 1	(0x04)	ckeckuser()
 *	bit 2	(0x08)	getuser()
 *	bit 3	(0x10)	usercnt()
 *	bit 4	(0x20)	maktmp() / cpytmp()
 *	bit 5	(0x40)	userscan()
 */

static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  MAILER [?] [-d level] [-p filespec] [-i IP-address] [-t n] [-e] [-z s]",
       "	?               List the Help Text and Exit MAILER",
       "	d  level	Debug Level",
       "		1 -	enable event printing",
       "		2 -	enable checkevent() printing",
       "		4 -	enable checkuser() printing",
       "		8 -	enable getuser() printing",
       "	       16 -	enable usercnt() printing",
       "	       32 -	enable maktmp() / cpytmp() printing",
       "	       64 -	disable deletion of temporary files",
#else
       "  MAILER [?] [-p filespec] [-i ip-address] [-t n] [-e] [-z s]",
       "	?               List the Help Text and Exit MAILER",
#endif
       "	e		Enable Monitoring of MAILER Transactions",
       "	i  IP-address	Intermediate Host Name / IP Address",
       "	p  filespec	Specify the Password Filespec",
       "	t  n (minutes)	Continuous Scanning Time Interval",
       "	v		Verbose Mode",
       "	z  s		Connection Timeout in Seconds",
       "",
       0
       };

/*
 *  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[];
{
	register int i,j,k;
	char *p;
	register char c;
	int sktnum;
	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)) {
#ifdef	DEBUGOPTION
			case 'd':	/* debug, optional level */
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = -1;
				break;
#endif
			case 'e':	/* enable SMTP monitoring */
				mflag = 1;
				break;

			case 'i':	/* Intermediate IP Address */
				ipadd = argv[++i];
				strlwr(ipadd);
				break;

			case 'p':	/* password file */
				passfl = argv[++i];
				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
			case 't':	/* time scan interval */
				sscanf(argv[++i],"%d",&tflag);
				if ((tflag < 0) || (tflag > 60))
					tflag = 0;
				break;

			case 'v':	/* verbose mode on */
				kb_prnt = 1;
				break;

			case 'z':	/* connection 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);

	/*
	 * Get Host Name / IP Address
	 */
	Snetinit();

	if ((sktnum = socket(0)) != -1) {
		skt = (struct socket *) mapskt(sktnum);
		strcpy(hostname,skt->hostname);
		p = skt->tcpout.i.ipsource;
		sprintf(hostip,"%d.%d.%d.%d",
			*(p+0)&0xFF, *(p+1)&0xFF, *(p+2)&0xFF, *(p+3)&0xFF);
		skrelease(sktnum,1);
	}

	do {
		userscan();

		if (tflag) {
			for(i=0; i<tflag; i++) {
				suspnd((int) 60*TICKSPERSECOND);
			}
		}

	} while (tflag && !fndbrk);

	smtpexit(0);
}

VOID
smtpexit(i)
{
	if(fndbrk) 
		kb_puts("\r\n^C\r\n");

	if(i == ABORT) {
		kb_puts("\r\nMAILER session aborted by TCPIP\r\n");
	}

	skclose(smtpcskt);
	skrelease(smtpcskt,1);

	suspnd(0);
	errhandle();

	cd("dk:");

	kb_puts("\r\nEscaping from MAILER\r\n");
	exit(0);
}

VOID
userscan()
{
	register int i,n;
	register char *p;
	char mailfl[20];
	FILE *idxfh;
	char idxspc[20];

	/*
	 * Find number of local users
	 */
	n = usercnt();

	/*
	 * Scan each user directory for messages to send
	 */
	for (i=0; i <= n; i++) {

	/*
	 * If user is inaccessable then skip the user
	 */
	if (!getuser(i))
		continue;

	/*
	 * Create User Index File if None Exists
	 */
	sprintf(idxspc,"%-.6s.idx", fromuser);
	if((idxfh = rtopen(idxspc,"rn",0,0)) == NULL) {
		/*
		 * Create index file if not opened
		 */
		if(!makidx(fromuser))
			continue;
	} else {
		rtclose(idxfh);
	}

	/*
	 * If user has no messages to send then skip user
	 */
	sprintf(mailfl,"ml%-.4s.*",fromuser);
	if ((p = firstname(mailfl)) == NULL)
		continue;

	do {
		/*
		 * Copy message file name
		 */
		strcpy(mailfspc, p);
		lastname();

		/*
		 * Create a unique temporary file name
		 */
		sprintf(tmpfspc,"wf:mailer.%03d",tljobn);

		/*
		 * If unable to create temporary file then skip user
		 */
		if (!maktmp(tmpfspc,mailfspc))
			break;

		/*
		 * Read in header information
		 */
		if (!getinfo(tmpfspc))
			break;
#ifdef	DEBUGOPTION
if(debug&0x40) {
		printf("userscan()\r\n");
		printf("header.to      = '%s'\r\n", header.to);
		printf("header.to_dest = '%s'\r\n", header.to_dest);
		printf("header.to_user = '%s'\r\n", header.to_user);
		printf("header.to_mach = '%s'\r\n", header.to_mach);
		printf("header.to_host = '%s'\r\n", header.to_host);
}
#endif

		/*
		 * Local User
		 */
		if ((*header.to_mach == '\0') ||
		     strneq(header.to_mach,hostname,strlen(header.to_mach)) ||
		     strneq(header.to_mach,hostip,strlen(header.to_mach))) {
			if (!(user.userflag[1] & LSMTP_PRIV)) {
				sprintf(scratch,
				    "Local Mail Delivery Disabled [%s]\r\n",
					header.to);
				if (!cpytmp(fromuser,tmpfspc,scratch))
					break;
			} else
			if (!checkuser(header.to_user)) {
				cd(frompath);
				sprintf(scratch,
				    "Unknown Local User [%s]\r\n",
					header.to);
				if (!cpytmp(fromuser,tmpfspc,scratch))
					break;
			} else
			if (!cpytmp(header.to_user,tmpfspc,NULL)) {
				cd(frompath);
				sprintf(scratch,
				    "Copy Message Failure [%s]\r\n",
					header.to);
				if (!cpytmp(fromuser,tmpfspc,scratch))
					break;
			}
		/*
		 * Remote User
		 */
		} else {
			if (!(user.userflag[1] & RSMTP_PRIV)) {
				sprintf(scratch,
				    "Remote Mail Delivery Disabled [%s]\r\n",
					header.to);
				if (!cpytmp(fromuser,tmpfspc,scratch))
					break;
			} else {
				sendmail();
				if (rsp[0] != '\0') {
					if (!cpytmp(fromuser,tmpfspc,rsp))
						break;
				}
			}
		}
		cd(frompath);
		delete(mailfspc);
#ifdef	DEBUGOPTION
if(!(debug&0x40)) {
		delete(tmpfspc);
}
#else
		delete(tmpfspc);
#endif
	} while ((p = firstname(mailfl)) != NULL);

#ifdef	DEBUGOPTION
if(!(debug&0x40)) {
	delete(tmpfspc);
}
#else
	delete(tmpfspc);
#endif

	} /* End for(i) */
}

/*
 * 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\r\n");
	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&0x04) {
	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&0x04) {
	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)) {
#ifdef	DEBUGOPTION
if(debug&0x04) {
	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)) {
#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("checkuser(): default directory: %s\r\n", user.defdir.path);
}
#endif
				return(user.defdir.path);
			}
			cd(pathname);
			return(NULL);
		}
	}
	rtclose(passfh);
	return(NULL);
}

int
usercnt()
{
	register int n;

	if (NULL == (passfh = rtopen(passfl,"rn",0,0))) {
#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("usercnt(): no passfl: %s\r\n", passfl);
}
#endif
		return(0);
	}
	/*
	 * Number of Users is file size.
	 */
	n = passfh->io_size;
	rtclose(passfh);
	return(n);
}



int
getuser(n)
register int n;
{
	if (NULL == (passfh = rtopen(passfl,"rn",0,0))) {
		return(0);
	}

	/*
	 * Scan to User n
	 */
	while (n-- > 0) {
		if (NULL == fread(&user,sizeof(struct userblock),1,passfh)) {
			return(0);
		}
	}

	rtclose(passfh);
	getpath(pathname);

	/*
	 * Get user name
	 */
	fromuser[0] = '\0';
	strncat(fromuser,user.username,USERPASSLEN);

	/*
	 * Set Users Mailbox Directory
	 */
	if(user.mailbox.path[0]!='\0' && !cd(&user.mailbox.path)) {
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("getuser(): mailbox directory: %s\r\n", user.mailbox.path);
}
#endif
		frompath[0] = '\0';
		strncat(frompath,user.mailbox.path,DIRPATHLEN);
		return(1);
	} else
	/*
	 * Set Users default directory
	 */
	if(user.defdir.path[0]!='\0' && !cd(&user.defdir.path)) {
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("getuser(): default directory: %s\r\n", user.defdir.path);
}
#endif
		frompath[0] = '\0';
		strncat(frompath,user.defdir.path,DIRPATHLEN);
		return(1);
	}
	cd(pathname);
	return(0);
}

int
maktmp(tmpfile,msgfile)
char *tmpfile,*msgfile;
{
	register FILE *inp,*oup;

	if((inp = rtopen(msgfile,"r",0,0)) == NULL) {
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("maktmp(): Unable to open message file %s\r\n", msgfile);
}
#endif
		return(0);
	}

	/*
	 * Open mail file
	 */
	if((oup = fopen(tmpfile, "wn")) == NULL) {
		rtclose(inp);
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("maktmp(): Unable to Create Temporary File\r\n");
}
#endif
		return(0);
	}

	/*
	 * Received:
	 */
	fprintf(oup,"Received: by %s  (%s%s)\r\n",
		hostname, vrstxt[1]+1, vrstxt[2]);

	/*
	 * Date:
	 */
	fprintf(oup,"      %s\r\n", datime());

	/*
	 * Copy message to the temporary file
	 */
	while(fgetss(xs,BUFFERS,inp)) {
		xs[BUFFERS-1] = '\0';
		fprintf(oup,"%s\r\n",xs);
	}		
	fclose(oup);
	rtclose(inp);

	return(1);
}

int
cpytmp(username,tmpfile,err)
char *username,*tmpfile,*err;
{
	char mailspec[16];
	register FILE *inp,*oup;
	register int idx;

	if (!(idx = modidx(username,0,INC_MLID|SET_RCVD|CLR_READ))) {
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("cpytmp(): Failed to update index file\r\n");
}
#endif
		return(0);
	}

	if((inp = fopen(tmpfile,"r")) == NULL) {
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("cpytmp(): Unable to Read Temporary File\r\n");
}
#endif
		return(0);
	}

	/*
	 * Open new file
	 */
	sprintf(mailspec,"%-.6s.%03d", username, idx);
	if((oup = rtopen(mailspec,"wn",0,0)) == NULL) {
#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("cpytmp(): Unable to Create Mail File\r\n");
}
#endif
		fclose(inp);
		return(0);
	}

	/*
	 * Add Error line if required
	 */
	if ((err != NULL) && (*err != '\0'))
		fprintf(oup,"Subject: Error: %s\r\n",err);

	/*
	 * copy mail to a new file
	 */
	while(fgetss(xs,BUFFERS,inp)) {
		/*
		 * Replace Subject Message with Error Message
		 */
		if ((err != NULL) &&
		    (*err != '\0') &&
		    strmatch("Subject:",xs,8)) {
			fprintf(oup,"Subject: Error: %s\r\n",err);
		} else {
			xs[BUFFERS-1] = '\0';
			fprintf(oup,"%s\r\n",xs);
		}
	}		
	rtclose(oup);
	fclose(inp);

	return(1);
}

/*
 * strmatch
 *
 * caseless string compare
 */
int strmatch(s,t)
register char *s,*t;
{
	register int i,j;

	j = strlen(s);
	for(i=0; i<j; i++) {
		if(tolower(*s++) != tolower(*t++))
			return(0);
	}
	return(1);
}

int
getinfo(str)
char *str;
{
	register FILE *fp;
	register char *p;
	char scr[INFO_LEN];

	zero(header.to,sizeof(header.to));
	zero(header.to_dest,sizeof(header.to_dest));
	zero(header.to_user,sizeof(header.to_user));
	zero(header.to_mach,sizeof(header.to_mach));
	zero(header.to_host,sizeof(header.to_host));

	if ((fp = fopen(str,"r")) == NULL)
		return(0);
	while (fgetss(scr,INFO_LEN-1,fp) != NULL) {
		if (strmatch("To:",scr)) {
			strncat(header.to,stpblk(scr+3),INFO_LEN-1);
			break;
		}
	}
	fclose(fp);

	/*
	 * To_Dest / To_User / To_Mach
	 */
	if((p = strrchr(header.to,':')) == NULL) {
		p = header.to - 1;
	}
	strncat(header.to_dest,p+1,INFO_LEN-1);

	p = stptok(p+1,header.to_user,INFO_LEN-1,"@");

	strncat(header.to_mach,p+1,INFO_LEN-1);

	/*
	 * To_Host
	 */
	if((p = strchr(header.to,'@')) != NULL) {
		stptok(p+1,header.to_host,INFO_LEN-1,":@");
	} else {
		header.to_host[0] = '\0';
	}

	/*
	 * Set to lower case
	 */
	strlwr(header.to_dest);
	strlwr(header.to_user);
	strlwr(header.to_mach);
	strlwr(header.to_host);

	return(1);
}

/*
 * Send the mail via SMTP
 */
VOID sendmail()
{
	int i;

	/*
	 * Check for Intermediate Host Specified
	 */
	if((ipadd != NULL) && !streq(header.to_mach,ipadd) &&
			      !streq(header.to_host,ipadd)) {
		strcpy(scratch,header.to);
		sprintf(header.to,"@%s:%s", ipadd, scratch);
		strcpy(header.to_host,ipadd);
	}
#ifdef	DEBUGOPTION
if(debug&0x40) {
		printf("sendmail()\r\n");
		printf("header.to      = '%s'\r\n", header.to);
		printf("header.to_dest = '%s'\r\n", header.to_dest);
		printf("header.to_user = '%s'\r\n", header.to_user);
		printf("header.to_mach = '%s'\r\n", header.to_mach);
		printf("header.to_host = '%s'\r\n", header.to_host);
}
#endif

	/*
	 * Open Connection
	 */
	if(smtpopen(header.to_host) != TRUE) {
		sprintf(rsp,
			"MAILER Failed to send to [%s]\r\n",header.to);
		return;
	}

	/*
	 * Send Message
	 */
	if(send()) {
		smtprsp("RSET");
		smtpreplies(&i);
	}

	/*
	 * close connection
	 */
	smtprsp("QUIT");
	do {
		i = smtpreplies(&i);
	} while (i != ABORT && i != NONE);

	skclose(smtpcskt);
	skrelease(smtpcskt,1);
	smtpcskt = -1;
}

int send()
{
	FILE *fp;
	int finished,rcode;

	if(smtpreplies(&rcode) != TRUE) {
		sprintf(rsp,
			"MAILER Invalid reply from [%s]\r\n", header.to);
		return(1);
	}

	sprintf(rsp,"HELO %s", hostname);
	smtprsp(rsp);
	if(smtpreplies(&rcode) != TRUE) {
		sprintf(rsp,
			"MAILER Invalid reply from [%s]\r\n", header.to);
		return(1);
	}

	sprintf(rsp,"MAIL FROM:<%s@%s>", fromuser, hostname);
	smtprsp(rsp);
	if(smtpreplies(&rcode) != TRUE) {
		sprintf(rsp,
			"MAILER Invalid reply from [%s]\r\n", header.to);
		return(1);
	}

	sprintf(rsp,"RCPT TO:<%s>", header.to);
	smtprsp(rsp);
	if(smtpreplies(&rcode) != TRUE) {
		sprintf(rsp,"MAILER User [%s] unknown\r\n", header.to);
		return(1);
	}

	sprintf(rsp,"DATA");
	smtprsp(rsp);
	if(smtprepies(&rcode) != INCOMPLETE) {
		sprintf(rsp,"MAILER Message transfer aborted [%s]\r\n",
			header.to);
		return(1);
	}

	if((fp = fopen(tmpfspc,"r",0,0)) == NULL) {
		sprintf(rsp,"MAILER Unable to open message file %s [%s]\r\n",
				tmpfspc, header.to);
		return(1);
	}

	/*
	 * Connection Timer
	 */
	skquetime = cticks(NULL);

	/*
	 * Transfer Message File
	 */
	finished = FALSE;
	rsp[0] = '\.';
	do {
		if(skroom(smtpcskt) > LOWWATER) {
			if (fgetss(rsp+1,RSPSTRING-2,fp) != NULL) {
				if(rsp[1] != '\.') {
					smtprsp(rsp+1);
				} else {
					smtprsp(rsp);
				}
			} else {
				finished = TRUE;
			}
		} else {
			suspnd(0);
		}
		if((zflag != 0) &&
		   (skquetime != 0L) &&
		   (elapsed(skquetime) > izaptime)) {
			break;
		}
	} while (finished != TRUE && !sktest(smtpcskt) && !fndbrk);
	/*
	 * End Message
	 */
	smtprsp(".");
	fclose(fp);

	if(finished != TRUE) {
		sprintf(rsp,"MAILER Connection Timeout/Abort");
	}

	if(smtpreplies(&rcode) != TRUE) {
		sprintf(rsp,"MAILER Invalid reply from [%s]\r\n", header.to);
		return(1);
	}

	if(finished != TRUE) {
		return(1);
	}

	rsp[0] = '\0';
	return(0);
}

/*
 * scrnview
 *
 * View data on screen
 */

VOID scrnview(cnt)
int cnt;
{
	register int i;

	/*
	 * put on screen
	 */
	for(i=0; i<cnt; i++) {
		if(xs[i] & 128) {
			/*
			 * if over ASCII 128 show as number
			 */
			kbprintf(" %d ", xs[i]&0xFF);
		} else {
			kb_out(xs[i]);
		}
	}
}

/*
 * Dump response to client
 */
VOID smtprsp(s)
char *s;
{
	skwrite(smtpcskt, s, strlen(s));
	skwrite(smtpcskt, "\r\n", 2);
	skenque(smtpcskt, 1);
	if(mflag) {
		kb_puts(s);
		kbprintf("  {%d}\r\n", strlen(s)+2);
	}
}

/*
 *  smtpopen
 *
 *  open a SMTP connection
 */
int smtpopen(dst)
char *dst;
{
	register struct socket *skt;
	register int flag;
	int cl,ev,dat;

	/*
	 * Initialize various things,
	 */
	Snetinit();

	/*
	 * Open a connection
 	 */
	smtpcskt = addsess(1,dst,HSMTP);
	if(smtpcskt >= 0) {
		flag = 1;
	} else {
		flag = 0;
	}
	while (flag && !fndbrk) {
		if(Sgetevent(ABORTCLASS,&cl,&dat))
			smtpexit(ABORT);

		errhandle();

		ev = Sgetevent(CONCLASS,&cl,&dat);
		switch(ev) {
		case CONOPEN:
			/*
			 * Log the Session
			 */
			sprintf(scratch,"MAILER: %s", header.to);
			logsession(dat,scratch);
			return(TRUE);

		case CONCLOSE:
		case CONFAIL:
			flag = 0;
			break;

		default:
			if(ev) {
				Stmrset(cl,ev,dat,1);
			} else {
				suspnd(0);
			}
		}
	}
	/*
	 * error on open
	 */
	skclose(smtpcskt);
	smtpcskt = skrelease(smtpcskt,1);
	errhandle();
	sprintf(rsp,"MAILER Unable to connect to [%s]\r\n",dst);
	return(FALSE);
}

/*
 * smtpreplies
 *
 * get responses to commands to server
 * return TRUE on successful completion,
 * INCOMPLETE if more commands needed for operation,
 * NONE on lost connection, ABORT on an abort, and ERROR on failure
 * PRELIMINARY responses wait for completion
 */
int smtpreplies(rcode)
int *rcode;
{
	register int cnt,j,digit,abrtcnt;

	abrtcnt = 0;
	j = 0;
loop:	while (1) {
		/*
		 * get line from remote host
		 */
		cnt = rgetline();

		if(cnt == NONE)
			/*
			 * lost connection
			 */
			return(NONE);

		if(cnt == ABORT)
			/*
			 * user abort
			 */
			return(ABORT);

		if(!sscanf(xs,"%d", rcode))
			/*
			 * continuation line
			 */
			*rcode = -1;

		/*
		 * informative message
		 */
		if(mflag)
			scrnview(cnt);

		if(xs[3] == '-') {
			/*
			 * line with continuations
			 * remember end code
			 */
			j = *rcode;
			continue;
		} else
		if(j) {
			if(*rcode == j) {
				/*
				 * end of continuation
				 */
				j = 0;
			} else {
				continue;
			}
		}

		/*
		 * first digit
		 */
		digit = (int)(*rcode/100);
		if(abrtcnt) {
			switch(digit) {
			case 1:		/* preliminary */
				continue;
	
			case 2:		/* positive completion */
			case 3:		/* intermediate */
			case 4:		/* transient negative completion */
			case 5:		/* Permanent negative completion */
			default:
				return(ABORT);

			}
		} else {
			switch(digit) {
			case 1:		/* preliminary */
				continue;
	
			case 2:		/* positive completion */
				return(TRUE);

			case 3:		/* intermediate */
				return(INCOMPLETE);
		
			case 4:		/* transient negative completion */
			case 5:		/* Permanent negative completion */
			default:
				return(ERROR);

			}
		}
	}
}

/*
 * rgetline - get a line from remote server
 *
 * return ABORT on an abort, NONE on lost connection,
 * length of received line on success
 */

int rgetline()
{
	register int i,cnt,icnt,ev;

	/*
	 * Inactivity Timer
	 */
	skquetime = cticks(NULL);

	i = 0;
	while (1) {
		if((zflag != 0) &&
		   (skquetime != 0L) &&
		   (elapsed(skquetime) > izaptime)) {
			ev = NONE;
		} else {
			ev = checkevent();
		}
		switch(ev) {
		case NONE:	/* lost connection */
			skdeque(smtpcskt);

		case ABORT:	/* abort */
			return(ev);

		case HAVEDATA:
			/*
			 * get some from queue
			 */
			icnt = 0;
			while (1) {
				cnt = skread(smtpcskt,&xs[i],1);
				if(cnt == 0)
					/*
					 * nothing available
					 */
					break;
				icnt = 1;
				if((xs[i++] == '\n') || (cnt < 0)) {
					/*
					 * end of line
					 */
					xs[i] = '\0';
					skdeque(smtpcskt);
					/*
					 * return line length
					 */
					return(i);
				}
				if(i > BUFFERS - 2)
					i = BUFFERS - 2;
			}
			if (icnt)
				skdeque(smtpcskt);
			break;

		default:	/* ignore other events */
			suspnd(0);
			break;
		}
	}
}

/*
 * checkevent
 *
 * get and process network events
 * returns ABORT on an abort, HAVEDATA if data available
 * on command connection, NONE if connection lost.
 * TRUE if no relevant event.
 */

int checkevent()
{
	int cl,ev,dat;

	/*
	 * check for abort
	 */
	if(fndbrk)
		return(ABORT);
	/*
	 * look for events
	 */
	ev = Sgetevent(CONCLASS | MSGCLASS |
		       ERRCLASS | ABORTCLASS, &cl,&dat);
	if(ev!=0) {
#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("checkevent(): cl = %d, ev = %d, dat = %u\r\n", cl,ev,dat);
}
#endif
	    switch(cl) {
		case ABORTCLASS:	/* abort */
		    smtpexit(ABORT);
		    break;

		case ERRCLASS:		/* error messages */
		case MSGCLASS:		/* messages */
		    kb_puts(skerrstring(ev));
		    kb_nline();
		    break;

		case CONCLASS:		/* connection */
		    switch(ev) {
		    case CONDATA:
			/*
			 * data has arrived
			 */
			return(HAVEDATA);

		    case CONCLOSE:
			/*
			 * connection lost
			 */
			skclose(smtpcskt);
			return(NONE);

		     default:
			break;
		    }
		    break;

		default:
		    break;
	    }
	}
	return(TRUE);
}

            