/* NOTE: because of size, the previous 'mailbox.c' has been
 * split in 3 parts:
 * pbbscmd.c, containing the 'pbbs' subcommands, and
 * mailbox.c, containing some user mailbox commands, and
 * mailbox2.c, containing the remaining user commands.
 * 921125 - WG7J
 */
/* 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
 *
 * Gateway function now support outgoing connects with the user's call
 * with inverted ssid. Users can connect to system alias as well...
 * See also several mods in socket.c,ax25.c and others
 * 11/15/91, WG7J/PA3DIS
 *
 * Userlogging, RM,VM and KM commands, and R:-line interpretation
 * added 920307 and later, Johan. K. Reinalda, WG7J/PA3DIS
 *
 * Inactivity timeout-disconnect added 920325 and later - WG7J
 *
 * Removed pbbs overhead from calling Convers directly from ax25 - KO4KS
 * Several other additions - KO4KS
 */

#include "global.h"
#include "ctype.h"
#include "commands.h"
#ifdef MSDOS
#include <dir.h>
#else
#include <time.h>
#endif
#ifdef  UNIX
#include <sys/stat.h>
#endif
#include "ax25.h"
#include "usock.h"
#include "session.h"
#include "smtp.h"
#include "ftpserv.h"
#include "bm.h"
#include "pktdrvr.h"
#include "color.h"
#include "delegate.h"
#include "domain.h"
#include "assoc.h"
#include "x.h"
#ifdef SQL
#include "sql.h"
#endif
#include "reject.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: mailbox.c,v 1.61 2001/05/06 16:32:56 brian Exp $";
#endif

#ifdef MAILBOX

/* By setting the fp to NULL, we can check in exitbbs()
 * wether a tempfile has been closed or not - WG7J
 */
#define MYfclose(x) { (void) fclose(x); x = (FILE *) 0; }

extern int HOLDindicator;
extern int enforceBhosts;
extern int MBackfilter;
extern char Badmsg[];
extern char AXRosecall[AXALEN];
extern char *Mbhaddress;
extern int32 mbxMaxTimer;
extern int32 Mbax25perms;
extern int32 MbAmprPerms;
extern int32 MbNonAmprPerms;
extern uint32 NonSecureAmpr;
extern int BIDuknos;
extern int MOTDalways;
extern int Mbconverse;
extern int MbLogging;
extern int mbxMaxUsers;
extern char *BIDsuffix;
extern int MBXMaint;
extern int MBXMaintMode;
extern char *MBXMaintStr;
extern int MbHoldall;
extern char *Callserver;
extern struct timer Smtpcli_t;
extern int MBnoSubjBell;
extern AarrayPtr Mbcustom;
extern char *MMotd;
extern char *MExit;
extern char *Mtmsg;
extern short SecureTelnet;
extern int BBSdump;
extern char *Mbprompt;
extern int Usenrid;
extern int MBSecure;
extern int Mbsendquery;
extern int Usehold, Usescan, Usearea, Usemore;
extern int Mbaxpasswd;
extern int LoginTimer;

#ifdef MBXTDISC
extern int32 Mbtdiscinit;
extern int MbtdiscSysop;
#endif

#ifdef MBFWD
extern int Mbfullsvc;
extern int Smtpheaders;
#endif

#ifdef TIPMAIL
extern int32 Mbtipperms;
#endif

#ifdef XFWD
extern int MXfwd;
#endif

#ifdef FBBFWD
#ifdef FBBCMP
extern int Mfbbcmp;
#endif
extern int Mfbb;
#endif


struct mbx **Mbox;
int BbsUsers = 0;
int Totallogins = 0, Tutorlogins[3] = {0, 0, 0}, BBSlogins = 0;

int NUMMBX = _NUMMBX;
int MbSent = 0;
int MbRead = 0;
int MbRecvd = 0;

#ifdef MBFWD
int MbForwarded = 0;
#endif


#ifdef MAILCMDS
static long lastsentid = 0;
#endif


extern void updatedefaults (struct mbx *, int);
extern int BBSlookup (char *bbs);
extern int isgroup (char *group);
extern char *mblookname (struct mbx *m, char *str);
extern int dombusers (int argc, char *argv[], void *p);
extern int sockblock (int s, int value);
extern int CountConfUsers (void);
extern void tutorserv (const char *call, struct mbx *m, int mode, int color, int ip);
extern int dosysop (int argc, char *argv[], void *p);
extern char *cmd_line (int argc, char *argv[], char stype);
extern char *nntp_name_expansion (char *name);
extern char *host_or_wpage_exp (char *to, int hier, int exphome, int *dns);
extern FILE *subdir_fopen (char *name, const char *mode);
extern int lockit (struct mbx *m);
extern int dochat (int argc, char *argv[], void *p);
extern void askhome (struct mbx *m, int initial);
extern void notifynewmail (struct mbx *m);
extern int doencode (int argc, char *argv[], void *p);

#ifdef USERLOG
extern void askrealname (struct mbx *m, int initial);
#endif

#ifdef ALLSERV
extern char *getquote (void);
#endif


static void putprompt (struct mbx *m);
static void sendbell (void);
static void mboxredundant (struct mbx *m);
static void loginredundant (struct mbx *m);
static int dombsemicolon (int argc, char *argv[], void *p);
char *addroot (const char *root, const char *name);
char *permtest (char *path, long privs, const char *name, int mode, char *root, int thedir);

#ifdef LZW
void togglelzw (int soc, int mode);
#endif

#ifdef WPAGES
void wpageAdd (char *entry, int bbs, int updateit, char which);
void add_WPUpdate (char *call, char *home, char *name, char which);
#endif

#ifdef STRICT_CALL
extern int PBBSstrict;
#endif


static int mbx_recvchar (struct mbx *m, int s);
static int mbx_getname (struct mbx *m);
static int dousermotd (int argc, char *argv[], void *p);

#ifdef MAILCMDS
static void statarea (register struct mbx *m, char *area, int *total, int *new, int *hold, int special);
static int allareas (struct mbx *m, char *cp);
#endif

#if defined(SAMCALLB) || defined(CALLCLI)
static int dombcallbook (int argc, char *argv[], void *p);
#endif

#ifdef GATECMDS
static void dombconnecthelp (void);
#endif

#ifdef HOLDMODS
extern int Sholdscanall;
int holdscan (char *buf, char *badbuf, char *badwords[]);
int holdjob (FILE *dfile, char *host, struct list *to, const char *from);
#endif

char stars[] = "*** ";
char Howtoend[] = "End with /EX or ^Z in first column (^A aborts):\n";
char MsgAborted[] = "*** Message aborted\n";
char CurUsers[] = "There are currently %d users on the BBS\n";
char Only[] = "You are the only user currently on the BBS\n";


#ifdef CATALOG
#include "catalog.h"

#define CAT mailbox_catalog

#define Availableports	__STR(0)
#define pwordprompt	__STR(1)
#define loganonystr	__STR(2)
#define loguserstr	__STR(3)
#define loginincorrect	__STR(4)
#define logfailedstr	__STR(5)
#define toomanymbx	__STR(6)
#define chainingstr	__STR(7)
#define UseLZW		__STR(8)
#define laston		__STR(9)
#define quotehdr	__STR(10)
#define delegationquery	__STR(11)
#define maxsession	__STR(12)
#define logmaxsession	__STR(13)
#ifdef ALLSERV
#define Pleasehangup	__STR(14)
#endif
#if defined(CALLBOOK) && defined(FOQ_CMDS)
#define callsvrnot	__STR(15)
#endif
#define callusage	__STR(16)
#define checkcallbook	__STR(17)
#define callsvcnotconfig __STR(18)
#define lookcall	__STR(19)
#define sendinvalid	__STR(20)
#define sendset		__STR(21)
#define syntaxerror	__STR(22)
#define syntaxerror2	__STR(23)
#define syntaxerror3	__STR(24)
#define permissionerror	__STR(25)
#define permissionerror2 __STR(26)
#define permissionerror3 __STR(27)
#define nobid		__STR(28)
#define nobid2		__STR(29)
#define Alreadyhave	__STR(30)
#define Alreadyhave2	__STR(31)
#if 0
#define permissionerror4 __STR(32)
#endif
#define permissionerror5 __STR(33)
#define refused1	__STR(34)
#define refused3	__STR(35)
#define refused5	__STR(36)
#define refused3rd	__STR(37)
#define badaddr1	__STR(38)
#define badaddr2	__STR(39)
#define notemp1		__STR(40)
#define notemp2		__STR(41)
#define tocheck1	__STR(42)
#define Insertingoriginal __STR(43)
#define Forwardedmessage __STR(44)
#define Endforwardedmessage __STR(45)
#define refused2	__STR(46)
#define forcalling	__STR(47)
#define Timethissession	__STR(48)
#define enforce		__STR(49)
#define enforce2	__STR(50)


#else /* CATALOG */

static const char Availableports[] = "Available ports:\n";
static const char pwordprompt[] = "Password: %c%c%c";
static const char loganonystr[] = "PBBS login: %s Password: %s";
static const char loguserstr[] = "PBBS login: %s";
static const char loginincorrect[] = "Login incorrect\n";
static const char logfailedstr[] = "PBBS login FAILED - login: %s Password: %s";
static const char toomanymbx[] = "Sorry, too many mailbox sessions at %s\n";
static const char chainingstr[] = "Chaining to BBS...\n";
static const char UseLZW[] = "Use LZW Compression (*y/n) ?\n";
static const char laston[] = "Last on the BBS: %s";
static const char quotehdr[] = "\nQuote of the Day:";
static const char delegationquery[] = "You have delegation enabled, do you wish to disable it (*y/n) ?\n";
static const char maxsession[] = "Maximum allowed time per session exceeded!\n\n";
static const char logmaxsession[] = "Mailbox MaxTimer expired for '%s'";

#ifdef ALLSERV
static const char Pleasehangup[] = "Please hang up now.\n";
#endif

#if defined(CALLBOOK) && defined(FOQ_CMDS)
static const char callsvrnot[] = "Internet callserver not configured\n";
#endif

static const char callusage[] = "Usage: %s callsign [callsign] [...]\n";
static const char checkcallbook[] = "%s checking callbook: %s";
static const char callsvcnotconfig[] = "Callbook Services Not Configured.\n";
static const char lookcall[] = "Looking for \"%s\" in the callbook at %s\n";
static const char sendinvalid[] = "Invalid message number %d\n";
static const char sendset[] = "Current message set to %d\n";
static const char syntaxerror[] = "NO - syntax error\n";
static const char syntaxerror2[] = "S command syntax error - format is:\nS[C|F] name [@ host] [< from_addr] [$bulletin_id]\nSR [number]\n";
static const char syntaxerror3[] = "%s: PBBS S syntax error - %s\n";
static const char permissionerror[] = "NO - permission denied\n";
static const char permissionerror2[] = "Sorry, only mail to 'sysop' allowed!\n";
static const char permissionerror3[] = "%s: no mail permission - %s\n";
static const char nobid[] = "NO - No BID!\n";
static const char nobid2[] = "PBBS %s: SB without BID - %s";
static const char Alreadyhave[] = "Already have %s\n";
static const char Alreadyhave2[] = "import/fbbfwd: Already have %s\n";

#if 0
static const char permissionerror4[] = "Permission denied\n";
#endif

static const char permissionerror5[] = "%s: no mail permission - %s\n";
static const char refused1[] = "NO - refused\n";
static const char refused3[] = "PBBS DELEGATE mail loop attempted: %s";
static const char refused5[] = "Bad user or hostname,  please mail 'sysop' for help\n";
static const char refused3rd[] = "%s: 3rd party mail refused - %s\n";
static const char badaddr1[] = "NO - bad address\n";
static const char badaddr2[] = "Bad user or hostname,  please mail 'sysop' for help\n";
static const char notemp1[] = "NO - no temp file\n";
static const char notemp2[] = "Can't create temp file for mail\n";
static const char tocheck1[] = "The system doesn't know where to route this message..\nSuggest you abort this message and re-enter!\n";
static const char Insertingoriginal[] = "Inserting original message...\n";
static const char Forwardedmessage[] = "----- Forwarded message -----\n\n";
static const char Endforwardedmessage[] = "----- End of forwarded message -----\n";
static const char refused2[] = "Sending to yourself with DELEGATION enabled causes mailing loops\nSend cancelled,  please mail 'sysop' for help\n\n";
static const char forcalling[] = ", for calling the %s TNOS TCP/IP BBS.\n";
static const char Timethissession[] = " --- Time this session: ";
static const char enforce[] = "Sorry, but bulletins require an '@host' to the address!\n";
static const char enforce2[] = "User '%s' tried to send Bulletin w/out @host: '%s'";

#endif /* CATALOG */



static const char porthook[] = "port.sys";
static const char portparam[] = "port %s";
static const char areaprompt[] = "Area: ";
static const char conferencestr[] = "conference";
static const char openCONF[] = "open CONF";
static const char ttylinkstr[] = "ttylink";
static const char tutorialstr[] = "tutorial";
static const char openTUTORIAL[] = "open TUTORIAL";
static const char openPBBS[] = "open PBBS for '%s'";
static const char logPBBSstr[] = "PBBS from %s %s on %s";
static const char newuserhook[] = "newuser.sys";
static const char areaparam[] = "area %s";
static const char areahook[] = "area.sys";
static const char newuserparam[] = "newuser %s";
static const char loginhook[] = "login.sys";
static const char loginparam[] = "login %s";
static const char Badsyntax[] = "Bad syntax.\n";
static const char logouthook[] = "logout.sys";
static const char logoutparam[] = "logout %s";
static const char Loggedoff[] = "Logged off: ";
static const char strCR[] = "%s\n";
static const char amprorg[] = ".ampr.org";
static const char vaguehook[] = "vague.sys";
static char Loginbanner[] = "\nKA9Q NOS - %s (%s)\n\n";
static char RLoginbanner[] = "\nRemote Login at %s - %s\n\n";
static char Mbwelcome[] = "\nWelcome %s, ";
static char Mbmenu2[] = "?,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z ";

static const char conhelp[] =
#ifdef NETROM
	"Syntax: 'C <node>'        for NET/ROM connects\n";
#else
	"Syntax: 'C <port> <call>' for AX.25 connects\n\n";
#endif


#ifdef MAILCMDS
static char MessageData[] = "%4d message%s - %4d new.\n";
static char SMessageData[] = "%4d message%s - %4d new. (%4d on hold)\n";
static char sendthemail[] = "Send this message (N=no)?";
static char CcLine[] = "Cc: ";
static char Mbwarning[] = "Third Party mail is not permitted.\n";
static char Mbbanner[] = "to the %s TCP/IP BBS (%s)\n";
static char Mbmenu[] = "Current msg# %-d of %-d.\n";
#else
static char Mbbanner[] = "to the %s TCP/IP Node (%s)\n";
#endif



#ifdef GATECMDS

/* Enlighten them a bit! */
static void
dombconnecthelp (void)
{
	tputs (conhelp);
}
#endif



static void
sendbell (void)
{
	tputc ('\007');
}



void
bbscolorcls (struct mbx *m)
{
	if (m->usecolor && !(m->sid & MBX_SID))
		colorcls ();
}



void
bbscolorchange (struct mbx *m, const char *str)
{
	if (m->usecolor && !(m->sid & MBX_SID))
		(void) colorchange ((const char *) str, m->colorset);
}



#ifdef GATECMDS
int
dombports (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
struct iface *ifp;
struct mbx *m = (struct mbx *) p;

	tputs (Availableports);
	for (ifp = Ifaces; ifp != NULLIF; ifp = ifp->next)
		if (ifp->type == CL_AX25 && (!(ifp->flags & HIDE_PORT) || (m && (m->privs & SYSOP_CMD)))) {
			tprintf ("%-7s", ifp->name);
			if (ifp->descr != NULLCHAR)
				tprintf (":  %s", ifp->descr);
			else
				tputc ('\n');
		}
	tputc ('\n');
#ifdef TUTOR
	if (m != NULLMBX) {
		sprintf (m->line, portparam, m->name);
		mbscripthook (m, porthook);
	}
#endif

	return 0;
}
#endif



/* This is called by the finger-daemon */
void
listusers (int s)
{
int outsave;
struct mbx m;

	m.privs = 0;
	m.stype = ' ';

	outsave = Curproc->output;
	Curproc->output = s;
	(void) dombusers (0, NULLCHARP, &m);
	Curproc->output = outsave;
}



struct mbx *
newmbx (int fwding)
{
int i;
struct mbx *m;

	if (!fwding && (BbsUsers >= mbxMaxUsers)) {
		/* if it is AX25, NETROM, and look to see if it a BBS in our
		   forward.bbs file. If so, allow it to see if there is room
		   for them. Also, allow it to try if it is a LOCAL connect */
		union sp sp;
		char tmp[MAXSOCKSIZE];
		char name[20], *cp;
		int len = MAXSOCKSIZE;

		sp.p = tmp;
		sp.sa->sa_family = AF_LOCAL;	/* default to AF_LOCAL */
		if (getpeername (Curproc->input, tmp, &len) != -1)	{

			switch (sp.sa->sa_family) {
				case AF_AX25:
				case AF_NETROM:
					(void) pax25 (name, sp.ax->ax25_addr);
					cp = strpbrk (name, DIGI_IDS);
					if (cp != NULLCHAR)	/* get rid of SSID */
						*cp = '\0';
					if (BBSlookup (name) >= 0) {
						log (-1, "Allowing user '%s' to attempt to override mbxMaxUsers", name);
						break;
					}
					/* else fall through */
				default:
					return NULLMBX;
			}
		}
	}
	for (i = 0; i < NUMMBX; i++) {
		if (Mbox[i] == NULLMBX) {
			m = Mbox[i] = (struct mbx *) callocw (1, sizeof (struct mbx));

			m->mbnum = i;
			m->subchannel = NOSUBCHANNEL;
			BbsUsers++;
#ifdef XSERVER
			xnotify (X_BBS);
#endif
			return m;
		}
	}
	/* If we get here, there are no free mailbox sessions */
	return NULLMBX;
}



static void
loginredundant (struct mbx *m)
{
struct proc *p;
int s;

	p = m->ps;
	s = m->user;

	log (s, "login timeout");
	exitbbs (m);
	close_s (s);
	killproc (p);
}



