/* There are only two functions in this mailbox code that depend on the
 * underlying protocol, namely mbx_getname() and dochat(). All the other
 * functions can hopefully be used without modification on other stream
 * oriented protocols than AX.25 or NET/ROM.
 *
 * SM0RGV 890506, most work done previously by W9NK
 *
 *** Changed 900114 by KA9Q to use newline mapping features in stream socket
 *	interface code; everything here uses C eol convention (\n)
 *
 *	Numerous new commands and other changes by SM0RGV, 900120
 */
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#ifdef	UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "global.h"
#include "config.h"
#include "bm.h"
#include "timer.h"
#include "mailbox.h"
#include "cmdparse.h"
#include "proc.h"
#include "socket.h"
#include "usock.h"
#include "session.h"
#include "ax25.h"
#include "smtp.h"
#include "dirutil.h"
#include "telnet.h"
#include "ftp.h"
#include "ftpserv.h"
#include "netrom.h"
#include "commands.h"
#include "netuser.h"
#include "files.h"

/*
#define MBDEBUG
*/

extern int Ax25mbox;
static int Stelnet = -1;
#ifdef	NETROM
static int Nrsocket = -1;
#endif
struct mbx *Mbox[NUMMBX];
static char *Motd = NULLCHAR;
static int Attended = 1;	/* default to attended mode */
unsigned Maxlet = BM_NLET;
unsigned Tiptimeout = 180;	/* Default tip inactivity timeout (seonds) */
static char Mbbanner[] = "[NET-H$]\nWelcome %s to the %s TCP/IP Mailbox (%s)\n%s";
static char Mbmenu[] = "Current msg# %d : A,B,C,D,E,F,G,H,I,J,K,L,N,R,S,T,U,V,W,Z,? >\n";
static char Longmenu1[] = "(?)help    (A)rea     (B)ye      (C)hat     (D)ownload (E)scape   (F)inger\n";   
static char Longmenu2[] = "(G)ateway  (H)elp     (I)nfo     (J)heard   (K)ill     (L)ist     (N)etrom\n";   
static char Longmenu3[] = "(R)ead     (S)end     (T)elnet   (U)pload   (V)erbose  (W)hat     (Z)ap\n";
#if	!defined(AX25)
static char Noservice[] = "Service unavailable.\n";
#endif
char Noperm[] = "Permission denied.\n";
static char Nosock[] = "Can't create socket\n";
static char Loginbanner[] = "\nKA9Q NOS (%s)\n\n";
static char Howtoend[] = "Terminate with /EX or ^Z in first column (^A aborts):\n";
static int doattend __ARGS((int argc,char *argv[],void *p));
static int domaxmsg __ARGS((int argc,char *argv[],void *p));
static int domotd __ARGS((int argc,char *argv[],void *p));
static int dosend __ARGS((int argc,char *argv[],void *p));
static int dosid __ARGS((int argc,char *argv[],void *p));
static int dotimeout __ARGS((int argc,char *argv[],void *p));
static int dochat __ARGS((int argc,char *argv[],void *p));
static int dombescape __ARGS((int argc,char *argv[],void *p));
static int dodownload __ARGS((int argc,char *argv[],void *p));
static int dombupload __ARGS((int argc,char *argv[],void *p));
static int dowhat __ARGS((int argc,char *argv[],void *p));
static int dozap __ARGS((int argc,char *argv[],void *p));
static int dosysop __ARGS((int argc,char *argv[],void *p));
static int dostars __ARGS((int argc,char *argv[],void *p));
static int doarea __ARGS((int argc,char *argv[],void *p));
static int dombhelp __ARGS((int argc,char *argv[],void *p));
static int dogateway __ARGS((int argc,char *argv[],void *p));
static int dombtelnet __ARGS((int argc,char *argv[],void *p));
static int dombfinger __ARGS((int argc,char *argv[],void *p));
#ifdef	NETROM
static int dombnetrom __ARGS((int argc,char *argv[],void *p));
static int dombnrident __ARGS((int argc,char *argv[],void *p));
static int dombnrnodes __ARGS((int argc,char *argv[],void *p));
static int dombnrconnect __ARGS((int argc,char *argv[],void *p));
#endif
static void gw_alarm __ARGS((void *p));
static int mbx_getname __ARGS((struct mbx *m));
static int mbxrecvline __ARGS((int s,char *buf,int len,int escape));
static int gw_connect __ARGS((struct mbx *m,int s,char *fsocket,int len));
static void gw_input __ARGS((int s,void *notused,void *p));
static void gw_superv __ARGS((int null,void *proc,void *p));
static int mbx_to __ARGS((int argc,char *argv[],void *p));
static int mbx_data __ARGS((struct mbx *m,struct list *cclist,char *extra));
static int msgidcheck __ARGS((char *string));
static int uuencode __ARGS((FILE *infile,int s,char *infilename));

static struct cmds Mbcmds[] = {
	"",		doreadnext,	0, 0, NULLCHAR,
	"area",		doarea,		0, 0, NULLCHAR,
	"send",		dosend,		0, 0, NULLCHAR,
	"read",		doreadmsg,	0, 2, "R numbers",
	"verbose",	doreadmsg,	0, 2, "V numbers",
#ifdef	AX25
	"jheard",	doaxheard,	0, 0, NULLCHAR,
#endif
	"kill",		dodelmsg,	0, 2, "K numbers",
	"list",		dolistnotes,	0, 0, NULLCHAR,
	"escape",	dombescape,	0, 0, NULLCHAR,
	"download",	dodownload,	0, 2, "D[U] filename",
	"upload",	dombupload,	0, 2, "U filename",
	"what",		dowhat,		0, 0, NULLCHAR,
	"zap",		dozap,		0, 2, "Z filename",
	"gateway",	dogateway,	0, 3, "G interface callsigns",
	"telnet",	dombtelnet,	0, 2, "T hostname",
	"finger",	dombfinger,	0, 0, NULLCHAR,
#ifdef	NETROM
	"netrom",	dombnetrom,	0, 0, NULLCHAR,
#endif
	"chat",		dochat,		0, 0, NULLCHAR,
	"bye",		domboxbye,	0, 0, NULLCHAR,
	"help",		dombhelp,	0, 0, NULLCHAR,
	"info",		dombhelp,	0, 0, NULLCHAR,
	"?",		dombhelp,	0, 0, NULLCHAR,
	"[",		dosid,		0, 0, NULLCHAR,
#ifdef	AX25
	"f>",		dorevfwd,	0, 0, NULLCHAR,
#endif
	"@",		dosysop,	0, 0, NULLCHAR,
	"***",		dostars,	0, 0, NULLCHAR,
	NULLCHAR,	NULLFP,		0, 0, "Huh?",
};

