/*
 *	Client routines for Simple Mail Transfer Protocol ala RFC821
 *	A.D. Barksdale Garbee II, aka Bdale, N3EUA
 *	Copyright 1986 Bdale Garbee, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 * 	Modified 14 June 1987 by P. Karn for symbolic target addresses,
 *	also rebuilt locking mechanism
 *	Copyright 1987 1988 David Trulli, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 */
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <setjmp.h>
#ifdef UNIX
#include <sys/types.h>
#endif
#ifdef	AMIGA
#include "stat.h"
#else
#include <sys/stat.h>
#endif
#ifdef	__TURBOC__
#include <dir.h>
#include <io.h>
#endif
#include "global.h"
#ifdef	ANSIPROTO
#include <stdarg.h>
#endif
#include "mbuf.h"
#include "cmdparse.h"
#include "proc.h"
#include "socket.h"
#include "timer.h"
#include "netuser.h"
#include "smtp.h"
#include "dirutil.h"
#include "commands.h"
#include "session.h"

static struct timer smtpcli_t;
static int32 gateway;

#ifdef SMTPTRACE
static unsigned short Smtptrace = 0;		/* used for trace level */
static int dosmtptrace __ARGS((int argc,char *argv[],void *p));
#endif

static unsigned  short smtpmaxcli  = MAXSESSIONS;	/* the max client connections allowed */
static int smtpsessions = 0;		/* number of client connections
					* currently open */
int	Smtpmode = 0;

static struct smtpcli *cli_session[MAXSESSIONS]; /* queue of client sessions  */

static char quitcmd[] = "QUIT\n";

static void abort_trans __ARGS((struct smtpcli *cb));
static void del_job __ARGS((struct smtp_job *jp));
static void del_session __ARGS((struct smtpcli *cb));
static int dogateway __ARGS((int argc,char *argv[],void *p));
static int dosmtpmaxcli __ARGS((int argc,char *argv[],void *p));
static int dotimer __ARGS((int argc,char *argv[],void *p));
static int dosmtpkill __ARGS((int argc,char *argv[],void *p));
static int dosmtplist __ARGS((int argc,char *argv[],void *p));
static void execjobs __ARGS((void));
static void logerr __ARGS((struct smtpcli *cb));
static struct smtpcli *lookup __ARGS((int32 destaddr));
static struct smtpcli *newcb __ARGS((void));
static int next_job __ARGS((struct smtpcli *cb));
static void retmail __ARGS((struct smtpcli *cb));
static void sendcmd __ARGS((struct smtpcli *cb,char *fmt,...));
static int smtpsendfile __ARGS((struct smtpcli *cb));
static void sendquit __ARGS((struct smtpcli *cb));
static int setsmtpmode __ARGS((int argc,char *argv[],void *p));
static struct smtp_job *setupjob __ARGS((struct smtpcli *cb,char *id,char *from));
static void smtp_send __ARGS((int unused,void *cb1,void *p));
static int smtpkick __ARGS((int argc,char *argv[],void *p));

static struct cmds Smtpcmds[] = {
	"gateway",	dogateway,	0,	0,	NULLCHAR,
	"mode",		setsmtpmode,	0,	0,	NULLCHAR,
	"kick",		smtpkick,	0,	0,	NULLCHAR,
	"kill",		dosmtpkill,	0,	2,	"kill <jobnumber>",
	"list",		dosmtplist,	0,	0,	NULLCHAR,
	"maxclients",	dosmtpmaxcli,	0,	0,	NULLCHAR,
	"timer",	dotimer,	0,	0,	NULLCHAR,
#ifdef SMTPTRACE
	"trace",	dosmtptrace,	0,	0,	NULLCHAR,
#endif
	NULLCHAR,
};

int
dosmtp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Smtpcmds,argc,argv,p);
}

static int
dosmtpmaxcli(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&smtpmaxcli,"Max clients",argc,argv);
}