static int
mbx_getname (struct mbx *m)
{
char *cp;
FILE *tfp;
union sp sp;
char tmp[MAXSOCKSIZE];
int len = MAXSOCKSIZE;
int anony = 0;
int oldmode;
int founddigit = 0;
int count = 0;
#ifdef AX25
int32 flags = 0;
int ax_25 = 0;
struct usock *up;
#endif

	sp.p = tmp;
	sp.sa->sa_family = AF_LOCAL;	/* default to AF_LOCAL */
	/* next line okay, since leaves blank if can't get peername */
	(void) getpeername (m->user, tmp, &len);
	m->family = sp.sa->sa_family;
	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_AX25:
			/* If this is not to the convers call, and this port is
			 * set for NO_AX25, then disconnect ! - WG7J
			 */
			if ((m->type != CONF_LINK) && ((up = itop (m->user)) != NULLUSOCK)) {
				if ((flags = up->cb.ax25->iface->flags) & NO_AX25)
					return -1;
			}
			ax_25 = 1;
			/* note fallthrough */
		case AF_NETROM:
			/* NETROM and AX25 socket address structures are "compatible" */
			/*Save user call, in case she/he wants to use gateway function*/
			memcpy (m->call, sp.ax->ax25_addr, AXALEN);
			(unsigned char) m->call[ALEN] &= 0xfc;	/*lint !e63 * Make sure E-bit isn't set !*/
			(void) pax25 (m->name, sp.ax->ax25_addr);
			cp = strpbrk (m->name, DIGI_IDS);
			if (cp != NULLCHAR)	/* get rid of SSID */
				*cp = '\0';
			/* SMTP wants the name to be in lower case */
			(void) strlwr (m->name);
			anony = 1;
         
#ifdef STRICT_AX25CALL
		    if (PBBSstrict && !iscall (m->name))
			    return -1;
#endif
         
			/* Try to find the privileges of this user from the userfile */
			if ((m->privs = userlogin (m->name, m->line, &m->path, MBXLINE, &anony)) == -1) {
				m->privs = 0;
				free (m->path);
				m->path = NULLCHAR;
				return -1;
			}
			if (m->privs & EXCLUDED_CMD)
				return -1;

			if (((flags & USERS_ONLY) && (m->privs & IS_BBS)) ||
			    ((flags & BBS_ONLY) && !(m->privs & IS_BBS)) ||
			  ((flags & SYSOP_ONLY) && !(m->privs & SYSOP_CMD)))
				return -1;

			if ((m->privs & WAS_ANONY) && Mbax25perms)
				m->privs = (Mbax25perms | WAS_ANONY);
#ifdef MSDOS
			if ((strlen (m->name) > 8) && (m->privs & WAS_ANONY))
				return -1;
#endif
			/* KF5MG - non-bbses get LOGIN: PASSWORD: prompts. */
			if (m->privs & (IS_BBS + WAS_ANONY) || !ax_25 || !Mbaxpasswd)
				return 0;
#endif
			/* fall through */
		case AF_LOCAL:
		case AF_INET:
			m->state = MBX_LOGIN;
			if (m->type == RLOGIN_LINK)
				tprintf (RLoginbanner, Hostname, Version);
			else {
				tprintf (Loginbanner, Version, (Hostname) ? Hostname : "localhost");
				if (Mtmsg != NULLCHAR)
					tputs (Mtmsg);
			}
			for ( ; ; ) {
				/* Maximum of 3 tries - WG7J */
				if (count++ == 3)
					return -1;
				oldmode = sockmode (m->user, SOCK_ASCII);
				m->privs = 0;	/* for 'chat' check in mbxrecvline */
				tputs ("login: ");
				usflush (m->user);
				if (mbxrecvline (m) == -1)
					return -1;
				if (*m->line == 4)	/* Control-d */
					return -1;
				if (*m->line == '\0')
					continue;
				if (strchr (m->line, ' ')) {
					tputs ("\nSpaces are not permitted in login names - try again\n\n");
					continue;
				}
#ifdef STRICT_CALL
				if (PBBSstrict && !(m->privs & SYSOP_CMD) && !iscall (m->line))
					continue;
#endif
				(void) strlwr (m->line);
				/* add a little test to avoid 'Mailfile busy' syndrome - WG7J */
				/* changed "if"s to "while"s - ko4ks */
				while ((cp = strchr (m->line, '.')) != NULLCHAR)
					*cp = '_';
				while ((cp = strchr (m->line, '/')) != NULLCHAR)
					*cp = '_';
				while ((cp = strchr (m->line, '\\')) != NULLCHAR)
					*cp = '_';
				cp = &m->line[strlen (m->line)];
				strcpy (cp, ".txt");
				if ((tfp = fopen (m->line, "w")) == NULL)	/* Invalid name */
					continue;
				(void) fclose (tfp);
				unlink (m->line);
				*cp = 0;
				if (strlen (m->line) < sizeof (m->name))
					strncpy (m->name, m->line, 20);
				else	/* Too long! */
					continue;
				sprintf (m->line, "Authentication: '%s'", m->name);
				chname (Curproc, m->line);
					
				/* KF5MG - send a password: prompt to ax.25 users */
				if (ax_25) {
					anony = 0;
					tputs ("Password: ");
					usflush (m->user);
					if (mbxrecvline (m) == -1)
						return -1;
				} else {
					tprintf (pwordprompt, IAC, WILL, TN_ECHO);
					usflush (m->user);
					(void) sockmode (m->user, SOCK_BINARY);
					if (mbxrecvline (m) == -1)
						return -1;
					tprintf ("%c%c%c", IAC, WONT, TN_ECHO);
					(void) 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 */
					(void) recv_mbuf (m->user, NULL, 0, NULLCHAR, 0);
				anony = 0;
				if ((m->privs = userlogin (m->name, m->line, &m->path, MBXLINE, &anony)) != -1) {
#ifdef MSDOS
					if ((strlen (m->name) > 8) && (m->privs & WAS_ANONY))
						return -1;
#endif
					if (m->privs & EXCLUDED_CMD)
						return -1;
					if (m->privs & WAS_ANONY) {
						if (issysarea (m->name)) {
							tprintf ("Permission denied!\n\n");
							log (m->user, logfailedstr, m->name, m->line);
							mail_error (logfailedstr, m->name, m->line);
							return -1;
						}
						log (m->user, loganonystr, m->name, m->line);
					} else	{
						m->authenticated = 1;
						log (m->user, loguserstr, m->name);
					}
#ifdef TIPMAIL
					if (m->type == TIP && (m->privs & WAS_ANONY) && Mbtipperms)
						m->privs = Mbtipperms;
					else
#endif
					if (m->privs & WAS_ANONY) {
						struct sockaddr fsocket;
						int i, trans;
						char *cptr, *cptr2;

						i = SOCKSIZE;
						trans = DTranslate;	/* Save IP address translation state */
						DTranslate = 0;	/* Force output to be numeric IP addr*/
						if (getpeername (m->user, (char *) &fsocket, &i) != -1)	{
							cptr = psocket (&fsocket);
							if ((cptr2 = strchr (cptr, ':')) != NULLCHAR)
								*cptr2 = 0;
							if (strcmp (inet_ntoa (NonSecureAmpr), cptr) && (!*cptr || !strnicmp (cptr, "44.", 3) || !strnicmp (cptr, "127.0.0.1", 9))) {
								if (MbAmprPerms)
									m->privs = MbAmprPerms;
							} else if (MbNonAmprPerms)
								m->privs = MbNonAmprPerms;
						} else if (MbNonAmprPerms)
							m->privs = MbNonAmprPerms;
						DTranslate = trans;	/* Restore original state */
					}
#ifdef AX25
					/* try to set the name as the user-call.
					 * this is a very crude test! Be careful...
					 * Login must have at least 1 digit (0-9) in it,
					 * and it must be possible to convert it to a call.
					 * if this doesn't work, disallow the gateway command,
					 * no matter if this was allowed by priviledges or not.
					 * Be careful, some one with login name '4us' and
					 * permission set to allow gateway/netrom, will
					 * go out as '4us-15' or '4us-0' !!!!!
					 * 11/15/91 WG7J/PA3DIS
					 */
					for (cp = m->name; *cp != '\0'; cp++)
						if (isdigit ((int) *cp))
							break;
					if (*cp != '\0')
						founddigit = 1;
					if ((setcall (m->call, m->name) == -1) || (!founddigit)) {
						m->privs &= ~AX25_CMD;
						m->privs &= ~NETROM_CMD;
					}
#else
					m->privs &= ~AX25_CMD;
					m->privs &= ~NETROM_CMD;

#endif /* AX25 */
					/* Set the morerows to MAXLIN for telnet logins - WG7J */
					m->morerows = MAXLIN - 1;
					return 0;
				}
				tputs (loginincorrect);
				log (m->user, logfailedstr, m->name, m->line);
				mail_error (logfailedstr, m->name, m->line);
				*m->name = '\0';	/* wipe any garbage */
			}
		default:
			break;
	}
	return 0;
}



/* put up the prompt */
static void
putprompt (struct mbx *m)
{
#ifdef MAILCMDS
char area[64];
char *cp1, *cp2;
int inconf;
#endif

#ifdef MAILCMDS
#ifdef FBBFWD
	if (m->sid & MBX_FBBFWD) {
		/* do nothing. */
	} else
#endif
	if (m->sid & MBX_SID)
		tputs (">\n");
	else {
#endif
		bbscolorchange (m, "09");
		if (m->sid & MBX_NRID)
			tputs (Mbnrid);
#ifdef MAILCMDS
		if (m->sid & MBX_AREA) {
			cp1 = m->area;
			cp2 = area;
			/* Convert / and \ into . */
			while (*cp1 != '\0') {
				if (*cp1 == '/')
					*cp2 = '.';
				else
					*cp2 = *cp1;
				cp1++;
				cp2++;
			}
			*cp2 = '\0';
			tputs (areaprompt);
			bbscolorchange (m, "0C");
			tprintf ("'%s' ", area);
			bbscolorchange (m, "09");
			if (m->privs & SYSOP_CMD) {
#ifdef CONVERS
				inconf = CountConfUsers ();
#else
				inconf = 0;
#endif
				if ((BbsUsers > 1) || inconf) {
					tputs ("(");
					if (BbsUsers > 1) {
						bbscolorchange (m, "0F");
						tprintf ("%-d", BbsUsers);
						bbscolorchange (m, "09");
						tputs (" users");
					}
#ifdef CONVERS
					if ((BbsUsers > 1) && inconf)
						tputs (", ");
					if (inconf) {
						bbscolorchange (m, "0F");
						tprintf ("%-d", inconf);
						bbscolorchange (m, "09");
						tputs (" in conference");
					}
#endif
					tputs (") ");
				}
			}
		}
#endif
		bbscolorchange (m, "0B");
		if (m->sid & MBX_EXPERT) {
#ifdef MAILCMDS
			tprintf ((m->sid & MBX_AREA) ? "(%-d/%-d)" : "", m->current, m->nmsgs);
#endif
		} else {
#ifdef MAILCMDS
			tprintf (Mbmenu, m->current, m->nmsgs);
#endif
			tprintf ((Mbprompt) ? Mbprompt : Mbmenu2);
		}
		bbscolorchange (m, "0A");
		tputs (">\n");
		bbscolorchange (m, "0E");
#ifdef MAILCMDS
	}
#endif
}



#ifdef MBXTDISC
/* Mailbox user has been idle for too long,
 * disconnect the socket - WG7J
 */
static void
mboxredundant (struct mbx *m)
{
#ifdef notdef
struct usock *up;

	/* After fix from Mark, ve3dte this is not needed anymore - WG7J */
	/* nasty hack! we may have screwed up reference count */
	/* by invoking newproc("smtp_send",....); Fudge it!   */
	if ((up = itop (m->user)) != NULLUSOCK)
		up->refcnt = 1;
#endif

	if ((m->state == MBX_LOGIN) || MbtdiscSysop || !(m->privs & SYSOP_CMD)) {	/* leave sysops alone */
		if (m->state == MBX_GATEWAY)
			m->privs = 0;
		/* Close the socket*/
		close_s (m->user);
	}
	return;
}
#endif



#ifdef MBFWD
void 
mbx_SendSid (m)
struct mbx *m;
{
#ifdef FBBCMP
int sendB = Mfbbcmp;
#endif
#ifdef FBBFWD
int sendF = Mfbb;
#endif
#ifdef XFWD
int sendX = MXfwd;
#endif

	if (m->sid & (int32) MBX_SENTSID)
		return;
	if (m->fwdbbs) {
#ifdef FBBCMP
		if (m->fwdbbs->limittype < FWDTYPE_FBB) {
			sendB = 0;
			m->sid &= ~MBX_FBBCMP;
		}
#endif
#ifdef FBBFWD
		if (m->fwdbbs->limittype < FWDTYPE_FBBNON) {
			sendF = 0;
			m->sid &= ~MBX_FBBFWD;
		}
#endif
#ifdef XFWD
		if (m->fwdbbs->limittype < FWDTYPE_XFWD) {
			sendX = 0;
			m->sid &= ~MBX_XFWD;
		}
#endif
	}
	tprintf ("[TNOS-" VERSION "-%s%sHIM%s%s$]\n",
#ifdef FBBCMP
		 sendB ? "B" : "",
#else
		 "",
#endif
#ifdef FBBFWD
		 sendF ? "F" : "",
#else
		 "",
#endif
#ifdef HTTPPBBS
		 "W",
#else
		 "",
#endif
#ifdef XFWD
		 sendX ? "X" : ""
#else
		 ""
#endif
		);
	m->sid |= (int32) MBX_SENTSID;
}
#endif



/* Incoming PBBS session */
void
pbbs_incom (int s, void *t, void *p OPTIONAL)
{
char *cp = NULLCHAR;
char tmp[AXBUF];
int rval;
time_t newuser;
#if defined(TIPMAIL) && defined(XMODEM)
struct tipcb *tip;
#endif
struct mbx *m;
#ifdef notdef
struct usock *up;
#endif
#ifdef MAILCMDS
char *buf[3];
#endif
#ifdef USERLOG 
char tmp1 [LINELEN];
#endif

	if ((int)t == (TELNET_LINK + JUMPSTARTED))	{
		t = (void *) TELNET_LINK;
		(void) seteol (s, "\r\n");
	}
	(void) sockmode (s, SOCK_ASCII);
	(void) 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 */
	(void) setflush (s, -1);

	if (!((int) t & (TUTOR_LINK + INFO_LINK + NEWS_LINK + RLOGIN_LINK + CONF_LINK + TTY_LINK)))
		if (MBXMaintMode && MBXMaint) {
			if (MBXMaintStr) {
				usprintf (s, "\n%s\n", MBXMaintStr);
				usflush (s);
				kpause (5000);
			}
			return;
		}

#ifdef RLOGINSERV

	/* Permit login if using rlogin port */
	if ((int) t == RLOGIN_LINK)
		m = newmbx (1);
	else
#endif   
   
		if ((m = newmbx (0)) == NULLMBX) {
			usprintf (s, toomanymbx, Hostname);
			usflush (s);
			kpause (5000);
			return;
		}

	m->user = s;
	m->escape = 20;		/* default escape character is Ctrl-T */
	m->type = (int) t;
	m->linemode = 0;
	m->colorset[0] = 0;
	m->ps = Curproc;
#if defined(TIPMAIL) && defined(XMODEM)
	if (m->type == TIP) {
		tip = (struct tipcb *) p;
		tip->raw = 0;
		m->tip = tip;
	}
#endif

#ifndef UNIX
	while (socklen (s, 0))	/* discard any remaining input */
		(void) recv_mbuf (s, NULL, 0, NULLCHAR, 0);
#endif

#ifdef MBXTDISC
	/* Start login inactivity timer */
	set_timer (&m->tdisc, LoginTimer * 1000L);
	m->tdisc.func = (void (*)(void *)) loginredundant;
	m->tdisc.arg = m;
	start_timer (&m->tdisc);
#endif
	chname (Curproc, "Authentication: waiting");
	/* get the name of the remote station */
	if (mbx_getname (m) == -1) {
		exitbbs (m);
		usflush (s);
		kpause (5000);
		return;
	}
	Totallogins++;
	newuser = loguser (m);

#ifdef MBXTDISC
	/*Start inactivity timer - WG7J */
	stop_timer (&m->tdisc);
	set_timer (&m->tdisc, Mbtdiscinit * 1000L);
	m->tdisc.func = (void (*)(void *)) mboxredundant;
	m->tdisc.arg = m;
	start_timer (&m->tdisc);
#endif
	m->privs &= ~(ALL_AREAS | SYS_CHAT);	/* clear these bits */

#ifdef RLOGINSERV
	if ((int) t == RLOGIN_LINK) {
		tputc ('\n');
#ifdef STATS_USE
		STATS_adduse (0);
#endif
		(void) dosysop (1, (char **) 0, (void *) m);
		exitbbs (m);
		return;
	}
#endif
#ifdef CONVERS
	if ((int) t != TIP && (int) t & CONF_LINK) {
		if (m->privs & NO_CONVERS)
			tputs (Noperm);
		else {
			m->state = MBX_CONVERS;
			sprintf (m->line, "%s: user '%s'", conferencestr, m->name);
			chname (Curproc, m->line);
			log (s, openCONF);
			{
				int fd = m->user;
				char name[20];

				strncpy (name, m->name, 20);
#ifdef MBXTDISC
				stop_timer (&m->tdisc);
#endif
				Mbox[m->mbnum] = NULLMBX;
				free ((char *) m);
				BbsUsers--;
#ifdef XSERVER
				xnotify (X_BBS);
#endif
				mbox_converse (fd, name, (int) -1, NULLMBX);
			}
		}
		close_s (Curproc->output);
		return;
	}
#endif

#ifdef NODECALL
	if ((int) t & NODE_LINK) {
		m->state = MBX_NODE;
		sprintf (m->line, "%s: user '%s'", "node", m->name);
		chname (Curproc, m->line);
		log (s, "open node: '%s'", m->name);

		(void) node_incom (0, NULLCHARP, (void *)m);
		exitbbs (m);

		usflush (Curproc->output);
		kpause (500L);
		close_s (Curproc->output);
		return;
	}
#endif

#ifdef TTYCALL
	if ((int) t & TTY_LINK) {
		int ostate = m->state;

		m->state = MBX_CHAT;
		sprintf (m->line, "%s: user '%s'", ttylinkstr, m->name);
		chname (Curproc, m->line);
		log (s, "open TTYLINK");
		if (dochat (0, 0, m) == 2) {
			exitbbs (m);
			return;
		}
		tputs (chainingstr);
		m->state = ostate;
	}
#endif

#ifdef TUTOR
	if ((int) t != TIP && ((int) t & TUTOR_LINK || (int) t & INFO_LINK || (int) t & NEWS_LINK)) {
		m->state = MBX_TUTOR;
		sprintf (m->line, "%s: user '%s'", tutorialstr, m->name);
		chname (Curproc, m->line);
		log (s, openTUTORIAL);
		{
			char name[20];

			strncpy (name, m->name, 20);
#ifdef MBXTDISC
			stop_timer (&m->tdisc);
#endif
			Mbox[m->mbnum] = NULLMBX;
			free ((char *) m);
			BbsUsers--;
#ifdef XSERVER
			xnotify (X_BBS);
#endif
			/*			setflush(s,'\n');  *//* automatic flushing each line */
			tutorserv (name, NULLMBX, ((int) t & INFO_LINK) ? 1 : ((int) t & NEWS_LINK) ? 2 : 0, m->usecolor, 0);
			kpause (2000);
		}
		close_s (Curproc->output);
		return;
	}
#endif

	sprintf (m->line, "pbbs: user '%s'", m->name);
	chname (Curproc, m->line);
	log (s, openPBBS, m->name);
#ifdef STATS_USE
	STATS_adduse (0);
#endif

	if (MbLogging) {
		char buff[128];
		FILE *out;

		sprintf (buff, "%s/pbbs.log", LOGdir);
		if ((out = fopen (buff, APPEND_TEXT)) != NULLFILE) {
			time_t tt;

			(void) time (&tt);
			fprintf (out, logPBBSstr, m->name, (m->realname) ? m->realname : "", ptime (&tt));
			(void) fclose (out);
		}
	}
	BBSlogins++;

	/* tdisc timer initialization WAS here */

	if (m->privs & IS_BBS)
		/*		m->sid = MBX_SID; *//*force bbs status*/
		m->sid = MBX_EXPERT;	/* make BBS 'SID' itself, else it's sysop enters as EXPERT */
	else if (m->privs & IS_EXPERT)
		m->sid |= MBX_EXPERT;

#ifdef LZW
	if (m->sid & MBX_TNOS) {
		tputs (UseLZW);
		if (mbxrecvline (m) != -1) {
			if (tolower (*m->line) != 'n')
				togglelzw (m->user, 1);
		}
	}
#endif
	m->state = MBX_CMD;	/* start in command state */
	if (!(m->privs & IS_BBS) && (m->sid & MBX_GFX))
		colorfile (ANSILogin, m->colorset);

#if defined(MAILCMDS) && defined(MBFWD)
	mbx_SendSid (m);
#endif


	/* Say 'hello' only if user is not a bbs - WG7J */
	if (!(m->privs & (IS_BBS + IS_EXPERT))) {
		bbscolorchange (m, "0F");
		if ((int32)newuser <= 0)	/* lint !e775 */
			m->morerows = Usemore;
#ifdef ASKHOME
		if (m->home && m->home[0] == '-')
			askhome (m, 1);
#endif
#ifdef USERLOG
		if (m->realname == NULLCHAR)
			askrealname (m, 1);
#endif
		if (MOTDalways || !(m->sid & MBX_RDMOTD)) {
			(void) DisplayFile (MOTDfile, s);
			if (!(m->sid & MBX_RDMOTD)) {
				m->sid |= MBX_RDMOTD;
				m->update = 1;
			}
		}
	}

	/* Whether Expert mode or not, always give motd.sys to SYSOPs */
	if (m->privs & SYSOP_CMD)
		(void) DisplayFile (MOTDsysfile, s);

		
	/* Finish saying 'hello' only if user is not a bbs */
	if (!(m->privs & (IS_BBS + IS_EXPERT))) {
#ifdef USERLOG
		if (strlen (m->realname) > 3) {
			strcpy (tmp1, m->realname +1);
			cp = strpbrk(tmp1, ") ");
			*cp = '\0';
			tprintf (Mbwelcome, tmp1);
		}
		else
#endif
			tprintf (Mbwelcome, m->name);
#ifdef AX25
		if (m->family == AF_INET)
#endif
			tprintf (Mbbanner, Hostname, Version);
#ifdef AX25
		else
			tprintf (Mbbanner, pax25 (tmp, Mycall), Version);
#endif
		(void) doregistry (0, (char **) 0, m);

		if (newuser > 0)
			tprintf (laston, ctime (&newuser));
		if (BbsUsers == 1)
			tputs (Only);
		else
			tprintf (CurUsers, BbsUsers);
#ifdef MAILCMDS
#ifdef MBFWD
		if (!ThirdParty || !Mbfullsvc)
#else
		if (!ThirdParty)
#endif
			tputs (Mbwarning);
#endif
		if (!newuser) {
			(void) DisplayFile (NEWUSERfile, s);
#ifdef TUTOR
			sprintf (m->line, newuserparam, m->name);
			mbscripthook (m, newuserhook);
#endif
		}
		tflush ();
#ifdef USERLOG
		if (m->sid & MBX_STATS)
			quickscan (m, (int) newuser, 0);
#endif
#ifdef ALLSERV
		cp = getquote ();
		if (cp) {
			tputs (quotehdr);
			bbscolorchange (m, "0A");
			tprintf ("\n%s\n", cp);
			bbscolorchange (m, "0F");
		}
#endif
		if (!cp || (MMotd && *MMotd && strcmp (MMotd, cp)))
			(void) dousermotd (0, NULLCHARP, (void *) 0);
		free (cp);
		usflush (m->user);
#ifdef oldway
		/* Enable our local message area,
	         * only if we're not a bbs - WG7J
        	 */
		buf[1] = m->name;
		doarea (2, buf, m);
	} else
		m->mysize = 0;
#else
		m->special = ' ';
	}
#ifdef MAILCMDS
#ifdef DELEGATE
	if (m->sid & MBX_DELEGATE) {
		sendbell ();
		tputs (delegationquery);
		if (mbxrecvline (m) != -1) {
			if (tolower (*m->line) != 'n') {
				clear_delegate (m);
				m->sid &= ~MBX_DELEGATE;
				m->update = 1;
			}
		}
	}
#endif
	/* Enable our local message area */
	buf[1] = m->name;
	(void) doarea (2, buf, m);
#endif
#endif
#ifdef TUTOR
	if (!(m->privs & IS_BBS)) {
		sprintf (m->line, loginparam, m->name);
		mbscripthook (m, loginhook);
	}
#endif

	/* Send prompt */
	if (!(m->privs & IS_BBS))
		putprompt (m);
	else			/* first prompt to a BBS has color disabled, after this it
		   depends on whether we have seen an SID. If an SID, no color.
		   This allows the BBS's SYSOP to 'live' login and have color
		   capabilities (after the first prompt).	*/
		tputs (">\n");

	while (mbxrecvline (m) != -1) {
		if (m->privs & EXCLUDED_CMD)
			break;
#ifdef FOQ_CMDS
		if (m->privs & SYS_CHAT) {
			(void) dochat (0, (char **) 0, m);
			m->privs &= ~SYS_CHAT;
			goto sane;
		}
#endif
		if (m->special)	/* no-color BBS first prompt, also */
			bbscolorchange (m, "0F");
#ifdef MAILCMDS
		notifynewmail (m);
		/* Do not check mailfile if we're bbs, saves a tmpfile- WG7J*/
		if (!(m->sid & MBX_SID)) {
			int current;

			current = m->current;
			scanmail (m);
			if (current)
				m->current = current;
		}
#endif
		if ((rval = mbx_parse (m)) == -2)
			break;
		if (rval == 2)
			break;
		if (rval && BBSdump && (m->sid & MBX_SID))
			break;
		if (m->privs & EXCLUDED_CMD)
			break;
		if (rval == 1)
			tputs (Badsyntax);
#if defined(FOQ_CMDS) || defined(TTYCALL)
		if (m->privs & SYS_CHAT) {
			(void) dochat (0, (char **) 0, m);
			m->privs &= ~SYS_CHAT;
		}
#endif
#ifdef FOQ_CMDS
sane:
#endif
#ifdef MAILCMDS
		notifynewmail (m);
		/* Do not check mailfile if we're bbs, saves a tmpfile- WG7J*/
		if (!(m->sid & MBX_SID)) {
			int current;

			current = m->current;
			scanmail (m);
			if (current)
				m->current = current;
		}
#endif
		if (mbxMaxTimer && (m->logontime + mbxMaxTimer) < time ((time_t *) 0)) {
			if (!(m->sid & MBX_SID)) {
				sendbell ();
				tputs (maxsession);
				(void) domboxbye (0, (char **) 0, m);
			}
			log (Curproc->output, logmaxsession, m->name);
			break;
		}
		putprompt (m);
		m->state = MBX_CMD;
	}
	exitbbs (m);

	usflush (Curproc->output);
	kpause (500L);
	close_s (Curproc->output);
}



