/* smtpcli.c
 *	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.
 */

#include <stdio.h>
#include "machdep.h"
#include "netuser.h"
#include "mbuf.h"
#include "timer.h"
#include "tcp.h"
#include "smtp.h"

extern int16 lport;			/* local port placeholder */
int32 aton();
static void sendit();
static struct timer smtpcli_t;
char *index(),*rindex();

/* init routine called when program fired up */
smtpclinit()
{
	int dosmtptick();

	smtpcli_t.func = (void (*)())dosmtptick;/* what to call on timeout */
	smtpcli_t.arg = 0;			/* dummy value */
	smtpcli_t.start = SMTPCLITIME;		/* set timer duration */
	start_timer(&smtpcli_t);		/* and fire it up */
}

/* this is the routine that gets called every so often to do outgoing mail
   processing */
int
dosmtptick()
{
	char 	lfilename[LINELEN],
		tmpstring[LINELEN],
		wfilename[13],
		*ptr;
	FILE *lfile;
	struct smtp_msg *mp;
	struct socket lsocket, fsocket;
	char *calloc(),*malloc();
	void smtp_rec(), smtp_cts(), smtp_state();

/*	printf("DOSMTPTICK() entered\n");	*/
	lsocket.address = ip_addr;	/* our ip address */
	fsocket.port = SMTP_PORT;
/* if lock file exists in mqueue dir, return */
	sprintf(lfilename,"%s%s",MAILQDIR,"lockfile");
	if ((lfile = fopen(lfilename,"x")) == NULL)
		return;
/* get next work filename from mqueue directory */
	sprintf(tmpstring,"%s%s",MAILQDIR,"*.wrk");
#ifndef	AMIGA
	filedir(tmpstring,0,wfilename);
#endif
	if (wfilename[0] == '\0')
		return;	/* no work to be done */
/* if we have work, rebuild the exact (non-wild) filename */
	mp = (struct smtp_msg *)calloc(1,sizeof (struct smtp_msg));
	sprintf(tmpstring,"%s%s",MAILQDIR,wfilename);
	ptr = &tmpstring[0];
	mp->filename = malloc((unsigned)strlen(ptr)+1);
	strcpy(mp->filename,ptr);
/*	printf("work file name: %s\n",mp->filename);	/* debug only */
	mp->wfile = fopen(mp->filename,"r");
/*   get ip address, from stuff, to stuff */
	fgets(tmpstring,LINELEN,mp->wfile);	/* read target ip addr */
/*	printf("target ip addr: %s\n",tmpstring);	*/
	fgets(mp->toaddr,LINELEN,mp->wfile);		/* who to */
	rip(mp->toaddr);
/*	printf("addressee: %s\n",mp->toaddr);		*/
	fgets(mp->fromaddr,LINELEN,mp->wfile);		/* who from */
	rip(mp->fromaddr);
/*	printf("sender: %s\n",mp->fromaddr);		*/
	fclose(mp->wfile);
/* set up the rest of the socket info from what we got */
	fsocket.address = aton(tmpstring);	/* destination ip address */
	lsocket.port = lport++;			/* next unused port */
/*   open smtp connection */
	mp->state = CLI_OPEN_STATE;		/* init state placeholder */
/*	printf("Opening TCP connection for SMTP client\n");	*/
	mp->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,1024,
			smtp_rec,smtp_cts,smtp_state,0,(int *)mp);
	mp->tcb->user = (int *)mp;		/* Upward pointer */

/*	printf("releasing lock\n");		*/
	if (lfile != NULL) {			/* release lock */
		fclose(lfile);
		unlink(lfilename);
	}
}

/* replace terminating end of line marker(s) with null */
rip(s)
char *s;
{
	char *c;

	c=s;
	while (*c != '\0') {
		switch (*c) {
		case '\r':
		case '\n':
			*c='\0';
			break;
		default:
			c++;
			break;
		}
	}
}