#ifdef	AX25
int
ax25start(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int s,type;

	if (Axi_sock != -1)
		return 0;

	psignal(Curproc,0);	/* Don't keep the parser waiting */
	chname(Curproc,"AX25 listener");
	Axi_sock = socket(AF_AX25,SOCK_STREAM,0);
	/* bind() is done automatically */
	if(listen(Axi_sock,1) == -1){
		close_s(Axi_sock);
		return -1;
	}
	for(;;){
		if((s = accept(Axi_sock,NULLCHAR,NULLINT)) == -1)
			break;	/* Service is shutting down */

		type = AX25TNC;
		/* Eat the line that triggered the connection
		 * and then start the mailbox
		 */
		sockmode(s,SOCK_ASCII);	/* To make recvline work */
		recvline(s,NULLCHAR,80); 
		newproc("mbox",2048,mbx_incom,s,(void *)type,NULL,0);
	}
	close_s(Axi_sock);
	Axi_sock = -1;
	return 0;
}
int
ax250(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Axi_sock);
	Axi_sock = -1;
	return 0;
}
#endif
/* Start up Telnet server */
int
telnet1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_in lsocket;
	int s;
	int type;

	if(Stelnet != -1){
		return 0;
	}
	psignal(Curproc,0); 	/* Don't keep the parser waiting */
	chname(Curproc,"Telnet listener");

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = IPPORT_TELNET;
	else
		lsocket.sin_port = atoi(argv[1]);
	Stelnet = socket(AF_INET,SOCK_STREAM,0);
	bind(Stelnet,(char *)&lsocket,sizeof(lsocket));
	listen(Stelnet,1);
	for(;;){
		if((s = accept(Stelnet,NULLCHAR,(int *)NULL)) == -1)
			break;	/* Service is shutting down */

		if(availmem() < Memthresh){
			shutdown(s,1);
		} else {
			/* Spawn a server */
			type = TELNET;
			newproc("mbox",2048,mbx_incom,s,(void *)type,NULL,0);
		}
	}
	return 0;
}
/* Stop telnet server */
int
telnet0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Stelnet);
	Stelnet = -1;
	return 0;
}

#ifdef	NETROM
int
nr4start(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int s,type;

	if (Nrsocket != -1)
		return -1;

	psignal(Curproc,0);	/* Don't keep the parser waiting */
	chname(Curproc,"NETROM listener");
	Nrsocket = socket(AF_NETROM,SOCK_SEQPACKET,0);
	/* bind() is done automatically */
	if (listen(Nrsocket,1) == -1) {
		close_s(Nrsocket);
		Nrsocket = -1;
		return -1;
	}
	for(;;){
		if((s = accept(Nrsocket,NULLCHAR,NULLINT)) == -1)
			break;	/* Service is shutting down */

		type = NRSESSION;
		newproc("mbox",2048,mbx_incom,s,(void *)type,NULL,0);
	}
	close_s(Nrsocket);
	Nrsocket = -1;
	return 0;
}
int
nr40(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Nrsocket);
	Nrsocket = -1;
	return 0;
}

#endif
/* mbox subcommand table */
static struct cmds Mbtab[] = {
	"attend",	doattend,	0, 0, NULLCHAR,
#ifdef	AX25
	"kick",		dombkick,	0, 0, NULLCHAR,
#endif
	"maxmsg",	domaxmsg,	0, 0, NULLCHAR,
	"motd",		domotd,		0, 0, NULLCHAR,
	"status",	domboxdisplay,	0, 0, NULLCHAR,
#ifdef	AX25
	"timer",	dombtimer,	0, 0, NULLCHAR,
#endif
 	"tiptimeout",	dotimeout,	0, 0, NULLCHAR,
	NULLCHAR,
};
int
dombox(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc == 1)
		return domboxdisplay(argc,argv,p);
	return subcmd(Mbtab,argc,argv,p);
}

/* if unattended mode is set, ax25, telnet and maybe other sessions will
 * be restricted.
 */
static int
doattend(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setbool(&Attended,"Attended flag",argc,argv);
}

static int
domaxmsg(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setuns(&Maxlet,"Maximum messages per area",argc,argv);
}

static int
dotimeout(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setuns(&Tiptimeout,"Tip connection timeout",argc,argv);
}

static int
domotd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc > 2) {
		tprintf("Usage: mbox motd \"<your message>\"\n");
		return 0;
	}

	if(argc < 2) {
		if(Motd != NULLCHAR)
			tputs(Motd);
	}
	else {
		if(Motd != NULLCHAR){
			free(Motd);
			Motd = NULLCHAR;	/* reset the pointer */
		}
		if(!strlen(argv[1]))
			return 0;		/* clearing the buffer */
		Motd = mallocw(strlen(argv[1])+5);/* allow for the EOL char */
		strcpy(Motd, argv[1]);
		strcat(Motd, "\n");		/* add the EOL char */
	}
	return 0;
}

int
domboxdisplay(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i, j, len;
	struct mbx *m;
	char fsocket[MAXSOCKSIZE];
	static char *states[] = {"LOGIN","CMD","SUBJ","DATA","REVFWD",
				"TRYING","FORWARD"};

	tprintf("User       State    S#  Where\n");

	for (i = 0; i < NUMMBX; i++){
		if((m = Mbox[i]) != NULLMBX){
			len = MAXSOCKSIZE;
			j = getpeername(m->user,fsocket,&len);
			if(tprintf("%-11s%-9s%-4u%s\n",m->name,
				   states[m->state],m->user,
				   j != -1 ? psocket(fsocket): "") == EOF)
				break;
		}
	}
	return 0;
}

struct mbx *
newmbx()
{
	int i;
	struct mbx *m;

	for(i = 0; i < NUMMBX; i++){
		if(Mbox[i] == NULLMBX){
			m = Mbox[i] = (struct mbx *)callocw(1,sizeof(struct mbx));
			m->mbnum = i;
			return m;
		}
	}
	/* If we get here, there are no free mailbox sessions */
	return NULLMBX;
}

/* Incoming mailbox session */
void
mbx_incom(s,t,p)
int s;
void *t;
void *p;
{
	struct mbx *m;
	struct usock *up;
	char *buf[3];
	int rval;

	sockmode(s,SOCK_ASCII);
	sockowner(s,Curproc);	/* We own it now */
	/* Secede from the parent's sockets, and use the network socket that
	 * was passed to us for both input and output. The reference
	 * count on this socket will still be 1; this allows the domboxbye()
	 * command to work by closing that socket with a single call.
	 * If we return, the socket will be closed automatically.
	 */
	close_s(Curproc->output);
	close_s(Curproc->input);
	Curproc->output = Curproc->input = s;

	/* We'll do our own flushing right before we read input */
	setflush(s,-1);

	log(s,"open MBOX");
	if((m = newmbx()) == NULLMBX){
		tprintf("Too many mailbox sessions\n");
		return;
	}
	m->user = s;
	m->escape = 24;		/* default escape character is Ctrl-X */
	m->type = (int) t;
	/* get the name of the remote station */
	if(mbx_getname(m) == -1) {
		exitbbs(m);
		return;
	}

	m->state = MBX_CMD;	/* start in command state */

	/* Now say hi */
	tprintf(Mbbanner,m->name,Hostname,Version,
		Motd != NULLCHAR ? Motd : "");
	/* Enable our local message area */
	buf[1] = m->name;
	doarea(2,buf,m);
	tprintf(Mbmenu,m->current);
	while(mbxrecvline(s,m->line,MBXLINE,-1) != EOF){
		if((rval = mbx_parse(m)) == -2)
			break;
		if(rval == 1)
			tprintf("Bad syntax.\n");
		if(!(m->sid & MBX_SID) && isnewprivmail(m) > 0L)
			tprintf("You have new mail.\n");
		scanmail(m);
		tprintf((m->sid & MBX_SID) ? ">\n" : Mbmenu, m->current);
		m->state = MBX_CMD;
	}
	exitbbs(m);
	/* nasty hack! we may have screwed up reference count */
	/* by invoking newproc("smtp_send",....); Fudge it!   */
	if((up = itop(Curproc->output)) != NULLUSOCK)
		up->refcnt = 1;
	close_s(Curproc->output);
}

void
exitbbs(m)
struct mbx *m;
{
	struct usock *up;
	closenotes(m);
	free(m->to);
	free(m->tofrom);
	free(m->origto);
	free(m->tomsgid);
	free(m->path);
	free((char *)m->mbox);
	Mbox[m->mbnum] = NULLMBX;
	free((char *)m);
}