void
exitbbs (struct mbx *m)
{
#ifdef MBXTDISC
	stop_timer (&m->tdisc);
#endif
#ifdef MAILCMDS
#ifdef WPAGES
	if (m->change & CHG_WP)
		if (!m->home || m->home[0] != '-')	/* should ALWAYS do this */
			add_WPUpdate (m->name, m->home, m->realname, 'U');
#endif
	(void) closenotes (m, 1);
	free (m->to);
	free (m->tofrom);
	free (m->origto);
	free (m->origbbs);
	free (m->subject);
	free (m->date);
	free (m->tomsgid);
#endif
	free (m->path);
	free (m->home);
#ifdef MAILCMDS
	/* Close the tempfiles if they are not nullpointers - WG7J */
	if (m->tfile != (FILE *) 0)
		(void) fclose (m->tfile);
	if (m->tfp != (FILE *) 0)
		(void) fclose (m->tfp);
	if (m->stdinbuf != NULLCHAR)
		free (m->stdinbuf);
	if (m->stdoutbuf != NULLCHAR)
		free (m->stdoutbuf);
#endif
#ifdef MBFWD
	fwdfree (&m->fwdbbs);
#endif
	free ((char *) m->mbox);
	if ((m->state != MBX_FORWARD) && m->name && *m->name)
		log (Curproc->output, "close PBBS from '%s'", m->name);
	Mbox[m->mbnum] = NULLMBX;
	free ((char *) m);
	if (BbsUsers) {
		BbsUsers--;
#ifdef XSERVER
		xnotify (X_BBS);
#endif
	}
}



/**********************************************************************/

static int dombsecurity (int argc, char *argv[], void *p);

#ifdef MAILCMDS
static int dosid (int argc, char *argv[], void *p);
static int mbx_to (int argc, char *argv[], void *p);
static int thirdparty (struct mbx *m);
#endif

#ifdef USERLOG
static int dombedituser (int argc, char *argv[], void *p);
#endif

#ifdef EDITHEADERS
static int dombedithdr (int argc, char *argv[], void *p);
static void pad_if_needed (FILE * fp, int orig, int new);
#endif

#ifdef GATECMDS
static int dombping (int argc, char *argv[], void *p);
int dombconnect (int argc, char *argv[], void *p);
#endif

#if defined(NETROM) && defined(GATECMDS)
static int dombnrnodes (int argc, char *argv[], void *p);
#endif

#if defined(GATECMDS) || defined(FOQ_CMDS)
static void gw_alarm (void *p);
static void gw_input (int s, void *notused, void *p);
static void gw_superv (int null, void *proc, void *p);
#endif

#if defined(CALLBOOK) && defined(FOQ_CMDS)
static int dombcall (int argc, char *argv[], void *p);
#endif

#ifdef SQL
static int dombsql (int argc, char *argv[], void *p);
#endif

#ifdef HTTPPBBS
static int dombhttp (int argc, char *argv[], void *p);
#endif


int dombconvers (int argc, char *argv[], void *p);
int dodownload (int argc, char *argv[], void *p);
int dowhat (int argc, char *argv[], void *p);
int dozap (int argc, char *argv[], void *p);
int dosend (int argc, char *argv[], void *p);
int dostars (int argc, char *argv[], void *p);
int dombhelp (int argc, char *argv[], void *p);
int dombfinger (int argc, char *argv[], void *p);
static int dombescape (int argc,char *argv[],void *p);
int dombtutor (int argc, char *argv[], void *p);
int dombnews (int argc, char *argv[], void *p);
int dombexpert (int argc, char *argv[], void *p);
int dobump (int argc, char *argv[], void *p);
int dombfdesc (int argc, char *argv[], void *p);
int dombwpages (int argc, char *argv[], void *p);
int dombset (int argc, char *argv[], void *p);
int dombget (int argc, char *argv[], void *p);
int dombrmail (int argc, char *argv[], void *p);
int dombgroup (int argc, char *argv[], void *p);
int doreply (int argc, char *argv[], void *p);
int douser (int argc, char *argv[], void *p);
int dombexpand (int argc, char *argv[], void *p);
int dombslip (int argc, char *argv[], void *p);
int dombjheard (int argc, char *argv[], void *p);
int dombiproute (int argc, char *argv[], void *p);
int dombencode (int argc, char *argv[], void *p);
int mbx_data (struct mbx *m, struct list *cclist, char *extra, int returnreceipt);
int msgidcheck (char *string);

#if defined(FOQ_CMDS) && defined(ALLSERV)
int dombquote (int argc, char *argv[], void *p);
#endif

#ifdef USERLOG
static int doquickscan (int argc, char *argv[], void *p);
#endif


#ifdef MBFWD
extern int dombroute (int argc, char *argv[], void *p);
extern int dombbid (int argc, char *argv[], void *p);
#endif

extern int dobbshome (int argc, char *argv[], void *p);

#ifdef USERLOG
extern int dorealname (int argc, char *argv[], void *p);
#endif

#ifdef NETROM
extern int donrneighbour (int argc, char *argv[], void *p);
#endif

#ifdef TUTOR
extern int dombscript (int argc, char *argv[], void *p);
#endif



struct cmds Mbcmds[] =
{
#ifdef MAILCMDS
	{ "",		doreadnext,		0, 0, NULLCHAR },
#endif
	{ "?",		dombhelp,		0, 0, NULLCHAR },
#ifdef MAILCMDS
	{ "Area",	doarea,			0, 0, NULLCHAR },
#endif
	{ "Bye",	domboxbye,		0, 0, NULLCHAR },
#ifdef MBFWD
	{ "BId",	dombbid,		0, 2, "BId bidtosearch4" },
#endif
	{ "BUmp",	dobump,			0, 2, "bump username" },
#ifdef GATECMDS
	{ "Connect",	dombconnect,		0, 0, NULLCHAR },
#endif
#if defined(CALLBOOK) && defined(FOQ_CMDS)
	{ "CAll",	dombcall,		0, 0, NULLCHAR },
#endif
#if defined(FOQ_CMDS) && (defined(CALLCLI) || defined(SAMCALLB))
	{ "CAll",	dombcallbook,		0, 0, NULLCHAR },
#endif
#ifdef FOQ_CMDS
	{ "CHat",	dochat,			0, 0, NULLCHAR },
#endif
#ifdef TUTOR
	{ "CMd",	dombscript,		0, 0, NULLCHAR },
#endif
#ifdef CONVERS
	{ "CONFerence",	dombconvers,		0, 0, NULLCHAR },
#endif
#ifdef AX25
	{ "COUnters",	doaxcounters,		0, 0, NULLCHAR },
#endif
#ifdef FILECMDS
#ifdef XMODEM
	{ "Download",	dodownload,		0, 2, "D[U|X] filename" },
#else
	{ "Download",	dodownload,		0, 2, "D[U] filename" },
#endif
#endif
/* next entry out of order DELIBERATELY, to make "D" download, not del! */
#ifdef FILECMDS
	{ "DEl",	dozap,			0, 2, "DEL filename" },
	{ "DIr",	dowhat,			0, 0, NULLCHAR },
#endif
#ifdef GATECMDS
	{ "Escape",	dombescape,		0, 0, NULLCHAR },
#endif
/* next entry out of order DELIBERATELY, to make "E" escape, not edithdr, edituser or encode! */
#ifdef EDITHEADERS
	{ "EDitheader",	dombedithdr,		0, 2, "EDitheader <msg#>" },
#endif
#ifdef USERLOG
	{ "EDITUser",	dombedituser,		0, 2, "EDITUser user" },
#endif
	{ "ENcode",	dombencode,		0, 0, NULLCHAR },
	{ "EXit",	domboxbye,		0, 0, NULLCHAR },
	{ "EXPand",	dombexpand,		0, 2, "EXPand aliasname" },
#ifdef FOQ_CMDS
	{ "Finger",	dombfinger,		0, 0, NULLCHAR },
#endif
/* next entry out of order DELIBERATELY, to make "F" finger, not fdesc! */
	{ "FDesc",	dombfdesc,		0, 2, "FD filename" },
	{ "Get",	dombget,		0, 0, NULLCHAR },
	{ "GRoup",	dombgroup,		0, 2, "GROUP action groupname" },
	{ "Help",	dombhelp,		0, 0, NULLCHAR },
#if (defined(ASKHOME) || defined(WPAGES))
	{ "HOme",	dobbshome,		0, 0, NULLCHAR },
#endif
#ifdef HTTPPBBS
	{ "HTtp",	dombhttp,		0, 0, NULLCHAR },
#endif
	{ "Info",	dombhelp,		0, 0, NULLCHAR },
/* next entry out of order DELIBERATELY, to make "I" info, not iheard! */
	{ "IHeard",	doipheard,		0, 0, NULLCHAR },
	{ "IProute",	dombiproute,		0, 0, NULLCHAR },
#ifdef	AX25
	{ "Jheard",	dombjheard,		0, 0, NULLCHAR },
#endif
#ifdef MAILCMDS
	{ "Kill",	dodelmsg,		0, 0, NULLCHAR },
	{ "List",	dolistnotes,		0, 0, NULLCHAR },
#endif
	{ "Mboxusers",	dombusers,		0, 0, NULLCHAR },
	{ "MOtd",	dousermotd,		0, 0, NULLCHAR },
#if defined(NETROM) && defined(GATECMDS)
	{ "Nodes",	dombnrnodes,		0, 0, NULLCHAR },
#endif
#ifdef USERLOG
	{ "NAme",	dorealname,		0, 0, NULLCHAR },
#endif
#ifdef TUTOR
	{ "NEws",	dombnews,		0, 0, NULLCHAR },
#endif
#if defined(NETROM) && defined(GATECMDS)
	{ "NRoutes",	donrneighbour,		0, 0, NULLCHAR },
#endif
#ifdef FOQ_CMDS
	{ "Operator",	dochat,			0, 0, NULLCHAR },
#endif
	{ "Pbbsusers",	dombusers,		0, 0, NULLCHAR },
	{ "PAssword",	dombpasswd,		0, 0, NULLCHAR },
#ifdef GATECMDS
	{ "PIng",	dombping,		0, 2, "Ping hostname" },
	{ "PRofile",	dombprofile,		0, 0, NULLCHAR },
#endif
#ifdef GATECMDS
	{ "POrts",	dombports,		0, 0, NULLCHAR },
#endif
	{ "Quit",	domboxbye,		0, 0, NULLCHAR },
/* next entry out of order DELIBERATELY, to make "Q" quit, not query! */
#if defined(FOQ_CMDS) && (defined(CALLCLI) || defined(SAMCALLB))
	{ "QUEry",	dombcallbook,		0, 0, NULLCHAR },
#endif
#ifdef USERLOG
	{ "QUICkscan",	doquickscan,		0, 0, NULLCHAR },
#endif
#if defined(FOQ_CMDS) && defined(ALLSERV)
	{ "QUOte",	dombquote,		0, 0, NULLCHAR },
#endif
#ifdef MAILCMDS
	{ "Read",	doreadmsg,		0, 0, NULLCHAR },
	{ "REGistry",	doregistry,		0, 0, NULLCHAR },
	{ "REPly",	doreply,		0, 0, NULLCHAR },
	{ "REView",	dombreview,		0, 0, NULLCHAR },
#ifdef RMAIL
	{ "RMAil",	dombrmail,		0, 2, "RMAIL destbbs" },
#endif
#ifdef MBFWD
	{ "ROute",	dombroute,		0, 2, "ROute address" },
#endif
	{ "Send",	dosend,			0, 0, NULLCHAR },
#endif
	{ "SECurity", dombsecurity,		0, 0, NULLCHAR },
	{ "SET",	dombset,		0, 2, "SET A|C|F|M|N|S|SI|X" },
#ifdef TIPMAIL
	{ "SLip",	dombslip,		0, 0, NULLCHAR },
#endif
#ifdef SQL
	{ "SQl",	dombsql,		0, 0, NULLCHAR },
#endif
#ifdef STATS
	{ "STAts",	dostats,		0, 2, "STAts subcommand" },
#endif
#ifdef GATECMDS
	{ "Telnet",	dombtelnet,		0, 2, "T hostname" },
#endif
	{ "TIme",	dosystime,		0, 0, NULLCHAR },
#ifdef NODECALL
	{ "TNode",	(int (*)(int, char **, void *)) node_incom, 0, 0, NULLCHAR },
#endif
#ifdef TUTOR
	{ "TUtor",	dombtutor,		0, 0, NULLCHAR },
#endif
#ifdef FILECMDS
#ifdef XMODEM
	{ "Upload",	dombupload,		0, 2, "U[U|X] filename" },
#else
	{ "Upload",	dombupload,		0, 2, "U[U] filename" },
#endif
#endif
	{ "UPTime",	douptime,		0, 0, NULLCHAR },
	{ "USEr",	douser,			0, 0, NULLCHAR },
#ifdef MAILCMDS
	{ "Verbose",	doreadmsg,		0, 0, NULLCHAR },
#endif
#ifdef ALLCMD
	{ "VERSion",	doversion,		0, 0, NULLCHAR },
#endif
#ifdef FILECMDS
	{ "What",	dowhat,			0, 0, NULLCHAR },
#endif
#ifdef WPAGES
	{ "WPages",	dombwpages,		0, 2, "WP call [@bbs]" },
#endif
	{ "Xpert",	dombexpert,		0, 0, NULLCHAR },
#ifdef FILECMDS
	{ "Zap",	dozap,			0, 2, "Z filename" },
#endif
/* the rest aren't displayed with "?" */
#ifdef MAILCMDS
	{ "[",		dosid,			0, 0, NULLCHAR },
#endif
	{ "@",		dosysop,		0, 0, NULLCHAR },
#ifdef	AX25
#ifdef MBFWD
	{ "f>",		dorevfwd,		0, 0, NULLCHAR },
#ifdef FBBFWD
	{ "fa",		dofbbfwd,		0, 0, NULLCHAR },
	{ "fb",		dofbbfwd,		0, 0, NULLCHAR },
	{ "ff",		dofbbfwd,		0, 0, NULLCHAR },
	{ "fq",		domboxbye,		0, 0, NULLCHAR },
#endif
#endif
#endif
	{ "***",	dostars,		0, 0, NULLCHAR },
	{ ";",		dombsemicolon,		0, 0, NULLCHAR },
	{ NULLCHAR,	NULLFP ((int, char **, void *)),
						0, 0, "Huh?" },
};



/* "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[] = "aklrsvdu[mxwiq";

int
mbx_parse (struct mbx *m)
{
char *cp, *cp2;
int i;
unsigned char specialfound = 0;
int inbid = 0, sendtype = 0;
AarrayPtr cus;
#ifdef MAILCMDS
char *newargv[2];
#endif

	/* Skip any spaces at the begining */
	for (cp = m->line; isspace (*cp); ++cp)
		;

	for (cus = Mbcustom; cus; cus = cus->next)
		if (!strnicmp (cp, cus->name, strlen (cus->name))) {
			strncpy (m->line, (char *) cus->macro, MBXLINE);
			cp = m->line;
			break;
		}
#ifdef MAILCMDS
	if (*cp == '!')
		return allareas (m, cp);
#endif

#ifdef FBBFWD
	if (((m->sid & MBX_FBBFWD) && (m->line[0] == 'f'))) {
		m->stype = (char) toupper (m->line[2]);
	} else {
#endif

		if (tolower (*cp) == 's')
			sendtype = 1;	/* a 'send' command, of some form */

		/* Translate entire buffer to lower case */
		/* fix by KO4KS to convert a message's BID to upper case.  */
		for (cp2 = cp; *cp2 != '\0'; ++cp2) {
			if (sendtype && !inbid && *cp2 == '$')
				inbid = 1;
			if (!inbid)
				*cp2 = (char) tolower (*cp2);
			else
				*cp2 = (char) toupper (*cp2);
			if (*cp2 == ' ')
				inbid = 0;
		}

		m->special = m->stype = ' ';
		if (*cp == '^' || *cp == '~') {
			if (cp[1] == 's') {
				m->special = *cp;
				specialfound = 0x80;
			}
			*cp++ = ' ';
		}
		if (*cp != '\0' && *(cp + 1) != '\0')
			for (i = 0; i < (int) strlen (twocmds); ++i) {
				if (*cp == twocmds[i] && (isspace (*(cp + 2)) || *(cp + 2) == '\0'
							  || *cp == '[')) {
					if (islower (*(++cp)))
						m->stype = (char) toupper (*cp);	/* Save the second character */
					else
						m->stype = *cp;
					m->stype |= (char) specialfound;
					*cp = ' ';
					break;
				}
			}
#ifdef MAILCMDS
		/* See if the input line consists solely of digits */
		cp = m->line;
		for (cp = m->line; isspace (*cp); ++cp)
			;
		if (*cp == ';' || *cp == '#')
			return 0;	/* allow self-identification */
		newargv[1] = cp;
		for ( ; *cp != '\0' && isdigit (*cp); ++cp)
			;
		if (*cp == '\0' && strlen (newargv[1]) > 0) {
			char buf[2];

			buf[0] = 'r';
			buf[1] = 0;
			newargv[0] = buf;
			return doreadmsg (2, newargv, (void *) m);
		}
#endif
#ifdef FBBFWD
	}
#endif
	return cmdparse (Mbcmds, m->line, (void *) m);
}



static int
mbx_recvchar (struct mbx *m, int s)
{
int block;
int c;

	block = sockblock (s, SOCK_NORXBLOCK);
	while ((c = recvchar (s)) == EOF) {
		if (errno != EWOULDBLOCK)
			break;
		if (m->privs & SYS_CHAT) {
			c = '\n';
			break;
		}
		kpause (500);
		if (m->state == MBX_SYSOP)	/* if possibly tracing socket */
			usflush (s);
	}
	(void) sockblock (s, block);
	return c;
}



/* 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.
 */
int
mbxrecvline (struct mbx *m)
{
int s = m->user;
int escape = m->escape;
char *buf = m->line;
int c, cnt = 0, opt, cl;

	if (buf == NULLCHAR)
		return 0;
#ifdef BBSIMPORT
	if (m->quickfile) {	/* taking input for dosend from import file */
		m->change = 1;
		(void) fgets (buf, MBXLINE, m->quickfile);
		if (feof (m->quickfile))
			return EOF;
		rip (buf);
		return ((int) strlen (buf));
	}
#endif
	usflush (Curproc->output);
#ifdef MBXTDISC
	start_timer (&m->tdisc);
#endif
	kwait (NULL);
	while ((c = mbx_recvchar (m, s)) != EOF) {
#ifdef MBXTDISC
		/* Restart the timeout-timer */
		start_timer (&m->tdisc);
#endif
		kwait (NULL);
		if ((m->type & (TELNET_LINK | RLOGIN_LINK)) && !m->inmessage && c == IAC) {	/* Telnet command escape */
			if ((c = recvchar (s)) == EOF)
				break;
			if (c >= 250 && c < 255 && (opt = recvchar (s)) != EOF) {
				switch (c) {
					case SB:
						opt = recvchar (s);	/* Get the real option */
						if (opt == EOF)
							break;
						cl = opt;
						c = recvchar (s);
						/* Gobble up until we see IAC SE */
						while ((c != EOF) && !(cl == IAC && c == SE)) {
							/* maybe check for timeout here, in case someone
						   happened to send a binary file with the IAC SB
						   sequence in it. */
							cl = c;	/* keep track of second last char read */
							c = recvchar (s);
						}
						/* and tell the client where to go... */
						/* tprintf("%c%c%c",IAC,WONT,opt); */
						break;
					case WILL:
						if (opt == TN_LINEMODE) {
							m->linemode = 1;
							/* we WANT linemode */
							tprintf ("%c%c%c", IAC, DO, opt);
							/* Tell client to do editing */
							tprintf ("%c%c%c%c%c%c%c", IAC, SB, TN_LINEMODE, 1, 1, IAC, SE);
						} else {
							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);
						break;
					default:
						break;
				}
				/* to be fixed 		   usflush(Curproc->output);*/
				kwait (NULL);
				continue;
			}
			if (c != IAC && (c = recvchar (s)) == EOF)
				break;
		}
		/* ordinary character */
#ifndef TNOS_68K
		if (c == '\r' || c == '\n')
#else
		if (c == '\r' || c == '\l')
#endif
			break;
		if (!m->inmessage && uchar (c) == escape) {
			if (socklen (s, 0))	/* discard any remaining input */
				(void) recv_mbuf (s, NULL, 0, NULLCHAR, 0);
			cnt = -2;
			break;
		}
		/* Handle <del> chars - from wa7tas */
		if ((c == 8 || c == 127) && (!m->inmessage || !(m->sid & MBX_SID))) {
			if (cnt > 0) {
				*--buf = 0;
				cnt--;
			}
		} else if (c) {	/* discard NULs */
			*buf++ = (char) c;
			++cnt;
		}
		if (cnt == (MBXLINE - 1)) {
			cnt = -2;
			break;
		}
		kwait (NULL);
	}
	if (c == EOF && cnt == 0)
		return -1;
	*buf = '\0';
#ifdef MBXTDISC
	/* Restart the timeout-timer - WG7J*/
	start_timer (&m->tdisc);
#endif
	return cnt;
}



/* New forwarding option, simply ignore all data - WG7J */
static int
dombsemicolon (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return 0;
}



#ifdef USERLOG
static int
doquickscan (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
	quickscan ((struct mbx *) p, 1, 0);
	return 0;
}
#endif


