/* ax.25 mailbox by w9nk */
/* adapted to CHLNET environment by PE1CHL */
/* outbound forwarding to AX.25 BBS systems added by PE1CHL */

#include <stdio.h>
#include <time.h>
#include <ctype.h>
#ifdef	UNIX
#include <sys/types.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "ax25.h"
#include "timer.h"
#include "iface.h"
#include "lapb.h"
#include "smtp.h"
#include "ax_mbx.h"
#include "cmdparse.h"

/* #define MBDEBUG */

static char mbmenu[]	= "(B)ye, (I)nfo, (S)end >\r";
static char mbprompt[]	= ">\r";
static char mbok[]	= "OK\r";
static char mbno[]	= "NO\r";
static char mberr[]	= "Huh?\r";
static char mbserr[]	= "S command syntax error - format is:\r  S name [@ host] [< from_addr] [$bulletin_id]\r";
static char mbbaduser[] = "Bad user or host name\r";
static char mbsubject[] = "Subject:\r";
static char mbmesg[]	= "Enter message.  Terminate with /EX or ^Z in first column:\r";
static char mbtemp[]	= "Can't create temp file for mail\r";
static char mbqerr[]	= "Couldn't queue message for delivery\r";
static char mbbye[]	= "Goodbye!\r";
static char mbcr[]	= "\r";

extern char hostname[];

char mbxinfofile[80] = "";
static struct mbx *fwdmbx;		/* currently-connecting mbx */

static int mbx_line(),mbx_sid(),mbx_script();
static void mbx_rx(),mbx_tx(),mbx_send(),mbx_doscript();
void mbxfwd();

static int dombxconnect();
struct cmds fwdcmds[] = {
	"connect",	dombxconnect,	3,	NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,		0,	NULLCHAR,	NULLCHAR
};

extern char *getnenv();
extern char *strstr();

/* find mbx struct for specified axp */

static struct mbx *
find_mbx(axp)
register struct ax25_cb *axp;
{
	register struct mbx *m;

	for (m = (struct mbx *) ((struct ax25_call *) axp->user)->user;
	     m != NULLMBX; m = m->next) {
		if(m->ax25_cb == axp)
		    break;
	}

	return m;
}

/* State-change upcall handler for mailbox */

void
mbx_state(axp,old,new,msg)
register struct ax25_cb *axp;
int old,new,msg;

{
	register struct mbx *m;
	char *cp;
	struct mbxpath *mbp;
	char mesg[160];

	switch (new)
	{
	case CONNECTED:
#ifdef MBDEBUG
	    printf("MBX: connected\n");
#endif
	    /* check if station already known, and register if not */
	    if((m = find_mbx(axp)) == NULLMBX) {
		if((m = (struct mbx *) calloc(1,sizeof(struct mbx))) == NULLMBX) {
		    axp->state = DISCONNECTED;	/* send DM */
		    return;
		}

		/* connect the user to this mbox's user list */
		if((m->next = (struct mbx *) ((struct ax25_call *) axp->user)->user)
			 != NULLMBX)
		    m->next->prev = m;

		((struct ax25_call *) axp->user)->user = (char *) m;
		m->ax25_cb = axp;

		pax25(m->name,&axp->addr.dest);
		cp = index(m->name,'-');
		if (cp != NULLCHAR)		/* get rid of SSID */
		    *cp = '\0';
		for (cp = m->name; *cp != '\0'; cp++)
		    *cp = tolower(*cp);		/* convert to lowercase */

		axp->r_upcall = mbx_rx;
		axp->t_upcall = mbx_tx;
#ifdef MBDEBUG
		printf("MBX: new user %s\n",m->name);
#endif
		log_ax(axp,"Conn to mbox");

		sprintf(mesg,"%c[NET-$]\rWelcome to the %s TCP/IP Mailbox\r%s",
			     PID_FIRST|PID_LAST|PID_NO_L3,hostname,mbmenu);

		enqueue(&axp->txq,qstring(mesg));
	    }

	    /* send first message when script starts with "s" */
	    if (m->state == MBX_CONN)
		mbx_tx(axp,1);

	    break;

	case DISCONNECTED:
	    if ((m = find_mbx(axp)) == NULLMBX)
		break;

	    if(m->prev != NULLMBX)
		m->prev->next = m->next;
	    else
		((struct ax25_call *) axp->user)->user = (char *) m->next;

	    if(m->next != NULLMBX)
		m->next->prev = m->prev;

	    if (m->to != NULLCHAR)
		free(m->to);

	    if (m->tofrom != NULLCHAR)
		free(m->tofrom);

	    if (m->tomsgid != NULLCHAR)
		free(m->tomsgid);

	    if (m->fullfrom != NULLCHAR)
		free(m->fullfrom);

	    if (m->subject != NULLCHAR)
		free(m->subject);

	    while (m->mbpath != NULLMBXPATH) {
		mbp = m->mbpath->next;
		free(m->mbpath);
		m->mbpath = mbp;
	    }

	    if (m->tfile != NULLFILE)
		fclose(m->tfile);

	    if (m->script != NULLFILE)
		fclose(m->script);

	    if (m->smtp_cb != NULLCB)
		if (m->state == MBX_DISC) {	/* expecting DISC? */
#ifdef MBDEBUG
		    printf("MBX: mbx_state calls mbxfwd\n");
#endif
		    mbxfwd(m->smtp_cb);		/* then, start fwd to next box */
		} else {
#ifdef MBDEBUG
		    printf("MBX: mbx_state calls del_session\n");
#endif
		    del_session(m->smtp_cb);	/* no, wait a while */
		}

	    free(m);
	    break;
	}
}