static int
setsmtpmode(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if (argc < 2) {
		tprintf("smtp mode: %s\n",
			(Smtpmode & QUEUE) ? "queue" : "route");
	} else {
		switch(*argv[1]) {
		case 'q':
			Smtpmode |= QUEUE;
			break;
		case 'r':
			Smtpmode &= ~QUEUE;
			break;
		default:
			tprintf("Usage: smtp mode [queue | route]\n");
			break;
		}
	}
	return 0;
}
static int
dogateway(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int32 n;

	if(argc < 2){
		tprintf("%s\n",inet_ntoa(gateway));
	} else if((n = resolve(argv[1])) == 0){
		tprintf(Badhost,argv[1]);
		return 1;
	} else
		gateway = n;
	return 0;
}

#ifdef SMTPTRACE
static int
dosmtptrace(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&Smtptrace,"SMTP tracing",argc,argv);
}
#endif

/* list jobs wating to be sent in the mqueue */
static int
dosmtplist(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	char tstring[80];
	char line[20];
	char host[LINELEN];
	char to[LINELEN];
	char from[LINELEN];
	char *cp;
	char	status;
	struct stat stbuf;
	struct tm *tminfo, *localtime();
	FILE *fp;

	Current->flowmode = 1; /* Enable the more mechanism */
	tprintf("S     Job    Size Date  Time  Host                 From\n");
	filedir(Mailqueue,0,line);
	while(line[0] != '\0') {
		sprintf(tstring,"%s/%s",Mailqdir,line);
		if ((fp = fopen(tstring,READ_TEXT)) == NULLFILE) {
			tprintf("Can't open %s: %s\n",tstring,sys_errlist[errno]);
			continue;
		}
		if ((cp = strrchr(line,'.')) != NULLCHAR)
			*cp = '\0';
		sprintf(tstring,"%s/%s.lck",Mailqdir,line);
		if (access(tstring,0))
			status = ' ';
		else
			status = 'L';
		sprintf(tstring,"%s/%s.txt",Mailqdir,line);
		stat(tstring,&stbuf);
		tminfo = localtime(&stbuf.st_mtime); /*MARC: was st_ctime*/
		fgets(host,sizeof(host),fp);
		rip(host);
		fgets(from,sizeof(from),fp);
		rip(from);
		tprintf("%c %7s %7ld %02d/%02d %02d:%02d %-20s %s\n      ",
			status, line, stbuf.st_size,
			tminfo->tm_mon+1,
			tminfo->tm_mday,
			tminfo->tm_hour,
			tminfo->tm_min,
			host,from);
		while (fgets(to,sizeof(to),fp) != NULLCHAR) {
			rip(to);
			tprintf("%s ",to);
		}
		tprintf("\n");
		(void) fclose(fp);
		pwait(NULL);
		filedir(Mailqueue,1,line);
	}
	Current->flowmode = 0;
	return 0;
}

/* kill a job in the mqueue */
static int
dosmtpkill(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	char s[SLINELEN];
	char *cp,c;
	sprintf(s,"%s/%s.lck",Mailqdir,argv[1]);
	cp = strrchr(s,'.');
	if (!access(s,0)) {
		c = keywait("Warning, the job is locked by SMTP. Remove (y/n)? ",0);
		if (c != 'y')
			return 0;
		(void) unlink(s);
	}
	strcpy(cp,".wrk");
	if (unlink(s))
		tprintf("Job id %s not found\n",argv[1]);
	strcpy(cp,".txt");
	(void) unlink(s);
	return 0;
}

/* Set outbound spool scan interval */
static int
dotimer(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2){
		tprintf("%lu/%lu\n",
		read_timer(&smtpcli_t) * MSPTICK/1000,
		dur_timer(&smtpcli_t) * MSPTICK/1000);
		return 0;
	}
	smtpcli_t.func = (void (*)())smtptick;/* what to call on timeout */
	smtpcli_t.arg = NULL;		/* dummy value */
	smtpcli_t.start = atol(argv[1])*(1000/MSPTICK);	/* set timer duration */
	start_timer(&smtpcli_t);		/* and fire it up */
	return 0;
}

static int
smtpkick(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	smtptick(NULL);
	return 0;
}