int
domboxbye (int argc, char *argv[], void *p)
{
struct mbx *m;
time_t elapsedtime;
char *cptr;

	m = (struct mbx *) p;

#ifdef USERLOG
	if (m->stype == 'S')
		return (doquickscan (argc, argv, p));
#endif

#ifdef TUTOR
	if (!(m->sid & MBX_SID)) {	/* not a BBS */
		sprintf (m->line, logoutparam, m->name);
		mbscripthook (m, logouthook);
	}
#endif
#ifdef USERLOG
#ifdef MAILCMDS
	if (!(m->sid & MBX_SID))/* not a BBS */
		setlastread (m);
#endif
	if (m->update)
		updatedefaults (m, 0);
#endif
	if (m->sid & MBX_SID) {	/* if a BBS */
		smtptick (NULL);/* clean up any unfiled mail */
		kpause (500L);
		return -2;	/* for bbs's, just disconnect */
	}
	/* Now say goodbye */
	bbscolorchange (m, "09");
	tputs ("\nThank you ");
	bbscolorchange (m, "0C");
	tputs (m->name);
	bbscolorchange (m, "09");
	tprintf (forcalling, Hostname);
	(void) time ((time_t *) & elapsedtime);
	cptr = ctime ((time_t *) & elapsedtime);
	rip (cptr);
	elapsedtime -= m->logontime;
	tputs (Loggedoff);
	bbscolorchange (m, "0F");
	tputs (cptr);
	bbscolorchange (m, "09");
	tputs (Timethissession);
	bbscolorchange (m, "0F");
	tprintf (strCR, tformat ((long) elapsedtime));
	if (MExit != NULLCHAR)
		tprintf (strCR, MExit);
#ifdef ALLSERV
	if (m->type == TIP)
		tputs (Pleasehangup);
#endif
	/*	usflush(m->user);	*/
	usflush (Curproc->output);
	return -2;		/* signal that exitbbs() should be called */
}



char *
addroot (const char *root, const char *name)
{
	return strdup (make_fname (root, (*name == '/') ? &name[1] : name));
}



char *
permtest (
char *path,
long privs,
char const *name,
int mode,
char *root,
int thedir
) {
char *file, *defdir, *cp;

	if (root == NULLCHAR) {
		if (path == NULLCHAR) {
			tputs (Noperm);
			return 0;
		}
		defdir = strdup (path);
		if ((cp = strchr (defdir, ';')) != NULLCHAR)
			*cp = 0;
	} else
		defdir = strdup (root);
	if (thedir)
		file = strdup (defdir);
	else
		file = addroot (defdir, name);
	free (defdir);
	if (!permcheck (path, privs, mode, file)) {
		free (file);
		tputs (Noperm);
		return 0;
	}
	return file;
}



#if defined(CALLBOOK) && defined(FOQ_CMDS)
extern char *InetCallserver;
extern char *InetCallserverport;

/*This is a simple way of estiblishing a connection
 *to a callbook-server via tcp - WG7J
 */
static int
dombcall (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
char *newargv[3];
struct mbx *m;

	m = (struct mbx *) p;
	if (InetCallserver != NULLCHAR) {
		newargv[0] = "C";
		newargv[1] = InetCallserver;
		newargv[2] = InetCallserverport;
		SecureTelnet = 0;
		m->stype = '+';
		return dombtelnet (3, newargv, p);
	} else
		tputs (callsvrnot);

	/* It returns only after a disconnect or refusal */
	return 0;
}
#endif /*CALLBOOK*/



#if defined(FOQ_CMDS) && (defined(CALLCLI)  || defined(SAMCALLB) || defined(CALLSERVER))
static int
dombcallbook (int argc, char *argv[], void *p)
{
struct mbx *m;
char buf[8], *newargv[3], buf2[2];
int req, ret = 0;

	if (argc < 2) {
		tprintf (callusage, (*argv[0] == 'q') ? "QUEry" : "CAll");
		return -1;
	}
	m = (struct mbx *) p;
#ifndef newcode
	sprintf (buf, "%d", IPPORT_CALLDB);
	buf2[0] = 'C';
#else /* to call KF5MG's callbook server, through FINGER */
	sprintf (buf, "%d", IPPORT_FINGER);
	buf2[0] = 't';
#endif
	buf2[1] = 0;
	newargv[0] = buf2;
	newargv[1] = Callserver;
	newargv[2] = buf;

	for (req = 1; req < argc; req++) {
		if (argv[req] == NULLCHAR)
			return ret;
		log (m->user, checkcallbook, m->name, argv[req]);
		if (!Callserver || !*Callserver) {
#if (defined(SAMCALLB) || defined(CALLSERVER))
#ifdef SAMCALLB
			cb_lookup (Curproc->output, argv[req], (FILE *) 0);
#else
			cb_lookup (Curproc->output, argv[req]);
#endif
#endif
			tputs (callsvcnotconfig);
			return 0;
		} else {
			m->startmsg = mallocw (80);	/* is freed each time by gw_connect() */
			sprintf (m->startmsg, "%s\n", argv[req]);
			tprintf (lookcall, argv[req], Callserver);
			SecureTelnet = 0;
			m->stype = '+';
			ret = dombtelnet (3, newargv, p);
		}
	}
	return ret;		/* It looks like all possible returns are zero anyway!    */
}
#endif /* CALLCLI || SAMCALLB || CALLSERVER */



#ifdef MAILCMDS
extern int Mbloophold;

/*Some additional security - WG7J
 *NO_3PARTY =  disallow all 3rd party mail
 *NO_SENDCMD = only allow mail to sysop
 */
extern int NoBid;