/* receive upcall for ax.25 */

static void mbx_rx(axp,cnt)
struct ax25_cb *axp;
int16 cnt;
{
	struct mbx *m;
	struct mbuf *bp;
	char line[MBXLINE + 1];
	struct mbuf *recvl_ax25();

	if((m = find_mbx(axp)) == NULLMBX) {
	    disc_ax25(axp);			/* who are you?? */
	    return;
	}

	while((bp = recvl_ax25(axp,0)) != NULLBUF){ /* get a line of text */
	    line[pullup(&bp,line,sizeof(line) - 1)] = '\0';
	    rip(line);
	    free_p(bp);
	    if(mbx_line(m,line) == -1)		/* evaluate inputline */
		break;				/* quit after "bye" */
	}
}

/* transmit upcall (used to send a file/mail) */

static void
mbx_tx(axp,cnt)
struct ax25_cb *axp;
int16 cnt;
{
	struct mbx *m;
	int remain;
	char *pac;
	char line[MBXLINE + 1];

	if((m = find_mbx(axp)) == NULLMBX) {
	    disc_ax25(axp);			/* who are you?? */
	    return;
	}

	switch (m->state)
	{
	case MBX_CONN:				/* connecting? */
	case MBX_SEND:				/* transferring mail? */
	    mbx_doscript(m);			/* keep it going */
	    break;

	case MBX_INFO:				/* sending info? */
	case MBX_BODY:				/* sending mail body? */
	    if (m->tfile != NULLFILE) {
		if ((remain = axp->maxframe - len_q(axp->txq)) < 2)
		    pac = NULLCHAR;
		else
		    if ((pac = malloc(axp->paclen)) != NULLCHAR)
			pac[0] = '\0';		/* clean packet buffer */

		while (fgets(line,sizeof(line),m->tfile) != NULLCHAR) {
		    rip(line);
		    strcat(line,mbcr);

		    if (pac == NULLCHAR) {
			mbx_msg(m,line);
			remain--;
		    } else {
			if ((strlen(pac) + strlen(line)) < axp->paclen){
			    strcat(pac,line);	/* still fits, concat it */
			    continue;		/* read next line */
			}
			if (pac[0] != '\0'){
			    mbx_msg(m,pac);	/* too long, flush old */
			    remain--;
			}
			strcpy(pac,line);
		    }

		    /* when only one packet left in window, stop buffering */
		    if (remain < 2 && pac != NULLCHAR){
			if (pac[0] != '\0'){
			    mbx_msg(m,pac);
			    remain--;
			}
			free(pac);
			pac = NULLCHAR;
		    }

		    if (remain <= 0)
			return;			/* window full, pause */
		}
		fclose(m->tfile);
		m->tfile = NULLFILE;

		/* don't forget buffered packet */
		if (pac != NULLCHAR){
		    if (pac[0] != '\0')
			mbx_msg(m,pac);
		    free(pac);
		}

		/* got EOF, see what to do next.  when INFO: ->command state */
		/* when BODY: send end-of-message and back to SEND state */
		if (m->state == MBX_INFO) {
		    m->state = MBX_CMD;
		    mbx_msg(m,(m->sid & MBX_SID) ? mbprompt : mbmenu);
		} else {
		    m->state = MBX_SEND;
		    if (m->sline[1] == '\0')
			strcpy(m->sline + 1,"\032"); /* default ^Z */
		    strcat(m->sline,mbcr);
		    mbx_msg(m,m->sline + 1);
		    if (mbx_script(m) == 0)
			mbx_doscript(m);
		}
	    }
	    break;
	}
}

/* process one line for the mailbox */