/* This is the routine that gets called every so often to do outgoing
 * mail processing. When called with a null argument, it runs the entire
 * queue; if called with a specific non-zero IP address from the remote
 * kick server, it only starts up sessions to that address.
 */
int
smtptick(t)
void *t;
{
	register struct smtpcli *cb;
	struct smtp_job *jp;
	struct list *ap;
	char	tmpstring[LINELEN], wfilename[13], prefix[9];
	char	from[LINELEN], to[LINELEN];
	char *cp, *cp1;
	int32 destaddr,target;
	FILE *wfile;

	target = (int32)t;
#ifdef SMTPTRACE
	if (Smtptrace > 5)
		printf("smtp daemon entered, target = %s\n",inet_ntoa(target));
#endif
	if(availmem() < Memthresh){
		/* Memory is tight, don't do anything */
		/* Restart timer */
		start_timer(&smtpcli_t);
		return 0;
	}
	for(filedir(Mailqueue,0,wfilename);wfilename[0] != '\0';
		filedir(Mailqueue,1,wfilename)){

		/* save the prefix of the file name which it job id */
		cp = wfilename;
		cp1 = prefix;
		while (*cp && *cp != '.')
			*cp1++ = *cp++;
		*cp1 = '\0';

		/* lock this file from the smtp daemon */
		if (mlock(Mailqdir,prefix))
			continue;

		sprintf(tmpstring,"%s/%s",Mailqdir,wfilename);
		if ((wfile = fopen(tmpstring,READ_TEXT)) == NULLFILE) {
			/* probably too many open files */
			(void) rmlock(Mailqdir,prefix);
			/* continue to next message. The failure
			* may be temporary */
			continue;
		}

		(void) fgets(tmpstring,LINELEN,wfile);	/* read target host */
		rip(tmpstring);

		if ((destaddr = mailroute(tmpstring)) == 0) {
			fclose(wfile);
			printf("** smtp: Unknown address %s\n",tmpstring);
			(void) rmlock(Mailqdir,prefix);
			continue;
		}
		if(target != 0 && destaddr != target){
			fclose(wfile);
			(void) rmlock(Mailqdir,prefix);
			continue;	/* Not the proper target of a kick */
		}
		if ((cb = lookup(destaddr)) == NULLSMTPCLI) {
			/* there are enough processes running already */
			if (smtpsessions >= smtpmaxcli) {
#ifdef SMTPTRACE
				if (Smtptrace) {
					printf("smtp daemon: too many processes\n");
				}
#endif
				fclose(wfile);
				(void) rmlock(Mailqdir,prefix);
				break;
			}
			if ((cb = newcb()) == NULLSMTPCLI) {
				fclose(wfile);
				(void) rmlock(Mailqdir,prefix);
				break;
			} 
			cb->ipdest = destaddr;
		} else {
			/* This system is already is sending mail lets not
			* interfere with its send queue.
			*/
			if (cb->state != CLI_INIT_STATE) {
				fclose(wfile);
				(void) rmlock(Mailqdir,prefix);
				continue;
			}
		}

		(void) fgets(from,LINELEN,wfile);	/* read from */
		rip(from);
		if ((jp = setupjob(cb,prefix,from)) == NULLJOB) {
			fclose(wfile);
			(void) rmlock(Mailqdir,prefix);
			del_session(cb);
			break;
		}
		while (fgets(to,LINELEN,wfile) != NULLCHAR) {
			rip(to);
			if (addlist(&jp->to,to,DOMAIN) == NULLLIST) {
				fclose(wfile);
				del_session(cb);
			}
		}
		fclose(wfile);
#ifdef SMTPTRACE
		if (Smtptrace > 1) {
			printf("queue job %s From: %s To:",prefix,from);
			for (ap = jp->to; ap != NULLLIST; ap = ap->next)
				printf(" %s",ap->val);
			printf("\n");
		}
#endif
	}

	/* start sending that mail */
	execjobs();

	/* Restart timer */
	start_timer(&smtpcli_t);
	return 0;
}