int
dosend (int argc, char *argv[], void *p)
{
int cccnt = 0, fail = 0;
char *host, *cp, *rhdr = NULLCHAR;
struct list *ap, *cclist = NULLLIST;
struct mbx *m;
FILE *fp;
int done = 0;
char *cp2;
char returnreceipt = 0, nosig = 0;
int foundCTLz = 0;
int c;
long here, datastart;
int recvsize;
int userlevelending = 0;
register int i;
long messagesize;
int dnsfound = 0, isampr = 0;
int zulu = 0;
#ifdef REJECT
int rejecthold = 0;
#endif
#ifdef ALTERBID
int bidbad = 0;
#endif
#ifdef notdef
time_t now;
#endif
#ifdef HOLDMODS
char *badwords[100];
char badbuf[LINELEN];
int totalindex;
int Holdit = 0;
#endif
#ifdef RLINE
struct tm t;

#define ODLEN   16
#define OBLEN   32
char origdate[ODLEN];
char origbbs[OBLEN];
char fwdbbs[NUMFWDBBS][FWDBBSLEN + 1];
int myfwds = 0;
int check_r = 0, check_r2 = 0;
int found_r = 0, wrotedata = 0;
int loops = 0;
int rline_error = 0, rline_warning = 0;
char Me[15];

	origdate[0] = '\0';
	origbbs[0] = '\0';
	Me[0] = '\0';
#endif

	m = (struct mbx *) p;

#ifdef REJECT
	if (m->stype != 'R') {
		char bf[128];

		bf[0] = 0;
		for (i = 0; i < argc; i++) {
			if (i)
				strcat (bf, " ");
			strcat (bf, argv[i]);
			if (!i) {
				bf[1] = m->stype;
				bf[2] = 0;
			}
		}
		rejecthold = reject (bf, (m->sid & MBX_SID), 0);

		if (rejecthold == REJ_REJECT) {
			log (m->user, "Rejected message: '%s'\n", bf);
			if (!m->quickfile)
				tprintf (refused1);
			return 0;
		}
	}
#endif
#ifdef XFWD
	if (m->sid & MBX_XFWD && (m->stype == 'X' || m->stype == 'Y' || m->stype == 'S'))
		return (doxfwd (argc, argv, p));
#endif

	if (m->stype & 0x80) {
		if (m->special == '~')
			nosig = 1;
		else
			returnreceipt = 1;
		m->stype &= 0x7f;
	}
	if (m->stype == 'M') {
		if (argc > 1) {
			zulu = atoi (argv[1]);
			if (zulu < 1 || zulu > m->nmsgs)
				tprintf (sendinvalid, zulu);
			else {
				tprintf (sendset, zulu);
				m->current = zulu;
			}
			return 0;
		}
	}
	if (m->stype == 'R' && m->privs & ALL_AREAS)
		return 0;	/* can't reply on all areas */

#ifdef TIPMAIL
	if (m->stype == 'L')
		return (dombslip (argc, argv, p));
#endif
	if ((m->stype != 'R' || (m->sid & MBX_SID)) && mbx_to (argc, argv, m) == -1) {
		tputs ((m->sid & MBX_SID) ? syntaxerror : syntaxerror2);
		mail_error (syntaxerror3, m->name, cmd_line (argc, argv, m->stype));
		return 0;
	}
	/* if enabled, don't allow bulletins to be sent without '@host' portion */
	if (m->stype == 'B' && enforceBhosts && !strchr (m->to, '@')) {
		tputs (enforce);
		log (m->user, enforce2, m->name, m->to);
		mail_error (enforce2, m->name, m->to);
		return 0;
	}
	bbscolorchange (m, "0D");

	/*Check for send permission */
	if (m->privs & NO_SENDCMD) {	/*is this to 'SYSOP' or 'sysop' ?*/
		if (stricmp (m->to, "sysop")) {
			tputs ((m->sid & MBX_SID) ? permissionerror : permissionerror2);
			mail_error (permissionerror3, m->name, m->to);
			return 0;
		}
	}
	/* Check for a BID on bulletins from other bbs's - WG7J */
	if ((m->sid & MBX_SID) && !NoBid &&
	    (m->stype == 'B') && (m->tomsgid == NULLCHAR)) {
		tputs (nobid);
		log (m->user, nobid2, m->name, m->to);
		mail_error (nobid2, m->name, m->to);
		return 0;
	}
	kwait (NULL);		/* just to be nice to others */
	if (m->stype != 'R' && msgidcheck (m->tomsgid)) {
		if (m->quickfile && (m->sid & MBX_FBBFWD))
			log (m->user, Alreadyhave2, m->tomsgid);
		else {
			if (m->sid & MBX_SID)
				tputs ("NO - ");
			tprintf (Alreadyhave, m->tomsgid);
		}
		return 0;
	}
	kwait (NULL);		/* just to be nice to others */
	/*	if (!(m->sid & MBX_SID))	*//* if BBS, no need! */
	if ((m->stype != 'B') && (m->stype != 'T') && (m->stype != 'R'))
		m->to = host_or_wpage_exp (m->to, 1, 0, &dnsfound);

	/* this section checks permission limits on mail messages */
	if (dnsfound) {
		char *cptmp;

		cptmp = strchr (m->to, '@');
		if (cptmp && !strchr (cptmp, '.')) {
			isampr = (int) strstr (domainsuffix (cptmp), amprorg);
		} else
			isampr = (int) strstr (m->to, amprorg);
	}
	/* we allow any mail without an '@' - others are checked for permissions */
	if ((m->to && strchr (m->to, '@')) && ((!dnsfound && (m->privs & NO_PBBSMAIL)) ||
			 (dnsfound && isampr && (m->privs & NO_AMPRMAIL)) ||
		       (dnsfound && !isampr && (m->privs & NO_INETMAIL)))) {
		if (m->sid & MBX_SID)
			tputs (permissionerror);
		else
			tprintf (permissionerror3, m->name, m->to);
		mail_error (permissionerror5, m->name, m->to);
		return 0;
	}
#ifdef RLINE
	check_r2 = 1;
#endif
	if (m->stype == 'R' && !(m->sid & MBX_SID))	{
		if (mbx_reply (argc, argv, m, &cclist, &rhdr) == -1 || !m->to)
			return 0;
#ifdef RLINE
		else
			check_r2 = 0;
#endif
	}
#ifdef TUTOR
	if (!(m->sid & MBX_SID)) {
		char tmp[MBXLINE + 1];

		memcpy (tmp, m->line, MBXLINE + 1);
		strncpy (m->line, m->to, MBXLINE + 1);
		mbscripthook (m, "dosend.sys");
		memcpy (m->line, tmp, MBXLINE + 1);
	}
#endif
	if ((cp = rewrite_address (m->to, 0)) != NULLCHAR)	{
		if (stricmp (m->to, cp) != 0) {
			m->origto = m->to;
			m->to = cp;
		} else
			free (cp);
	}

#ifdef DELEGATE
	if ((m->sid & MBX_DELEGATE) && !stricmp (m->name, m->to)) {
		if (!(m->sid & MBX_SID))
			sendbell ();
		tputs ((m->sid & MBX_SID) ? refused1 : refused2);
		log (m->user, refused3, m->name);
		mail_error (refused3, m->name);
		free (rhdr);
		return 0;
	}
#endif

	kwait (NULL);		/* just to be nice to others */
	/* refuse any mail that gets rewritten into 'refuse' - WG7J */
	if (!stricmp (m->to, "refuse")) {
		tputs ((m->sid & MBX_SID) ? refused1 : refused5);
		free (rhdr);
		return 0;
	}
#ifdef MBFWD
	if (((!Mbfullsvc || !ThirdParty) && !(m->privs & SYSOP_CMD)) || (m->privs & NO_3PARTY))
#else
	if (((!ThirdParty) && !(m->privs & SYSOP_CMD)) || (m->privs & NO_3PARTY))
#endif
		if (thirdparty (m) == 0) {
			if (!m->quickfile) {
				if (m->sid & MBX_SID)
					tputs ("NO - ");
				tputs (Mbwarning);
			}
			mail_error (refused3rd, m->name, m->to);
			free (rhdr);
			return 0;
		}
	/* Send the new 'To:' line to sysops only - WG7J */
	if ((m->privs & SYSOP_CMD) && (m->origto != NULLCHAR || m->stype == 'R') && !(m->sid & MBX_SID))
		tprintf ("To: %s\n", m->to);
	if (validate_address (m->to) == 0) {
		if (!m->quickfile)	{
			tputs ((m->sid & MBX_SID) ? badaddr1 : badaddr2);
			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;
		} else	{
			if (!m->origto)
				m->origto = m->to;
			else
				free (m->to);
			m->to = strdup ("check");
		}
	}
	kwait (NULL);		/* just to be nice to others */
	/* Display the Cc: line (during SR command) */
	for (ap = cclist; ap != NULLLIST; ap = ap->next) {
		if (cccnt == 0) {
			tputs (Hdrs[CC]);
			cccnt = 4;

		} else {
			tputs (", ");
			cccnt += 2;
		}
		if (cccnt + (int) strlen (ap->val) > 80 - 3) {
			tputs ("\n    ");
			cccnt = 4;
		}
		tputs (ap->val);
		cccnt += (int) strlen (ap->val);
	}
	if (cccnt)
		tputc ('\n');

	kwait (NULL);		/* just to be nice to others */
	/* If the the command was 'SC' then read the Cc: list now - WG7J */
	if ((m->stype == 'C') && !(m->sid & MBX_SID)) {
		m->stype = 'P';	/* make everything private */
#ifdef RMAIL
		if (m->change == -1)
			tputs ("RMail: ");
		else
#endif
			tputs (CcLine);
		if (mbxrecvline (m) != -1) {
			if (strlen (m->line)) {
				if (*m->line == 0x01) {	/* CTRL-A, abort */
					free (rhdr);
					del_list (cclist);
					tputs (MsgAborted);
					return 0;
				}
				cp = m->line;
				/* get all the Cc addresses, separated by comma's */
				while ((cp2 = strchr (cp, ',')) != NULLCHAR) {
					*cp2 = '\0';
					/*get rid of leading spaces or tabs*/
					while (*cp == ' ' || *cp == '\t')
						cp++;
					if (strlen (cp)) {
						cp = host_or_wpage_exp (strdup (cp), 0, 0, &dnsfound);
						(void) addlist (&cclist, cp, 0, cp);
						free (cp);
					}
					cp = cp2 + 1;
				}
				/* Do the last or only one */
				/* get rid of leading spaces or tabs*/
				while (*cp == ' ' || *cp == '\t')
					cp++;
				if (strlen (cp)) {
					cp = host_or_wpage_exp (strdup (cp), 0, 0, &dnsfound);
					(void) addlist (&cclist, cp, 0, cp);
					free (cp);
				}
			}
#ifdef RMAIL
			else if (m->change == -1) {
				free (rhdr);
				del_list (cclist);
				tprintf ("RMail requires destination addresses!\n");
				return (0);
			}
#endif
		} else {
			free (rhdr);
			del_list (cclist);
			return 0;
		}
	}
	/* Now check to make sure we can create the needed tempfiles - WG7J */
	if ((m->tfile = tmpfile ()) == NULLFILE) {
		free (rhdr);
		del_list (cclist);
		tputs ((m->sid & MBX_SID) ? notemp1 : notemp2);
		return -2;	/* disconnect if no files!! */
	}
#ifdef RLINE
	kwait (NULL);		/* just to be nice to others */
	/* Only accept R: lines from bbs's */
	if ((m->sid & MBX_SID) && (Rdate || Rreturn || Rfwdcheck || Mbloophold)) {
		/* Going to interpret R:headers, we need another tempfile !  */
		if ((m->tfp = tmpfile ()) == NULLFILE) {
			free (rhdr);
			del_list (cclist);
			tputs (notemp1);
			return 0;
		}
		/* Now we got enough :-) */
		check_r = 1;
		/* Set the call, used in loop detect code - WG7J */
		if (Mbloophold) {
			(void) pax25 (Me, Mycall);
			if ((cp = strpbrk (Me, DIGI_IDS)) != NULLCHAR)
				*cp = '\0';	/* remove SSID */
		}
	}
#endif

	m->state = MBX_SUBJ;
	kwait (NULL);		/* just to be nice to others */
#ifdef HOLDMODS
	/* Naughty word filtering */
	if (Sholdscanall > 1)
		totalindex = load_badwords (badbuf, badwords, sizeof(badbuf));
#endif
#ifdef FBBCMP
	if (Mfbbcmp && (m->sid & MBX_FBBCMP)) {
		/* NOOP - m->subject is set prior to calling dosend() */ ;
	} else {
#endif
		if (m->stype != 'R' || (m->sid & MBX_SID)) {
#ifdef FBBFWD
			if (!Mfbb || !(m->sid & MBX_FBBFWD))
				/* Don't display the OK or Subject: tag on FBB forwards.	*/
#endif
				tputs ((m->sid & MBX_SID) ? "OK\n" : "Subject:\n");
			if (mbxrecvline (m) == -1) {
#ifdef RLINE
				if (check_r) {
					MYfclose (m->tfp);
				}
#endif
				free (rhdr);
				return -1;
			}
		} else		/* Replying to a message */
			tprintf ("Subject: %s\n", m->line);

		m->subject = strdup (m->line);
#ifdef HOLDMODS
		if (Sholdscanall > 1 && Holdit == 0)
			Holdit = holdscan (m->line, badbuf, badwords);
#endif

#ifdef FBBCMP
	}
#endif

	if (MBnoSubjBell && m->subject) {
		char *tcp, *tcp2;

		for (tcp = m->subject; *tcp; tcp++) {
			if (*tcp == 0x07) {
				tcp2 = tcp;
				while (*tcp2 == 0x07)
					tcp2++;
				strcpy (tcp, tcp2);
			}
		}
	}
#if 0
#ifdef RLINE
	if (!check_r) {
#endif
		(void) mbx_data (m, cclist, rhdr, returnreceipt);
#ifdef RLINE
		wrotedata = 1;
	}
#endif
	free (rhdr);
#endif
	m->state = MBX_DATA;
	kwait (NULL);		/* just to be nice to others */

#ifdef RMAIL
	if (m->change == -1) {
		/* Write RMAIL line */
#ifdef MBFWD
		if (Smtpheaders)
			fseek (m->tfile, -2, 1);
		else
#endif
			fprintf (m->tfile, "%s%s", Hdrs[TO], (m->origto != NULLCHAR) ? m->origto : m->to);

		for (ap = cclist; ap != NULLLIST; ap = ap->next) {
#if 0
			if (cccnt == 0) {
				fprintf (m->tfile, "%s", Hdrs[TO]);
				cccnt = 4;
				cp2 = (m->origto) ? m->origto : m->to;
				cp = strchr (cp2, '.');
				i = (cp) ? (int) (cp - cp2) : strlen (cp2);
				fwrite (cp2, 1, i, m->tfile);
				cccnt += i;
			}
#endif
			fprintf (m->tfile, ", ");
			cccnt += 2;
			fputs (ap->val, m->tfile);
			cccnt += (int) strlen (ap->val);
			if (cccnt > 508)
				break;
		}
		if (cccnt)
			fputs ("\n\n", m->tfile);
		del_list (cclist);
		cclist = NULLLIST;
	}
	kwait (NULL);		/* just to be nice to others */
#endif

	if (!(m->sid & MBX_SID)) {
		if (!(m->privs & SYSOP_CMD)) {
#ifdef TUTOR
			char tmp[MBXLINE + 1];

			memcpy (tmp, m->line, MBXLINE + 1);
			strncpy (m->line, (m->origto) ? m->origto : m->to, MBXLINE + 1);
			mbscripthook (m, vaguehook);
			memcpy (m->line, tmp, MBXLINE + 1);
#endif
			if ((m->origto != NULLCHAR || m->stype == 'R') && !strcmp (m->to, "check")) {
				sendbell ();
				tputs (tocheck1);
			}
		}
		tprintf ("Enter %smessage%s%s", (m->stype == 'F') ? "a personal " : "",
			 (m->stype == 'F') ? " to precede the forwarded message.\n" : ".  ", Howtoend);
		bbscolorchange (m, "07");
	}
#if 0				/* now in mbx_data() */
	/* Add a return receipt line, if requested */
	if (returnreceipt) {
		fseek (m->tfile, -1, 1);
		fprintf (m->tfile, "%s%s@%s\n\n", Hdrs[RRECEIPT], m->name, Hostname);
		returnreceipt = 0;
	}
#endif
	datastart = ftell (m->tfile);
	m->inmessage = 1;
#ifdef RLINE
	if (Rfwdcheck) {	/* checking sender here JUST IN CASE R: line is bad or missing */
		for (i = 0; i < Numfwds; i++) {
			if (!stricmp (MyFwds[i].name, m->name)) {
				/*Found it !*/
				strncpy (fwdbbs[myfwds++], m->name, 20);
				break;
			}
		}
	}
#endif
	while (!foundCTLz && ((recvsize = mbxrecvline (m)) != -1)) {
		kwait (NULL);	/* just in case sender is hosing us */
		if ((cp = strchr (m->line, CTLZ)) != NULLCHAR) {
			*(cp + 1) = 0;
			*cp = '\n';
			foundCTLz = 1;
		}
		if (MBackfilter && !strnicmp (m->line, "/ack", 4))
			continue;
		if (!(m->sid & MBX_SID) && (m->line[0] == 0x01 || !strnicmp (m->line, "/a", 2))) {	/* CTRL-A */
			MYfclose (m->tfile);
#ifdef RLINE
			if (check_r)
				MYfclose (m->tfp);
#endif
			tputs (MsgAborted);
			del_list (cclist);
			return 0;
		}
		userlevelending = (!strnicmp (m->line, "/e", 2) || !strnicmp (m->line, "/b", 2) || !strnicmp (m->line, "/q", 2) || !strcmp (m->line, ".")) ? 1 : 0;
		if (m->sid & MBX_SID)
			userlevelending = 0;
		if (m->line[0] != CTLZ && stricmp (m->line, "/ex") && !userlevelending) {
#ifdef HOLDMODS
			if (Sholdscanall > 1 && Holdit == 0)
				Holdit = holdscan (m->line, badbuf, badwords);
#endif
			if (*m->line == '/' && !(m->sid & MBX_SID)) {
				cp = &m->line[2];
				cp = skipnonwhite (cp);
				cp = skipwhite (cp);
				c = i = 0;
                         
				/* Mini Editor */
				switch (tolower (m->line[1])) {
					case 'o':
						i = 1;
						/* fall through */
					case 'v':
						if (m->stype != 'R')
							break;
						if ((fp = tmpfile ()) != NULLFILE) {
							tprintf ("%sOriginal message #%-d\n", stars, m->current);
							(void) msgtofile (m, m->current, fp, i, i);
							rewind (fp);
							(void) sendfile (fp, m->user, ASCII_TYPE, 0);
							tprintf ("%s\n", stars);
							(void) fclose (fp);
						}
						break;
					case 'i':
						if (m->stype != 'R')
							break;
						if ((fp = tmpfile ()) != NULLFILE) {
							int startat = 0,
							    endat = 32767,
							    lineno = 0;
							char linebuf[LINELEN + 1];

							startat = atoi (cp);
							if (startat)
								startat--;
							if (*cp) {
								cp = skipnonwhite (cp);
								cp = skipwhite (cp);
								endat = atoi (cp);
								if (endat)
									endat--;
								else
									endat = 32767;
							}
							(void) msgtofile (m, m->current, fp, 1, 0);
							rewind (fp);
							if (!wrotedata)	{
								(void) mbx_data (m, cclist, rhdr, returnreceipt);
								datastart = ftell (m->tfile);
								wrotedata = 1;
							}
							while (fgets (linebuf, LINELEN, fp) != NULLCHAR) {
								if (endat >= lineno && startat <= lineno) {
									fputs ("> ", m->tfile);
									fputs (linebuf, m->tfile);
								}
								lineno++;
							}
							(void) fclose (fp);
						}
						break;
					case 'f':
						if ((cp = permtest (m->path, m->privs, cp, RETR_CMD, NULLCHAR, 0)) == NULLCHAR)
							break;
						if ((fp = fopen (cp, READ_TEXT)) != NULLFILE) {
							tprintf ("%sAdded file: %s\n", stars, cp);
							if (!wrotedata)	{
								(void) mbx_data (m, cclist, rhdr, returnreceipt);
								datastart = ftell (m->tfile);
								wrotedata = 1;
							}
							while (fgets (m->line, MBXLINE, fp) != NULLCHAR)
								fputs (m->line, m->tfile);
							(void) fclose (fp);
						}
						free (cp);
						break;
					case 'd':
						if (*cp)
							i = atoi (cp);
						if (!i)
							i = 1;
						tprintf ("%sDeleted %d line%s\n", stars, i, (i - 1) ? "s" : "");
						here = ftell (m->tfile);
						while (here > 0) {
							fseek (m->tfile, --here, SEEK_SET);
							if ((fgetc (m->tfile)) == '\n' && !(i--))
								break;
							fseek (m->tfile, here, SEEK_SET);
							(void) putc ('\0', m->tfile);
						}
						break;
					case '/':
						strncpy (m->line, &m->line[1], MBXLINE);
						if (!MBackfilter || strnicmp (m->line, "/ack", 4))
							c = 1;
						break;
					case 's':
						nosig ^= 1;
						tprintf ("%sSignature o%s\n", stars, nosig ? "ff" : "n");
						break;
					case 'p':
						here = ftell (m->tfile);
						fseek (m->tfile, datastart, SEEK_SET);
						(void) sendfile (m->tfile, m->user, ASCII_TYPE, 0);
						fseek (m->tfile, here, SEEK_SET);
						tprintf ("%s\n", stars);
						break;
					case 'c':
						if (strlen (cp)) {
							cp = host_or_wpage_exp (strdup (cp), 0, 0, &dnsfound);
							(void) addlist (&cclist, cp, 0, cp);
							tprintf ("%sAdded call: %s\n", stars, cp);
							free (cp);
						}
						break;
					case 'm':
						i = atoi (cp);
						if (i && i <= m->nmsgs) {
							if (!wrotedata)	{
								(void) mbx_data (m, cclist, rhdr, returnreceipt);
								datastart = ftell (m->tfile);
								wrotedata = 1;
							}
							(void) msgtofile (m, i, m->tfile, 0, 0);
							tprintf ("%sAdded Message: %d\n", stars, i);
						}
						break;
					case 'h':
					case '?':
						bbscolorchange (m, "0B");
						(void) DisplayFile (EditorHelp, m->user);
						bbscolorchange (m, "07");
						break;
					default:
						break;
				}
				if (!c)
					continue;
			}
			if ((!strnicmp (m->line, "R:", 2)) && (*(m->line + 8) == '/')) {
				/* if we found an R: line and NOT a BBS, then an error */
				if (!(m->sid & MBX_SID))	{
					if (!rline_error)	/* only output one log line */
						log (m->user, "Holding message with R: lines by NON-PBBS %s (%s < %s)\n",
							m->name, m->to, (m->tofrom) ? m->tofrom : m->name);
					rline_error = 1;
				}
				
				/* if a BBS, but NO BID given, then an error */
				else if (m->tomsgid == NULLCHAR)	{
					if (m->stype == 'B' || !strncmp (m->to, "sysop", 5))	{	/* bulletins always need a BID if R: lines */
						if (!rline_error)	/* only output one log line */
							log (m->user, "Holding message with no BID, but R: lines by %s (%s < %s)\n",
								m->name, m->to, (m->tofrom) ? m->tofrom : m->name);
						rline_error = 1;
					} else	{	/* all other types will log a warning, but allow it */
						if (!rline_warning)	/* only output one log line */
							log (m->user, "Non-bulletin received with no BID/MID, but R: lines by %s (%s < %s)\n",
								m->name, m->to, (m->tofrom) ? m->tofrom : m->name);
						rline_warning = 1;
					}
				}
			}

#ifdef RLINE
			if (check_r || check_r2) {
				kwait (NULL);	/* just to be nice to others */
#ifdef ALTERBID
				if (m->tomsgid && chkBidAltered && (m->stype == 'B' || (m->sid & MBX_MID))) {
					/* Find the '$:MSGID' string */
					if ((cp = strstr (m->line, "$:")) != NULLCHAR) {
						char tbuf[16];

						cp += 2;
						if (*cp == ' ')
							cp++;
						strncpy (tbuf, cp, 15);
						tbuf[15] = 0;
						if ((cp = strchr (tbuf, ' ')) != NULLCHAR)
							*cp = 0;
						if (stricmp (tbuf, m->tomsgid))	{
							if (strlen (tbuf) >= strlen (m->tomsgid))
								bidbad = 1;
							else if (strnicmp (tbuf, m->tomsgid, strlen(tbuf)))
								bidbad = 1;
						}
					}
				}
#endif
				/* Check for R: lines to start with */
				/* Invalidate R: lines that don't have R:YYMMDD/ */
				if ((!strnicmp (m->line, "R:", 2)) && (*(m->line + 8) == '/')) {	/* found one */
					found_r = 1;
					/*Write this line to the second tempfile
					 *for later rewriting to the real one   */
					fprintf (m->tfp, "%s\n", m->line);
					/* Find the '@[:]CALL.STATE.COUNTRY'or
					 * or the '?[:]CALL.STATE.COUNTRY' string
					 * The : is optional.     */
					if (((cp = strchr (m->line, '@')) != NULLCHAR) || ((cp = strchr (m->line, '?')) != NULLCHAR)) {
						if ((cp2 = strpbrk (cp, " \n\t")) != NULLCHAR)
							*cp2 = '\0';
						/* Some bbs's send @bbs instead of @:bbs*/
						if (*++cp == ':')
							cp++;
						kwait (NULL);	/* just to be nice to others */
						/* if we use 'return addres' copy whole 'domain' name */
						if (Rreturn)
							if ((strlen (cp) <= OBLEN) && (strlen (cp)))
								strncpy (origbbs, cp, OBLEN);
						/* Optimize forwarding ? */
						if (Rfwdcheck || Mbloophold) {
							/*if there is a HADDRESS, cut off after '.'*/
							if ((cp2 = strchr (cp, '.')) != NULLCHAR)
								*cp2 = '\0';
							if (Mbloophold)
								/* check to see if this is my call ! */
								if (!stricmp (Me, cp))
									loops++;
							/*cross-check with MyFwds list*/
							if (Rfwdcheck) {
								if (stricmp (cp, m->name)) {
									for (i = 0; i < Numfwds; i++) {
										if (!stricmp (MyFwds[i].name, cp)) {
											/*Found one !*/
											strncpy (fwdbbs[myfwds++], cp, 20);
											break;
										}
									}
								}
							}
						}
					}
					if (Rdate) {
						/* Find the 'R:yymmdd/hhmmz' string */
						if ((cp = strchr (m->line, ' ')) != NULLCHAR) {
							*cp = '\0';
							if (strlen (m->line + 2) <= ODLEN) {
								strncpy (origdate, m->line + 2, ODLEN - 1);
								origdate[ODLEN - 1] = 0;
							}
						}
					}
				} else {	/* if not R: line */
					/* The previous line was last R: line
					 * so we're done checking
					 * now write the smtp headers and
					 * all saved R: lines to the right tempfile	*/
					if (check_r) {
						check_r = 0;
					}
					check_r2 = 0;
					/*Did we actually find one ?*/
					if (found_r) {
						if (Rreturn)
							m->origbbs = strdup (strlwr (origbbs));
						if (Rdate) {
							if ((cp = strchr (origdate, '/')) != NULLCHAR) {
								if ((*(cp + 5) == 'z') || (*(cp + 5) == 'Z')) {
									*(cp + 5) = '\0';
									zulu = 1;
								}
								t.tm_min = atoi (cp + 3);
								*(cp + 3) = '\0';
								t.tm_hour = atoi (cp + 1);
								*cp = '\0';
								t.tm_mday = atoi (&origdate[4]);
								origdate[4] = '\0';
								t.tm_mon = (atoi (&origdate[2]) - 1);
								origdate[2] = '\0';
								t.tm_year = atoi (origdate);
								/* Set the date in rfc 822 format */
								m->date = mallocw (40);
								sprintf (m->date, "%.2d %s %02d %02d:%02d:00 %.3s\n",
									 t.tm_mday, Months[t.tm_mon], t.tm_year, t.tm_hour, t.tm_min, zulu ? "UTC" : "");
							}
						}
					}
#ifdef HOLDMODS
					if (Holdit)
						fprintf (m->tfile, "%sNaughty word\n", Hdrs[XBBSHOLD]);
#endif
					/* Now write the headers,
					 * possibly adding Xforwarded lines for bulletins,
					 * or anything that has a BID.
					 * Add the X-Forwarded lines FIRST,
					 * this speeds up forwarding...  */
#ifdef ALTERBID
					if (chkBidAltered && bidbad)
						fprintf (m->tfile, "%sBid altered\n", Hdrs[XBBSHOLD]);
#endif
					if (rline_error)
						fprintf (m->tfile, "%sRline error\n", Hdrs[XBBSHOLD]);
					if (Mbloophold && loops >= Mbloophold)
						fprintf (m->tfile, "%sLoop\n", Hdrs[XBBSHOLD]);
					if (Rfwdcheck && found_r && ((m->stype == 'B') || (m->tomsgid))) {
						/*write Xforwarded headers*/
						for (i = 0; i < myfwds; i++)
							fprintf (m->tfile, "%s%s\n", Hdrs[XFORWARD], fwdbbs[i]);
					}
					/*write regular headers*/
					if (!wrotedata) {
						(void) mbx_data (m, cclist, NULLCHAR, returnreceipt);
						datastart = ftell (m->tfile);
						wrotedata = 1;
					}
					kwait (NULL);	/* just to be nice to others */

					/* Now copy the R: lines back */
					if (found_r) {
						char tmpline[MBXLINE];

						rewind (m->tfp);
						while (fgets (tmpline, sizeof (tmpline), m->tfp) != NULLCHAR) {
							kwait (NULL);	/* just to be nice to others */
							fputs (tmpline, m->tfile);
						}
					}
					if (m->tfp)
						MYfclose (m->tfp);
					/* And add this first non-R: line */
					fprintf (m->tfile, "%s\n", m->line);
				}
			} else {/* not check_r or check_r2 */
#endif
				if (!wrotedata)	{
					if (rline_error)
						fprintf (m->tfile, "%sRline error\n", Hdrs[XBBSHOLD]);
					(void) mbx_data (m, cclist, rhdr, returnreceipt);
					datastart = ftell (m->tfile);
					wrotedata = 1;
				}
				
				fprintf (m->tfile, "%s", m->line);
				if (recvsize != -2)
					(void) putc ('\n', m->tfile);
#ifdef RLINE
			}
#endif
		} else {	/* CTLZ or /ex */
#ifdef RLINE
			if (check_r) {
				/* Hmm, this means we never finished the R: headers
				 * tmp file still open !         */
				MYfclose (m->tfp);
			}
#endif
			done = 1;	/* To indicate the difference between
       	                           * mbxrecvline() returning -1 and /ex ! - WG7J
               	                   * Now also used to indicate if the message should
                       	           * be sent or not !    */
			/* Now ask users if they want to send this ! - WG7J */
			if (Mbsendquery && !(m->sid & MBX_SID)) {
				if (m->type == TELNET || m->type == TIP)
					c = tkeywait (sendthemail, 0, m->linemode);
				else	/* For AX.25 and NET/ROM connects */
					c = mykeywait (sendthemail, m);
				if (c == -1 || c == 'n' || c == 'N') {
					done = 0;	/* signal delete of message */
					tputs (MsgAborted);
				}
			}
			break;	/* all done */
		}
	}
	if (!done && !foundCTLz) {
		/* We did NOT get ^Z or /EX, but mbxrecvline returned -1 !!!
		 * This means the connection is gone ! - WG7J	*/
		MYfclose (m->tfile);
#ifdef RLINE
		if (check_r)
			MYfclose (m->tfp);
#endif
		del_list (cclist);
		/*     	        free(rhdr); *//*Just in case*/
		return -1;
	}
	m->inmessage = 0;
	if (!wrotedata)
		(void) mbx_data (m, cclist, rhdr, returnreceipt);

	if (m->stype == 'F') {	/* type == 'F' */
		tputs (Insertingoriginal);
		usflush (m->user);
		fprintf (m->tfile, Forwardedmessage);
		(void) msgtofile (m, m->current, m->tfile, 0, 0);
		fprintf (m->tfile, Endforwardedmessage);
	}
	/* Insert customized signature if one is found */
	if (!nosig && !(m->sid & MBX_SID)) {	/* not a forwarding BBS */
		char sigwork[LINELEN];

		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) {
				kwait (NULL);	/* just to be nice to others */
				fputs (sigwork, m->tfile);
			}
			(void) 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 */

#ifdef ALTERBID
	if (bidbad) {
		if (!holdBidAltered) {
			/* instead of holding it, we are to throw it away */
			log (m->user, "Dropping message with altered BID: %s\n", m->tomsgid);
			del_list (cclist);
			MYfclose (m->tfile);
			kwait (NULL);
			return 0;
		}
	}
#endif
	{
		char fullfrom[MBXLINE];

		/* make up full from name for work file */
		if (m->tofrom != NULLCHAR)
			sprintf (fullfrom, "%s%%%s@%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 */
			kwait (NULL);	/* just to be nice to others */
#ifdef HOLDMODS
			if (Holdit != 0) {
				fail += holdjob (m->tfile, Hostname, cclist, fullfrom);
				rewind (m->tfile);
				(void) rdaemon (m->tfile, NULLCHAR, NULLCHAR, NULLCHAR, "Message with naughty word in hqueue", 'P', 0);
			} else
#endif		
			fail = queuejob (m->tfile, Hostname, cclist, fullfrom);
			del_list (cclist);
			cclist = NULLLIST;
		}
		(void) addlist (&cclist, m->to, 0, m->to);
		fseek (m->tfile, 0L, 0);
		kwait (NULL);	/* just to be nice to others */
#ifdef HOLDMODS
		if (Holdit != 0) {
			fail += holdjob (m->tfile, host, cclist, fullfrom);
			rewind (m->tfile);
			(void) rdaemon (m->tfile, NULLCHAR, NULLCHAR, NULLCHAR, "Message with naughty word in hqueue", 'P', 0);
		} else
#endif
		fail += queuejob (m->tfile, host, cclist, fullfrom);
	}
	del_list (cclist);
	messagesize = (long) filelength (fileno (m->tfile));
	MYfclose (m->tfile);
	if (fail) {
		if (!(m->sid & MBX_SID))	/* only when we're not a bbs */
			tputs ("Couldn't queue message for delivery\n");
	} else {
		if (!(m->sid & MBX_SID))	/* only when we're not a bbs */
			tputs ("Message queued\n");
		if (m->sid & MBX_SID)
			MbRecvd++;
		else {
			MbSent++;
			if ((MbHolding && m->stype == 'B') || MbHoldall ||
#ifdef REJECT
			    rejecthold ||
#endif
			    (m->sid & MBX_HOLD) || (m->privs & HOLD_MAIL))
				tprintf ("%ss are held till reviewed by the SYSOP\n", (m->stype == 'B') ? "Bulletin" : "Message");
		}
		kwait (NULL);	/* just to be nice to others */
	}
	if (m->sid & MBX_SID) {
#ifdef STATS_FWD
		STATS_addfwd (0, 1, m->name);
#endif
		m->mysize = ((m->mysize + 1) % 10);
		if (!m->mysize)
			smtptick (NULL);	/* wake SMTP to send that mail */
	} else {
		char buf[50], *cpp;

		sprintf (buf, "%s",
#ifdef MBFWD
			 (BIDsuffix) ? BIDsuffix :
#endif
			 Hostname);
		if ((cpp = strchr (buf, '.')) != NULLCHAR)
			*cpp = '\0';
		(void) strupr (buf);
		tprintf ("\n*** Message forwarding to: %s\n", m->to);
#ifdef MBFWD
		tputs ("*** MID: ");
		if (m->tomsgid)
			tputs (m->tomsgid);
		else
			tprintf ("%ld_%s", lastsentid, buf);
		tprintf ("    Size: %ld bytes\n\n", messagesize);
#else
		tprintf ("*** Size: %ld bytes\n\n", messagesize);
#endif
		/* Instead of kicking off the smtp now, let it happen in a short time.
		 * That way the user gets the prompt back immediately, and the smtp kick
		 * will run when the user most likely is working on typing the next command.
		 * this *should* improve the user's perception of system's speed, and
		 * might speed up bbs forwarding a bit too... :-) - WG7J
		 */
		{
#ifdef UNIX
#define WAITTM 500L
#else
#define WAITTM 5000L
#endif
			long tt;
			int32 old;

			stop_timer (&Smtpcli_t);
			tt = read_timer (&Smtpcli_t);
			old = Smtpcli_t.duration;
			if (tt <= 0 || tt > WAITTM)
				set_timer (&Smtpcli_t, WAITTM);	/* set timer duration */
			if (Smtpcli_t.func == NULL) {	/* in case not set yet */
				Smtpcli_t.func = (void (*)(void *)) smtptick;	/* what to call on timeout */
				Smtpcli_t.arg = NULL;	/* dummy value */
			}
			start_detached_timer (&Smtpcli_t);	/* and fire it up */
			Smtpcli_t.duration = old;
		}
#ifdef nope
		smtptick (NULL);/* wake SMTP to send that mail */
#endif
	}
	kwait (NULL);
	return 0;
}
#endif



#if defined(NETROM) && defined(GATECMDS)
static int
dombnrnodes (int argc, char *argv[], void *p)
{
	if (argc < 2)
		return doroutedump ();
	if (*argv[1] == '*')
		argc = 1;
	return dorouteinfo (argc, argv, p);
}
#endif



