/* mailb.c */

/*
 *  Includes
 */
#include <stdio.h>
#include <string.h>

#include "vrsion.h"
#include "os.h"
#include "vmail.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "mailpi.h"
#include "cliutl.h"
#include "tsxutl.h"
#include "kbdutl.h"
#include "rtfile.h"
#include "inicli.h"
#include "suspnd.h"
#include "usrblk.h"
#include "rcdlck.h"
#include "mailbx.h"
#include "datime.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();
extern	int	finduniq();
extern	int	toggle();
extern	VOID	prnthelp();
extern	int	mailpi();
extern	VOID	more();
extern	VOID	mailexit();
extern	int	question();
extern	int	rtresp();
extern	int	tstbrk();
extern	int	loadpath();
extern	VOID	lcd();
extern	VOID	ldelete();
extern	VOID	ldelfil();
extern	VOID	lls();
extern	VOID	lmkdir();
extern	VOID	lprotect();
extern	VOID	setfilp();
extern	VOID	lrename();
extern	VOID	newname();
extern	VOID	lrmdir();
extern	VOID	rmdir();
extern	VOID	lunprotect();
extern	VOID	clrfilp();
extern	VOID	lsterr();
extern	int	mailgets();
extern	int	getword();
extern	int	checkusps();
extern	int	Scompass();
extern	VOID	strlwr();
extern	int	strmatch();
extern	VOID	getinfo();
extern	int	wrtmsg();
extern	VOID	sendmail();
extern	int	send();
extern	VOID	lsendmail();
extern	int	lsend();
extern	VOID	scrnview();
extern	VOID	mailrsp();
extern	int	mailopen();
extern	int	mailreplies();
extern	int	rgetline();
extern	int	checkevent();
#endif

extern	char	*stptok();
extern	char	*stpblk();

/*
 *	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 BUFFERS		1024
#define	RSPSTRING	200
#define GENBUFR		200

/*
 *	Global Variables For MAIL Control Channel
 */

extern
int	pswdreqd;		/* password required */

extern
int	fromtty;		/* tty / file input flag */

extern
int	hash,			/* hash mark flag */
	verbose,		/* print mail transactions */
	prompt,			/* prompt for answer */
	wild,			/* wild cards enabled */
	uflag,			/* username / password flag */
	lflag,			/* local mail flag */
	msgcnt,			/* number of unread mail messages */
	msgflag,		/* created message flag */
	mailcskt;		/* socket for outgoing mail */

extern
int	zflag;			/* connection timeout flag */

extern
long	izaptime;		/* connection zap time in ticks */

/*
 *	Global Buffers / Pointers
 */

#define	INFO_LEN	(80)
#define	HDR_R		(0x0001)
#define	HDR_D		(0x0002)
#define	HDR_F		(0x0004)
#define	HDR_M		(0x0008)
#define	HDR_T		(0x0010)
#define	HDR_S		(0x0020)

extern
struct mesginfo {
	char	rcvd[INFO_LEN];
	char	date[INFO_LEN];
	char	from[INFO_LEN];
	char	mesgid[INFO_LEN];
	char	to[INFO_LEN];
	char	to_dest[INFO_LEN];
	char	to_user[INFO_LEN];
	char	to_mach[INFO_LEN];
	char	to_host[INFO_LEN];
	char	subject[INFO_LEN];
	char	filename[INFO_LEN];
	char	expanded[INFO_LEN];
};

extern
struct mesginfo
	header;			/* message header information */

extern
struct userblock
	user;			/* usernames / passwords / mailboxes */

extern
char	xs[BUFFERS];		/* buffer space for incoming data */
				/* and TSXUTL.C command files */

extern
char	ml_cmnd[GENBUFR],		/* command string */
	scratch[GENBUFR],		/* scratch string */
	rsp[RSPSTRING],			/* response string */
	filespec[GENBUFR],		/* current file filespec */
	mailspec[GENBUFR],		/* mailbox file filespec */
	hostname[INFO_LEN],		/* local host name */
	username[USERPASSLEN+2],	/* username */
	password[USERPASSLEN+2],	/* password */
	mailpath[DIRPATHLEN+2],		/* mailbox path */
	lclmpath[DIRPATHLEN+2],		/* local mail path */
	uspspath[DIRPATHLEN+2],		/* path returned by checkusps */
	defpath[GENBUFR],		/* default path */
	pathname[GENBUFR];		/* space to keep path name */

extern
char *	ipadd;				/* intermediate IP Address */

extern
char *	pass;				/* password file */

extern
char	fromfile[20];			/* command file */