/* "twocmds" defines the MBL/RLI two-letter commands, eg. "SB", "SP" and so on.
 * They have to be treated specially since cmdparse() wants a space between
 * the actual command and its arguments.
 * "SP FOO" is converted to "s  foo" and the second command letter is saved
 * in m->stype. Longer commands like "SEND" are unaffected, except for
 * commands starting with "[", i.e. the SID, since we don't know what it will
 * look like.
 */
static char twocmds[] = "slrd[";	/* S,L,R,D are two-letter commands */
int
mbx_parse(m)
struct mbx *m;
{
	char *cp;
	int i;
	char *newargv[2];
	/* Translate entire buffer to lower case */
	for (cp = m->line; *cp != '\0'; ++cp)
		if(isupper(*cp))
			*cp = tolower(*cp);
	/* Skip any spaces at the begining */
	for(cp = m->line;isspace(*cp);++cp)
		;
	m->stype = ' ';
	if(*cp != '\0' && *(cp+1) != '\0')
	for(i=0; i<strlen(twocmds); ++i){
		if(*cp == twocmds[i] && (isspace(*(cp+2)) || *(cp+2) == '\0'
		 || *cp == '[')){
			if(islower(*(++cp)))
				m->stype = toupper(*cp); /* Save the second character */
			else
				m->stype = *cp;
			*cp = ' ';
			break;
		}
	}
	/* See if the input line consists solely of digits */
	cp = m->line;
	for(cp = m->line;isspace(*cp);++cp)
		;
	newargv[1] = cp;
	for(;*cp != '\0' && isdigit(*cp);++cp)
		;
	if(*cp == '\0' && strlen(newargv[1]) > 0) {
		newargv[0] = "read";
		return doreadmsg(2,newargv,(void *)m);
	}
	else
		return cmdparse(Mbcmds,m->line,(void *)m);
}

static int
mbx_getname(m)
struct mbx *m;
{
#ifdef	AX25
	char *cp;
#endif
	union sp sp;
	char tmp[MAXSOCKSIZE];
	char buf[MBXLINE];
	int len = MAXSOCKSIZE;
	int anony = 0;
	int oldmode;

	sp.p = tmp;
	sp.sa->sa_family = AF_LOCAL;	/* default to AF_LOCAL */
	getpeername(m->user,tmp,&len);
	m->path = mallocw(MBXLINE);
	/* This is one of the two parts of the mbox code that depends on the
	 * underlying protocol. We have to figure out the name of the
	 * calling station. This is only practical when AX.25 or NET/ROM is
	 * used. Telnet users have to identify themselves by a login procedure.
	 */
	switch(sp.sa->sa_family){
#ifdef	AX25
	case AF_NETROM:
	case AF_AX25:
		/* NETROM and AX25 socket address structures are "compatible" */
		pax25(m->name,sp.ax->ax25_addr);
		cp = strchr(m->name,'-');
		if(cp != NULLCHAR)			/* get rid of SSID */
			*cp = '\0';
		/* SMTP wants the name to be in lower case */
		cp = m->name;
		while(*cp){
			if(isupper(*cp))
				*cp = tolower(*cp);
			++cp;
		}
		anony = 1;
		/* Try to find the privileges of this user from the userfile */
		if((m->privs = userlogin(m->name,buf,&m->path,MBXLINE,&anony)) == -1)
			if((m->privs = userlogin("bbs",buf,&m->path,MBXLINE,&anony)) == -1)
				if((m->privs = userlogin("anonymous",buf,&m->path,MBXLINE,
					 &anony)) == -1){
						m->privs = 0;
						free(m->path);
						m->path = NULLCHAR;
				}
		if(m->privs & EXCLUDED_CMD)
			return -1;
		return 0;
#endif
	case AF_LOCAL:
	case AF_INET:
		m->state = MBX_LOGIN;
		tprintf(Loginbanner,Hostname);
		for(;;){
			tputs("login: ");
			usflush(m->user);
			if(mbxrecvline(m->user,m->name,sizeof(m->name),-1) == EOF)
				return -1;
			if(*m->name == '\0')
				continue;
			tprintf("Password: %c%c%c",IAC,WILL,TN_ECHO);
			usflush(m->user);
			oldmode = sockmode(m->user,SOCK_BINARY);
			if(mbxrecvline(m->user,buf,MBXLINE,-1) == EOF)
				return -1;
			tprintf("%c%c%c",IAC,WONT,TN_ECHO);
			sockmode(m->user,oldmode);
			tputc('\n');
			usflush(m->user);
			/* This is needed if the password was send before the
			 * telnet no-echo options were receied. We neeed to
			 * flush the eold sequence from the input buffers, sigh
			 */
			if(socklen(m->user,0))/* discard any remaining input */
				recv_mbuf(m->user,NULL,0,NULLCHAR,0);
			if((m->privs = userlogin(m->name,buf,&m->path,MBXLINE,&anony))
			 != -1){
				if(anony)
					log(m->user,"MBOX login: %s Password: %s",m->name,buf);
				else
					log(m->user,"MBOX login: %s",m->name);
				if(m->privs & EXCLUDED_CMD)
					return -1;
				return 0;
			}
			tprintf("Login incorrect\n");
			*m->name = '\0';	/* wipe any garbage */
		}
	}
	return 0;
}

/* This works like recvline(), but telnet options are answered and the
 * terminating newline character is not put into the buffer. If the
 * incoming character equals the value of escape, any queued input is
 * flushed and -2 returned.
 */
static int
mbxrecvline(s,buf,len,escape)
int s;
char *buf;
int len;
int escape;
{
	int c, cnt = 0, opt;
	if(buf == NULLCHAR)
		return 0;
	usflush(Curproc->output);
	while((c = recvchar(s)) != EOF){
		if(c == IAC){		/* Telnet command escape */
			if((c = recvchar(s)) == EOF)
				break;
			if(c > 250 && c < 255 && (opt = recvchar(s)) != EOF){
#ifdef	foo
				switch(c){
				case WILL:
					tprintf("%c%c%c",IAC,DONT,opt);
					break;
				case WONT:
					tprintf("%c%c%c",IAC,DONT,opt);
					break;
				case DO:
					tprintf("%c%c%c",IAC,WONT,opt);
					break;
				case DONT:
					tprintf("%c%c%c",IAC,WONT,opt);
				}
#endif
/* to be fixed 			usflush(Curproc->output);*/
				continue;
			}
			if(c != IAC && (c = recvchar(s)) == EOF)
				break;
		}
		/* ordinary character */
		if(c == '\r' || c == '\n')
			break;
		if(uchar(c) == escape){
			if(socklen(s,0)) /* discard any remaining input */
				recv_mbuf(s,NULL,0,NULLCHAR,0);
			cnt = -2;
			break;
		}
		*buf++ = c;
		++cnt;
		if(cnt == len - 1)
			break;
	}
	if(c == EOF && cnt == 0)
		return -1;
	*buf = '\0';
	return cnt;
}

int
domboxbye(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;

	m = (struct mbx *)p;
	/* Now say goodbye */
	tprintf("Thank you %s, for calling the %s Tcp/Ip Mailbox.\n",m->name,
		Hostname);
	if(m->type == TIP)
		tprintf("Please hang up now.\n");

	return -2;	/* signal that exitbbs() should be called */
}
static int
dombhelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	char buf[255];
	int i;
	FILE *fp;
	if(*argv[0] == '?') {
		tputs(Longmenu1);
		tputs(Longmenu2);
		tputs(Longmenu3);
		return 0;
	}
	buf[0] = '\0';
	if(argc > 1)
		for(i=0; Mbcmds[i].name != NULLCHAR; ++i)
			if(!strncmp(Mbcmds[i].name,argv[1],strlen(argv[1]))) {
				sprintf(buf,"%s/%s.hlp",Helpdir,Mbcmds[i].name);
				break;
			}
	if(buf[0] == '\0')
		if(*argv[0] == 'i')			/* INFO command */
			sprintf(buf,"%s/info.hlp",Helpdir);
		else
			sprintf(buf,"%s/help.hlp",Helpdir);
	if((fp = fopen(buf,READ_TEXT)) != NULLFILE) {
		sendfile(fp,Curproc->output,ASCII_TYPE,0);
		fclose(fp);
	}
	else
		tprintf("No help available. (%s not found)\n",buf);
	return 0;
}