#ifdef MAILCMDS
static int
dosid (int argc, char *argv[], void *p)
{
struct mbx *m;
char *cp, *flags;
#ifdef LZW
struct usock *up;
#endif

	m = (struct mbx *) p;
	if (argc == 1)
		return 1;
	if (argv[1][strlen (argv[1]) - 1] != ']')	/* must be an SID */
		return 1;
	m->usecolor = 0;
#ifdef notdef
	if (m->stype == 'Z' && strnicmp (argv[1], "cz", 2) == 0) {
		/* LAN-LINK's [ZCZ] */
		m->sid |= MBX_LL;
		return 0;
	}
#endif
	/* Other bbs's */
	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' && strnicmp (argv[1], "li", 2) == 0)	/* [RLI] at a minimum */
		m->sid |= MBX_RLI_SID;
	else if (m->stype == 'F' && strnicmp (argv[1], "bb", 2) == 0)	/* [FBB] at a minimum */
		m->sid |= MBX_FBB;


	/* Check to see if the BBS supports a kludge called "hierarchical
	 * routing designators."
	 *
	 * No need to check for ']' -- it must be there or this is not
	 * a valid mbox id -- it is checked earlier (fix de OH3LKU)
	 *
	 * Sid format is [BBSTYPE-VERSION-OPTIONS]
	 * check for LAST -, to allow for version portion. - WG7J
	 */
	flags = strrchr (argv[1], '-');
	if (flags != NULLCHAR && (cp = strchr (flags + 1, 'h')) != NULLCHAR && strchr (cp + 1, '$'))
		m->sid |= MBX_HIER_SID;

	if (flags != NULLCHAR && (cp = strchr (flags + 1, 'c')) != NULLCHAR)
		m->sid |= MBX_CLOCK;

#ifdef LZW
	/* Check to see if it's another TNOS board */
	if (flags != NULLCHAR && (cp = strchr (flags + 1, 't')) != NULLCHAR) {
		if (m->state != MBX_FORWARD) {	/* if forwarding, wait till we can send out SSID */
			up = itop (m->user);
			if (up->zout == NULLLZW)
				togglelzw (m->user, 1);
		}
		m->sid |= MBX_TNOS;
	}
#endif
#if 0				/* def HTTPPBBS */
	/* Check to see if it supports the HTTP/PBBS extensions */
	if (flags != NULLCHAR && (cp = strchr (flags + 1, 'w')) != NULLCHAR)
		m->sid |= MBX_HTTP;
#endif
#ifdef XFWD
	/* Check to see if it supports the X-forwarding protocol */
	if (flags != NULLCHAR && (cp = strchr (flags + 1, 'x')) != NULLCHAR)
		m->sid |= MBX_XFWD;
#endif
	if (flags != NULLCHAR && (cp = strchr (flags + 1, 'm')) != NULLCHAR)
		m->sid |= MBX_MID;
#ifdef FBBFWD
	if (Mfbb && flags != NULLCHAR && (cp = strchr (flags + 1, 'f')) != NULLCHAR) {
		m->sid |= (MBX_FBBFWD | MBX_MID);
#ifdef FBBCMP
		/* according to FBB spec, the 'b' is ignored, unless the 'f'
		   is found. So we use the same criteria.... */
		if (Mfbbcmp && flags != NULLCHAR && (cp = strchr (flags + 1, 'b')) != NULLCHAR)
			m->sid |= MBX_FBBCMP;
#endif
	}
#endif

	m->mysize = 0;
	return 0;
}
#endif



#ifdef GATECMDS
static int
dombescape (int argc, char *argv[], void *p)
{
struct mbx *m;

	m = (struct mbx *) p;
	if (argc < 2) {
		tprintf ("Escape is %s, Escape char: ", (m->privs & NO_ESCAPE) ? "OFF" : "ON");
		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 {
			if (!strnicmp (argv[1], "OFF", 3) || !strnicmp (argv[1], "dis", 3))
				m->privs |= NO_ESCAPE;
			else
				m->privs &= ~NO_ESCAPE;
		}
	} else
		m->escape = *argv[1];
	return 0;
}
#endif



#ifdef MAILCMDS
/* Routine for finding out total number of messages for an area and the
   number of messages that are new to this user. Just reports NEW messages,
   does not check their read status (for personal areas). Though it USES the
   mbox structure, it returns it back to the previous state. This routine
   reads the area.ctl files, which just contain a bid for each message.  */

static void
statarea (
register struct mbx *m,
char *area,
int *total,
int *new,
int *hold,
int special
) {
register int tnew, ttotal, thold;
#ifdef USERLOG
long last, newlast;
char oldarea[64], buf[256];
FILE *fp;
long size;
struct let *lt;
register struct let *cmsg;
register int i;
#endif

	ttotal = tnew = thold = 0;
#ifdef USERLOG
	last = m->lastread;
	newlast = m->newlastread;
	m->lastread = 0;
	strncpy (oldarea, m->area, 64);
	strncpy (m->area, area, 64);
	(void) strlwr (m->area);
	if (!lockit (m)) {
		/* only read last read message-id if this is not a bbs,
		 * current area is a public area and not 'help'
		 * or area starts with 'sys'
		 * read in all cases if a SYSOP
		 */

		if (!(m->sid & MBX_SID))
			if ((stricmp (m->area, "help") && isarea (m->area)) || !strnicmp (m->area, "sys", 3) || (m->privs & SYSOP_CMD))
				getlastread (m);
		kwait (NULL);
		sprintf (buf, "%s/control/%s", Mailspool, m->area);
		(void) nntp_name_expansion (buf);
		strcat (buf, ".ctl");
		if ((fp = subdir_fopen (buf, READ_BINARY)) != NULLFILE) {
			size = (long) filelength (fileno (fp));
			lt = (struct let *) mallocw ((unsigned) size);
			(void) fread (lt, (unsigned) size, 1, fp);
			(void) fclose (fp);
			ttotal = (int) (size / (long) sizeof (struct let));

			kwait (NULL);
			for (cmsg = lt, i = 0; i < ttotal; i++, cmsg++)
				if (!special) {
					if ((cmsg->bid > m->lastread) && !(cmsg->status & BM_DELETE))
						tnew += 1;
					if (cmsg->status & BM_ONHOLD)
						thold += 1;
				} else {
					if (!(cmsg->status & BM_READ))
						tnew += 1;
					if (cmsg->status & BM_ONHOLD)
						thold += 1;
				}
			free (lt);
		}
		kwait (NULL);
		m->lastread = last;
		m->newlastread = newlast;
		rmlock (Mailspool, m->area);
		strncpy (m->area, oldarea, 64);
	}
#endif
	*new = tnew;
	*total = ttotal;
	*hold = thold;
}



int
doarea (int argc, char *argv[], void *p)
{
struct mbx *m;
char *cp, *area, *tmparea;
const char *theone;
FILE *fp;
char buf[MBXLINE];
int k;
int hold, total, new, xtotal = 0, xnew = 0, xhold = 0;
struct ffblk ff;

	m = (struct mbx *) p;
	if ((m->stype == 'F') && m->usecolor && !access (ANSIArea, 0)) {
		colorfile (ANSIArea, m->colorset);
		return 0;
	}
	if (argc < 2 || m->stype == 'N' || m->stype == 'S') {
		area = strdup (m->area);
		bbscolorcls (m);
		if (m->stype == 'H')
			tprintf ("Summary of areas with held messages\n\n");
		else	{
			tmparea = strdup (m->area);
			while ((cp = strchr (tmparea, '/')) != NULLCHAR)
				*cp = '.';
			tprintf ("Current message area is: %s\n\n", tmparea);
			free (tmparea);

			bbscolorchange (m, "0B");
			tputs ("Available areas are:\n");

			total = m->nmsgs;
			new = m->newmsgs;
			if (stricmp (m->area, m->name)) {
				changearea (m, m->name, (int) 1);
#ifdef STATS_AREA
				STATS_addarea (3, -1, m->area);
#endif
			} else
				scanmail (m);
			if ((m->stype == 'N' && m->newmsgs) || m->stype != 'N') {
				bbscolorchange (m, "09");
				tprintf ("%-15s  ", m->name);
			}
			if (m->stype == 'F')
				tputs ("  Your private mail area\n");
			else if ((m->stype == 'S') || (m->stype == 'N' && m->newmsgs)) {
				bbscolorchange (m, "07");
				tprintf ((m->hmsgs) ? SMessageData : MessageData, m->nmsgs,
				   m->nmsgs == 1 ? " " : "s", m->newmsgs, m->hmsgs);
				/*			tprintf(MessageData, total,total == 1 ? " " : "s", new); */
				xtotal += total;
				xnew += new;
			} else if (m->stype != 'N')
				tputc ('\n');
		}
		theone = Arealist;
		if ((m->privs & SYSOP_CMD) && (!access (AreaSlist, 0)))
			theone = AreaSlist;
		if ((fp = fopen (theone, READ_TEXT)) == NULLFILE)
			return 0;
		if (m->stype == 'F') {
			(void) sendfile (fp, m->user, ASCII_TYPE, 0);
			tputc ('\n');
		} else if (m->stype == 'S' || m->stype == 'N' || m->stype == 'H') {
			while (fgets (buf, MBXLINE, fp) != NULLCHAR) {
				kwait (NULL);
				if (isalnum (buf[0])) {	/* skip comments */
					if ((cp = strpbrk (buf, " \t\n\r")) != NULLCHAR)
						*cp = '\0';
					statarea (m, buf, &total, &new, &hold, 0);
					if (m->stype == 'S' || (m->stype != 'H' && new) || (m->stype == 'H' && hold)) {
						bbscolorchange (m, "09");
						tprintf ("%-15.15s  ", buf);
						bbscolorchange (m, "07");
						if ((m->privs & SYSOP_CMD) && hold)
							tprintf (SMessageData, total,
								 total == 1 ? " " : "s", new, hold);
						else
							tprintf (MessageData, total,
								 total == 1 ? " " : "s", new);
#if 0
						usflush (Curproc->output);
#endif
						xtotal += total;
						xnew += new;
						xhold += hold;
					}
				}
			}
			if (((m->privs & SYSOP_CMD) && argc > 1 && argv[1][0] == 'a') || m->stype == 'H') {	/* also list mailfor's if 'AN|S all' or 'AH' */
				sprintf (buf, "%s/*.txt", Mailspool);
				if (findfirst (buf, &ff, 0) == 0) {
					do {
						char *ctmp;
						kwait (NULL);	/* Let others run */
						ctmp = strchr (ff.ff_name, '.');
						if (ctmp)
							*ctmp = '\0';
						/*must be private mail area, and not on exclude list !*/
						if (!issysarea (ff.ff_name)) {
							if (!stricmp (ff.ff_name, m->name))
								continue;
							statarea (m, ff.ff_name, &total, &new, &hold, 1);
							if (m->stype == 'S' || (m->stype != 'H' && new) || (m->stype == 'H' && hold)) {
								bbscolorchange (m, "09");
								tprintf ("%-15.15s  ", ff.ff_name);
								bbscolorchange (m, "07");
								if (hold)
									tprintf (SMessageData, total,
										 total == 1 ? " " : "s", new, hold);
								else
									tprintf (MessageData, total,
										 total == 1 ? " " : "s", new);
#if 0
								usflush (Curproc->output);
#endif
								xtotal += total;
								xnew += new;
								xhold += hold;
							}
						}
					} while (findnext (&ff) == 0);
				}
			}
			bbscolorchange (m, "0B");
			k = 42;
			if ((m->privs & SYSOP_CMD) && xhold)
				k += 15;
			while (k--)
				tputc ('-');
			tputs ("\n*** Total ***   ");
			if (xtotal < 10000)
				tputc (' ');
			if ((m->privs & SYSOP_CMD) && xhold)
				tprintf (SMessageData, xtotal, xtotal == 1 ? " " : "s", xnew, xhold);
			else
				tprintf (MessageData, xtotal, xtotal == 1 ? " " : "s", xnew);
			usflush (Curproc->output);
			tputc ('\n');
		} else {
			/* send only the area names, not the description */
			k = 0;
			while (fgets (buf, MBXLINE, fp) != NULLCHAR) {
				if (isalnum (buf[0])) {	/* skip comments */
					if ((cp = strpbrk (buf, " \t\n\r")) != NULLCHAR)
						*cp = '\0';
					tprintf ("%-15.15s", buf);
					if (++k == 5) {
						tputc ('\n');
						k = 0;
					}
				}
			}
			if (k)
				tputc ('\n');
			bbscolorchange (m, "0A");
			tputs ("\nType AF to get description of areas\n\n");
		}
		if (stricmp (area, m->area)) {
			changearea (m, area, (int) 0);
#ifdef STATS_AREA
			STATS_addarea (3, -1, area);
#endif
		}
		free (area);
		(void) fclose (fp);
		return 0;
	}
	if ((m->privs & SYSOP_CMD) || stricmp (m->name, argv[1]) == 0) {
		/*Private mail-areas*/
		changearea (m, argv[1], (int) 1);
		if (stricmp (m->name, m->area) && !(m->privs & IS_BBS) && !(m->sid & MBX_EXPERT))
			dohelper (NULLCHAR, (struct cmds *) 0, NULLCHAR, AreaData, m->area);
		if (m->nmsgs && !(m->privs & IS_BBS)) {
			if (!stricmp (m->name, m->area))
				tputs ("You have ");
			else {
				area = strdup (m->area);
				while ((cp = strchr (area, '/')) != NULLCHAR)
					*cp = '.';
				tprintf ("%s: ", area);
				free (area);
			}
			tprintf ((m->hmsgs) ? SMessageData : MessageData, m->nmsgs,
			   m->nmsgs == 1 ? " " : "s", m->newmsgs, m->hmsgs);
		}
#ifdef TUTOR
		sprintf (m->line, areaparam, m->area);
		mbscripthook (m, areahook);
#endif

		if (m->stype == 'L') {
			m->stype = ' ';
			(void) dolistnotes (0, 0, m);
		}
		return 0;
	}
	if (isarea (argv[1])) {
		/*Public mail areas*/
		changearea (m, argv[1], (int) 1);
		if (!(m->privs & IS_BBS) && !(m->sid & MBX_EXPERT))
			dohelper (NULLCHAR, (struct cmds *) 0, NULLCHAR, AreaData, m->area);
		area = strdup (m->area);
		while ((cp = strchr (area, '/')) != NULLCHAR)
			*cp = '.';
		if (!(m->privs & IS_BBS))
#ifdef USERLOG
			tprintf ("%s: %d message%s -  %d new\n", area, m->nmsgs,
				 m->nmsgs == 1 ? " " : "s ", m->newmsgs);
#else
			tprintf ("%s: %d message%s.\n", area, m->nmsgs,
				 m->nmsgs == 1 ? "" : "s");
#endif
		free (area);
#ifdef TUTOR
		sprintf (m->line, areaparam, m->area);
		mbscripthook (m, areahook);
#endif
		if (m->stype == 'L') {
			m->stype = ' ';
			(void) dolistnotes (0, 0, m);
		}
	} else
		tprintf ("No such message area: %s\n", argv[1]);
	return 0;
}
#endif



char *
nntp_name_expansion (char *name)
{
#if 1
char *cp;

	while ((cp = strpbrk (&name[1], ".\\")) != NULLCHAR)
		*cp = '/';
#endif
	return name;
}



#ifdef MAILCMDS

/* subroutine to do the actual switch from one area to another */
/* USERLOGGING added by WG7J */
void
changearea (struct mbx *m, char *area, int setit)
{
#ifdef USERLOG
	if (setit)
		setlastread (m);
#endif
	if (stricmp (area, m->area)) {	/* only if a different area */
#ifdef STATS_AREA
		STATS_addarea (3, 1, area);
#endif
		(void) closenotes (m, 0);
		m->nmsgs = m->newmsgs = m->newmsgsonentry = m->current = 0;
		strncpy (m->area, area, 64);
		(void) strlwr (m->area);
		(void) nntp_name_expansion (m->area);

#ifdef USERLOG
		/* only read last read message-id if this is not a bbs,
		 * current area is a public area and not 'help'
		 * or area starts with 'sys'
		 * read in all cases if a SYSOP
		 */
		if (!(m->sid & MBX_SID))
			if ((stricmp (m->area, "help") && isarea (m->area)) || !strnicmp (m->area, "sys", 3) || (m->privs & SYSOP_CMD))
				getlastread (m);
#endif
		scanmail (m);
	}
}
#endif



#if defined(GATECMDS) || defined(FOQ_CMDS)
/* Generic mbox gateway code. It sends and frees the contents of m->startmsg
 * when the connection has been established unless it a null pointer.
 */
int
gw_connect (struct mbx *m, int s, struct sockaddr *fsocket, int len)
{
int c = 0;
char const *cp;
char *cp1;
struct proc *child;
struct gwalarm *gwa;
char *tocall, whereto[128], buf[80];
int oldblock;
#ifdef NETROM
char *node;
char temp[AXBUF];
struct nrroute_tab *rp;
#endif

	(void) sockmode (s, SOCK_ASCII);
	sprintf (buf, "gw supervisor: %s", m->name);
	child = newproc (buf, 256, gw_superv, 0, Curproc, m, 0);
	if ((m->privs & (SYS_CHAT | ALL_AREAS)) != (SYS_CHAT | ALL_AREAS))
		if (!m->quickfile && !(m->authenticated & 0x10))
			tputs ("Trying...");

	if (!m->quickfile && !(m->authenticated & 0x10)) {
		if (m->privs & NO_ESCAPE)
			tputc ('\n');
		else {
			tputs ("  The escape character is: ");
			if (m->escape < 32)
				tprintf ("CTRL-%c\n", m->escape + 'A' - 1);
			else
				tprintf ("'%c'\n", m->escape);
		}
		usflush (Curproc->output);
	}
	if ((fsocket->sa_family != AF_NETROM) && (fsocket->sa_family != AF_AX25))
		m->privs &= ~NO_ESCAPE;

	/*find out where we're going to*/
	tocall = strdup (psocket (fsocket));
	if ((cp1 = strchr (tocall, ' ')) != NULLCHAR)
		*cp1 = '\0';
#ifdef NETROM
	if (fsocket->sa_family == AF_NETROM) {
		/*find the node alias*/
		(void) setcall (temp, tocall);
		if ((rp = find_nrroute (temp)) == NULLNRRTAB)
			strncpy (whereto, tocall, 128);
		else {
			node = strdup (rp->alias);
			if ((cp1 = strchr (node, ' ')) != NULLCHAR)
				*cp1 = '\0';
			sprintf (whereto, "%s:%s", node, tocall);
			free (node);
		}
	} else
#endif
		strncpy (whereto, tocall, 128);
	free (tocall);

	if (connect (s, (char *) fsocket, len) == -1) {
		if ((cp = sockerr (s)) != NULLCHAR) {
			switch (cp[0]) {
				case 'R':
					sprintf (buf, "%susy from",
						 (m->family == AF_NETROM) ? "B" : "*** b");
					break;
				case 'T':
					if (m->family != AF_NETROM) {
						sprintf (buf, "*** timeout with");
						break;
					}
					/* fall through */
				default:
					sprintf (buf, "%sailure with",
						 (m->family == AF_NETROM) ? "F" : "*** f");
					break;
			}
			if (!m->quickfile || (m->authenticated & 0x10))
				tprintf ("%s%s %s\n\n", (m->family == AF_NETROM) ? Mbnrid : "", buf, whereto);
		}
		(void) shutdown (s, 2);	/* HB9RWM suggestion */
		close_s (s);
		killproc (child);
		/* Free m->startmsg if set ! - WG7J */
		if (m->startmsg != NULLCHAR) {
			free (m->startmsg);
			m->startmsg = NULLCHAR;
		}
		if (m->privs & NO_ESCAPE)
			return 2;
		else
			return 0;
	}
	/* The user did not type the escape character */
	killproc (child);

	if ((!m->quickfile || (m->authenticated & 0x10)) && (m->privs & (SYS_CHAT | ALL_AREAS)) != (SYS_CHAT | ALL_AREAS))
		tprintf ("%s%sonnected to %s\n",
			 (m->family == AF_NETROM) ? Mbnrid : "",
			 (m->family == AF_NETROM) ? "C" : "*** c", whereto);

	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, 2 * 1000L);
	gwa->t.func = gw_alarm;
	gwa->t.arg = (void *) gwa;
	start_timer (&gwa->t);
	if (m->privs & NO_ESCAPE) {
		(void) seteol (s, "\r");
		(void) seteol (Curproc->input, "\r");
		(void) seteol (Curproc->output, "\r");
		(void) sockmode (s, SOCK_BINARY);
		(void) sockmode (Curproc->input, SOCK_BINARY);
		(void) sockmode (Curproc->output, SOCK_BINARY);
	}
	/* Fork off the receive process */
	sprintf (buf, "gw in: %s", m->name);
	child = newproc (buf, 1024, gw_input, s, m, Curproc, 0);

	oldblock = sockblock (Curproc->input, SOCK_NORXBLOCK);
	m->inmessage = 1;
#ifdef MBXTDISC
	if (!m->quickfile)
		start_timer (&m->tdisc);
#endif
	for ( ; ; ) {
		if (m->quickfile) {
			while (m->inmessage)
				kpause (100);
		} else
			while ((c = recvchar (Curproc->input)) == EOF) {
				if ((errno != EWOULDBLOCK) || !m->privs || !m->inmessage)
					break;
				kpause (100);
			}
		if (nullsocket (s) || nullsocket (Curproc->input) || (c == EOF) || !m->privs || !m->inmessage)
			break;

		if (mbxMaxTimer && (m->logontime + mbxMaxTimer) < time ((time_t *) 0))
			break;
#ifdef MBXTDISC
		if (!m->quickfile)
			start_timer (&m->tdisc);
#endif
		/* Only check ESCAPE char if that is currently turned on */
		if (!(m->privs & NO_ESCAPE) && c == m->escape) {
			if (socklen (Curproc->input, 0))
				(void) recv_mbuf (Curproc->input, NULL, 0, NULLCHAR, 0);
			break;
		}
		if (usputc (s, uchar (c)) == EOF)
			break;
	}
	(void) sockblock (Curproc->input, oldblock);
	stop_timer (&gwa->t);
	free ((char *) gwa);
	close_s (s);
	killproc (child);	/* get rid of the receive process */
	(void) sockmode (Curproc->input, SOCK_ASCII);
	if (m->family == AF_INET)
		tprintf ("%c%c%c\n", IAC, WONT, TN_ECHO);
#ifdef TTYCALL
	if (fsocket->sa_family == AF_INET &&
	    ((struct sockaddr_in *) fsocket)->sin_port == IPPORT_TTYLINK &&
	    ismyaddr (((struct sockaddr_in *) fsocket)->sin_addr.s_addr))
		return 2;	/* magic success code to avoid BBS chaining */
#endif
	if (m->privs & NO_ESCAPE)
		return 2;
	else
		return 0;
}