extern
FILE	*fromfp;			/* command file */


VOID getinfo(gt,fp)
register int gt;
FILE *fp;
{
	char scr[INFO_LEN];

	header.rcvd[0]    = '\0';
	header.date[0]    = '\0';
	header.from[0]    = '\0';
	header.mesgid[0]  = '\0';
	header.to[0]      = '\0';
	header.subject[0] = '\0';

	while(gt && (fgetss(scr,INFO_LEN-1,fp) != NULL)) {
		if((gt&HDR_R) && strmatch("Received: ",scr)) {
			strncpy(header.rcvd,scr,INFO_LEN-1);
			header.rcvd[INFO_LEN-1] = '\0';
			gt &= ~HDR_R;
		} else
		if((gt&HDR_D) && strmatch("Date: ",scr)) {
			strncpy(header.date,scr,INFO_LEN-1);
			header.date[INFO_LEN-1] = '\0';
			gt &= ~HDR_D;
		} else
		if((gt&HDR_F) && strmatch("From: ",scr)) {
			strncpy(header.from,scr,INFO_LEN-1);
			header.from[INFO_LEN-1] = '\0';
			gt &= ~HDR_F;
		} else
		if((gt&HDR_M) && strmatch("Message-Id: ",scr)) {
			strncpy(header.mesgid,scr,INFO_LEN-1);
			header.mesgid[INFO_LEN-1] = '\0';
			gt &= ~HDR_M;
		} else
		if((gt&HDR_T) && strmatch("To: ",scr)) {
			strncpy(header.to,scr,INFO_LEN-1);
			header.to[INFO_LEN-1] = '\0';
			gt &= ~HDR_T;
		} else
		if((gt&HDR_S) && strmatch("Subject: ",scr)) {
			strncpy(header.subject,scr,INFO_LEN-1);
			header.subject[INFO_LEN-1] = '\0';
			gt &= ~HDR_S;
		}
	}
}

/*
 * wrtmsg
 *
 * write the message to a unique file
 */
int wrtmsg()
{
	FILE *fp;
	register int idx;

	msgflag = 0;

	if(header.filename[0] != '\0') {
		return(0);
	}

	/*
	 * Load mail directory path and update index file
	 */
	if(loadpath(mailpath)) {
		return(1);
	}

	/*
	 * 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.
	 */
	idx = modidx(username,0,INC_MLTMP);
	if(!idx) {
		kb_puts("?-MAIL-Failed to update index file\r\n");
		return(1);
	}

	sprintf(header.filename,"%s%-.4sml.%03d", getdef(), username, idx);
	expand(header.expanded,header.filename);

	if((fp = rtopen(header.filename,"w",0,0)) != NULL) {
		kbprintf("Message file %s opened\r\n", header.expanded);
		kb_puts("Enter Message: [last line is '.<CR>']\r\n");
	} else {
		kbprintf("Unable to open message file %s\r\n",
			header.expanded);
		return(1);
	}

	while(!rtresp(scratch,sizeof(scratch))) {
		if(streq(scratch,".")) {
			rtclose(fp);
			msgflag = 1;
			return(0);
		}
		fprintf(fp,"%s\r\n", scratch);
	}

	rtclose(fp);
	delfil(0,header.filename,1,0);
	lsterr();
	return(1);
}

VOID sendmail()
{
	int i;

	if(mailopen(header.to_host) != TRUE) {
		if(msgflag) {
			kbprintf("?-MAIL-Message saved in file %s\r\n",
				header.expanded);
			msgflag = 0;
		}
		return;
	}

	if(send()) {
		kbprintf("?-MAIL-Send mail aborting\r\n");
		if(msgflag) {
			kbprintf("?-MAIL-Message saved in file %s\r\n",
				header.expanded);
			msgflag = 0;
		}
		mailrsp("RSET");
		mailreplies(&i);
	}

	mailrsp("QUIT");
	do {
		i = mailreplies(&i);
	} while(i != ABORT && i != NONE);

	/*
	 * close connection
	 */
	skclose(mailcskt);
	skrelease(mailcskt,1);
	mailcskt = -1;
}