/* this is the master state machine that handles a single SMTP transaction */
/* it is called with a queue of jobs for a particular host. */
static void
smtp_send(unused,cb1,p)
int unused;
void *cb1;
void *p;
{
	register struct smtpcli *cb;
	register char reply;
	register struct list *tp;
	struct sockaddr_in fsocket;
	struct mbuf *bp,*bpl;
	char *cp;
	char tbuf[LINELEN];
	int rcode;

	cb = (struct smtpcli *)cb1;
	fsocket.sin_family = AF_INET;
	fsocket.sin_addr.s_addr = cb->ipdest;
	fsocket.sin_port = IPPORT_SMTP;

	cb->s = socket(AF_INET,SOCK_STREAM,0);
	cb->state = CLI_OPEN_STATE;	/* init state placeholder */
#ifdef SMTPTRACE
	if (Smtptrace) 
		printf("SMTP client Trying...\n");
#endif
	if(connect(cb->s,(char *)&fsocket,SOCKSIZE) == 0){
#ifdef SMTPTRACE
	if (Smtptrace) 
		printf("Connected\n");
#endif
		;
	} else {
		cp = sockerr(cb->s);
#ifdef SMTPTRACE
		if (Smtptrace) 
			printf("Connect failed: %s\n",cp != NULLCHAR ? cp : "");
#endif
		log(cb->s,"SMTP %s Connect failed: %s",psocket(&fsocket),
		    cp != NULLCHAR ? cp : "");
	}

	while(1) {
		
		if (recvline(cb->s,cb->buf,LINELEN) == -1)
			goto quit;
		rip(cb->buf);
#ifdef SMTPTRACE
		if (Smtptrace)
			printf("smtp rcvd: '%s'\n",cb->buf);
#endif

		/* Another line follows, ignore this one */
		if(cb->buf[0] == '0' || cb->buf[3] == '-')
			continue;

		reply = cb->buf[0];
		rcode = atoi(cb->buf);

		/* if service shuting down */
		if (rcode == 421) {
			sendquit(cb);
			continue;
		}

		switch(cb->state) {
		case CLI_OPEN_STATE:
			if (reply != '2')
				sendquit(cb);
			else {
				cb->state = CLI_HELO_STATE;
				sendcmd(cb,"HELO %s\nMAIL FROM:<%s>\n",
				Hostname,cb->jobq->from);
			}
			break;
		case CLI_HELO_STATE:
			if (reply != '2')
				sendquit(cb);
			else 
				cb->state = CLI_MAIL_STATE;
			break;			
		case CLI_MAIL_STATE:
			if (reply != '2')
				sendquit(cb);
			else {
				cb->state = CLI_RCPT_STATE;
				cb->rcpts = 0;
				bpl = NULLBUF;
				for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
					sprintf(tbuf,"RCPT TO:<%s>\n",tp->val);
					bp = qdata(tbuf,(int16)strlen(tbuf));
					if (bp == NULLBUF) {
						free_p(bpl);
						sendquit(cb);
						goto quit;
					}
					append(&bpl,bp);
					cb->rcpts++;
#ifdef SMTPTRACE
					if (Smtptrace) {
						printf("smtp sent: %s",tbuf);
					}
#endif
				}
				send_mbuf(cb->s,bpl,0,NULLCHAR,0);
			}
			break;
		case CLI_RCPT_STATE:
			if (reply == '5') {
				logerr(cb);
			} else if (reply == '2') {
				cb->goodrcpt = 1;
			} else {
				/* some kind of temporary failure */
				abort_trans(cb);
				break;
			}
			/* if more rcpts stay in this state */
			if (--cb->rcpts != 0)
				break;

			/* check for no good rcpt on the list */
			if (cb->goodrcpt == 0) {
				if (cb->errlog != NULLLIST)
					retmail(cb);
				(void) unlink(cb->wname);	/* unlink workfile */
				(void) unlink(cb->tname);	/* unlink text */
				abort_trans(cb);
				break;
			}
			/* if this file open fails abort */
			if ((cb->tfile = fopen(cb->tname,READ_TEXT)) == NULLFILE)
				abort_trans(cb);
			else {
				sendcmd(cb,"DATA\n");
				cb->state = CLI_SEND_STATE;
			}
			break;
		case CLI_SEND_STATE:
			if (reply == '3') {
				cb->state = CLI_UNLK_STATE;
				smtpsendfile(cb);
			} else {
				sendquit(cb);
			}
			break;
		case CLI_UNLK_STATE:
			/* if a good transfer or permanent failure remove job */
			if (reply == '2' || reply == '5') {
				if (reply == '5')
					logerr(cb);
				/* close and unlink the textfile */
				if(cb->tfile != NULLFILE) {
					fclose(cb->tfile);
					cb->tfile = NULLFILE;
				}
				if (cb->errlog != NULLLIST)
					retmail(cb);
				(void) unlink(cb->tname);
				(void) unlink(cb->wname);	/* unlink workfile */
				log(cb->s,"SMTP sent job %s To: %s From: %s",
				cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
			}
			if (next_job(cb)) {
				cb->state = CLI_MAIL_STATE;
				sendcmd(cb,"MAIL FROM:<%s>\n",cb->jobq->from);
			} else {
				sendquit(cb);
				cb->state = CLI_QUIT_STATE;
			}
			break;
		case CLI_IDLE_STATE:	/* used after a RSET and more mail to send */
			if (reply != '2')
				sendquit(cb);
			else {
				cb->state = CLI_MAIL_STATE;
				sendcmd(cb,"MAIL FROM:<%s>\n",cb->jobq->from);
			}
			break;			
		case CLI_QUIT_STATE:
			goto quit;
		}
	}
quit:
	(void) close_s(cb->s);
	if(cb->tfile != NULLFILE)
		fclose(cb->tfile);
	del_session(cb);
}