static void
gw_input (int s, void *mb, void *par OPTIONAL)
{
int c;
struct mbx *m;
char const *cp;
char *cp1, *cp2;
unsigned char response[4];
#if 1
struct proc *parent;

	parent = (struct proc *) par;
#endif
	m = (struct mbx *) mb;

	(void) sockblock (s, SOCK_NORXBLOCK);
	cp1 = strdup (Mbnrid);
	if ((cp2 = strchr (cp1, '}')) != NULLCHAR)
		*cp2 = '\0';
	(void) strupr (cp1);

	for ( ; ; ) {
		while ((c = recvchar (s)) == EOF) {
			if ((errno != EWOULDBLOCK) || !m->privs || !m->inmessage)
				break;
			kpause (100);
		}
		if (nullsocket (s) || nullsocket (Curproc->input) || (c == EOF) || !m->privs || !m->inmessage)
			break;

#ifdef MBXTDISC
		if (!m->quickfile)
			start_timer (&m->tdisc);
#endif
		if ((m->privs & NO_ESCAPE) || (c != IAC)) {
			if (m->quickfile)
				fputc (c, m->quickfile);
			else
				tputc (uchar (c));
			continue;
		}
		/* IAC received, get command sequence */
		c = recvchar (s);
		switch (c) {
			case WILL:
				response[0] = IAC;
				response[1] = DONT;
				response[2] = uchar(recvchar (s));
				response[3] = '\0';
				usputs (s, (char *) response);
				break;
			case WONT:
			case DONT:
				c = recvchar (s);
				break;
			case DO:
				response[0] = IAC;
				response[1] = WONT;
				response[2] = uchar(recvchar (s));
				response[3] = '\0';
				usputs (s, (char *) response);
				break;
			case IAC:	/* Escaped IAC */
				usputc (s, IAC);
				break;
			default:
				break;
		}
	}

	if ((cp = sockerr (s)) != NULLCHAR && m->family != AF_NETROM) {
		if (!m->quickfile) {
			switch (cp[0]) {
				case 'T':
					usprintf (m->user, "\n*** %s: Link failure", cp1);
					break;
				case 'R':
					usprintf (m->user, "*** DM received");
					break;
				default:
					break;
			}
		}
	}
	if (!m->quickfile && !(m->authenticated & 0x10) && (m->privs & (SYS_CHAT | ALL_AREAS)) != (SYS_CHAT | ALL_AREAS))
		usprintf (m->user, "\n%s%seconnected to %s\n\n",
			  (m->family == AF_NETROM) ? Mbnrid : "",
			  (m->family == AF_NETROM) ? "R" : "*** r", cp1);

	free (cp1);
	cp1 = NULLCHAR;

	/* Tell the parent that we are no longer connected */
#if 1
	alert (parent, ENOTCONN);
#endif
	m->inmessage = 0;
	kwait (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 (int null OPTIONAL, 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 (!(m->privs & NO_ESCAPE) && (c == m->escape)) {
			/* flush anything in the input queue */
			if (socklen (Curproc->input, 0))
				(void) recv_mbuf (Curproc->input, NULL, 0, NULLCHAR, 0);
			break;
		}
	alert (parent, EINTR);	/* Tell the parent to quit */
	kwait (Curproc);	/* Please kill me */
}



static void
gw_alarm (void *p)
{
struct gwalarm *gwa = (struct gwalarm *) p;
char oldbl;
#if 0
struct usock *up;
#endif

	/* 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 0
	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;
	}
#else
	oldbl = (char) sockblock (gwa->s1, SOCK_NOTXBLOCK);
	usflush (gwa->s1);
	(void) sockblock (gwa->s1, oldbl);
	oldbl = (char) sockblock (gwa->s2, SOCK_NOTXBLOCK);
	usflush (gwa->s2);
	(void) sockblock (gwa->s2, oldbl);
#endif
	start_timer (&gwa->t);
}
#endif



#ifdef MAILCMDS
/* 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 (int argc, char *argv[], void *p)
{
register char *cp;
register struct mbx *m;
register int state, i;
char *user = NULLCHAR, *host = NULLCHAR, *from = NULLCHAR, *msgid = NULLCHAR;
int userlen = 0, hostlen = 0, fromlen = 0, msgidlen = 0;

	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;
	free (m->origbbs);
	m->origbbs = NULLCHAR;
#ifdef FBBCMP
	/* if we're doing a compressed forwarded message.... we don't free the subject. */
	if (!Mfbbcmp && !(m->sid & MBX_FBBCMP)) {
#endif
		free (m->subject);
		m->subject = NULLCHAR;
#ifdef FBBCMP
	}
#endif
	free (m->date);
	m->date = NULLCHAR;

	if (argc == 1)
		return -1;

	i = 1;
	cp = argv[i];
	state = LOOK_FOR_USER;
	while (state < FINAL_STATE) {
		switch (state) {
			case LOOK_FOR_USER:
#if 0
				if (*cp == '@' || *cp == '<' || *cp == '$') {
#else
				if (*cp == '@') {
#endif
					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;
#if 0
					case '<':
						state = LOOK_FOR_FROM;	/* from name should follow */
						break;
					case '$':
						state = LOOK_FOR_MSGID;	/* message id should follow */
						break;
#endif
					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 0
				if (*cp == '@' || *cp == '<' || *cp == '$') {
#else
				if (*cp == '@') {
#endif
					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;
#if 0
					case '<':
						state = LOOK_FOR_FROM;	/* fromname follows */
						break;
					case '$':
						state = LOOK_FOR_MSGID;	/* message id follows */
						break;
#endif
					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;
#if 0
					case '<':
						state = ERROR_STATE;	/* user@host <foo< */
						break;
					case '$':
						state = LOOK_FOR_MSGID;	/* message id follows */
						break;
#endif
					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 ((unsigned) (userlen + hostlen + 2));

	if (user == NULLCHAR)
		return -1;

	strncpy (m->to, user, (unsigned) userlen);
	m->to[userlen] = '\0';

	if (host != NULLCHAR && hostlen) {
		m->to[userlen] = '@';
		strncpy (m->to + userlen + 1, host, (unsigned) hostlen);
		m->to[userlen + hostlen + 1] = '\0';
	}
	if (from != NULLCHAR && fromlen) {
		m->tofrom = mallocw ((unsigned) (fromlen + 1));
		strncpy (m->tofrom, from, (unsigned) fromlen);
		m->tofrom[fromlen] = '\0';
	}
	if (msgid != NULLCHAR && msgidlen) {
		m->tomsgid = mallocw ((unsigned) (msgidlen + 1));
		strncpy (m->tomsgid, msgid, (unsigned) 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.
 */
int
mbx_data (
struct mbx *m,
struct list *cclist,		/* list of carbon copy recipients */
char *extra,			/* optional extra header lines */
int returnreceipt OPTIONAL
) {
time_t t;
struct list *ap;
int cccnt = 0;
char buf[80];
char stype, *cp = NULLCHAR;

	stype = m->stype;
	if (isspace (stype))
		stype = isarea (m->to) ? 'B' : 'P';
	else if (stype == 'R' || stype == 'F')
		stype = 'P';
	fprintf (m->tfile, "%s%c\n", Hdrs[BBSTYPE], stype);

	if ((m->sid & MBX_HOLD) || (m->privs & HOLD_MAIL))
		fprintf (m->tfile, "%sYes\n", Hdrs[XBBSHOLD]);

#ifdef UKIP
	/* Modifications for UKIP <> AX25 BBS compatibility area */
	/* If the message is from a BBS add X-BID and X-BBS-TO   */
	if (m->sid & MBX_SID) { 
		fprintf (m->tfile, "%s%s\n", Hdrs[XBID], m->tomsgid);
		if ((stype == 'B') && (m->origto != NULLCHAR))
			fprintf (m->tfile, "%s%s\n", Hdrs[XBBSTO], m->origto);
	}
#endif
	
	/* If m->date is set, use this one (comes from bbs-forwarded mail) */
	if (m->date != NULLCHAR)
		fprintf (m->tfile, "%s%s", Hdrs[DATE], m->date);
	else {
		(void) time (&t);
		fprintf (m->tfile, "%s%s", Hdrs[DATE], ptime (&t));
	}

	/* Bulletin ID, if any */
	fprintf (m->tfile, Hdrs[MSGID]);
	if (m->tomsgid)
		fprintf (m->tfile,
#ifdef MBFWD
			 (BIDuknos) ? "<%s@hamradio>\n" :
#endif
			 "<%s@%s.bbs>\n", m->tomsgid, m->name);
	else {
		lastsentid = get_msgid (1);
		fprintf (m->tfile,
#ifdef MBFWD
			 (BIDuknos) ? "<%ld_%s@hamradio>\n" :
#endif
			 "<%ld@%s>\n", lastsentid,
#ifdef MBFWD
			 (BIDsuffix) ? BIDsuffix :
#endif
			 Hostname);
	}

	/* From : , could use 'real bbs address', if origbbs is set */
	fprintf (m->tfile, Hdrs[FROM]);
	if (m->tofrom) {	/* BBS style '< call' */
		if (m->origbbs != NULLCHAR)
			sprintf (buf, "%s@%s\n", m->tofrom, m->origbbs);
		else
			sprintf (buf, "%s%%%s@%s\n", m->tofrom, m->name, Hostname);
	} else {
		if (m->origbbs != NULLCHAR)
			sprintf (buf, "%s@%s\n", m->name, m->origbbs);
		else
			sprintf (buf, "%s@%s ", m->name, Hostname);
	}

#ifdef WPAGES
	if (m->origbbs != NULLCHAR) {
		char *cptr;

		cptr = strdup (buf);
		rip (cptr);
		wpageAdd (cptr, 0, 1, 'G');	/* update users file */
		free (cptr);
	}
#endif
	if (!m->tofrom && m->origbbs == NULLCHAR) {
		if (m->realname && strcmp (m->realname, "()")) {
			strcat (buf, m->realname);
		}
		strcat (buf, "\n");
	}
	fputs (buf, m->tfile);
	if ((m->origto != NULLCHAR) && ((cp = strchr (m->origto, '(')) != 0))
		*(cp - 1) = 0;
	fprintf (m->tfile, "%s%s\n", Hdrs[TO], (m->origto != NULLCHAR) ? m->origto : m->to);
#if 0
#ifdef WPAGES
	wpageAdd ((m->origto != NULLCHAR) ? m->origto : m->to, 0, 1, 'G');	/* update users file */
#endif
#endif

	if (cp)
		*(cp - 1) = ' ';

	if (m->change != -1) {
		/* 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 + (int) strlen (ap->val) > 80 - 3) {
				fprintf (m->tfile, "\n    ");
				cccnt = 4;
			}
			fputs (ap->val, m->tfile);
			cccnt += (int) strlen (ap->val);
		}
		if (cccnt)
			fputc ('\n', m->tfile);
	}
	fprintf (m->tfile, "%s%s\n", Hdrs[SUBJECT], m->subject);

	if (extra != NULLCHAR)
		fprintf (m->tfile, extra);

	/*Finish smtp headers*/
	fprintf (m->tfile, "\n");

#ifdef MBFWD
	/* RFC headers in data area, unless this is a "SC" message */
	if (m->stype != 'C' && Smtpheaders && m->origbbs == NULLCHAR) {
		char *cpp;

		fprintf (m->tfile, Hdrs[MSGID]);
		if (m->tomsgid)
			fprintf (m->tfile, "%s\n", m->tomsgid);
		else {
			strncpy (buf, (BIDsuffix) ? BIDsuffix : Hostname, 80);
			if ((cpp = strchr (buf, '.')) != NULLCHAR)
				*cpp = '\0';
			fprintf (m->tfile, "%ld_%s%s\n", lastsentid, buf,
				 (BIDuknos) ? "@hamradio" : "");
		}

		/* Add a return receipt line, if requested */
		if (returnreceipt) {
			(void) pax25 (buf, Mycall);
			if ((cpp = strpbrk (buf, DIGI_IDS)) != NULLCHAR)
				*cpp = '\0';	/* remove SSID */
			(void) strlwr (buf);
			fprintf (m->tfile, "%s%s@%s", Hdrs[RRECEIPT], m->name, buf);

			sprintf (buf, "%s%s ", (Mbhaddress != NULLCHAR) ? "." : "",
				 (Mbhaddress != NULLCHAR) ? Mbhaddress : "");
			(void) strlwr (buf);
			if (m->realname && strcmp (m->realname, "()"))
				strcat (buf, m->realname);
			strcat (buf, "\n");
			fputs (buf, m->tfile);
		}
		if (m->to && isgroup (m->to))
			fprintf (m->tfile, "%s%s@%s\n", Hdrs[XMAILGROUP], m->to, Hostname);

		(void) pax25 (buf, Mycall);
		if ((cpp = strpbrk (buf, DIGI_IDS)) != NULLCHAR)
			*cpp = '\0';	/* remove SSID */
		(void) strlwr (buf);
		fprintf (m->tfile, "%s%s@%s", Hdrs[FROM], m->name, buf);

		sprintf (buf, "%s%s ", (Mbhaddress != NULLCHAR) ? "." : "",
			 (Mbhaddress != NULLCHAR) ? Mbhaddress : "");
		(void) strlwr (buf);
		if (m->realname && strcmp (m->realname, "()"))
			strcat (buf, m->realname);
		strcat (buf, "\n");
		fputs (buf, m->tfile);

		cpp = (m->origto != NULLCHAR) ? m->origto : m->to;
		fprintf (m->tfile, "%s%s", Hdrs[TO], cpp);
		if ((cpp = mblookname (NULLMBX, cpp)) != 0) {
			fputs (cpp, m->tfile);
			free (cpp);
		}
		fputs ("\n\n", m->tfile);
	}
#endif /* MBFWD */
	return 0;
}



/* Attempt to determine if this is third-party mail. */
static int
thirdparty (struct mbx *m)
{
char buf[MBXLINE], *cp, *rp;
FILE *fp;

	if (strpbrk (m->to, "@%!") != NULLCHAR)
		return 0;

	rp = strdup (Hostname);

	if ((cp = strchr (rp, '.')) != NULLCHAR)
		*cp = '\0';

	if (stricmp (m->to, rp) == 0) {
		free (rp);
		return -1;
	}
	free (rp);

	if (stricmp (m->to, "sysop") == 0)
		return -1;

	if ((fp = fopen (Arealist, READ_TEXT)) == NULLFILE)
		return 0;

	while (fgets (buf, MBXLINE, fp) != NULLCHAR) {
		/* The first word on each line is all that matters */
		if ((cp = strpbrk (buf, " \t")) != NULLCHAR)
			*cp = '\0';
		if (stricmp (m->to, buf) == 0) {
			(void) fclose (fp);
			return -1;
		}
	}
	(void) fclose (fp);
	return 0;
}
#endif



#ifdef GATECMDS
int
dombconnect (int argc, char *argv[], void *p)
{
struct mbx *m;
int s;
struct sockaddr_ax alsocket;	/*the local socket*/
struct sockaddr_ax afsocket;	/*the remote socket*/
struct iface *ifp;
char local_call[AXALEN];
char target[AXALEN];
char tmp[AXBUF];
int didrose = 0;
#ifdef NETROM
struct nrroute_tab *np;
struct sockaddr_nr lsocket, fsocket;
char alias[AXBUF];
#endif
#ifdef CONVERS
struct session *sp;
#endif

	m = (struct mbx *) p;

	if (argc == 1) {
#ifdef  CONVERS
		if (!Mbconverse) {
			tputs ("Conference server not enabled\n");
			return 0;
		}
		if (m->privs & NO_CONVERS) {
			tputs (Noperm);
			mail_error ("%s: converse denied\n", m->name);
#ifdef TUTOR
			strcpy (m->line, "denied converse");
			mbscripthook (m, "denied.sys");
#endif
			return 0;
		}
#ifdef GWTRACE
		log (m->user, "PBBS CONFERENCE: %s", m->name);
#endif
		m->state = MBX_CONVERS;
		usflush (Curproc->output);
		if (Current->type == 1)
			tprintf ("%c%c", IAC, CLEARSCREEN);	/* sets Current->split */
		sp = Current;
		kwait (NULL);
		mbox_converse (m->user, m->name, (int) -1, m);
		if (sp->split) {
			usflush (Curproc->output);
			tprintf ("%c%c", IAC, CLEARSPLIT);	/* clears Current->split */
			usflush (Curproc->output);
			sp->split = 0;
		}
#else
		dombconnecthelp ();
#endif
		return 0;
	}
	if (MBSecure)
#ifdef NETROM
		if ((m->family != AF_AX25) && (m->family != AF_NETROM) && (m->privs & WAS_ANONY)) {
#else
		if ((m->family != AF_AX25) && (m->privs & WAS_ANONY)) {
#endif
			tputs (Noperm);
			mail_error ("%s: gateway denied (secure mode): %s\n", m->name, cmd_line (argc, argv, m->stype));
#ifdef TUTOR
			strcpy (m->line, "denied secure");
			mbscripthook (m, "denied.sys");
#endif
			return 0;
		}
	if (argc == 2) {
#ifndef NETROM
		dombconnecthelp ();
		return 0;
	}
#else
		/*NETROM connection wanted*/
		if (!(m->privs & NETROM_CMD)) {
			tputs (Noperm);
			mail_error ("%s: NETROM gate to %s denied\n", m->name, argv[1]);
#ifdef TUTOR
			strcpy (m->line, "denied netrom");
			mbscripthook (m, "denied.sys");
#endif
			return 0;
		}
		if (Nr_iface == NULLIF) {
			tputs ("NET/ROM not activated.\n\n");
			return 0;
		}
		/* See if the requested destination is a known alias or call,
		 * use it if it is.  Otherwize give an error message.
		 */
		(void) putalias (alias, argv[1], 0);
		(void) strupr (argv[1]);	/*make sure it's upper case*/
		if ((np = find_nrboth (alias, argv[1])) == NULLNRRTAB) {
			/*no such call or node alias*/
			tputs ("no such node\n\n");
			dombconnecthelp ();
			(void) dombports (0, NULL, NULL);
			return 0;
		}
		if ((s = socket (AF_NETROM, SOCK_SEQPACKET, 0)) == -1) {
			tputs (Nosock);
			return 0;
		}
#ifdef GWTRACE
		log (m->user, "PBBS NETROM: %s to %s", m->name, argv[1]);
#endif
		lsocket.nr_family = AF_NETROM;

		/* Set up our local username, bind would use Mycall instead */
		memcpy (lsocket.nr_addr.user, m->call, AXALEN);
		/* Set up our source address */
		memcpy (lsocket.nr_addr.node, Nr_iface->hwaddr, AXALEN);

		(void) bind (s, (char *) &lsocket, sizeof (struct sockaddr_nr));

		memcpy (fsocket.nr_addr.user, np->call, AXALEN);
		memcpy (fsocket.nr_addr.node, np->call, AXALEN);
		fsocket.nr_family = AF_NETROM;
		m->state = MBX_GATEWAY;
		return gw_connect (m, s, (struct sockaddr *) &fsocket, sizeof (struct sockaddr_nr));
	}
#endif /*NETROM*/

#ifdef AX25
	if (argc > 2) {
		/*AX25 gateway connection wanted*/
		if (!(m->privs & AX25_CMD)) {
			tputs (Noperm);
			mail_error ("%s: AX.25 gate to %s on %s denied\n", m->name, argv[2], argv[1]);
#ifdef TUTOR
			strcpy (m->line, "denied ax25");
			mbscripthook (m, "denied.sys");
#endif
			return 0;
		}
		if (((ifp = if_lookup (argv[1])) == NULLIF) ||
		    ((ifp->flags & HIDE_PORT) && !(m->privs & SYSOP_CMD)) ||
		    (ifp->type != CL_AX25)) {
			tprintf ("Unknown port %s\n", argv[1]);
			(void) dombports (0, NULL, NULL);
			return 0;
		}
		if (ifp->type != CL_AX25) {
			tprintf ("Port %s not usable for AX.25 connects\n", argv[1]);
			(void) dombports (0, NULL, NULL);
			return 0;
		}
		if (argc > 3 && !strcmp (argv[3], "@")) {
			didrose = 1;
			argv[3] = strdup (pax25 (tmp, AXRosecall));
		}
		if (setcall (target, argv[2]) == -1) {
			tprintf ("Bad callsign %s\n", argv[2]);
			return 0;
		}
		/* If digipeaters are given, put them in the routing table */
		if (argc > 3)
			if (connect_filt (argc, argv, target, ifp, AX_SETUP) == 0)
				return 0;

		if ((s = socket (AF_AX25, SOCK_STREAM, 0)) == -1) {
			tputs (Nosock);
			return 0;
		}
#ifdef GWTRACE
		log (m->user, "PBBS AX25: %s to %s on %s %s %s %s", m->name, argv[2], argv[1], (argc > 3) ? "via" : "", (argc > 3) ? argv[3] : "", (argc > 4) ? argv[4] : "");
#endif

		if (didrose)
			free (argv[3]);

		/*fill in the known stuff*/
		alsocket.sax_family = afsocket.sax_family = AF_AX25;

		/*the remote call to connect to*/
		(void) setcall (afsocket.ax25_addr, argv[2]);

		/*the outgoing interface*/
		strncpy (afsocket.iface, argv[1], ILEN - 1);

		/*now set local user call, invert ssid*/
		memcpy (local_call, m->call, AXALEN);
		local_call[AXALEN - 1] ^= 0x1e;

		memcpy (alsocket.ax25_addr, local_call, AXALEN);
		/*and bind it (otherwize Mycall will be used!)*/
		(void) bind (s, (char *) &alsocket, sizeof (struct sockaddr_ax));

		m->state = MBX_GATEWAY;
		return gw_connect (m, s, (struct sockaddr *) &afsocket, sizeof (struct sockaddr_ax));
	}
#endif /* AX25 */

	return 0;
}
#endif



static struct perms {
	const char *label;
	long maskvalue;
	short sense;
} permlevels[] =	{
	{ "Reading files",		0x00000001, 1 },
	{ "Creating files",		0x00000002, 1 },
	{ "Writing/deleting files",	0x00000004, 1 },
	{ "AX25 Gateway usage",		0x00000008, 1 },
	{ "Netrom gateway usage",	0x00000020, 1 },
	{ "Ampr telnet usage",		0x00020000, 1 },
	{ "Non-ampr telnet usage",	0x00000010, 1 },
	{ "Conference Bridge access",	0x00008000, 0 },
	{ "BBS forwarding access",	0x00002000, 1 },
	{ "Special gateway access",	0x00010000, 1 },
	{ "SYSOP access",		0x00000040, 1 },
#ifdef TIPMAIL
	{ "SLIP access via TIPMAIL",	0x00100000, 0 },
#endif
	{ "Sending email",		0x00000400, 0 },
	{ "Reading email",		0x00000800, 0 },
	{ "Sending non-sysop email",	0x00001000, 0 },
	{ "Sending PBBS mail",		0x00200000, 0 },
	{ "Sending Ampr mail",		0x00800000, 0 },
	{ "Sending Non-ampr mail",	0x00400000, 0 },
	{ "Email held for review",	0x01000000, 1 },
#ifdef HTTPPBBS
	{ "Access PBBS via HTTP/IP",	0x02000000, 0 },
	{ "Access PBBS via HTTP/AX25",	0x04000000, 0 },
	{ "Sending email via HTTP",	0x08000000, 0 },
#endif
	{ NULLCHAR,			0,	    0 }
};



static int
dombsecurity (int argc, char *argv[], void *p)
{
struct mbx *m;
struct perms *pm;
char const *isit;

	m = (struct mbx *) p;
	if (argc > 1 && argv[1][0] == 'e' && (m->privs & SYSOP_CMD))
		(void) doencode (0, (char **) 0, (void *) 0);
	else {
		tputs ("Your current security level profile:\n\n");
		for (pm = permlevels; pm->label; pm++) {
			if (pm->sense)
				isit = (m->privs & pm->maskvalue) ? "is" : "NOT";
			else
				isit = (!(m->privs & pm->maskvalue)) ? "is" : "NOT";
			tprintf ("     %-26.26s %3.3s enabled\n", pm->label, isit);
		}
	}
	return 0;
}



#ifdef GATECMDS
static int
dombping (int argc, char *argv[], void *p)
{
#ifdef PING_SECURE
struct mbx *m = (struct mbx *) p;

	if (!(m->privs & (TELNET_CMD | AMPR_CMD))) {	/* mbox user allowed to ping? */
		tputs (Noperm);
		return 0;
	}
#endif
	return (doping (argc, argv, p));
}
#endif



#ifdef MAILCMDS
static int
allareas (struct mbx *m, char *cp)
{
char line[MBXLINE + 1];
char area[64], c;
char buf[MBXLINE + 1];
char const *theone;
FILE *fp;

	m->privs |= ALL_AREAS;
	cp = skipwhite (++cp);
	strncpy (line, cp, MBXLINE + 1);	/* save the original line */
	strncpy (area, m->area, 64);		/* save the original area */

	c = (char) tolower (*cp);
	if (!strnicmp (cp, "vers", 4) || (c != 'k' && c != 'r' && c != 'l' && c != 'v' && c != 'm' && !isdigit (c))) {
		tputs ("This command can't be applied to all areas....\n\n");
		strncpy (m->line, line, MBXLINE + 1);
		m->privs &= ~ALL_AREAS;
		return (mbx_parse (m));
	}
	if (c == 'k') {
		tputs ("Are you sure you wish to apply this command to all areas?\n");
		if (mbxrecvline (m) != -1) {
			if (tolower (*m->line) != 'y') {
				tputs ("Command canceled!\n");
				return 0;
			}
		}
	}
	theone = Arealist;
	if ((m->privs & SYSOP_CMD) && (!access (AreaSlist, 0)))
		theone = AreaSlist;
	if ((fp = fopen (theone, READ_TEXT)) == NULLFILE)
		return 0;
	while (fgets (buf, MBXLINE, fp) != NULLCHAR) {
		kwait (NULL);
		if ((cp = strpbrk (buf, " \t\n\r")) != NULLCHAR)
			*cp = '\0';
		changearea (m, buf, 1);
		strncpy (m->line, line, MBXLINE + 1);
		(void) mbx_parse (m);
		kwait (NULL);
	}
	changearea (m, area, 1);
	(void) fclose (fp);
	m->privs &= ~ALL_AREAS;
	return 0;
}
#endif




#ifdef USERLOG
static int
dombedituser (int argc OPTIONAL, char *argv[], void *p)
{
struct mbx *m;
struct mbx *m2;
int done = 0, didsome = 0;
char *cp;

	m = (struct mbx *) p;
	if (!(m->privs & SYSOP_CMD)) {
		tputs ("Sorry, this command only available to SYSOPs!\n");
		mail_error ("%s: EDITUser %s attempted\n", m->name, argv[1]);
		return 0;
	}
	m2 = (struct mbx *) callocw (1, sizeof (struct mbx));

	strncpy (m2->name, argv[1], 20);
	m2->morerows = -1;
	(void) loguser (m2);
	if (m2->morerows == -1) {
		tprintf ("User '%s' not found in file, creating with defaults\n", argv[1]);
		m2->morerows = 23;
		m2->sid = (Usearea) ? MBX_AREA : 0;
		if (Usehold)
			m2->sid |= MBX_HOLD;
		if (Usenrid)
			m2->sid |= MBX_NRID;
		if (Usescan)
			m2->sid |= MBX_STATS;
		m2->family = AF_LOCAL;
	}
	do {
		tprintf ("\nUser: %-27.27sName              (E): %s\nHome BBS         (B): %-10.10s Morelines         (M): %d\n",
			 m2->name, (m2->realname) ? m2->realname : "()", (m2->home) ? ((m2->home[0] == '-') ? "unknown" : m2->home) : "*here*",
			 m2->morerows);
		tprintf ("Area Indication  (A): o%-2.2s        Netrom Indication (N): o%s\n",
			 (m2->sid & MBX_AREA) ? "n" : "ff", (m2->sid & MBX_NRID) ? "n" : "ff");
		tprintf ("Use Color GFX    (G): o%-2.2s        Has read MOTD     (R): o%s\n",
			 (m2->sid & MBX_GFX) ? "n" : "ff", (m2->sid & MBX_RDMOTD) ? "n" : "ff");
		tprintf ("Hold sent mail   (H): o%-2.2s        Display new mail  (S): o%s\n",
			 (m2->sid & MBX_HOLD) ? "n" : "ff", (m2->sid & MBX_STATS) ? "n" : "ff");
		tprintf ("Use TNOS LZW     (L): o%-2.2s        Expert user mode  (X): o%s\n",
			 (m2->sid & MBX_TNOS) ? "n" : "ff", (m2->sid & MBX_EXPERT) ? "n" : "ff");
		tputs ("\nSelect A,B,E,G,H,L,M,N,R,S,X,(Q=quit):\n");
		if (mbxrecvline (m) == -1)
			return -2;
		cp = skipwhite (m->line);
		switch (tolower (*cp)) {
			case 'a':
				m2->sid ^= MBX_AREA;
				break;
			case 'b':
				tputs ("\nEnter new home BBS:\n");
				if (mbxrecvline (m) == -1)
					return -2;
				cp = strchr (m->line, '.');
				if (cp) {
					tputs ("Removing haddress....\n");
					*cp = 0;
				}
				m2->home = strdup (m->line);
				m2->change |= CHG_WP;
				break;
			case 'e':
				tputs ("\nEnter new realname:\n");
				if (mbxrecvline (m) == -1)
					return -2;
				m2->realname = mallocw (strlen (m->line) + 3);
				sprintf (m2->realname, "(%s)", m->line);
				break;
			case 'g':
				m2->sid ^= MBX_GFX;
				break;
			case 'h':
				m2->sid ^= MBX_HOLD;
				break;
			case 'l':
				m2->sid ^= MBX_TNOS;
				break;
			case 'm':
				tputs ("\nEnter number of more lines:\n");
				if (mbxrecvline (m) == -1)
					return -2;
				m2->morerows = atoi (m->line);
				break;
			case 'n':
				m2->sid ^= MBX_NRID;
				break;
			case 'r':
				m2->sid ^= MBX_RDMOTD;
				break;
			case 's':
				m2->sid ^= MBX_STATS;
				break;
			case 'x':
				m2->sid ^= MBX_EXPERT;
				break;
			case 'q':
				done = 1;
				continue;
			default:
				tputs ("Invalid option!\n");
				continue;
		}
		didsome = 1;
	} while (!done);

	if (didsome)
		updatedefaults (m2, 0);
#ifdef WPAGES
	if (m2->change & CHG_WP)
		add_WPUpdate (m2->name, m2->home, m2->realname, 'U');
#endif
	tprintf ("\nUser '%s' %sUpdated!\n", m2->name, (!didsome) ? "NOT " : "");
	if (m2->realname)
		free (m2->realname);
	if (m2->home)
		free (m2->home);
	free (m2);
	return (0);
}
#endif



#ifdef EDITHEADERS
static void
pad_if_needed (FILE *fp, int orig, int new)
{
	while (orig > new) {
		fputc (' ', fp);
		orig--;
	}
}



static int
dombedithdr (int argc OPTIONAL, char *argv[], void *p)
{
struct mbx *m;
int abortit = 0, done = 0, didsome = 0, msg, action, hdrlen = 0;
char *cp;
char subject[80], from[80], to[80], msgid[80], type = 0;
char const *prompt;
char tmptype, *variable = NULLCHAR;
long current, *thisoff = (long *) 0, soffset, foffset, toffset;
long moffset, yoffset;
int *thislen = (int *) 0, flen, slen, tlen, mlen;

	m = (struct mbx *) p;

	from[0] = to[0] = subject[0] = msgid[0] = 0;
	/* First, make sure this is a sysop, and allowed to DO this */
	if (!(m->privs & SYSOP_CMD)) {
		tputs ("Sorry, this command only available to SYSOPs!\n");
		mail_error ("%s: EDitheader %s attempted\n", m->name, argv[1]);
		return 0;
	}
	/* Now, read in the needed parts of the message */
	msg = atoi (argv[1]);
	if (msg < 1 || msg > m->nmsgs) {
		tprintf (Badmsg, msg);
		return 0;
	}
	fseek (m->mfile, m->mbox[msg].start, 0);
	soffset = toffset = foffset = moffset = yoffset = 0L;
	do {
		current = ftell (m->mfile);
		if (fgets (m->line, MBXLINE, m->mfile) == NULLCHAR) {
			done = 1;
			break;
		}
		rip (m->line);
		action = htype (m->line);
		if (action == UNKNOWN)
			continue;
		if (action == NOHEADER && !*m->line)
			break;
		if (action == NOHEADER)
			continue;
		hdrlen = (long) strlen (Hdrs[action]);
		switch (action) {
			case SUBJECT:
				variable = subject;
				thisoff = &soffset;
				break;
			case FROM:
				variable = from;
				thisoff = &foffset;
				break;
			case TO:
				variable = to;
				thisoff = &toffset;
				break;
			case MSGID:
				variable = msgid;
				thisoff = &moffset;
				break;
			case BBSTYPE:
				type = (char) toupper (m->line[hdrlen]);
				yoffset = current + (long) hdrlen;
				/* and fall through */
			default:
				continue;
		}
		strncpy (variable, &m->line[strlen (Hdrs[action])], 80);
		*thisoff = current + (long) hdrlen;
	} while (!soffset || !toffset || !foffset || !moffset || !yoffset);

	/* we now allow X-BBS-Type to be optional */
	if (done || !soffset || !toffset || !foffset || !moffset) {
		tprintf ("Error parsing message #%d!\n", msg);
		return 0;
	}
	flen = (int) strlen (from);
	trimright (from);
	tlen = (int) strlen (to);
	trimright (to);
	slen = (int) strlen (subject);
	trimright (subject);
	mlen = (int) strlen (msgid);
	trimright (msgid);

	/* Now, allow the sysop to record the desired changes */
	do {
		tprintf ("\n(F)rom: %s\n(T)o: %s\n(M)sgID: %s\n(S)ubject: %s\n", from, to, msgid, subject);
		if (type)
			tprintf ("Message t(Y)pe: %c\n", type);
		tprintf ("\nSelect F,T,M,S,%s(A=abort),(Q=quit):\n", (type) ? "Y," : "");
		if (mbxrecvline (m) == -1)
			return -2;
		cp = skipwhite (m->line);
		action = tolower (*cp);
		if (action == 'y' && !type)
			action = 'z';
		switch (action) {
			default:
			      saywhat:tprintf ("Huh?\n\n");
				continue;
			case 'a':
				abortit = 1;
				tprintf ("Changes aborted!\n");
				/* and fall through */
			case 'q':
				done = 1;
				continue;
			case 't':
				prompt = "To Address";
				thislen = &tlen;
				variable = to;
				break;
			case 'f':
				prompt = "From Address";
				variable = from;
				thislen = &flen;
				break;
			case 'm':
				prompt = "Message ID (BID)";
				variable = msgid;
				thislen = &mlen;
				break;
			case 's':
				prompt = "Subject";
				variable = subject;
				thislen = &slen;
				break;
			case 'y':
				prompt = "Message Type [(P)ersonal/(B)ulletin/N(T)S]";
		}
		tprintf ("\nEnter new %s:\n", prompt);
		if (mbxrecvline (m) == -1)
			return -2;
		rip (m->line);
		switch (action) {
			case 'y':
				tmptype = (char) toupper (m->line[0]);
				if (tmptype != 'B' && tmptype != 'P' && tmptype != 'T')
					goto saywhat;
				type = tmptype;
				break;
			default:
				if (variable != NULLCHAR)	{
					strncpy (variable, m->line, 80);
					if (thislen)	{
						if ((int) strlen (variable) > *thislen)
							variable[*thislen] = 0;
					}
					if (action == 'm') {
						m->line[13] = 0;
						sprintf (m->line, "<%s>", msgid);
						strncpy (msgid, m->line, 80);
					}
				}
		}
		didsome = 1;
	} while (!done);

	/* If changes were given, make them in the message */
	if (didsome && !abortit) {
		sprintf (m->line, "%s/%s.txt", Mailspool, m->area);
		(void) fclose (m->mfile);
		if ((m->mfile = subdir_fopen (m->line, UPDATE_TEXT)) != NULLFILE) {
			fseek (m->mfile, foffset, 0);
			fputs (from, m->mfile);
			pad_if_needed (m->mfile, flen, (int) strlen (from));
			fseek (m->mfile, toffset, 0);
			fputs (to, m->mfile);
			pad_if_needed (m->mfile, tlen, (int) strlen (to));
			fseek (m->mfile, moffset, 0);
			fputs (msgid, m->mfile);
			pad_if_needed (m->mfile, mlen, (int) strlen (msgid));
			fseek (m->mfile, soffset, 0);
			fputs (subject, m->mfile);
			pad_if_needed (m->mfile, slen, (int) strlen (subject));
			if (type) {
				fseek (m->mfile, yoffset, 0);
				fputc (type, m->mfile);
			}
			(void) fclose (m->mfile);
		} else
			tprintf ("Error opening '%s' to write headers!\n", m->line);
		m->mfile = subdir_fopen (m->line, READ_TEXT);
	}
	tprintf ("\nMessage #%d %sUpdated!\n", msg, (!didsome) ? "NOT " : "");
	return (0);
}
#endif



#if defined(STRICT_CALL) || defined(STRICT_HTTPCALL)

int 
iscall (char *call)			/* stolen from Wampes */
{
int l, d;
char *cp;

	l = (int) strlen (call);

	/* is the name of an acceptable length ? */
	if (l < 3 || l > 6)
		return 0;	/* nope */

	/* does it start with 2 digits ? */
	if (isdigit (call[0] & 0xff) && isdigit (call[1] & 0xff))
		return 0;	/* yep! invalid */

	/* are the 2nd and 3rd characters both digits ? */
	if (!(isdigit (call[1] & 0xff) || isdigit (call[2] & 0xff)))
		return 0;	/* yep, also invalid */

	/* is the last character alpha (non-numeric) ? */
	if (!(isalpha (call[l - 1] & 0xff)))
		return 0;	/* nope, invalid also! */

	/* check entire name for alphanumeric characters */
	for (cp = call; *cp != '\0'; cp++)
		if (!(isalnum (*cp & 0xff)))
			return 0;

	/* now count how many digits in the string */
	for (d = 0, cp = call; *cp != '\0'; cp++)
		if (isdigit ((int) *cp & 0xff))
			d++;

	/* are there either 1 or 2 digits? */
	if (d < 1 || d > 2)
		return 0;	/* nope, invalid */

	return 1;
}
#endif /* STRICT_CALL || STRICT_HTTPCALL */



#ifdef SQL
static int
dombsql (int argc OPTIONAL, char *argv[], void *p)
{
struct mbx *m;

	m = (struct mbx *) p;
	if (!(m->privs & SYSOP_CMD)) {
		if (argc == 1 || argv[1][0] == '?') {
			tputs ("valid subcommands:\n userquery\n");
			return 0;
		}
		if ((argc > 1) && (argv[1][0] != 'u')) {
			tputs ("Sorry, but only the 'sql userquery' command is available to non-SYSOPs\n");
			return 0;
		}
	}
	return (dosql (argc, argv, p));
}
#endif



#ifdef HTTPPBBS
static int
dombhttp (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
struct mbx *m;

	m = (struct mbx *) p;
	if (!(m->sid & MBX_HTTP)) {
		tputs ("Sorry, but your software doesn't support the HTTP command.\n");
		return 0;
	}
	if (m->privs & NO_HTTP_AX) {
		tputs ("NO - Sorry, but you do not have permission for http access.\n");
		return 0;
	}
#if 0
	dohttpTransaction (argv[1], argv[2], argv[3]);
#endif
	return 0;
}
#endif



static int
dousermotd (int argc OPTIONAL, char *argv[]OPTIONAL, void *p OPTIONAL)
{
	if (MMotd != NULLCHAR && *MMotd)
		tprintf ("\n%s", MMotd);
	return 0;
}



static void
do_a_command (struct mbx *m, char const *cmd)
{
	strncpy (m->line, cmd, MBXLINE);
	(void) mbx_parse (m);
}


static int
review_area (struct mbx *m, char *area)
{
int total, new, hold, i, k;
register struct let *cmsg;
int done;
char buf[MBXLINE];

	statarea (m, area, &total, &new, &hold, 0);
	if (!hold)
		return 0;

	changearea (m, area, 0);
#if 0
	do_a_command (m, "lh");
#endif
	for (cmsg = &m->mbox[1], i = 1; i <= m->nmsgs; i++, cmsg++)	{
		if ((cmsg->status & (BM_ONHOLD | BM_DELETE)) == (BM_ONHOLD | BM_DELETE))
			cmsg->status &= ~BM_ONHOLD;

		if (cmsg->status & BM_ONHOLD)	{
			done = 0;
			while (!done)	{
				for (k = 0; k < 70; k++)
					tputc ('-');
				tputc ('\n');
				sprintf (buf, "l %d %d", i, i);
				do_a_command (m, buf);
prompt:				tprintf ("\n** Action [AHKLNSQRV?]:\n");
				m->line[0] = 0;
				if (m->quickfile != NULLFILE)	{	/* from command session */
					if (recvline (Curproc->input, (unsigned char *) m->line, MBXLINE) == 0)
						return 0;
				} else {
					if (mbxrecvline (m) == -1)
						return 0;
				}
				switch (toupper (m->line[0]))	{
					case '?':	/* Help display */
							tprintf ("\nAvailable commands:\n\n"
								 "\tA\tRelease all messages in this area\n"
								 "\tH\tDisplays updated held message summary\n"
								 "\tK\tKill this message\n"
								 "\tL\tLook at this message (the default command)\n"
								 "\tN\tMove to the Next area - skipping rest of this area\n"
								 "\tS\tSkip this message - leave it held\n"
								 "\tR\tRelease this message from hold\n"
								 "\tQ\tQuit the message review - skip other held messages\n"
								 "\tV\tView this message, with headers\n"
								 "\t?\tThis Help display\n\n");
							break;
					case 'H':	/* list those still remaining and held */
							tputc ('\n');
							do_a_command (m, "ah");
							tputc ('\n');
							break;
					case 'K':	/* kill this message */
							sprintf (buf, "k %d", i);
							do_a_command (m, buf);
							done = 1;
							break;
					case 'A':	/* release all messages in this area */
							do_a_command (m, "ma");
							return 0;
					case 'S':	/* skip this message */
							tprintf ("Message skipped\n");
							done = 1;
							break;
					case 'N':	/* skip rest of this area - move to next area */
							tprintf ("Rest of area '%s' skipped - moving to next area\n", area);
							return 0;
					case 0:
					case '\n':
					case 'L':	/* look at this message */
							sprintf (buf, "%d", i);
							do_a_command (m, buf);
							break;
					case 'R':	/* release this message */
							sprintf (buf, "ma %d", i);
							do_a_command (m, buf);
							done = 1;
							break;
					case 'V':	/* verbose view of message w/headers */
							sprintf (buf, "v %d", i);
							do_a_command (m, buf);
							break;
					case 'Q':	/* quit the review */
							tprintf ("Review terminated\n");
							return 1;
					default:	/* huh! */
							tprintf ("Huh?\n");
							goto prompt;
				}
			}
		}
	}
	return 0;
}


int
dombreview (int argc OPTIONAL, char *argv[]OPTIONAL, void *p OPTIONAL)
{
struct mbx *m;
char buf[MBXLINE], *cp;
FILE *fp;
struct ffblk ff;
char *origarea;
int origmore;
int quit = 0;

	m = (struct mbx *) p;
	if (!(m->privs & SYSOP_CMD)) {
		tputs ("NO - Sorry, but you do not have permission to review email.\n");
		return 0;
	}

	if (*m->area)
		changearea (m, m->area, 1);	/* saves changes already made in current area */
	
	do_a_command (m, "AH");
	if ((fp = fopen (AreaSlist, READ_TEXT)) == NULLFILE)
		return 0;

	origarea = strdup (m->area);
	origmore = m->morerows;
	if (m->quickfile != (FILE *) -1 && (!m->morerows || m->morerows > 100))	/* only override if disabled and in bbs */
		m->morerows = 22;

	while (!quit && fgets (buf, MBXLINE, fp) != NULLCHAR) {
		kwait (NULL);
		if (isalnum (buf[0])) {	/* skip comments */
			if ((cp = strpbrk (buf, " \t\n\r")) != NULLCHAR)
				*cp = '\0';
			quit = review_area (m, buf);
		}
	}
	(void) fclose (fp);
	if (!quit)	{
		sprintf (buf, "%s/*.txt", Mailspool);
		if (findfirst (buf, &ff, 0) == 0) {
			do {
				char *ctmp;
				kwait (NULL);	/* Let others run */
				ctmp = strchr (ff.ff_name, '.');
				if (ctmp)
					*ctmp = '\0';
				/*must be private mail area, and not on exclude list !*/
				if (!issysarea (ff.ff_name))
					quit = review_area (m, ff.ff_name);
			} while (!quit && findnext (&ff) == 0);
		}
	}
	tprintf ("\nMessage review complete\n");
	changearea (m, origarea, 0);
	m->morerows = origmore;

	/* the equivalent of a 'pbbs mailfor hold clear' command */
	HOLDindicator = 0;
#ifdef XSERVER
	xnotify (X_HOLD);
#endif

	return 0;
}

#endif /* MAILBOX */