int send()
{
	FILE *fp;
	int finished,rcode,sverbose;
	register int hashcnt,hashnum;

	if(mailreplies(&rcode) != TRUE) {
		kbprintf("?-MAIL-Invalid reply from %s\r\n", header.to_host);
		return(1);
	}

	sprintf(rsp,"HELO %s", hostname);
	mailrsp(rsp);
	if(mailreplies(&rcode) != TRUE) {
		kbprintf("?-MAIL-Invalid reply from %s\r\n", header.to_host);
		return(1);
	}

	sprintf(rsp,"MAIL FROM:<%s@%s>", header.from, hostname);
	mailrsp(rsp);
	if(mailreplies(&rcode) != TRUE) {
		kbprintf("?-MAIL-Invalid reply from %s\r\n", header.to_host);
		return(1);
	}

	sprintf(rsp,"RCPT TO:<%s>", header.to);
	mailrsp(rsp);
	if(mailreplies(&rcode) != TRUE) {
		kbprintf("?-MAIL-User '%s' unknown\r\n", header.to);
		return(1);
	}

	sprintf(rsp,"DATA");
	mailrsp(rsp);
	if(mailrepies(&rcode) != INCOMPLETE) {
		kbprintf("?-MAIL-Message transfer aborted\r\n");
		return(1);
	}

	if((fp = rtopen(header.filename,"r",0,0)) == NULL) {
		kbprintf("?-MAIL-Unable to open message file %s\r\n",
				header.expanded);
		return(1);
	}

	/*
	 * Send the Header
	 */
	sprintf(rsp,"Received: by %s", header.rcvd);
	mailrsp(rsp);
	sprintf(rsp,"Date: %s", header.date);
	mailrsp(rsp);
	sprintf(rsp,"From: <%s@%s>", header.from, hostname);
	mailrsp(rsp);
	sprintf(rsp,"Message-Id: %s @%s", header.expanded, hostname);
	mailrsp(rsp);
	sprintf(rsp,"To: %s", header.to_dest);
	mailrsp(rsp);
	sprintf(rsp,"Subject: %s", header.subject);
	mailrsp(rsp);
	/*
	 * Start Message
	 */
	kb_puts("... Sending Message File ...\r\n");
	sverbose = verbose;
	verbose = 0;
	mailrsp("");
	/*
	 * Connection Timer
	 */
	skquetime = cticks(NULL);
	/*
	 * Transfer Message File
	 */
	finished = FALSE;
	hashnum = 0;
	hashcnt = 0;
	rsp[0] = '\.';
	do {
		if(skroom(mailcskt) > LOWWATER) {
			if(fgetss(rsp+1,RSPSTRING-2,fp) != NULL) {
				if(rsp[1] != '\.') {
					mailrsp(rsp+1);
				} else {
					mailrsp(rsp);
				}
				if(hash) {
					hashcnt += strlen(rsp+1);
					/*
					 * hash mark printing
					 */
					if(hashcnt >= HASHSIZE) {
						hashcnt -= HASHSIZE;
						if(++hashnum >= 75) {
							kb_puts("#\r\n");
							hashnum = 0;
						} else {
							kb_out('#');
						}
					}
				}
			} else {
				finished = TRUE;
				if(hash && hashnum) {
					kb_puts("\r\n");
				}
			}
		} else {
			suspnd(0);
		}
		if((zflag != 0) &&
		   (skquetime != 0L) &&
		   (elapsed(skquetime) > izaptime)) {
			break;
		}
	} while(finished != TRUE && !sktest(mailcskt) && !tstbrk());
	/*
	 * End Message
	 */
	mailrsp(".");
	verbose = sverbose;
	rtclose(fp);

	if(finished != TRUE) {
		kbprintf("?-MAIL-Connection Timeout/Abort");
	}

	if(mailreplies(&rcode) != TRUE) {
		kbprintf("?-MAIL-Invalid reply from %s\r\n", header.to);
		return(1);
	}

	if(finished != TRUE) {
		return(1);
	}

	return(0);
}

VOID lsendmail()
{
	if(lsend()) {
		kbprintf("?-MAIL-Send local mail aborting\r\n");
		if(msgflag) {
			kbprintf("?-MAIL-Message saved in file %s\r\n",
				header.expanded);
			msgflag = 0;
		}
	}
}