static int
mbx_line(m,line)
struct mbx *m;
char *line;
{
    switch (m->state)
    {
    case MBX_CMD:
	switch (tolower(line[0])) {
	    case 'b':				/* bye - bye */
		if (!(m->sid & MBX_SID))
		   mbx_msg(m,mbbye);
		close_ax25(m->ax25_cb);
		return -1; /* tell line processor to quit */

	    case 'i': {				/* info */
		if (mbxinfofile[0] == '\0' ||
		    (m->tfile = fopen(mbxinfofile,"r")) == NULLFILE)
		    mbx_msg(m,(m->sid & MBX_SID) ? mbprompt : mbmenu);
		else {
		    m->state = MBX_INFO;
		    mbx_tx(m->ax25_cb,1);
		}
	    }
		break;

	    case 's': {
		int badsubj = 0;

		/* Get S-command type (B,P,T, etc.) */

		if (line[1] == '\0')
		    m->stype = ' ';
		else
		    m->stype = toupper(line[1]);

		if (mbx_to(m,line) == -1) {
		    if (m->sid & MBX_SID)
			mbx_msg(m,mbno);
		    else {
			mbx_msg(m,mbserr);
		    }
		    badsubj++;
		}
		else if (validate_address(m->to) == 0) {
		    if (m->sid & MBX_SID)
			mbx_msg(m,mbno);
		    else
			mbx_msg(m,mbbaduser);
		    free(m->to);
		    m->to = NULLCHAR;
		    if (m->tofrom) {
			free(m->tofrom);
			m->tofrom = NULLCHAR;
		    }
		    if (m->tomsgid) {
			free(m->tomsgid);
			m->tomsgid = NULLCHAR;
		    }
		    badsubj++;
		}

		if (badsubj)
		    mbx_msg(m,(m->sid & MBX_SID) ? mbprompt : mbmenu);
		else {
		    m->state = MBX_SUBJ;
		    mbx_msg(m,(m->sid & MBX_SID) ? mbok : mbsubject);
		}
		break;
	    }
	    case '[':				/* SID */
		if (mbx_sid(m,line))		/* check and process it */
		    goto huh;			/* error, say huh */

		mbx_msg(m,mbprompt);		/* it's okay! */
		break;


	    case 'f':
		if (line[1] == '>' && (m->sid & MBX_SID)) {
		    /* RLI BBS' expect us to disconnect if we */
		    /* have no mail for them, which of course */
		    /* we don't, being rather haughty about our */
		    /* protocol superiority. */
		    if (m->sid & MBX_SID_RLI) {
			close_ax25(m->ax25_cb);
			return -1;
		    } else
			mbx_msg(m,mbprompt);
		    break;
		}
		/* Otherwise drop through to "huh?" */

	    huh:
	    default:
		mbx_msg(m,mberr);
		mbx_msg(m,(m->sid & MBX_SID) ? mbprompt : mbmenu);
	}
	break;

    case MBX_SUBJ:
	if ((m->subject = malloc(strlen(line) + 1)) == NULLCHAR) {
		close_ax25(m->ax25_cb);
		return -1; /* tell line processor to quit */
	}

	strcpy(m->subject,line);

	if ((m->sid & MBX_SID) == 0)
	    mbx_msg(m,mbmesg);

	m->state = MBX_HEAD;
	break;

    case MBX_HEAD:
	if (!strncmp(line,"R:",2) && strlen(line) > 14) {
	    register struct mbxpath *mbp;
	    register char *p;
	    char *q;

	    if ((mbp = (struct mbxpath *) calloc(1,sizeof(struct mbxpath))) == NULLMBXPATH) {
		close_ax25(m->ax25_cb);
		return -1; /* tell line processor to quit */
	    }

	    mbp->next = m->mbpath;
	    m->mbpath = mbp;

	    /* parse the "received date/time" field */
	    if (isalpha(line[13]))	/* timezone given? */
		mbp->z = line[13];

	    line[13] = '\0';
	    mbp->mm = atoi(&line[11]);
	    line[11] = '\0';
	    mbp->hh = atoi(&line[9]);
	    mbp->d = atoi(&line[6]);
	    line[6] = '\0';
	    mbp->m = atoi(&line[4]);
	    line[4] = '\0';
	    mbp->y = atoi(&line[2]);

	    /* parse the @bbs field */
	    for (p = &line[14]; *p != '\0' && *p != '@'; p++)
		;

	    if (*p++ == '@') {
		if (*p == ':')
		    p++;
		while (*p == ' ')
		    p++;
		q = mbp->name;
		while (isalnum(*p) && (q - mbp->name) < 6) {
		    *q++ = tolower(*p);
		    p++;
		}
	    }
	    break;
	}
	m->state = MBX_DATA;			/* no more headers, DATA state */

	/* generate full "from" address (return path to sender) */
	{
	    char fullfrom[80];

	    strcpy(fullfrom,(m->tofrom != NULLCHAR)? m->tofrom : m->name);
	    if (m->mbpath != NULLMBXPATH && m->mbpath->next != NULLMBXPATH)
		sprintf(fullfrom + strlen(fullfrom),"%%%s",m->mbpath->name);
	    sprintf(fullfrom + strlen(fullfrom),"%%%s%s@%s",
		m->name,(m->sid & MBX_SID)? ".bbs":"",hostname);

	    if ((m->fullfrom = malloc(strlen(fullfrom) + 1)) == NULLCHAR) {
		close_ax25(m->ax25_cb);
		return -1;
	    }

	    strcpy(m->fullfrom,fullfrom);
	}

	if (mbx_data(m) == -1) {		/* create temp & write headers */
	    mbx_msg(m,mbtemp);
	    close_ax25(m->ax25_cb);
	    return -1;
	}
	if (line[0] == '\0')			/* skip first line when empty */
	    break;

    case MBX_DATA:
	if (line[0] == 0x1a ||
	    strcmp(line,"/ex") == 0 ||
	    strcmp(line,"/EX") == 0)
	{
	    char *host;

	    if ((host = index(m->to,'@')) == NULLCHAR)
		host = hostname;		/* use our hostname */
	    else
		host++;				/* use the host part of address */

	    fseek(m->tfile,0L,0);		/* reset to beginning */
	    if (queuejob(NULL,m->tfile,host,m->to,m->fullfrom) != 0)
		mbx_msg(m,mbqerr);

	    free(m->to);
	    m->to = NULLCHAR;
	    free(m->fullfrom);
	    m->fullfrom = NULLCHAR;
	    if (m->tofrom) {
		free(m->tofrom);
		m->tofrom = NULLCHAR;
	    }
	    if (m->tomsgid) {
		free(m->tomsgid);
		m->tomsgid = NULLCHAR;
	    }
	    free(m->subject);
	    m->subject = NULLCHAR;
	    fclose(m->tfile);
	    m->tfile = NULLFILE;
	    m->state = MBX_CMD;
	    mbx_msg(m,(m->sid & MBX_SID) ? mbprompt : mbmenu);
	    break;
	}
	/* not done yet! */
	fprintf(m->tfile,"%s\n",line);
	break;

    case MBX_CONN:			/* connecting a box */
	mbx_sid(m,line);		/* when a SID received, note it */
					/* and process incoming data... */
    case MBX_SEND:			/* sending mail */
    case MBX_BODY:			/* transferring mail text */
	switch (m->sline[0])		/* look at current script op */
	{
	case 'r':			/* receiving something */
	    if (line[0] == '\0')	/* ignore empty lines */
		break;

	    if (strstr(line,m->sline + 1) != NULL) {
		if (mbx_script(m) == 0)
		    mbx_doscript(m);
		else
		    m->state = MBX_DISC;
	    } else {
		if (m->state == MBX_CONN) {
		    close_ax25(m->ax25_cb);
		} else {
		    strcpy(m->smtp_cb->buf,line);
		    logerr(m->smtp_cb);
		}
	    }
	    break;

	case 'w':			/* awaiting something */
	    if (strstr(line,m->sline + 1) != NULL) {
		if (mbx_script(m) == 0)
		    mbx_doscript(m);
		else
		    m->state = MBX_DISC;
	    }
	    break;
	}
	break;
    }

    return 0;
}