static int
dochat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	char buf[8], *newargv[3];

	if(Attended){
		newargv[0] = "telnet";
		newargv[1] = Hostname;
		sprintf(buf,"%d",IPPORT_TTYLINK);
		newargv[2] = buf;
		return dombtelnet(3,newargv,p);
	}
	else {
		tprintf("Sorry - the system is unattended.\007\n");
	}
	/* It returns only after a disconnect or refusal */
	return 0;
}

static int
dosend(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int cccnt = 0, fail = 0;
	char *host, *cp, fullfrom[MBXLINE], sigwork[LINELEN], *rhdr = NULLCHAR;
	struct list *ap, *cclist = NULLLIST;
	struct mbx *m;
	FILE *fp;

	m = (struct mbx *)p;
	if((m->stype != 'R' || (m->sid & MBX_SID)) && mbx_to(argc,argv,m)
	   == -1){
		if(m->sid & MBX_SID)
			tprintf("NO - syntax error\n");
		else {
			tprintf("S command syntax error - format is:\n");
			tprintf("  S[F] name [@ host] [< from_addr] [$bulletin_id]\n");
			tprintf("  SR [number]\n");
		}
		return 0;
	}
	if(m->stype != 'R' && msgidcheck(m->tomsgid)) {
		if(m->sid & MBX_SID)
			tputs("NO - ");
		tprintf("Already have %s\n",m->tomsgid);
		return 0;
	}
	if(m->stype == 'R' && !(m->sid & MBX_SID) &&
	   mbx_reply(argc,argv,m,&cclist,&rhdr) == -1)
		return 0;
	if((cp = rewrite_address(m->to)) != NULLCHAR)
	     if(strcmp(m->to,cp) != 0){
		  m->origto = m->to;
		  m->to = cp;
	     }
	     else
		  free(cp);
	if((m->origto != NULLCHAR || m->stype == 'R') && !(m->sid & MBX_SID))
		tprintf("To: %s\n", m->to);
	if(validate_address(m->to) == 0){
		if(m->sid & MBX_SID)
			tprintf("NO - bad address\n");
		else
			tprintf("Bad user or host name\n");
		free(rhdr);
		del_list(cclist);
		/* We don't free any more buffers here. They are freed upon
		 * the next call to mbx_to() or to domboxbye()
		 */
		return 0;
	}
	/* Display the Cc: line (during SR command) */
	for(ap = cclist; ap != NULLLIST; ap = ap->next) {
		if(cccnt == 0){
			tprintf("%s",Hdrs[CC]);
			cccnt = 4;
		}
		else {
			tputs(", ");
			cccnt += 2;
		}
		if(cccnt + strlen(ap->val) > 80 - 3) {
			tputs("\n    ");
			cccnt = 4;
		}
		tputs(ap->val);
		cccnt += strlen(ap->val);
	}
	if(cccnt)
		tputc('\n');
	m->state = MBX_SUBJ;
	if(m->stype != 'R' || (m->sid & MBX_SID) != 0) {
		tprintf((m->sid & MBX_SID) ? "OK\n" : "Subject: ");
		if(mbxrecvline(m->user,m->line,MBXLINE,-1) == -1)
			return 0;
	}
	else				/* Replying to a message */
		tprintf("Subject: %s\n",m->line);
	if(mbx_data(m,cclist,rhdr) == -1){
		free(rhdr);
		del_list(cclist);
		tputs("Can't create temp file for mail\n");
		return 0;
	}
	free(rhdr);
	m->state = MBX_DATA;
	if((m->sid & MBX_SID) == 0 && m->stype != 'F')
		tprintf("Enter message.  %s",Howtoend);

	if(m->stype != 'F' || (m->sid & MBX_SID) != 0)
		while(mbxrecvline(m->user,m->line,MBXLINE,-1) != -1){
			if(m->line[0] == 0x01){  /* CTRL-A */
				fclose(m->tfile);
				tputs("Aborted.\n");
				del_list(cclist);
				return 0;
			}
			if(m->line[0] != CTLZ && stricmp(m->line, "/ex"))
				fprintf(m->tfile,"%s\n",m->line);
			else
				break;	/* all done */
		}
	else {
		fprintf(m->tfile,"----- Forwarded message -----\n\n");
		msgtofile(m,m->current,m->tfile,0);
		fprintf(m->tfile,"----- End of forwarded message -----\n");
	}

	/* Insert customised signature if one is found */
	if(!(m->sid & MBX_SID)) {	/* not a forwarding BBS */
	     sprintf(sigwork,"%s/%s.sig",Signature,
		     m->tofrom ? m->tofrom : m->name);
	     if((fp = fopen(sigwork,READ_TEXT)) != NULLFILE){
		  while(fgets(sigwork,LINELEN,fp) != NULLCHAR)
			fputs(sigwork,m->tfile);
		  fclose(fp);
	     }
	}

	if((host = strrchr(m->to,'@')) == NULLCHAR) {
		host = Hostname;	/* use our hostname */
		if(m->origto != NULLCHAR) {
			/* rewrite_address() will be called again by our
			 * SMTP server, so revert to the original address.
			 */
		 	free(m->to);
			m->to = m->origto;
			m->origto = NULLCHAR;
		}
	}
	else
		host++;	/* use the host part of address */

	/* make up full from name for work file */
	if(m->tofrom != NULLCHAR)
		sprintf(fullfrom,"%s%%%s.bbs@%s",m->tofrom, m->name, Hostname);
	else
		sprintf(fullfrom,"%s@%s",m->name,Hostname);
	if(cclist != NULLLIST && stricmp(host,Hostname) != 0) {
		fseek(m->tfile,0L,0);	/* reset to beginning */
		fail = queuejob(m->tfile,Hostname,cclist,fullfrom);
		del_list(cclist);
		cclist = NULLLIST;
	}
	addlist(&cclist,m->to,0);
	fseek(m->tfile,0L,0);
	fail += queuejob(m->tfile,host,cclist,fullfrom);
	del_list(cclist);
	fclose(m->tfile);
	if(fail)
	     tputs("Couldn't queue message for delivery\n");
	else
	     if(m->tomsgid != NULLCHAR &&
		(fp = fopen(Historyfile,APPEND_TEXT)) != NULLFILE) {
		  fprintf(fp,"%s\n",m->tomsgid); /* Save BID in history file */
		  fclose(fp);
	     }
	smtptick(NULL);		/* wake SMTP to send that mail */
	return 0;
}

static int
dosid(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;

	m = (struct mbx *)p;
	if(argc == 1)
		return 1;
	if(argv[1][strlen(argv[1]) - 1] != ']') /* must be an SID */
		return 1;
	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(m->stype == 'R' && strncmp(argv[1],"li",2) == 0)/* [RLI] at a minimum */
		m->sid |= MBX_RLI_SID;
	/* Check to see if the BBS supports a kludge called "hierarchical
	 * routing designators."
	 */
	if(strlen(argv[1]) > 2 && strcmp(argv[1]+strlen(argv[1])-3,"h$]") == 0)
		m->sid |= MBX_HIER_SID;
	return 0;
}