/* abort the currrent job.
 * If more work exists set up the next job if
 * not then shut down.
*/
static void
abort_trans(cb)
register struct smtpcli *cb;
{
#ifdef SMTPSTRACE
	if (Smtptrace)
		printf("smtpcli: abort transaction\n");
#endif
	if(cb->tfile != NULLFILE) {
		fclose(cb->tfile);
		cb->tfile = NULLFILE;
	}
	if (next_job(cb)) {
		sendcmd(cb,"RSET\n");
		cb->state = CLI_IDLE_STATE;
	} else 
		sendquit(cb);
}

static void
sendquit(cb)
struct smtpcli *cb;
{
	cb->state = CLI_QUIT_STATE;
	sendcmd(cb,quitcmd);	/* issue a quit command */
}

/* Send message back to server */
#if	defined(ANSIPROTO)
static void
sendcmd(struct smtpcli *cb,char *fmt,...)
{
	va_list ap;
	struct mbuf *bp;
	char tmpstring[256];

	va_start(ap,fmt);

#ifdef SMTPTRACE
	if (Smtptrace) {
		printf("smtp sent: ");
		vprintf(fmt,ap);
	}
#endif
	vsprintf(tmpstring,fmt,ap);
	va_end(ap);
	bp = qdata(tmpstring,(int16)strlen(tmpstring));
	send_mbuf(cb->s,bp,0,NULLCHAR,0);
}
#else
static void
sendcmd(cb,fmt,arg1,arg2)
struct smtpcli *cb;
char *fmt,*arg1,*arg2;
{
	struct mbuf *bp;
	char tmpstring[256];

#ifdef SMTPTRACE
	if (Smtptrace) {
		printf("smtp sent: ");
		printf(fmt,arg1,arg2);
	}
#endif
	sprintf(tmpstring,fmt,arg1,arg2);
	bp = qdata(tmpstring,(int16)strlen(tmpstring));
	send_mbuf(cb->s,bp,0,NULLCHAR,0);
}
#endif
/* create mail lockfile */
int
mlock(dir,id)
char *dir,*id;
{
	char lockname[LINELEN];
	int fd;

	/* Try to create the lock file in an atomic operation */
	sprintf(lockname,"%s/%s.lck",dir,id);
#ifdef        AMIGA
	/* don't ask, really, just don't ask... I'd do file locking on
	 * an Amiga much more differently than this.
	 */
	if(access(lockname, 0) == 0)
		return -1;
#endif
	if((fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT,0600)) == -1)
		return -1;
	close(fd);
	return 0;
}