/* process the SID [BOX-VER-CAP] */
static int
mbx_sid(m,line)
struct mbx *m;
char *line;
{
    int len = strlen(line);

    if (line[len - 1] == ']') {		/* must be an SID */
	m->sid = MBX_SID;

	/* Now check to see if this is an RLI board. */
	/* As usual, Hank does it a bit differently from */
	/* the rest of the world. */
	if (len >= 5 &&			/* [RLI] at a minimum */
	    strncmp(&line[1],"RLI",3) == 0) {
	    m->sid |= MBX_SID_RLI;
	}

	return 0;
    }

    return 1;				/* not an SID */
}

/* send something to the connected station */
static
mbx_msg(m,msg)
struct mbx *m;
char msg[];
{
    int len;
    struct mbuf *bp;

    len = strlen(msg);

    if ((bp = alloc_mbuf(len+1)) == NULLBUF) {
	disc_ax25(m->ax25_cb);
	return -1;
    }

    bp->cnt = len + 1;
    *bp->data = PID_FIRST | PID_LAST | PID_NO_L3;
    memcpy(bp->data+1,msg,len);

    send_ax25(m->ax25_cb,bp);
    return 0;
}


/* States for send line parser state machine */

#define	    SKIP_CMD	    1
#define	    LOOK_FOR_USER   2
#define	    IN_USER	    3
#define	    AFTER_USER	    4
#define	    LOOK_FOR_HOST   5
#define	    IN_HOST	    6
#define	    AFTER_HOST	    7
#define	    LOOK_FOR_FROM   8
#define	    IN_FROM	    9
#define	    AFTER_FROM	    10
#define	    LOOK_FOR_MSGID  11
#define	    IN_MSGID	    12
#define	    FINAL_STATE	    13
#define	    ERROR_STATE	    14

/* Prepare the addressee.  If the address is bad, return -1, otherwise
 * return 0
 */