static int
dombescape(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;

	m = (struct mbx *)p;
	if(argc < 2){
		tprintf("The escape character is: ");
		if(m->escape < 32)
			tprintf("CTRL-%c\n",m->escape+'A'-1);
		else
			tprintf("'%c'\n",m->escape);
		return 0;
	}
	if(strlen(argv[1]) > 1)
		if(isdigit(*argv[1]))
			m->escape = (char) atoi(argv[1]);
		else
			return 1;
	else
		m->escape = *argv[1];
	return 0;
}

static int
dodownload(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	FILE *fp;
	char *file;

	m = (struct mbx *)p;
	file = pathname(m->path,argv[1]);
	if(!permcheck(m->path,m->privs,RETR_CMD,file)){
		tprintf(Noperm);
		return 0;
	}
	if((fp = fopen(file,READ_TEXT)) == NULLFILE)
		tprintf("Can't open \"%s\": %s\n",file,sys_errlist[errno]);
	else
		if(m->stype == 'U'){			/* uuencode ? */
			fclose(fp);
			fp = fopen(file,READ_BINARY);	/* assume non-ascii */
			uuencode(fp,m->user,file);
		} else
			sendfile(fp,m->user,ASCII_TYPE,0);
	free(file);
	fclose(fp);
	return 0;
}

static int
dombupload(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	FILE *fp;
	char *file, buf[LINELEN];

	m = (struct mbx *)p;
	file = pathname(m->path,argv[1]);
	if(!permcheck(m->path,m->privs,STOR_CMD,file)){
		tprintf(Noperm);
		return 0;
	}
	if((fp = fopen(file,WRITE_TEXT)) == NULLFILE){
		tprintf("Can't create \"%s\": %s\n",file,sys_errlist[errno]);
		free(file);
		return 0;
	}
	log(m->user,"MBOX upload: %s",file);
	tprintf("Send file,  %s",Howtoend);
	for(;;){
		if(mbxrecvline(m->user,buf,LINELEN,-1) == -1){
			unlink(file);
			break;
		}
		if(buf[0] == 0x01){  /* CTRL-A */
			unlink(file);
			tprintf("Aborted.\n");
			break;
		}
		if(buf[0] == CTLZ || !stricmp("/ex",buf))
			break;
		fputs(buf,fp);
#if !defined(UNIX) && !defined(__TURBOC__) && !defined(AMIGA)
		/* Needed only if the OS uses a CR/LF
		 * convention and putc doesn't do
		 * an automatic translation
		 */
		if(putc('\r',fp) == EOF)
			break;
#endif
		if(putc('\n',fp) == EOF)
			break;
	}
	free(file);
	fclose(fp);
	return 0;
}

static int
dowhat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	FILE *fp;
	char *file;

	m = (struct mbx *)p;
	if(argc < 2)
		file = strdup(m->path);
	else
		file = pathname(m->path,argv[1]);
	if(!permcheck(m->path,m->privs,RETR_CMD,file)){
		tprintf(Noperm);
		return 0;
	}
	if((fp = dir(file,1)) == NULLFILE)
		tprintf("Can't read directory: \"%s\": %s\n",file,sys_errlist[errno]);
	else
		sendfile(fp,m->user,ASCII_TYPE,0);
	free(file);
	fclose(fp);
	return 0;
}

static int
dozap(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	char *file;

	m = (struct mbx *)p;
	file = pathname(m->path,argv[1]);
	if(!permcheck(m->path,m->privs,DELE_CMD,file)){
		tprintf(Noperm);
		return 0;
	}
	if(unlink(file))
		tprintf("Zap failed: %s\n",sys_errlist[errno]);
	log(m->user,"MBOX Zap: %s",file);
	free(file);
	return 0;
}

static int
dosysop(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	int c;
	extern struct cmds Cmds[];

	m = (struct mbx *) p;
	if(!(m->privs & SYSOP_CMD)){
		tprintf(Noperm);
		return 0;
	}
	dombescape(1,NULLCHARP,p);
	for(;;){
		tprintf("Net> ");
		usflush(Curproc->output);
		c = mbxrecvline(Curproc->input,m->line,MBXLINE,m->escape);
		if(c == EOF || c == -2)
			break;
		log(m->user,"MBOX sysop: %s",m->line);
		cmdparse(Cmds,m->line,NULL);
	}
	return 0;
}

/* Handle the "*** Done" command when reverse forwarding ends or the
 * "*** LINKED to" command.
 */
static int
dostars(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	int anony = 1;
	m = (struct mbx *)p;
	/* The "*** LINKED to" command is only allowed to stations with
	 * SYSOP privileges to prevent others from obtaining the same.
	 */
	if((m->privs & SYSOP_CMD) && argc == 4 && !strcmp(argv[1],"linked")) {
		strcpy(m->name,argv[3]);
		/* Try to find the privileges of this user from the userfile */
		if((m->privs = userlogin(m->name,NULLCHAR,&m->path,MBXLINE,
					 &anony)) == -1)
		     if((m->privs = userlogin("bbs",NULLCHAR,&m->path,
				      MBXLINE,&anony)) == -1)
			  if((m->privs = userlogin("anonymous",NULLCHAR,
					   &m->path,MBXLINE,&anony)) == -1){
						m->privs = 0;
						free(m->path);
						m->path = NULLCHAR;
			  }
		tprintf("Oh, hello %s.\n",m->name);
		if(m->privs & EXCLUDED_CMD)
			return domboxbye(0,NULLCHARP,p);
		changearea(m,m->name);
		return 0;
	}
	if(argc > 1 && (m->sid & MBX_SID))	/* "*** Done" or similar */
		return 2;
	return -1;
}

static int
doarea(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	FILE *fp;

	m = (struct mbx *) p;
	if(argc < 2){
		tprintf("Current message area is: %s\n",m->area);
		tprintf("Available areas are:\n%-15s  Your private mail area\n",
		  m->name);
		if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
			return 0;
		sendfile(fp,m->user,ASCII_TYPE,0);
		fclose(fp);
		return 0;
	}
	if((m->privs & SYSOP_CMD) || strcmp(m->name,argv[1]) == 0){
		changearea(m,argv[1]);
		if(m->nmsgs){
			if(!strcmp(m->name,m->area))
				tprintf("You have ");
			else
				tprintf("%s: ",m->area);
			tprintf("%d message%s -  %d new.\n", m->nmsgs,
			  m->nmsgs == 1 ? " " : "s ", m->newmsgs);
		}
		return 0;
	}
	if(isarea(argv[1])) {
		changearea(m,argv[1]);
		tprintf("%s: %d message%s.\n", m->area, m->nmsgs,
		  m->nmsgs == 1 ? "" : "s");
	}
	else
		tprintf("No such message area: %s\n",argv[1]);
	return 0;
}

/* subroutine to do the actual switch from one area to another */
void
changearea(m,area)
struct mbx *m;
char *area;
{
	closenotes(m);
	m->nmsgs = m->newmsgs = m->current = 0;
	strcpy(m->area,area);
	scanmail(m);
}

#ifdef AX25
static int
dogateway(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	struct sockaddr_ax fsocket;
	int ndigis,i,s;
	char digis[MAXDIGIS][AXALEN];
	char target[AXALEN];

	m = (struct mbx *)p;
	if(!(m->privs & AX25_CMD)){
		tprintf(Noperm);
		return 0;
	}
	/* If digipeaters are given, put them in the routing table */
	if(argc > 3){
		setcall(target,argv[2]);
		ndigis = argc - 3;
		if(ndigis > MAXDIGIS){
			tprintf("Too many digipeaters\n");
			return 1;
		}
		for(i=0;i<ndigis;i++){
			if(setcall(digis[i],argv[i+3]) == -1){
				tprintf("Bad digipeater %s\n",argv[i+3]);
				return 1;
			}
		}
		if(ax_add(target,AX_LOCAL,digis,ndigis) == NULLAXR){
			tprintf("Route add failed\n");
			return 1;
		}
	}
	if((s = socket(AF_AX25,SOCK_STREAM,0)) == -1){
		tprintf(Nosock);
		return 0;
	}
	fsocket.sax_family = AF_AX25;
	setcall(fsocket.ax25_addr,argv[2]);
	strncpy(fsocket.iface,argv[1],ILEN);
	m->startmsg = mallocw(80);
	sprintf(m->startmsg,"*** LINKED to %s\n",m->name);
	return gw_connect(m,s,(char *)&fsocket, sizeof(struct sockaddr_ax));
}
#else
static int
dogateway(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	tputs(Noservice);
	return 0;
}
#endif