/* remove mail lockfile */
int
rmlock(dir,id)
char *dir,*id;
{
	char lockname[LINELEN];
	sprintf(lockname,"%s/%s.lck",dir,id);
	return(unlink(lockname));
}

/* free the message struct and data */
static void
del_session(cb)
register struct smtpcli *cb;
{
	register struct smtp_job *jp,*tp;
	register int i;

	if (cb == NULLSMTPCLI)
		return;
	for(i=0; i<MAXSESSIONS; i++) 
		if(cli_session[i] == cb) {
			cli_session[i] = NULLSMTPCLI;
			break;
		}

	free(cb->wname);
	free(cb->tname);
	for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
			tp = jp->next;
			del_job(jp);
	}
	del_list(cb->errlog);
	free((char *)cb);
	smtpsessions--;	/* number of connections active */
}

static void
del_job(jp)
register struct smtp_job *jp;
{
	if ( *jp->jobname != '\0')
		(void) rmlock(Mailqdir,jp->jobname);
	free(jp->from);
	del_list(jp->to);
	free((char *)jp);
}

/* delete a list of list structs */
void
del_list(lp)
struct list *lp;
{
	register struct list *tp, *tp1;
	for (tp = lp; tp != NULLLIST; tp = tp1) {
			tp1 = tp->next;
			free(tp->val);
			free((char *)tp);
	}
}

/* return message to sender */
static void
retmail(cb)
struct smtpcli *cb;
{
	register struct list *lp;
	register FILE *tfile;
	register int c;
	FILE *infile;
	char *to;
	time_t t;
	
#ifdef SMTPTRACE
	if (Smtptrace > 5) {
		printf("smtp job %s returned to sender\n",cb->wname);
	}
#endif
	/* A null From<> so no looping replys to MAIL-DAEMONS */
	to = cb->jobq->from;
	if (*to == '\0')
		return;
	if ((infile = fopen(cb->tname,READ_TEXT)) == NULLFILE)
		return;
	if ((tfile = tmpfile()) == NULLFILE) {
		fclose(infile);
		return;
	}
	time(&t);
	fprintf(tfile,"Date: %s",ptime(&t));
	fprintf(tfile,"Message-Id: <%ld@%s>\n",get_msgid(),Hostname);
	fprintf(tfile,"From: MAILER-DAEMON@%s\n",Hostname);
	fprintf(tfile,"To: %s\n",to);
	fprintf(tfile,"Subject: Failed mail\n\n");
	fprintf(tfile,"  ===== transcript follows =====\n\n");

	for (lp = cb->errlog; lp != NULLLIST; lp = lp->next)
		fprintf(tfile,"%s\n",lp->val);

	fprintf(tfile,"\n  ===== Unsent message follows ====\n");

	while((c = getc(infile)) != EOF)
		if (putc(c,tfile) == EOF)
			break;
	fclose(infile);
	fseek(tfile,0L,0);
	(void) mailuser(tfile,"",to);
	fclose(tfile);
}

/* look to see if a smtp control block exists for this ipdest */
static struct smtpcli *
lookup(destaddr)
int32 destaddr;
{
	register int i;

	for(i=0; i<MAXSESSIONS; i++) {
		if (cli_session[i] == NULLSMTPCLI)
			continue;
		if(cli_session[i]->ipdest == destaddr)
			return cli_session[i];
	}
	return NULLSMTPCLI;
}

/* create a new  smtp control block */
static struct smtpcli *
newcb()
{
	register int i;
	register struct smtpcli *cb;

	for(i=0; i<MAXSESSIONS; i++) {
		if(cli_session[i] == NULLSMTPCLI) {
			cb = (struct smtpcli *)callocw(1,sizeof(struct smtpcli));
			cb->wname = mallocw((unsigned)strlen(Mailqdir)+JOBNAME);
			cb->tname = mallocw((unsigned)strlen(Mailqdir)+JOBNAME);
			cb->state = CLI_INIT_STATE;
			cli_session[i] = cb;
			smtpsessions++;	/* number of connections active */
			return(cb);
		}
	}
	return NULLSMTPCLI;
}