static
mbx_to(m,cp)
struct mbx *m;
register char *cp;
{
    int state;
    char *user,*host,*from,*msgid;
    int userlen = 0,hostlen = 0,fromlen = 0,msgidlen = 0;

    for (state = SKIP_CMD; state < FINAL_STATE; cp++) {
#ifdef MBDEBUG
	printf("MBX: State is %d, char is %c\n", state, *cp);
#endif
	switch (state) {
	    case SKIP_CMD:
		if (*cp == '\0')
		    state = ERROR_STATE;	/* no user */
		else if (isspace(*cp))
		    state = LOOK_FOR_USER;
		break;
	    case LOOK_FOR_USER:
		if (*cp == '\0' || *cp == '@' || *cp == '<' || *cp == '$')
		    state = ERROR_STATE;	/* no user */
		else if (!isspace(*cp)) {	/* found start of user */
		    user = cp;			/* point at start */
		    userlen++;			/* start counting */
		    state = IN_USER;
		}
		break;
	    case IN_USER:
		switch (*cp) {
		    case '\0':			/* found username only */
			state = FINAL_STATE;
			break;
		    case '@':
			state = LOOK_FOR_HOST;	/* hostname should follow */
			break;
		    case '<':
			state = LOOK_FOR_FROM;	/* from name should follow */
			break;
		    case '$':
			state = LOOK_FOR_MSGID; /* message id should follow */
			break;
		    default:
			if (isspace(*cp))
			    state = AFTER_USER; /* white space */
			else
			    userlen++;		/* part of username */
		}
		break;
	    case AFTER_USER:
		switch (*cp) {
		    case '\0':
			state = FINAL_STATE;	/* found username only */
			break;
		    case '@':
			state = LOOK_FOR_HOST;	/* hostname follows */
			break;
		    case '<':
			state = LOOK_FOR_FROM;	/* fromname follows */
			break;
		    case '$':
			state = LOOK_FOR_MSGID; /* message id follows */
			break;
		    default:
			if (!isspace(*cp))
			    state = ERROR_STATE;
		}
		break;
	    case LOOK_FOR_HOST:
		switch (*cp) {
		    case '\0':			/* user@? */
		    case '@':			/* user@@ */
		    case '<':			/* user@< */
		    case '$':			/* user@$ */
			state = ERROR_STATE;
			break;
		    default:
			if (!isspace(*cp)) {
			    host = cp;
			    hostlen++;
			    state = IN_HOST;
			}
		}
		break;
	    case IN_HOST:
		switch (*cp) {
		    case '\0':
			state = FINAL_STATE;	/* found user@host */
			break;
		    case '@':
			state = ERROR_STATE;	/* user@host@? */
			break;
		    case '<':
			state = LOOK_FOR_FROM;	/* fromname follows */
			break;
		    case '$':
			state = LOOK_FOR_MSGID; /* message id follows */
			break;
		    default:
			if (isspace(*cp))
			    state = AFTER_HOST;
			else
			    hostlen++;
		}
		break;
	    case AFTER_HOST:
		switch (*cp) {
		    case '\0':
			state = FINAL_STATE;	/* user@host */
			break;
		    case '@':
			state = ERROR_STATE;	/* user@host @ */
			break;
		    case '<':
			state = LOOK_FOR_FROM;	/* user@host < */
			break;
		    case '$':
			state = LOOK_FOR_MSGID; /* user@host $ */
			break;
		    default:
			if (!isspace(*cp))
			    state = ERROR_STATE;/* user@host foo */
		}
		break;
	    case LOOK_FOR_FROM:
		switch (*cp) {
		    case '\0':			/* user@host <? */
		    case '@':			/* user@host <@ */
		    case '<':			/* user@host << */
		    case '$':			/* user@host <$ */
			state = ERROR_STATE;
			break;
		    default:
			if (!isspace(*cp)) {
			    from = cp;
			    fromlen++;
			    state = IN_FROM;
			}
		}
		break;
	    case IN_FROM:
		switch (*cp) {
		    case '\0':
			state = FINAL_STATE;	/* user@host <foo */
			break;
		    case '<':
			state = ERROR_STATE;	/* user@host <foo< */
			break;
		    case '$':
			state = LOOK_FOR_MSGID; /* message id follows */
			break;
		    default:
			if (isspace(*cp))
			    state = AFTER_FROM;
			else
			    fromlen++;
		}
		break;
	    case AFTER_FROM:
		switch (*cp) {
		    case '\0':
			state = FINAL_STATE;	/* user@host <foo */
			break;
		    case '@':			/* user@host <foo @ */
		    case '<':			/* user@host <foo < */
			state = ERROR_STATE;
			break;
		    case '$':
			state = LOOK_FOR_MSGID; /* user@host <foo $ */
			break;
		    default:
			if (!isspace(*cp))
			    state = ERROR_STATE;/* user@host foo */
		}
		break;
	    case LOOK_FOR_MSGID:
		if (*cp == '\0')
		    state = ERROR_STATE;	/* msgid = $? */
		else if (isspace(*cp))
		    state = ERROR_STATE;	/* user@host <foo $ bar */
		else {
		    msgid = cp;
		    msgidlen++;
		    state = IN_MSGID;
		}
		break;
	    case IN_MSGID:
		if (*cp == '\0')
		    state = FINAL_STATE;
		else if (isspace(*cp))
		    state = FINAL_STATE;
		else
		    msgidlen++;
		break;
	    default:
		/* what are we doing in this state? */
		state = ERROR_STATE;
	}
    }

    if (state == ERROR_STATE)
	return -1;     /* syntax error */

    if ((m->to = malloc(userlen + hostlen + 2)) == NULLCHAR)
	return -1;     /* no room for to address */

    strncpy(m->to,user,userlen);
    m->to[userlen] = '\0';

    if (hostlen) {
	m->to[userlen] = '@';
	strncpy(m->to + userlen + 1,host,hostlen);
	m->to[userlen + hostlen + 1] = '\0';
    }

    for (cp = m->to; *cp != '\0'; cp++)
	*cp = tolower(*cp);

    if (fromlen) {
	if ((m->tofrom = malloc(fromlen + 1)) == NULLCHAR) {
	    free(m->to);
	    m->to = NULLCHAR;
	    return -1;
	}
	strncpy(m->tofrom,from,fromlen);
	m->tofrom[fromlen] = '\0';

	for (cp = m->tofrom; *cp != '\0'; cp++)
	    *cp = tolower(*cp);
    }

    if (msgidlen) {
	if ((m->tomsgid = malloc(msgidlen + 1)) == NULLCHAR) {
	    free(m->to);
	    m->to = NULLCHAR;
	    if (fromlen) {
		free(m->tofrom);
		m->tofrom = NULLCHAR;
	    }
	    return -1;
	}
	strncpy(m->tomsgid,msgid,msgidlen);
	m->tomsgid[msgidlen] = '\0';
    }

    return 0;
}

/* This opens the data file and writes the mail header into it.
 * Returns 0 if OK, and -1 if not.
 */

static
mbx_data(m)
struct mbx *m;
{
    time_t t;
    long m_id;
    int i;
    struct mbxpath *mbp;
    char *ptime(),*pxtime();
    extern FILE *tmpfile();
    extern long get_msgid();