/* this is the master state machine that handles a single SMTP transaction */
smtp_transaction(mp)
struct smtp_msg *mp;
{
	char tmpstring[LINELEN];	/* where we build command lines */

/*	printf("SMTP_TRANSACTION() called, state=%u\n",mp->state);	*/
	if (affirmative(mp)) {
		switch(mp->state) {
		case CLI_OPEN_STATE:
			mp->state = CLI_MAIL_STATE;
			/* issue MAIL command */
/*			printf("FROMADDR = %s\n",mp->fromaddr);		*/
			sprintf(tmpstring,"mail from:<%s>\r\n",mp->fromaddr);
			sendit(mp,tmpstring);
			break;			
		case CLI_MAIL_STATE:
			mp->state = CLI_RCPT_STATE;
			/* issue RCPT command */
			sprintf(tmpstring,"rcpt to:<%s>\r\n",mp->toaddr);
			sendit(mp,tmpstring);
			break;
		case CLI_RCPT_STATE:
			mp->state = CLI_SEND_STATE;
			/* open text file */
			strcpy(tmpstring,mp->filename);
			strcpy(index(tmpstring,'.'),".txt");
/*			printf("text filename: %s",tmpstring);		*/
			mp->tfile = fopen(tmpstring,"r");
			/* issue DATA command */
			sprintf(tmpstring,"data\r\n");
			sendit(mp,tmpstring);
			break;
		case CLI_SEND_STATE:
			/* the transmitter upcall routine will advance the
			   state pointer on end of file, so we do nada... */
			break;
		case CLI_UNLK_STATE:
			unlink(mp->filename);	/* unlink workfile */
			/* close and unlink the textfile */
			fclose(mp->tfile);
			strcpy(tmpstring,mp->filename);
			strcpy(index(tmpstring,'.'),".txt");
			unlink(tmpstring);
			mp->state = CLI_QUIT_STATE;
			/* issue a quit command */
			sprintf(tmpstring,"quit\r\n");
			sendit(mp,tmpstring);
			break;
		case CLI_QUIT_STATE:
			/* either start next transaction, or quit */
			close_tcp(mp->tcb);	/* close up connection */
			break;
		}
	} else {	/* if we get here, means we got a negative reply */
			/* for the moment, just let that hose us... */
		mp->state = CLI_QUIT_STATE;
		/* issue a quit command */
		sprintf(tmpstring,"quit\r\n");
		sendit(mp,tmpstring);
	}
}

/* return true if the passed string contains a positive response code */
affirmative(mp)
struct smtp_msg *mp;
{
	/* 2 is always good, 3 is ok if we've just sent 'data' command */
	if ((*mp->buf = '2') || 
           ((*mp->buf = '3') && (mp->state = CLI_DATA_STATE)))
		return 1;
	else 	return 0;
}

/* smtp receiver upcall routine.  fires up the state machine to parse input */
static
void
smtp_rec(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct smtp_msg *mp;
	char *inet_ntoa(), c;
	struct mbuf *bp;
	/* may want a void line here for procedures used */

/*	printf("SMTP_REC called\n");		*/
	mp = (struct smtp_msg *)tcb->user;	/* point to our struct */
	recv_tcp(tcb,&bp,cnt);	/* suck up chars from low level routine */

	/* Assemble input line in buffer, return if incomplete */
	while(pullup(&bp,&c,1) == 1) {
		switch(c) {
		case '\r':	/* strip cr's */
			continue;
		case '\n':	/* line is finished, go do it! */
			mp->buf[mp->cnt] = '\0';
			smtp_transaction(mp);
			break;
		default:	/* other chars get added to buffer */
			mp->buf[mp->cnt++] = c;
			break;
		}
	}
}

/* smtp transmitter ready upcall routine.  twiddles cts flag */
static 
void
smtp_cts(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct smtp_msg *mp;
	struct mbuf *bp;
	char tmpstring[LINELEN];
	char *cp;
	int c;

/*	printf("SMTP_CTS called\n");		*/
	mp = (struct smtp_msg *)tcb->user;	/* point to our struct */

	/* don't do anything until/unless we're supposed to be sending */
	if(mp->state != CLI_SEND_STATE) return;

	if((bp = alloc_mbuf(cnt)) == NULLBUF){
		/* Hard to know what to do here */
		return;
	}
	cp = bp->data;
	while(cnt > 1 && (c = getc(mp->tfile)) != EOF){
		*cp++ = c;
		bp->cnt++;
		cnt--;
	}
	if(bp->cnt != 0)
		send_tcp(tcb,bp);
	else
		free_p(bp);

	if(cnt > 1){	/* EOF seen */
		sprintf(tmpstring,"\r\n.\r\n");
		sendit(mp,tmpstring);
		mp->state = CLI_UNLK_STATE;
	}
}

/* smtp state change upcall routine.  cans connection on error */
static
void
smtp_state(tcb,old,new)
struct tcb *tcb;
char old,new;
{
	struct smtp_msg *mp;

/*	printf("SMTP_STATE called, state=%u\n",new);	*/
	mp = (struct smtp_msg *)tcb->user;
	switch(new) {
	case ESTABLISHED:
		mp->state = CLI_OPEN_STATE;	/* shouldn't be needed */
		break;
	case CLOSE_WAIT:
		close_tcp(tcb);			/* shut things down */
			/* may want to do something here to shut down
			   the rest of the transaction? */
		break;
	case CLOSED:
		del_tcp(tcb);			/* hosed for good */
		if(mp->filename != NULLCHAR)
			free(mp->filename);
		free((char *)mp);
		break;
	}
}

/* Send message back to server */
static
void
sendit(mp,message)
struct smtp_msg *mp;
char *message;
{
	struct mbuf *bp,*qdata();

/*	printf("SENDIT called: %s",message);		*/
	bp = qdata(message,(int16)strlen(message));
	send_tcp(mp->tcb,bp);
}