static void
execjobs()
{
	register struct smtpcli *cb;
	register int i;

	for(i=0; i<MAXSESSIONS; i++) {
		cb = cli_session[i];
		if (cb == NULLSMTPCLI) 
			continue;
		if(cb->state != CLI_INIT_STATE)
			continue;

		sprintf(cb->tname,"%s/%s.txt",Mailqdir,cb->jobq->jobname);
		sprintf(cb->wname,"%s/%s.wrk",Mailqdir,cb->jobq->jobname);

		newproc("smtp_send", 1024, smtp_send, 0, cb,NULL);

#ifdef SMTPTRACE
		if (Smtptrace) 
			printf("Trying Connection to %s\n",inet_ntoa(cb->ipdest));
#endif


	}
}
	
/* add this job to control block queue */
static struct smtp_job *
setupjob(cb,id,from)
struct smtpcli *cb;
char *id,*from;
{
	register struct smtp_job *p1,*p2;

	p1 = (struct smtp_job *)callocw(1,sizeof(struct smtp_job));
	p1->from = mallocw((unsigned)strlen(from) + 1);
	strcpy(p1->from,from);
	strcpy(p1->jobname,id);
	/* now add to end of jobq */
	if ((p2 = cb->jobq) == NULLJOB)
		cb->jobq = p1;
	else {
		while(p2->next != NULLJOB)
			p2 = p2->next;
		p2->next = p1;
	}
	return p1;
}

/* called to advance to the next job */
static int
next_job(cb)
register struct smtpcli *cb;
{
	register struct smtp_job *jp;

	jp = cb->jobq->next;
	del_job(cb->jobq);
	if (jp == NULLJOB) {
		cb->jobq = NULLJOB;
		return 0;
	}
	/* remove the error log of previous message */
	del_list(cb->errlog);
	cb->errlog = NULLLIST;
	cb->goodrcpt = 0;
	cb->jobq = jp;
	sprintf(cb->tname,"%s/%s.txt",Mailqdir,jp->jobname);
	sprintf(cb->wname,"%s/%s.wrk",Mailqdir,jp->jobname);
#ifdef SMTPTRACE
	if (Smtptrace > 5) {
		printf("sending job %s\n",jp->jobname);
	}
#endif
		return 1;

}


/* mail routing funtion. For now just used the hosts file */
int32
mailroute(dest)
char *dest;
{
	int32 destaddr;

	/* look up address or use the gateway */
	if ((destaddr = resolve(dest)) == 0)
		if (gateway != 0) 
			destaddr = gateway; /* Use the gateway  */
	return destaddr;
	
}

/* save error reply for in error list */
static void
logerr(cb)
struct smtpcli *cb;
{
	register struct list *lp,*tp;
	tp = (struct list *)callocw(1,sizeof(struct list));
	tp->val = mallocw((unsigned)strlen(cb->buf)+1);
	/* find end of list */
	if ((lp = cb->errlog) == NULLLIST)
		cb->errlog = tp;
	else {
		while(lp->next != NULLLIST)
			lp = lp->next;
		lp->next = tp;
	}
	strcpy(tp->val,cb->buf);
}

static int
smtpsendfile(cb)
register struct smtpcli *cb;
{
	int error = 0;

	strcpy(cb->buf,"\n");
	while(fgets(cb->buf,sizeof(cb->buf),cb->tfile) != NULLCHAR) {
		/* Escape a single '.' character at the beginning of a line */
		if(strcmp(cb->buf,".\n") == 0)
			usputc(cb->s,'.');
		usputs(cb->s,cb->buf);
	}
	fclose(cb->tfile);
	cb->tfile = NULLFILE;
	/* Send the end-of-message command */
	if(cb->buf[strlen(cb->buf)-1] == '\n')
		sendcmd(cb,".\n");
	else
		sendcmd(cb,"\n.\n");
	return error;
}