    if ((m->tfile = tmpfile()) == NULLFILE)
	return -1;

    time(&t);
    fprintf(m->tfile,"Received: from %s%s by %s with %s\n\tid AA%ld ; %s",
		     m->name,(m->sid & MBX_SID)? ".bbs":"",
		     hostname,(m->sid & MBX_SID)? "BBSFWD":"AX25",
		     m_id = get_msgid(),ptime(&t));
    fprintf(m->tfile,"Date: %s",m->mbpath == NULLMBXPATH?
		     ptime(&t) :
		     pxtime(m->mbpath->y,m->mbpath->m,m->mbpath->d,
			    m->mbpath->hh,m->mbpath->mm,m->mbpath->z));
    if (m->tomsgid != NULLCHAR)
	fprintf(m->tfile,"Message-Id: <%s@%s>\n",m->tomsgid,hostname);
    else
	fprintf(m->tfile,"Message-Id: <%ld@%s>\n",m_id,hostname);
    fprintf(m->tfile,"From: %s\n",m->fullfrom);
    fprintf(m->tfile,"To: %s\n",m->to);
    fprintf(m->tfile,"Subject: %s\n",m->subject);
    if (m->stype != ' ')
	fprintf(m->tfile,"X-BBS-Msg-Type: %c\n",m->stype);
    if (m->mbpath != NULLMBXPATH) {
	if (m->mbpath->next == NULLMBXPATH) {
	    free(m->mbpath);
	    m->mbpath = NULLMBXPATH;
	} else {
	    fprintf(m->tfile,"X-BBS-Path: ");
	    i = 0;
	    while (m->mbpath != NULLMBXPATH) {
		fprintf(m->tfile,"%s",m->mbpath->name);
		mbp = m->mbpath->next;
		free(m->mbpath);
		m->mbpath = mbp;
		if (m->mbpath != NULLMBXPATH) {
		    if (++i > 8) {
			fprintf(m->tfile,"\n\t\t");
			i = 0;
		    } else {
			fprintf(m->tfile,"!");
		    }
		}
	    }
	    fprintf(m->tfile,"\n");
	}
    }
    fprintf(m->tfile,"\n");			/* blank line before message body */

    return 0;
}

/* forward jobs to specified AX.25 mailboxes */
void
mbxfwd(cb)
struct smtp_cb *cb;
{
	FILE *script;
	char tmp[MBXLINE];

	if (cb->jobq != NULLJOB) do
	{
#ifdef MBDEBUG
	    printf("MBX: AX.25 forward to %s\n",cb->jobq->system);
#endif
	    sprintf(tmp,"%s%s",getnenv(MAILQDIR),cb->jobq->system);
	    if ((script = fopen(tmp,"r")) == NULL)
		continue;

	    /* read the initial connection line (skip comments) */
	    do
	    {
		if (fgets(tmp,MBXLINE,script) == NULL)
		    tmp[0] = '\0';

		rip(tmp);
	    } while (tmp[0] == '#');

#ifdef MBDEBUG
	    printf("MBX: attempt %s\n",tmp);
#endif

	    if (cmdparse(fwdcmds,tmp)) {	/* attempt a connect */
#ifdef MBDEBUG
		printf("MBX: connect failed\n");
#endif
		fclose(script);
		continue;			/* it failed, try next */
	    }

	    fwdmbx->smtp_cb = cb;		/* set values in mbx struct */
	    fwdmbx->script = script;

	    if (mbx_script(fwdmbx) != 0)	/* read next script line */
		continue;

	    fwdmbx = NULLMBX;			/* prevent mis-use */
	    return;				/* wait for connection */
	}
	while (nextjob(cb));			/* some error, try next job */

#ifdef MBDEBUG
	printf("MBX: mbxfwd calls del_session\n");
#endif
	del_session(cb);			/* all jobs failed */
}

/* read next connection/mailing script line */
static int
mbx_script (m)
register struct mbx *m;

{
    do
    {
	if (fgets(m->sline,MBXLINE,m->script) == NULL) {
#ifdef MBDEBUG
	    printf("MBX script: EOF\n");
#endif
	    m->sline[0] = '\0';
	    if (m->state != MBX_DISC)
		close_ax25(m->ax25_cb); /* terminate connection */
	    m->state = MBX_DISC;	/* we expect a disconnect */
	    return 1;
	}
	rip(m->sline);
    } while (m->sline[0] == '\0' || m->sline[0] == '#');

#ifdef MBDEBUG
    printf("MBX script: %s\n",m->sline);
#endif
    return 0;
}

/* interpret current script line and send output */

static void
mbx_doscript (m)
register struct mbx *m;