static int
dombtelnet(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	int s, len, i;
	char dsocket[MAXSOCKSIZE];
	struct sockaddr_in fsocket;

	m = (struct mbx *) p;
	fsocket.sin_family = AF_INET;
	if(argc < 3)
		fsocket.sin_port = IPPORT_TELNET;
	else
		fsocket.sin_port = atoi(argv[2]);

	if((fsocket.sin_addr.s_addr = resolve(argv[1])) == 0){
		tprintf(Badhost,argv[1]);
		return 0;
	}
	/* Only local telnets are are allowed to the unprivileged user */
	if(!(m->privs & TELNET_CMD) && !ismyaddr(fsocket.sin_addr.s_addr)){
		tprintf(Noperm);
		return 0;
	}
	if((s = socket(AF_INET,SOCK_STREAM,0)) == -1){
		tprintf(Nosock);
		return 0;
	}
	if(fsocket.sin_port == IPPORT_TTYLINK) {
		m->startmsg = mallocw(80);
		len = MAXSOCKSIZE;
		i = getpeername(m->user,dsocket,&len);
		sprintf(m->startmsg,"*** Incoming call from %s@%s ***\n",
			m->name,i != -1 ? psocket(dsocket): Hostname);
	}
	return gw_connect(m,s,(char *)&fsocket,SOCKSIZE);
}

static int
dombfinger(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	char *host, *user = NULLCHAR, buf[8], *newargv[3];

	if(argc > 2){
		tprintf("Usage: F user@host  or  F @host  or  F user.\n");
		return 0;
	}
	host = Hostname;
	if(argc == 2){
		if((host = strchr(argv[1], '@')) != NULLCHAR){
			*host = '\0';
			host++;
		} else
			host = Hostname;
		user = argv[1];
	}
	m = (struct mbx *) p;
	m->startmsg = mallocw(80);
	if(user != NULLCHAR)
		sprintf(m->startmsg,"%s\n",user);
	else
		strcpy(m->startmsg,"\n");
	newargv[0] = "telnet";
	newargv[1] = host;
	sprintf(buf,"%d",IPPORT_FINGER);
	newargv[2] = buf;
	return dombtelnet(3,newargv,p);
}

/* Generic mbox gateway code. It sends and frees the contents of m->startmsg
 * when the connection has been established unless it a null pointer.
 */
static int
gw_connect(m,s,fsocket,len)
struct mbx *m;
int s;
char *fsocket;
int len;
{
	int c;
	char *cp;
	struct proc *child;
	struct gwalarm *gwa;

	sockmode(s,SOCK_ASCII);
	child = newproc("gateway supervisor",256,gw_superv,0,Curproc,m,0);
	tprintf("Trying %s...  ",psocket((struct sockaddr *)fsocket));
	dombescape(0,NULLCHARP,(void *)m);
	usflush(Curproc->output);
	if(connect(s,fsocket,len) == -1){
		cp = sockerr(s);
		tprintf("Connection failed: ");
		if(cp != NULLCHAR)
			tprintf("%s errno %d\n",cp,errno);
		else
			tprintf("Escape character sent.\n");
		free(m->startmsg);
		m->startmsg = NULLCHAR;
		killproc(child);
		close_s(s);
		return 0;
	}
	/* The user did not type the escape character */
	killproc(child);
	tputs("Connected.\n");
	
	if(m->startmsg != NULLCHAR){
		usputs(s,m->startmsg);
		free(m->startmsg);
		m->startmsg = NULLCHAR;
	}
	/* Since NOS does not flush the output socket after a certain
	 * period of time, we have to arrange that ourselves.
	 */
	gwa = (struct gwalarm *) mallocw(sizeof(struct gwalarm));
	gwa->s1 = Curproc->output;
	gwa->s2 = s;
	set_timer(&gwa->t,240L);
	gwa->t.func = gw_alarm;
	gwa->t.arg = (void *) gwa;
	start_timer(&gwa->t);
	/* Fork off the receive process */
	child = newproc("gateway in",1024,gw_input,s,NULL,Curproc,0);
	
	for(;;){
		if((c = recvchar(Curproc->input)) == EOF)
			break;
		if(c == m->escape){
			tputs("Disconnecting.");
			if(socklen(Curproc->input,0))
				recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0);
			break;
		}
		if(usputc(s,(char)c) == EOF)
			break;
	}
	stop_timer(&gwa->t);
	free((char *)gwa);
	close_s(s);
	killproc(child); /* get rid of the receive process */
	tprintf("%c%c%c\n",IAC,WONT,TN_ECHO);
	return 0;
}

static void
gw_input(s,notused,p)
int s;
void *notused;
void *p;
{
	int c;
	char *cp;
	struct proc *parent;

	parent = (struct proc *) p;
	while((c = recvchar(s)) != EOF)
		tputc((char)c);
	tprintf("Disconnected ");
	cp = sockerr(s);
	if(cp != NULLCHAR)
		tputs(cp);
	/* Tell the parent that we are no longer connected */
	alert(parent,ENOTCONN);
	pwait(Curproc); /* Now wait to be killed */
}

/* Check if the escape character is typed while the parent process is busy
 * doing other things. 
 */
static void
gw_superv(null,proc,p)
int null;
void *proc;
void *p;
{
	struct proc *parent;
	struct mbx *m;
	int c;
	parent = (struct proc *) proc;
	m = (struct mbx *) p;
	while((c = recvchar(Curproc->input)) != EOF)
		if(c == m->escape){
			/* flush anything in the input queue */
			if(socklen(Curproc->input,0))
				recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0);
			break;
		}
	alert(parent,EINTR);	 /* Tell the parent to quit */
	pwait(Curproc);		 /* Please kill me */
}

static void
gw_alarm(p)
void *p;
{
	struct gwalarm *gwa = (struct gwalarm *)p;
	char oldbl;
	struct usock *up;

	/* Flush sockets s1 and s2, but first make sure that the socket
	 * is set to non-blocking mode, to prevent the flush from blocking
	 * if the high water mark has been reached.
	 */
	if((up = itop(gwa->s1)) != NULLUSOCK) {
		oldbl = up->noblock;
		up->noblock = 1;
		usflush(gwa->s1);
		up->noblock = oldbl;
	}
	if((up = itop(gwa->s2)) != NULLUSOCK) {
		oldbl = up->noblock;
		up->noblock = 1;
		usflush(gwa->s2);
		up->noblock = oldbl;
	}
	start_timer(&gwa->t);
}

#ifdef	NETROM
static char mbnrid[80];
static struct cmds Mbnrcmds[] = {
	"",		donothing,	0, 0, NULLCHAR,
	"connect",	dombnrconnect,	0, 0, NULLCHAR,
	"ident",	dombnrident,	0, 0, NULLCHAR,
	"nodes",	dombnrnodes,	0, 0, NULLCHAR,
	"users",	dombox,		0, 0, NULLCHAR,
	NULLCHAR,	NULLFP,		0, 0, NULLCHAR,
};