int lsend()
{
	register FILE *inp,*oup;
	register int idx;

	if((inp = rtopen(header.filename,"r",0,0)) == NULL) {
		kb_puts("?-MAIL-Unable to open message file %s\r\n",
				header.expanded);
		return(1);
	}

	/*
	 * Create a unique file name for temporary mail data
	 */
	sprintf(filespec,"wf:mailbx.%03d", tljobn);

	/*
	 * Open mail file
	 */
	if((oup = fopen(filespec, "wn")) == NULL) {
		rtclose(inp);
		kb_puts("?-MAIL-Unable to Create Temporary File\r\n");
		return(1);
	}

	/*
	 * Write the Header
	 */
	fprintf(oup,"Received: by %s\r\n", header.rcvd);
	fprintf(oup,"Date: %s\r\n", header.date);
	fprintf(oup,"From: %s\r\n", header.from);
	fprintf(oup,"Message-Id: %s\r\n", header.expanded);
	fprintf(oup,"To: %s\r\n", header.to_user);
	fprintf(oup,"Subject: %s\r\n", header.subject);
	fprintf(oup,"\r\n");
	/*
	 * 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);

	/*
	 * Load local mail directory path and update index file
	 */
	if(loadpath(lclmpath)) {
		return(1);
	}

	/*
	 * 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.
	 */
	idx = modidx(header.to_user,0,INC_MLID|SET_RCVD|CLR_READ);
	if(!idx) {
		/*
		 * Create index file if not opened
		 */
		if(!makidx(header.to_user)) {
			kb_puts(
			"?-MAIL-Failed to create index file\r\n");
			return(1);
		} else {
			idx=modidx(header.to_user,1,
				SET_MLID|SET_RCVD|CLR_READ);
			if(!idx) {
				kb_puts(
				"?-MAIL-Failed to update index file\r\n");
				return(1);
			}
		}
	}

	if((inp = fopen(filespec,"r")) == NULL) {
		kb_puts("?-MAIL-Unable to Read Temporary File\r\n");
		return(1);
	}

	/*
	 * Open new file
	 */
	sprintf(mailspec,"%-.6s.%03d", header.to_user, idx);
	if((oup = rtopen(mailspec,"wn",0,0)) == NULL) {
		kb_puts("?-MAIL-Unable to Create File\r\n");
		fclose(inp);
		return(1);
	}

	/*
	 * copy mail to a new file
	 */
	while(fgetss(xs,BUFFERS,inp)) {
		xs[BUFFERS-1] = '\0';
		fprintf(oup,"%s\r\n",xs);
	}		
	rtclose(oup);
	fclose(inp);

	/*
	 * Delete temporary file
	 */
	delete(filespec);
	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 mailrsp(s)
char *s;
{
	skwrite(mailcskt, s, strlen(s));
	skwrite(mailcskt, "\r\n", 2);
	skenque(mailcskt, 1);
	if(verbose) {
		kb_puts(s);
		kbprintf("  {%d}\r\n", strlen(s)+2);
	}
}

/*
 *  mailopen
 *
 *  open a MAIL connection
 */
int mailopen(dst)
char *dst;
{
	register int flag;
	int cl,ev,dat;

	/*
	 * Initialize various things,
	 */
	Snetinit();

	/*
	 * Open a connection
 	 */
	mailcskt = addsess(0,dst,HSMTP);
	if(mailcskt >= 0) {
		flag = 1;
	} else {
		flag = 0;
	}
	while(flag && !tstbrk()) {
		if(Sgetevent(ABORTCLASS,&cl,&dat))
			mailexit(ABORT);

		errhandle();
		ev = Sgetevent(CONCLASS,&cl,&dat);
		switch(ev) {
		case CONOPEN:
			/*
			 * Log the Session
			 */
			sprintf(scratch,"MAIL: %s", header.to_host);
			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(mailcskt);
	mailcskt = skrelease(mailcskt,1);
	errhandle();
	kbprintf("?-MAIL-Unable to connect to %s\r\n",dst);
	return(FALSE);
}

/*
 * mailreplies
 *
 * 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 mailreplies(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(verbose)
			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(mailcskt);

		case ABORT:	/* abort */
			return(ev);

		case HAVEDATA:
			/*
			 * get some from queue
			 */
			icnt = 0;
			while(1) {
				cnt = skread(mailcskt,&xs[i],1);
				if(cnt == 0)
					/*
					 * nothing available
					 */
					break;
				icnt = 1;
				if((xs[i++] == '\n') || (cnt < 0)) {
					/*
					 * end of line
					 */
					xs[i] = '\0';
					skdeque(mailcskt);
					/*
					 * return line length
					 */
					return(i);
				}
				if(i > BUFFERS - 2)
					i = BUFFERS - 2;
			}
			if (icnt)
				skdeque(mailcskt);
			break;

		default:	/* ignore other events */
			suspnd(10);
			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(tstbrk())
		return(ABORT);
	/*
	 * look for events
	 */
	ev = Sgetevent(CONCLASS | MSGCLASS |
		       ERRCLASS | ABORTCLASS, &cl,&dat);
	if(ev) {
	    switch(cl) {
		case ABORTCLASS:	/* abort */
		    mailexit(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(mailcskt);
			return(NONE);

		     default:
			break;
		    }
		    break;

		default:
		    break;
	    }
	}
	return(TRUE);
}

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   