{
    char *p;
    struct list *lp;
    int d;
    static char type[] = "P";
    char line[80],tmp[80];

#ifdef MBDEBUG
    printf("MBX do_script: %s\n",m->sline);
#endif

    switch (m->sline[0])		/* first char is opcode */
    {
    case 'b':				/* send mail body */
	m->state = MBX_BODY;
	mbx_tx(m->ax25_cb,1);		/* kick off */
	break;

    case 'l':				/* loopback point for mail */
	m->state = MBX_SEND;
	m->mpos = ftell(m->script);
	mbx_script(m);
	mbx_send(m);			/* send the message */
	break;

    case 'm':				/* send a mail message */
	/* make sure we have a message-id.  use job-id when not avail. */
	if (m->tomsgid == NULLCHAR) {
	    sprintf(tmp,"%s_%s",m->smtp_cb->jobq->jobname,hostname);
	    if ((m->tomsgid = malloc(strlen(tmp) + 1)) != NULLCHAR)
		strcpy(m->tomsgid,tmp);
	}
	/* filter some characters likely to be magic */
	for (p = m->tomsgid; *p != '\0'; p++)
	    if (index("\"$'();<=>?@[]`{}",*p) != NULLCHAR)
		*p = '_';

	/* make sure we have a "from:" address */
	if (m->tofrom == NULLCHAR) {
	    if ((m->tofrom = malloc(strlen(m->smtp_cb->jobq->from) + 1)) != NULLCHAR)
		strcpy(m->tofrom,m->smtp_cb->jobq->from);
	}

	/* scan "from:", probably of form "rob@pe1chl" or "pe1chl@pe1chl" */
	/* we want to extract a callsign from this, which is not easy! */
	/* first, examine the part before the @ sign */
	d = 0;
	for (p = m->tofrom; *p != '\0'; p++) {
	    if (!isalnum(*p))
		break;
	    if (isdigit(*p))		/* count digits */
		d++;
	}
	/* when we got a digit we probably have a callsign */
	/* when not, look further but watch out for things like: */
	/* "rob@sys2.pe1chl".  in this case, skip the first part */
	while (d == 0) {
	    if (p[0] == '\0' || p[1] == '\0') /* more coming? */
		break;			/* no, use what we have now */

	    strcpy(m->tofrom,p + 1);	/* shift left */

	    for (p = m->tofrom; *p != '\0'; p++) {
		if (!isalnum(*p)) {	/* next separator */
		    if (!isdigit(p[-1])) /* hack! trap "sys2.pe1chl"-like */
			d++;		/* break from outer loop */
		    break;
		}
	    }
	}
	*p = '\0';			/* terminate what we have */

	/* process the destination address.  we want call and bbs in */
	/* separate fields to be able to massage them in the script */
	strcpy(tmp,m->smtp_cb->jobq->to->val);
	if ((p = rindex(tmp,'@')) != NULLCHAR)
	    *p = '\0';			/* chop off @xxxxxx.bbs */
	if ((p = index(tmp,'%')) != NULLCHAR) {
	    *p++ = '\0';
	} else {
	    strcpy(p = tmp + 40,m->smtp_cb->jobq->system);
	    if ((p = index(p,'.')) != NULLCHAR)
		*p = '\0';
	    p = tmp + 40;
	}

	/* make a message type. a string allows more flexibility */
	type[0] = m->stype;

	/* now, create the "send" command from script and args */
	strcat(m->sline,mbcr);
	sprintf(line,m->sline + 1,type,tmp,p,m->tofrom,m->tomsgid,NULL,NULL);
	mbx_msg(m,line);
	mbx_script(m);
	break;

    case 'n':				/* next mail */
	log_ax(m->ax25_cb,"MBX sent job %s To: %s From: %s",
	       m->smtp_cb->jobq->jobname,
	       m->smtp_cb->jobq->to->val,
	       m->smtp_cb->jobq->from);

	/* drop this recepient and check if more */
	lp = m->smtp_cb->jobq->to->next;
	free(m->smtp_cb->jobq->to);
	if ((m->smtp_cb->jobq->to = lp) != NULLLIST && m->mpos != 0) {
	    fseek(m->script,m->mpos,0); /* back to loopback point */
	    mbx_script(m);
	    mbx_send(m);
	    break;
	}

	if (m->smtp_cb->errlog != NULLLIST)
	    retmail(m->smtp_cb);	/* return mail when error */

	unlink(m->smtp_cb->tname);	/* unlink textfile */
	unlink(m->smtp_cb->wname);	/* unlink workfile */

	/* check if next job happens to be addressed to same system */
	strcpy(tmp,m->smtp_cb->jobq->system);
	if (nextjob(m->smtp_cb) && !strcmp(tmp,m->smtp_cb->jobq->system) && m->mpos != 0) {
	    fseek(m->script,m->mpos,0); /* back to loopback point */
	    mbx_script(m);
	    mbx_send(m);
	    break;
	}

	mbx_script(m);			/* no, continue script */
	mbx_doscript(m);		/* (it will log-off the box) */
	break;

    case 's':				/* send a string */
	strcat(m->sline,mbcr);
	mbx_msg(m,m->sline + 1);
	mbx_script(m);
	break;

    case 'u':				/* send subject */
	strcat(m->sline,mbcr);
	sprintf(line,m->sline + 1,m->subject != NULLCHAR? m->subject : "",NULL,NULL);
	mbx_msg(m,line);
	mbx_script(m);
	break;
    }
}