static int
dombnetrom(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	char *cp;

	if(Nrifaces[0].iface == NULLIF){
		tprintf("NET/ROM not activated.\n");
		return 0;
	}
	m = (struct mbx *) p;
	sprintf(mbnrid,"%s:%s",Nrifaces[0].alias,
	  pax25(m->line,Nrifaces[0].iface->hwaddr));
	tprintf("Connected to %s	",mbnrid);
	dombescape(1,NULLCHARP,p);

	while(mbxrecvline(m->user,m->line,MBXLINE,m->escape) >= 0) {
		for(cp = m->line; *cp; ++cp)	/* convert to lower case */
			if(isupper(*cp))
				*cp = tolower(*cp);
		if(cmdparse(Mbnrcmds,m->line,(void *)m) == -1)
			tprintf("%s> Invalid command (CONNECT IDENT NODES USERS)\n",
			  mbnrid);
	}
	return 0;
}

static int
dombnrident(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	tprintf("%s> %s (%s)\n",mbnrid,Hostname,Version);
	return 0;
}

static int
dombnrnodes(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2)
		return doroutedump();
	return dorouteinfo(argc,argv,p);
}

static int
dombnrconnect(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct mbx *m;
	char *np, buf[7];
	int s;
	struct sockaddr_nr lsocket, fsocket;
	char alias[AXBUF];

	m = (struct mbx *) p;
	if(!(m->privs & NETROM_CMD)){
		tprintf(Noperm);
		return 0;
	}
	if((s = socket(AF_NETROM,SOCK_SEQPACKET,0)) == -1){
		tprintf(Nosock);
		return 0;
	}
	lsocket.nr_family = AF_NETROM;
	/* Set up our local username, bind would use Mycall instead */
	if(strlen(m->name) > 6)
		strncpy(buf,m->name,6);
	else
		strcpy(buf,m->name);
	buf[6] = '\0';
	putalias(alias,buf,0);
	setcall(lsocket.nr_addr.user,alias);

	/* Putting anything else than Mycall here will not work */
	memcpy(lsocket.nr_addr.node,Mycall,AXALEN);
	bind(s,(char *)&lsocket,sizeof(struct sockaddr_nr));

	/* See if the requested destination could be an alias, and
	 * find and use it if it is.  Otherwise assume it is an ax.25
	 * address.
	 */
	if(putalias(alias,argv[1],0) != -1 &&
		(np = find_nralias(alias)) != NULLCHAR){
		memcpy(fsocket.nr_addr.user,np,AXALEN);
		memcpy(fsocket.nr_addr.node,np,AXALEN);
	} else {	/* parse ax25 callsign */
		/* Only the user callsign of the remote station is never used by */
		/* NET/ROM, but it is needed for the psocket() call. */
		setcall(fsocket.nr_addr.user,argv[1]);
		setcall(fsocket.nr_addr.node,argv[1]);
	}
	fsocket.nr_family = AF_NETROM;
	return gw_connect(m,s,(char *)&fsocket, sizeof(struct sockaddr_nr));
}
#endif