/* send a message to the (already connected) ax.25 mailbox */
static void
mbx_send (m)
register struct mbx *m;
{
	register char *p;
	struct list *lp;
	int n = 'A' - 1;
	char line[MBXLINE];
	char *getname();

	if (m->subject != NULLCHAR)
	    free(m->subject);
	m->subject = NULLCHAR;

	if (m->tofrom != NULLCHAR)
	    free(m->tofrom);
	m->tofrom = NULLCHAR;

	if (m->tomsgid != NULLCHAR)
	    free(m->tomsgid);
	m->tomsgid = NULLCHAR;
	m->stype = 'P';

	/* open message and read headers to get some reqd info */

	if ((m->tfile = fopen(m->smtp_cb->tname,"r")) == NULLFILE) {
	    close_ax25(m->ax25_cb);		/* too many files? */
	    return;				/* try again later */
	}

	while (fgets(line,MBXLINE,m->tfile) != NULL) {
	    rip(line);
	    if (line[0] == '\0')		/* empty line ends headers */
		break;

	    if (!strncasecmp(line,"subject:",8)) {
		if (m->subject != NULLCHAR)
		    free(m->subject);

		for (p = line + 8; *p == ' '; p++)
		    ;

		if ((m->subject = malloc(strlen(p) + 1)) != NULLCHAR)
		    strcpy(m->subject,p);

#ifdef MBDEBUG
		printf("MBX: subject %s\n",m->subject);
#endif
		continue;
	    }

	    if (!strncasecmp(line,"from:",5)) {
		if (m->tofrom != NULLCHAR)
		    free(m->tofrom);

		for (p = line + 5; *p == ' '; p++)
		    ;

		if ((m->tofrom = malloc(strlen(p) + 1)) != NULLCHAR)
		    strcpy(m->tofrom,p);

#ifdef MBDEBUG
		printf("MBX: from %s\n",m->tofrom);
#endif
		continue;
	    }

	    if (!strncasecmp(line,"message-id:",11)) {
		if (m->tomsgid != NULLCHAR) {
		    free(m->tomsgid);
		    m->tomsgid = NULLCHAR;
		}

		if ((p = getname(line)) != NULLCHAR) {
		    if ((m->tomsgid = malloc(strlen(p) + 2)) != NULLCHAR) {
			/* when multiple recepients, generate unique msgid */
			if ((lp = m->smtp_cb->jobq->to->next) != NULLLIST) {
			    while (lp != NULLLIST) {
				n++;
				lp = lp->next;
			    }

			    m->tomsgid[0] = n;		/* A B C... */
			    m->tomsgid[1] = '\0';
			} else
			    m->tomsgid[0] = '\0';

			strcat(m->tomsgid,p);		/* plus org id */
		    }

#ifdef MBDEBUG
		    printf("MBX: message-id %s\n",m->tomsgid);
#endif
		}

		continue;
	    }

	    if (!strncasecmp(line,"x-bbs-msg-type:",15)) {
		for (p = line + 15; *p == ' '; p++)
		    ;

		m->stype = *p;

#ifdef MBDEBUG
		printf("MBX: message type %c\n",m->stype);
#endif
		continue;
	    }
	}

	fseek(m->tfile,0L,0);		/* rewind to beginning */
	mbx_doscript(m);		/* start the message xfer */
}

static int
dombxconnect (argc,argv)
int argc;
char *argv[];
{
	struct ax25 addr;
	struct ax25_cb *open_ax25();
	register struct ax25_call *axc;
	register struct mbx *m;
	struct interface *ifp;
	extern int16 axwindow;
	int i;
	struct ax25_cb *find_ax25();

	if((ifp = if_lookup(argv[1])) == NULLIF)
		return 1;

	if(!(ifp->flags & IF_AX25))
		return 1;

	memset(&addr,0,sizeof(addr));
	if(setcall(&addr.dest,argv[2]) < 0)
		return 1;

	/* Find the MBX call on this interface */
	for (axc = ax25_port; axc != NULLAXCALL; axc = axc->p_next){
		if(axc->port == MBOXPORT &&
		   ((axc->flags & MULTI_IF) || axc->interface == ifp)){
		    ASSIGN(addr.source,axc->addr);
		    break;
		}
	}
	if (axc == NULLAXCALL)
		return 1;

	/* Check if already connected to that station */
	if(find_ax25(&addr.dest,&addr.source) != NULLAX25) {
#ifdef MBDEBUG
		printf("MBX: already connected!\n");
#endif
		return 1;
	}

	/* Set digipeater path */
	if((addr.ndigis = argc - 3) > MAXDIGIS)
		return 1;
	for(i=3; i < argc; i++)
		if(setcall(&addr.digis[i-3],argv[i]) < 0)
			return 1;

	if((m = (struct mbx *) calloc(1,sizeof(struct mbx))) == NULLMBX)
	    return 1;

	/* connect the user to this mbox's user list */
	if((m->next = (struct mbx *) axc->user) != NULLMBX)
	    m->next->prev = m;

	axc->user = (char *) m;

	m->state = MBX_CONN;
	fwdmbx = m;				/* keep forwarded mbx */

#ifdef MBDEBUG
	printf("MBX: calling open_ax25 for %lx\n",m);
#endif
	if ((m->ax25_cb = open_ax25(&addr,axwindow,mbx_rx,mbx_tx,mbx_state,
				       ifp,(char *)axc)) == NULLAX25){
	    if(m->next != NULLMBX)
		m->next->prev = NULLMBX;

	    axc->user = (char *) m->next;
	    free(m);
	    return 1;
	}

	/* set a reasonable value for the idle-link disconnect time */
	if (m->ax25_cb->t4.start == 0 || TICK2SEC(m->ax25_cb->t4.start) > 1000)
	    m->ax25_cb->t4.start = SEC2TICK(300);

	return 0;
}