/* States for send line parser state machine */
#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 int
mbx_to(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register char *cp;
	int state, i;
	char *user, *host, *from, *msgid;
	int userlen = 0, hostlen = 0, fromlen = 0, msgidlen = 0;
	struct mbx *m;

	m = (struct mbx *)p;
	/* Free anything that might be allocated
	 * since the last call to mbx_to() or mbx_reply()
	 */
	free(m->to);
	m->to = NULLCHAR;
	free(m->tofrom);
	m->tofrom = NULLCHAR;
	free(m->tomsgid);
	m->tomsgid = NULLCHAR;
	free(m->origto);
	m->origto = NULLCHAR;

	if(argc == 1)
		return -1;
	i = 1;
	cp = argv[i];
	state = LOOK_FOR_USER;
	while(state < FINAL_STATE){
#ifdef MBDEBUG
		tprintf("State is %d, char is %c\n", state, *cp);
#endif
		switch(state){
		case LOOK_FOR_USER:
			if(*cp == '@' || *cp == '<' || *cp == '$'){
				state = ERROR_STATE;		/* no user */
			} else {
				user = cp;			/* point at start */
				userlen++;			/* start counting */
				state = IN_USER;
			}
			break;
		case IN_USER:
			switch(*cp){
			case '\0':
				state = AFTER_USER;		/* done with username */
				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:
				userlen++;			/* part of username */
			}
			break;
		case AFTER_USER:
			switch(*cp){
			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:
				state = ERROR_STATE;
			}
			break;
		case LOOK_FOR_HOST:
			if(*cp == '@' || *cp == '<' || *cp == '$'){
				state = ERROR_STATE;
				break;
			}
			if(*cp == '\0')
				break;
			host = cp;
			hostlen++;
			state = IN_HOST;
			break;
		case IN_HOST:
			switch(*cp){
			case '\0':
				state = AFTER_HOST;		/* 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:
				hostlen++;
			}
			break;
		case AFTER_HOST:
			switch(*cp){
			case '@':
				state = ERROR_STATE;		/* user@host @ */
				break;
			case '<':
				state = LOOK_FOR_FROM;		/* user@host < */
				break;
			case '$':
				state = LOOK_FOR_MSGID;	/* user@host $ */
				break;
			default:
				state = ERROR_STATE;		/* user@host foo */
			}
			break;
		case LOOK_FOR_FROM:
			if(*cp == '@' || *cp == '<' || *cp == '$'){
				state = ERROR_STATE;
				break;
			}
			if(*cp == '\0')
				break;
			from = cp;
			fromlen++;
			state = IN_FROM;
			break;
		case IN_FROM:
			switch(*cp){
			case '\0':
				state = AFTER_FROM;		/* user@host <foo */
				break;
			case '<':
				state = ERROR_STATE;		/* user@host <foo< */
				break;
			case '$':
				state = LOOK_FOR_MSGID;	/* message id follows */
				break;
			default:
				fromlen++;
			}
			break;
		case AFTER_FROM:
			switch(*cp){
			case '@':				/* user@host <foo @ */
			case '<':				/* user@host <foo < */
				state = ERROR_STATE;
				break;
			case '$':
				state = LOOK_FOR_MSGID;	/* user@host <foo $ */
				break;
			default:
				state = ERROR_STATE;		/* user@host foo */
			}
			break;
		case LOOK_FOR_MSGID:
			if(*cp == '\0')
				break;
			msgid = cp;
			msgidlen++;
			state = IN_MSGID;
			break;
		case IN_MSGID:
			if(*cp == '\0')
				state = FINAL_STATE;
			else
				msgidlen++;
			break;
		default:
			/* what are we doing in this state? */
			state = ERROR_STATE;
		}
		if(*(cp) == '\0'){
			++i;
			if(i < argc)
			cp = argv[i];
			else break;
		} else
			++cp;
	}
	if(state == ERROR_STATE || state == LOOK_FOR_HOST
	 || state == LOOK_FOR_FROM || state == LOOK_FOR_MSGID)
		return -1;		/* syntax error */

	m->to = mallocw(userlen + hostlen + 2);

	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';
	}
	if(fromlen){
		m->tofrom = mallocw(fromlen + 1);
		strncpy(m->tofrom, from, fromlen);
		m->tofrom[fromlen] = '\0';
	}
	if(msgidlen){
		m->tomsgid = mallocw(msgidlen + 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 int
mbx_data(m,cclist,extra)
struct mbx *m;
struct list *cclist;	/* list of carbon copy recipients */
char *extra;		/* optional extra header lines */
{
	time_t t;
	struct list *ap;
	int cccnt = 0;
	
	if((m->tfile = tmpfile()) == NULLFILE)
		return -1;
	time(&t);
	fprintf(m->tfile,Hdrs[RECEIVED]);
	if(m->tofrom != NULLCHAR)
		fprintf(m->tfile,"from %s.bbs ",m->name);
	fprintf(m->tfile,"by %s (%s)\n\tid AA%ld ; %s",
		Hostname, Version, get_msgid(), ptime(&t));
	fprintf(m->tfile,"%s%s",Hdrs[DATE],ptime(&t));
	fprintf(m->tfile,Hdrs[MSGID]);
	if(m->tomsgid)
		fprintf(m->tfile,"<%s@%s.bbs>\n", m->tomsgid, m->name);
	else
		fprintf(m->tfile,"<%ld@%s>\n",get_msgid(), Hostname);
	fprintf(m->tfile,Hdrs[FROM]);
	if(m->tofrom)
		fprintf(m->tfile,"%s%%%s.bbs@%s\n",
			m->tofrom, m->name, Hostname);
	else
		fprintf(m->tfile,"%s@%s\n", m->name, Hostname);
	fprintf(m->tfile,"%s%s\n",Hdrs[TO],m->origto != NULLCHAR ? m->origto : m->to);
	/* Write Cc: line */
	for(ap = cclist; ap != NULLLIST; ap = ap->next) {
		if(cccnt == 0){
			fprintf(m->tfile,"%s",Hdrs[CC]);
			cccnt = 4;
		}
		else {
		       fprintf(m->tfile,", ");
		       cccnt += 2;
		}
		if(cccnt + strlen(ap->val) > 80 - 3) {
		       fprintf(m->tfile,"\n    ");
		       cccnt = 4;
		}
		fputs(ap->val,m->tfile);
		cccnt += strlen(ap->val);
	}
	if(cccnt)
		fputc('\n',m->tfile);
	fprintf(m->tfile,"%s%s\n",Hdrs[SUBJECT],m->line);
	if(!isspace(m->stype) && ((m->stype != 'R' && m->stype != 'F') ||
	  (m->sid & MBX_SID) !=0))
		  fprintf(m->tfile,"%s%c\n", Hdrs[BBSTYPE],m->stype);
	if(extra != NULLCHAR)
		fprintf(m->tfile,extra);
	fprintf(m->tfile,"\n");

	return 0;
}

/* Returns true if string is in history file or if string appears to be a
 * message id generated by our system.
 */
static int
msgidcheck(string)
char *string;
{
     FILE *fp;
     char buf[LINELEN], *cp;
     if(string == NULLCHAR)
	  return 0;
     /* BID's that we have generated ourselves are not kept in the history
      * file. Such BID's are in the nnnn_hhhh form, where hhhh is a part of
      * our hostname, truncated so that the BID is no longer than 11
      * characters.
      */
     if((cp = strchr(string,'_')) != NULLCHAR && *(cp+1) != '\0' && 
	strnicmp(cp+1,Hostname,strlen(cp+1)) == 0)
	  return 1;

     if((fp = fopen(Historyfile,READ_TEXT)) == NULLFILE)
	  return 0;
     while(fgets(buf,LINELEN,fp) != NULLCHAR) {
	  rip(buf);
	  if(stricmp(string,buf) == 0) {	/* found */
	       fclose(fp);
	       return 1;
	  }
     }
     fclose(fp);
     return 0;
}
     
/* Read the rewrite file for lines where the first word is a regular
 * expression and the second word are rewriting rules. The special
 * character '$' followed by a digit denotes the string that matched
 * a '*' character. The '*' characters are numbered from 1 to 9.
 * Example: the line "*@*.* $2@$1.ampr.org" would rewrite the address
 * "foo@bar.xxx" to "bar@foo.ampr.org".
 * $H is replaced by our hostname, and $$ is an escaped $ character.
 * If the third word on the line has an 'r' character in it, the function
 * will recurse with the new address.
 */
char *
rewrite_address(addr)
char *addr;
{
	char *argv[10], buf[MBXLINE], *cp, *cp2, *retstr;
	int cnt;
	FILE *fp;
	if ((fp = fopen(Rewritefile,READ_TEXT)) == NULLFILE)
		return NULLCHAR;
	memset((char *)argv,0,10*sizeof(char *));
	while(fgets(buf,MBXLINE,fp) != NULLCHAR) {
		if(*buf == '#')		/* skip commented lines */
			continue;
		if((cp = strchr(buf,' ')) == NULLCHAR) /* get the first word */
			if((cp = strchr(buf,'\t')) == NULLCHAR)
				continue;
		*cp = '\0';
		if((cp2 = strchr(buf,'\t')) != NULLCHAR){
			*cp = ' ';
			cp = cp2;
			*cp = '\0';
		}
		if(!wildmat(addr,buf,argv))
			continue;		/* no match */
		rip(++cp);
		cp2 = retstr = (char *) callocw(1,MBXLINE);
		while(*cp != '\0' && *cp != ' ' && *cp != '\t')
			if(*cp == '$') {
				if(isdigit(*(++cp)))
					if(argv[*cp - '0'-1] != '\0')
						strcat(cp2,argv[*cp - '0'-1]);
				if(*cp == 'h' || *cp == 'H') /* Our hostname */
					strcat(cp2,Hostname);
				if(*cp == '$')	/* Escaped $ character */
					strcat(cp2,"$");
				cp2 = retstr + strlen(retstr);
				cp++;
			}
			else
				*cp2++ = *cp++;
		for(cnt=0; argv[cnt] != NULLCHAR; ++cnt)
			free(argv[cnt]);
		fclose(fp);
		/* If there remains an 'r' character on the line, repeat
		 * everything by recursing.
		 */
		if(strchr(cp,'r') != NULLCHAR || strchr(cp,'R') != NULLCHAR) {
			if((cp2 = rewrite_address(retstr)) != NULLCHAR) {
				free(retstr);
				return cp2;
			}
		}
		return retstr;
	}
	fclose(fp);
	return NULLCHAR;
}

/* uuencode a file -- translated from C++; both versions copyright 1990
   by David R. Evans, G4AMJ/NQ0I
*/

static int
uuencode(infile,s,infilename)
FILE *infile;
int s;			/* output socket */
char *infilename;
{
  int n_read_so_far = 0, n_written_so_far = 0, in_chars, n, mode = 0755;
  unsigned int32 cnt = 0;
  unsigned char in[3], out[4], line[100];
#ifdef UNIX
  struct stat stb;
  
  if(stat(infilename,&stb) != -1)
       mode = stb.st_mode & 0777;	/* get real file protection mode */
#endif
  usprintf(s, "begin %03o %s\n", mode, infilename);

  /* do the encode */
  while ((in_chars = fread(in, 1, 3, infile)) != 0) {
    out[0] = in[0] >> 2;
    out[1] = in[0] << 6;
    out[1] = out[1] >> 2;
    out[1] = out[1] | (in[1] >> 4);
    out[2] = in[1] << 4;
    out[2] = out[2] >> 2;
    out[2] = out[2] | (in[2] >> 6);
    out[3] = in[2] << 2;
    out[3] = out[3] >> 2;
    for (n = 0; n < 4; n++)
      out[n] += ' ';
    n_read_so_far += in_chars;
    for (n = 0; n < 4; n++)
      line[n_written_so_far++] = out[n];
    if ((in_chars != 3) || (n_written_so_far == 60)) {
      line[(n_read_so_far + 2) / 3 * 4] = '\0';
      
      usprintf(s,"%c%s\n",n_read_so_far + ' ', line);
      cnt += n_read_so_far;
      n_read_so_far = 0;
      n_written_so_far = 0;
    }
  }
  if (usprintf(s," \nend\nsize %lu\n", cnt) == EOF)
    return 1;
  return 0;
}
