/* 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>
#include <dos.h>
#else
#include <time.h>
#endif
#ifdef  UNIX
#include <sys/stat.h>
#endif
#include "timer.h"
#include "proc.h"
#include "socket.h"
#include "usock.h"
#include "session.h"
#include "smtp.h"
#include "ftpserv.h"
#include "bm.h"
#include "pktdrvr.h"
#include "help.h"

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

#ifdef DELEGATE
#include "delegate.h"
extern int Mbdelegate;
#endif

#ifdef UNIX
extern int ftpsecuritycheck (char *filename, int perms, int mode);
extern long CREATEgid, CREATEuid;
extern short CREATEmask;
extern int CREATEsecure;

#ifndef R_OK
#define R_OK 4
#endif
#ifndef W_OK
#define W_OK 2
#endif
#endif

extern int AMPRonly;
extern char Compiled[];
extern int MAttended;
extern char *MAttendedAt;
extern char Mbpasswd[];
extern int BbsUsers;
extern char Only[];
extern char CurUsers[];
extern char Howtoend[];
extern char MsgAborted[];
extern char *findhome (char *);
extern void suspendTipMail (struct mbx *m);
extern void tutorserv (const char *call, struct mbx *m, int mode, int color, int ip);

#ifdef MBFWD
extern void forwardingSummary (void);
#endif

extern int dombusers (int argc, char *argv[], void *p);
extern struct list *expandalias (struct list **head, const char *user, const char *orig);
extern char *Mbhaddress;
extern int doencode (int argc, char *argv[], void *p);
extern struct cmds Cmds[];
extern struct cmds Mbcmds[];


#ifdef MAILBOX
void bbsbump (char *user, int zap);
int bid_check (register char *string);
#ifdef WPAGES
static void wpageUpdate (FILE * fp, char *string, char *entry, char *oldtime, int bbs, int updateit, char which);
#endif
int groupcommand (char *action, char *group, char *user, int local, FILE ** out, FILE * in);
int isgroup (char *group);
static void informGroup (char *action, char *group, char *user, char *buf, FILE * old, FILE * new, FILE * in);

#ifdef USERLOG
static int quickstat (register struct mbx *m, char *area);
#endif

static int dombgetall (struct mbx *m);
static void expertstat (struct mbx *m);

#ifdef FILECMDS
static int uuencode (FILE *infile, int s, char *infilename);
static int uudecode (FILE *infile, FILE *outfile);
#endif

int dombencode (int argc, char *argv[], void *p);
int dodownload (int argc, char *argv[], void *p);
int dombfdesc (int argc, char *argv[], void *p);
int dobump (int argc, char *argv[], void *p);
int msgidcheck (char *string);
int dombwpages (int argc, char *argv[], void *p);
int dombnews (int argc, char *argv[], void *p);
int dombtutor (int argc, char *argv[], void *p);
int dombgroup (int argc, char *argv[], void *p);
int dombquote (int argc, char *argv[], void *p);
int dombset (int argc, char *argv[], void *p);
int dombrmail (int argc, char *argv[], void *p);
int dombroute (int argc, char *argv[], void *p);
int dombexpand (int argc, char *argv[], void *p);
int dombexpert (int argc, char *argv[], void *p);
int dombslip (int argc, char *argv[], void *p);
int douser (int argc, char *argv[], void *p);
int doreply (int argc, char *argv[], void *p);
int dombget (int argc, char *argv[], void *p);
int dombmovemail (int argc, char *argv[], void *p);
int dombhelp (int argc, char *argv[], void *p);
int dombconvers (int argc, char *argv[], void *p);
int dombfinger (int argc, char *argv[], void *p);
int dosysop (int argc, char *argv[], void *p);
int dostars (int argc, char *argv[], void *p);
int dochat (int argc, char *argv[], void *p);
int dombjheard (int argc, char *argv[], void *p);
int dombiproute (int argc, char *argv[], void *p);

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

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

char *wpage_exp (char *to, int hier, int exphome);
int dozap (int argc, char *argv[], void *p);
int dowhat (int argc, char *argv[], void *p);
extern int dosend (int argc, char *argv[], void *p);
extern char *permtest (char *path, long privs, const char *name, int mode, char *root, int thedir);
extern char *cmd_line (int argc, char *argv[], char stype);
extern char *nntp_name_expansion (char *name);
extern void updateCtl (const char *who, struct let *info);

#if defined(FOQ_CMDS) || defined(TTYCALL) || defined(ALLSERV)
const char SysopBusy[] = "The system is unattended. Try again later.\n";
#endif

#ifdef CATALOG
#include "catalog.h"

#define CAT mailbox2_catalog

#define Alsomenu		__STR(0)
#define Alsomenu2		__STR(1)
#define index_dat		__STR(2)
#define sorry			__STR(3)
#define wr_syntax		__STR(4)
#define wa_syntax		__STR(5)
#define noread			__STR(6)
#define dirdenied		__STR(7)
#define zapfailed		__STR(8)
#define logzap			__STR(9)
#define zapdenied		__STR(10)
#define attemptsysop		__STR(11)
#define sysopdenied		__STR(12)
#define validationcomplete	__STR(13)
#define sysopwarning		__STR(14)
#define logsysop		__STR(15)
#define loglinkdenied		__STR(16)
#define loglink			__STR(17)
#define linkhello		__STR(18)
#define ampronly_str		__STR(19)
#define telnetdenied		__STR(20)
#define logtelnet		__STR(21)
#define incomingcall		__STR(22)
#define finger_usage		__STR(23)
#define confnotenabled		__STR(24)
#define convdenied		__STR(25)
#define logconf			__STR(26)
#define logtutor		__STR(27)
#define nohelp			__STR(28)
#define onlyfirst		__STR(29)
#define cannotlock		__STR(30)
#define message			__STR(31)
#define moreafter		__STR(32)
#define areaadded		__STR(33)
#define nameadded		__STR(34)
#define newscanlogin		__STR(35)
#define usaANSI			__STR(36)
#define permissionlevel		__STR(37)
#define fwddelegation		__STR(38)
#define usecompressed		__STR(39)
#define expertprompts		__STR(40)
#define usersettings		__STR(41)
#define useSET			__STR(42)
#define getheader		__STR(43)
#define gettrailer		__STR(44)
#define noconfig		__STR(45)
#define delegationdisabled	__STR(46)
#define expandsto		__STR(47)
#define noaddress		__STR(48)
#define addressroutes		__STR(49)
#define unreadmessages		__STR(50)
#define newmessages		__STR(51)
#define nounreadmessages	__STR(52)
#if 0
#define systemtime		__STR(53)
#endif
#define inform_str		__STR(54)
#define groupindex		__STR(55)
#define newsdisabled		__STR(56)
#define lognews			__STR(57)
#define wp_syntax		__STR(58)
#define notinwp			__STR(59)
#if 0
#define wplists			__STR(60)
#endif
#define wplists2		__STR(61)
#define wp_sysopsyntax		__STR(62)
#define updating		__STR(63)
#define filedesc		__STR(64)
#define downloaddenied		__STR(65)
#define xmodemtip		__STR(66)
#define uploaddenied		__STR(67)
#define cannotopen		__STR(68)
#define cannotcreate		__STR(69)
#define logupload		__STR(70)
#define Sysopmenu		__STR(71)

#else
static const char Alsomenu[] =
"\nFor further help, type 'help cmdname', where 'cmdname' is one of the above."
"\nFor a brief listing of all commands, type '?'\n\n";
static const char Alsomenu2[] =
"Others: AF, AL, AN, AS, DU, KM, KS, KU, L$, L>, L<, LA, LB, LL, LM, LS, LT,\n"
"        ML, MS, RH, RM, SB, SC, SF, SP, SR, ST, VM, XA, XC, XM, XN, XS\n";
static const char Sysopmenu[] = "Sysops: IF, MA, MC, MF, MH, MK, MM, MP, MR, MT, MW, MX, WA, WR, @\n";
static const char index_dat[] = "index.dat";
static const char sorry[] = "Sorry, command is only available to the SYSOP!\n";
static const char wr_syntax[] = "Syntax: WR user \"message\"\n";
static const char wa_syntax[] = "Syntax: WA \"message\"\n";
static const char noread[] = "Can't read directory: \"%s\": %s\n";
static const char dirdenied[] = "%s: directory denied: %s\n";
static const char zapfailed[] = "Zap failed: %s\n";
static const char logzap[] = "PBBS Zap: %s";
static const char zapdenied[] = "%s: zap denied: %s\n";
static const char attemptsysop[] = "PBBS: %s attempting SYSOP";
static const char sysopdenied[] = "%s: SYSOP denied!\n";
static const char validationcomplete[] = "Validation %somplete!\n";
static const char sysopwarning[] = "\nType 'exit' to return\n\nBe VERY careful!!\n";
static const char logsysop[] = "PBBS sysop: %s";
static const char loglinkdenied[] = "PBBS LINKED: %s permission denied";
static const char loglink[] = "PBBS LINKED: %s changed to %s";
static const char linkhello[] = "Oh, hello %s.\n";
static const char ampronly_str[] = "Sorry, but only Amateur Radio IP stations can be reached from here!\n";
static const char telnetdenied[] = "%s: Telnet denied: %s\n";
static const char logtelnet[] = "PBBS TELNET: %s to %s:%d";
static const char incomingcall[] = "*** Incoming call from %s@%s ***\n";
static const char finger_usage[] = "Usage: F user@host  or  F @host  or  F user.\n";
static const char confnotenabled[] = "Conference server not enabled\n";
static const char convdenied[] = "%s: converse denied\n";
static const char logconf[] = "PBBS CONFERENCE: %s";
static const char logtutor[] = "PBBS TUTORIAL: %s";
static const char nohelp[] = "No help available.\n";
static const char onlyfirst[] = "Only first %d arguments used\n";
static const char cannotlock[] = "Can't lock '%s', please try later\n";
static const char message[] = "Message %d %s...\n";
static const char moreafter[] = "-more- after %d lines\n";
static const char areaadded[] = "Area added to prompt";
static const char nameadded[] = "Name added to prompt";
static const char newscanlogin[] = "New message scan at login";
static const char usaANSI[] = "Use ANSI color graphics";
static const char permissionlevel[] = "Permission level: %ld (0x%08lx)\n";
static const char fwddelegation[] = "Forward delegation";
static const char usecompressed[] = "Use Compressed streams";
static const char expertprompts[] = "Expert user prompts";
static const char usersettings[] = "*** User profile settings: %s ***\n\n";
static const char useSET[] = "\nUse the SET command to change these options. For help, type HELP SET.\n";
static const char getheader[] = "---------------- Current %s Information ----------------\n";
static const char gettrailer[] = "---------------- %s %s Information ----------------\n";
static const char noconfig[] = "This command not configured for this interface\n";
static const char delegationdisabled[] = "Forwarding delegation disabled by SYSOP\n";
static const char expandsto[] = "Address '%s' expands to: ";
static const char noaddress[] = "No address specified.\n";
static const char addressroutes[] = "Address '%s' routes to:";
static const char unreadmessages[] = "%s ha%s unread messages, in the following message areas:\n";
static const char newmessages[] = "As a new user, you will find messages in the following message areas:\n";
static const char nounreadmessages[] = "No unread messages in the public areas! %s %s up-to-date!\n";

#if 0
static const char systemtime[] = "The system time at '%s' is now: %s\n";
#endif

static const char inform_str[] = "%s: SUBSCRIBED %s";
static const char groupindex[] = "Index of mailing lists/groups\n\n";
static const char newsdisabled[] = "News Center not enabled\n";
static const char lognews[] = "PBBS NEWS: %s";
static const char wp_syntax[] = "Syntax: WP user%s\n";
static const char notinwp[] = "Sorry, user not in whitepages! If local user, try \"ML user\"...\n";

#if 0
static const char wplists[] = "White Pages lists this person as '%s@%s'\n";
#endif

static const char wplists2[] = "White Pages lists this person as '%s'\n";
static const char wp_sysopsyntax[] = "Syntax: WP user @bbsname\n";
static const char updating[] = "Updating database....\n";
static const char filedesc[] = "Enter description lines, each line 37 characters maximum.\n'/EX' when complete:\n";
static const char downloaddenied[] = "%s: download denied: %s\n";
static const char xmodemtip[] = "Xmodem on TIP connects only\n";
static const char uploaddenied[] = "%s: upload denied: %s\n";
static const char cannotopen[] = "Can't open \"%s\": %s\n";
static const char cannotcreate[] = "Can't create \"%s\": %s\n";
static const char logupload[] = "PBBS upload: %s";

#endif


short SecureTelnet = 1;


#ifdef MSDOS
#define WPAGEENTRYSIZE    30
#define WPAGEBBSENTRYSIZE 49
#else
#define WPAGEENTRYSIZE    29
#define WPAGEBBSENTRYSIZE 48
#endif



int
dowhat (int argc, char *argv[], void *p)
{
struct mbx *m;
#ifdef FILECMDS
FILE *fp, *in = NULLFILE;
char *file;
char *cp, *nm, *ptr;
char buf[50], buf2[80];
int yep;
#endif
#ifdef DOS_GETFILEATTR
unsigned attr;
#endif

	m = (struct mbx *) p;
#ifdef WPAGES
	if (m->stype == 'P')
		return (dombwpages (argc, argv, p));
#endif
	if (((m->stype == 'R') || (m->stype == 'A')) && !(m->privs & SYSOP_CMD)) {
		tputs (sorry);
		return 0;
	}
	if (m->stype == 'R') {
		if (argc < 3)
			tputs (wr_syntax);
		else
			(void) dowrite (0, argv, 0);
		return 0;
	}
	if (m->stype == 'A') {
		if (argc < 2)
			tputs (wa_syntax);
		else
			(void) dowriteall (0, argv, 0);
		return 0;
	}
#ifdef FILECMDS
	if ((file = permtest (m->path, m->privs, (argc > 1) ? argv[1] : ".", RETR_CMD, NULLCHAR, (argc < 2))) != NULLCHAR) {
		m->state = MBX_WHAT;

		/* check for index.dat file in that directory */
		cp = strdup (file);
#ifdef DOS_GETFILEATTR
		(void) _dos_getfileattr (cp, &attr);
		if (!(attr & FA_DIREC)) {
			ptr = strrchr (cp, '/');
			if (ptr)
				*ptr = 0;
		}
#endif
		nm = pathname (cp, index_dat);
		free (cp);
		in = fopen (nm, READ_TEXT);
		free (nm);
		if (in != NULLFILE) {
			tputc ('\n');
			while (fgets (buf2, 80, in) != NULLCHAR) {
				if (*buf2 == ' ' || *buf2 == '\t' || *buf2 == '\n')
					tputs (buf2);
				else
					break;
			}
		}
		if ((fp = dir (file, (in == NULLFILE) ? 1 : 2)) == NULLFILE)
			tprintf (noread, file, SYS_ERRLIST(errno));
		else {
#ifdef MBXTDISC
			stop_timer (&m->tdisc);
#endif
			if (in == NULLFILE)
				(void) sendfile (fp, m->user, ASCII_TYPE, 0);
			else {
				while (fgets (buf, 50, fp) != NULLCHAR) {
					yep = 0;
					if (*buf == ' ')
						continue;
					rewind (in);
					while (fgets (buf2, 80, in) != NULLCHAR) {
						if (!strnicmp (buf, buf2, 12)) {
							yep = 1;
							rip (buf2);
							break;
						}
					}
					if (yep)
						rip (buf);
					tputs (buf);
					if (yep) {
						ptr = &buf2[13];
						ptr = skipwhite (ptr);
						tprintf ("%-37.37s\n", ptr);
						while (fgets (buf2, 80, in) != NULLCHAR) {
							if (*buf2 != ' ' && *buf2 != '\t')
								break;
							rip (buf2);
							ptr = skipwhite (buf2);
							tprintf ("%39s| %-37.37s\n", "", ptr);
						}
					}
				}
				(void) fclose (in);
			}
			(void) fclose (fp);
		}
		free (file);
	} else {
		tputs (Noperm);
		mail_error (dirdenied, m->name, cmd_line (argc, argv, m->stype));
	}
#endif
	return 0;
}



#ifdef FILECMDS
int
dozap (int argc, char *argv[], void *p)
{
struct mbx *m;
char *file;

	m = (struct mbx *) p;
	if ((file = permtest (m->path, m->privs, argv[1], DELE_CMD, NULLCHAR, 0)) != NULLCHAR) {
		if (unlink (file))
			tprintf (zapfailed, SYS_ERRLIST(errno));
		else
			log (m->user, logzap, file);
		free (file);
	} else {
		tputs (Noperm);
		mail_error (zapdenied, m->name, cmd_line (argc, argv, m->stype));
	}
	return 0;
}
#endif



extern void dumproute (struct route *rp, char *p);
extern const char RouteHeader[];



/* Show non-private routes only */
int
dombiproute (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
register int i, bits;
register struct route *rp;
char buf[85];

	tputs (RouteHeader);
	for (bits = 31; bits >= 0; bits--) {
		for (i = 0; i < HASHMOD; i++) {
			for (rp = Routes[bits][i]; rp != NULLROUTE; rp = rp->next) {
				if (!(rp->flags & RTPRIVATE)) {
					dumproute (rp, buf);
					if (tprintf ("%s\n", &buf[4]) == EOF)
						return 0;
				}
			}
		}
	}
	if (R_default.iface != NULLIF && !(R_default.flags & RTPRIVATE)) {
		dumproute (&R_default, buf);
		if (tprintf ("%s\n", &buf[4]) == EOF)
			return 0;
	}
	return 0;
}



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

	if (argc > 1) {
		if (((ifp = if_lookup (argv[1])) == NULLIF) || (ifp->type != CL_AX25) ||
		    ((ifp->flags & HIDE_PORT) && !(m->privs & MBX_SYSOP))) {
			tprintf (Badinterface, argv[1]);
			return 0;
		}
		(void) axheard (ifp);
		return 0;
	}
	for (ifp = Ifaces; ifp != NULLIF; ifp = ifp->next) {
		if ((ifp->flags & LOG_AXHEARD) && (!(ifp->flags & HIDE_PORT) || m->privs & MBX_SYSOP))
			if (axheard (ifp) == EOF)
				break;
	}
	return 0;
}
#endif



#if defined(FOQ_CMDS) || defined(TTYCALL)
int
dochat (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
char buf[8], *newargv[3], doitat[50], buf2[2];
struct mbx *m;

	m = (struct mbx *) p;

	if (MAttended) {
		m->state = MBX_CHAT;
		strcpy (buf2, "C");
		newargv[0] = buf2;
		newargv[1] = Hostname;
		if (MAttendedAt != NULLCHAR) {
			strncpy (doitat, MAttendedAt, 50);
			newargv[1] = doitat;
			rip (newargv[1]);
		}
		sprintf (buf, "%d", IPPORT_TTYLINK);
		newargv[2] = buf;
		SecureTelnet = 0;
		m->stype = '+';
		(void) dombtelnet (3, newargv, p);
	} else
		tputs (SysopBusy);

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



/*Password protection added - 920118, WG7J */
int
dosysop (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
struct mbx *m;
int c;
int len, pwdc[5], i, valid = 0;
char *cp;

	m = (struct mbx *) p;
	log (m->user, attemptsysop, m->name);

	/*If you want anyone with the password to go sysop-mode
	 *comment out the next 4 line ! -WG7J
	 */
	if (!(m->privs & SYSOP_CMD)) {
		tputs (Noperm);
		mail_error (sysopdenied, m->name);
		return 0;
	}
	/*only if set,
	 *check for the password before letting users proceed
	 */
	m->state = MBX_SYSOPTRY;
	if ((len = (int) strlen (Mbpasswd)) != 0) {
		for (i = 0; i < 5; i++)
			tprintf ("%d ", (pwdc[i] = RANDOM (len)) + 1);	/*print the random chars*/
		tputc ('\n');
		for ( ; ; ) {
			c = mbxrecvline (m);
			if (c == EOF || c == -2)
				return 0;
			if (*m->line == '\0')
				break;
			if (*m->line == '?') {
				tprintf (validationcomplete, valid ? "C" : "Inc");
				continue;
			}
			cp = m->line;
			for (i = 0; i < 5; i++)
				if (tolower (*cp++) != tolower (Mbpasswd[pwdc[i]]))
					break;
			if (i == 5)
				valid = 1;
		}
		if (!valid)
			return 0;
	}
	log (m->user, "PBBS: %s is now SYSOP", m->name);
	m->state = MBX_SYSOP;
	tputs (sysopwarning);

	for ( ; ; ) {
		netPrompt ();
		usflush (Curproc->output);
		if (mbxrecvline (m) < 0)
			break;
		log (m->user, logsysop, m->line);
#ifdef MBXTDISC
		stop_timer (&m->tdisc);
#endif
		if (*m->line == '\0')
			continue;

		cp = _variable_expansion (strdup (m->line));
		strncpy (m->line, cp, MBXLINE);
		free (cp);
		/* Check for special commands. Execute these mailbox cmds. KF5MG. */
#if defined AX25 || defined NETROM
		if (strncmp (m->line, "connect", strcspn (m->line, " ")) == 0)
			valid = cmdparse (Mbcmds, m->line, (void *) m);
		else
#endif
#ifdef FOQ_CMDS
		if (strncmp (m->line, "finger", strcspn (m->line, " ")) == 0)
			valid = cmdparse (Mbcmds, m->line, (void *) m);
		else
#endif
#ifdef GATECMDS
		if (strncmp (m->line, "ping", strcspn (m->line, " ")) == 0)
			valid = cmdparse (Mbcmds, m->line, (void *) m);
		else if (strncmp (m->line, "telnet", strcspn (m->line, " ")) == 0)
			valid = cmdparse (Mbcmds, m->line, (void *) m);
		else
#endif

			valid = cmdparse (Cmds, m->line, NULL);
#ifdef MBXTDISC
		start_timer (&m->tdisc);
#endif
		if (valid == -2)
			break;
	}
	/* remove potential remote traces - WG7J */
	removetrace ();
	return 0;
}



/* Handle the "*** Done" command when reverse forwarding ends or the
 * "*** LINKED to" command.
 */
int
dostars (int argc, char *argv[], void *p)
{
struct mbx *m;
int anony = 1;
int founddigit = 0;
int oldprivs;
char *cp;

	m = (struct mbx *) p;

	/* Allow 'linked to' from anyone, but reset SYSOP priviledges
	 * when the sysop-password is not set !
	 * Also try to set the new call !
	 * Inspired by Kurt, wb5bbw
	 * Check for the strange TEXNET linked message !
	 * 920220 - WG7J
	 */
	if ((argc >= 4) && !strcmp (argv[1], "linked") && !strcmp (argv[2], "to")) {
		if (m->privs & NO_LINKEDTO) {
			puts (Noperm);
#ifdef GWTRACE
			log (m->user, loglinkdenied, m->name);
#endif
			return 0;
		}
#ifdef GWTRACE
		log (m->user, loglink, m->name, argv[3]);
#endif
		strncpy (m->name, argv[3], MBXLINE);
		oldprivs = m->privs;	/*Save this !*/
		/* Try to find the privileges of this user from the userfile */
		if ((m->privs = userlogin (m->name, NULLCHAR, &m->path, MBXLINE,
					   &anony)) == -1)
			if ((m->privs = userlogin ("bbs", NULLCHAR, &m->path,
						   MBXLINE, &anony)) == -1)
				if ((m->privs = userlogin ("anonymous", NULLCHAR,
					&m->path, MBXLINE, &anony)) == -1) {
					m->privs = 0;
					free (m->path);
					m->path = NULLCHAR;
				}
		if (m->privs & EXCLUDED_CMD)
			return domboxbye (0, NULLCHARP, p);
#ifdef AX25
		/* Set the call */
		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
		/*Kill ssid in name, if any*/
		if ((cp = strchr (m->name, '-')) != NULLCHAR)
			*cp = '\0';
		/*Check if sysop password is set,
		 *if not, disallow sysop privs no matter what !
		 */
		if (*Mbpasswd == '\0')
			m->privs &= ~SYSOP_CMD;
		/*Check to see if any of NO_READ,NO_SEND or NO_3PARTY were set,
		 *if so, dis-allow those no matter what
		 *(so that users cannot get priviledges by issuing a ***linked)
		 * 920220 - WG7J
		 */
		if (oldprivs & NO_SENDCMD)
			m->privs |= NO_SENDCMD;
		if (oldprivs & NO_READCMD)
			m->privs |= NO_READCMD;
		if (oldprivs & NO_3PARTY)
			m->privs |= NO_3PARTY;

		tprintf (linkhello, m->name);
#ifdef MAILCMDS
		changearea (m, m->name, (int) 1);
#endif
		return 0;
	}
	if (argc > 1 && (m->sid & MBX_SID))	/* "*** Done" or similar */
		return -2;
	return -1;
}



#if defined(GATECMDS) || defined(FOQ_CMDS)
int
dombtelnet (int argc, char *argv[], void *p)
{
struct mbx *m;
int s, len, i;
int nonampr = 0;
char dsocket[MAXSOCKSIZE];
struct sockaddr_in fsocket;
short wasSecureTelnet;

	wasSecureTelnet = SecureTelnet;
	SecureTelnet = 1;
	m = (struct mbx *) p;
	fsocket.sin_family = AF_INET;
	if (argc < 3)
		fsocket.sin_port = IPPORT_TELNET;
	else	{
		/* if m->stype == '+' we were called internally */
		if (m->stype != '+' && (m->privs & NO_NON23))	{
			tputs ("\nSorry, but you do not have permission to specify a non-standard telnet port!\n");
			return 0;
		}
		fsocket.sin_port = (int16) atoip (argv[2]);
	}

	if ((fsocket.sin_addr.s_addr = resolve (argv[1])) == 0) {
		tprintf (Badhost, argv[1]);
		/* Free m->startmsg if set ! - WG7J */
		if (m->startmsg != NULLCHAR) {
			free (m->startmsg);
			m->startmsg = NULLCHAR;
		}
		return 0;
	}
	nonampr = (((fsocket.sin_addr.s_addr & 0xff000000LU) >> 24) != 44);

	if (AMPRonly && (m->privs & WAS_ANONY) && nonampr) {
		tputs (ampronly_str);
		/* Free m->startmsg if set ! - WG7J */
		if (m->startmsg != NULLCHAR) {
			free (m->startmsg);
			m->startmsg = NULLCHAR;
		}
		return 0;
	}
	/* Only local telnets are are allowed to the unprivileged user */
	/* The above is a security hole ! DO NOT allow this anymore ! - WG7J */

	/* If the first letter of the command is 'C', then it was
	 * the CALL,QUERY,OPERATOR or CONFERENCE command !
	 * Allow these, no matter what.
	 * This way, you can give access to the Internet callserver,
	 * but disable all other telnets - WG7J
	 */
	if (wasSecureTelnet && *argv[0] != 'C') {
		if ((nonampr && !(m->privs & TELNET_CMD)) || (!nonampr && !(m->privs & AMPR_CMD))) {
			tputs (Noperm);
			mail_error (telnetdenied, m->name, cmd_line (argc, argv, m->stype));
#ifdef TUTOR
			strcpy (m->line, "denied telnet");
			mbscripthook (m, "denied.sys");
#endif
			/* Free m->starmsg if set ! - WG7J */
			/* Shouldn't happen here, but just in case */
			if (m->startmsg != NULLCHAR) {
				free (m->startmsg);
				m->startmsg = NULLCHAR;
			}
			return 0;
		}
	}
	if ((s = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
		tputs (Nosock);
		/* Free m->starmsg if set ! - WG7J */
		if (m->startmsg != NULLCHAR) {
			free (m->startmsg);
			m->startmsg = NULLCHAR;
		}
		return 0;
	}
#ifdef GWTRACE
	log (m->user, logtelnet, m->name, argv[1], fsocket.sin_port);
#endif
	if (fsocket.sin_port == IPPORT_TTYLINK) {
		m->startmsg = mallocw (80);
		len = MAXSOCKSIZE;
		i = getpeername (m->user, dsocket, &len);
		sprintf (m->startmsg, incomingcall, m->name, i != -1 ? psocket (dsocket) : Hostname);
	}
	m->state = MBX_GATEWAY;
	return gw_connect (m, s, (struct sockaddr *) &fsocket, SOCKSIZE);
}
#endif



#ifdef FOQ_CMDS
int
dombfinger (int argc, char *argv[], void *p)
{
struct mbx *m;
char *host, *user = NULLCHAR, buf[8], *newargv[3], buf2[2];

	if (argc > 2) {
		tputs (finger_usage);
		return -1;
	}
	host = Hostname;
	if (argc == 2) {
		if ((host = strchr (argv[1], '@')) != NULLCHAR) {
			*host = '\0';
			host++;
		} else
			host = Hostname;
		user = argv[1];
	}
	m = (struct mbx *) p;
	m->startmsg = mallocw (80);
	if (user != NULLCHAR)
		sprintf (m->startmsg, "%s\n", user);
	else
		strcpy (m->startmsg, "\n");
	buf2[0] = 't';
	buf2[1] = 0;
	newargv[0] = buf2;
	newargv[1] = host;
	sprintf (buf, "%d", IPPORT_FINGER);
	newargv[2] = buf;
	SecureTelnet = 0;
	m->stype = '+';
	return dombtelnet (3, newargv, p);
}
#endif



extern int Mbconverse;



#ifdef  CONVERS
int
dombconvers (int argc, char *argv[], void *p)
{
struct mbx *m;
int channel = -1;
struct session *sp;

	m = (struct mbx *) p;

	if (!Mbconverse) {
		tputs (confnotenabled);
		return 0;
	}
	if (m->privs & NO_CONVERS) {
		tputs (Noperm);
		mail_error (convdenied, m->name);
#ifdef TUTOR
		strcpy (m->line, "denied converse");
		mbscripthook (m, "denied.sys");
#endif
		return 0;
	}
	if (argc != 1)
		channel = atoi (argv[1]);
#ifdef GWTRACE
	log (m->user, logconf, 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, channel, m);
	if (sp->split) {
		usflush (Curproc->output);
		tprintf ("%c%c", IAC, CLEARSPLIT);	/* clears Current->split */
		usflush (Curproc->output);
		sp->split = 0;
	}
	return 0;
}
#endif



#ifdef TUTOR
extern int Sinfo;
#endif



int
dombhelp (int argc, char *argv[], void *p)
{
char buf[255];
FILE *fp;
struct mbx *m;

	m = (struct mbx *) p;

#ifdef MBFWD
	if ((m->stype == 'F') && (m->privs & SYSOP_CMD)) {
		forwardingSummary ();
		return 0;
	}
#endif
	if (m->stype == 'H')
		return (doipheard (argc, argv, p));
	if (m->stype == 'P')
		return (dombiproute (argc, argv, p));

	buf[0] = '\0';
	/* check for "?" and "help" here */
	if (*argv[0] == '?' || *argv[0] == 'h') {
		bbscolorcls (m);
		bbscolorchange (m, "07");
		(void) dombxhelp (argc, argv, p);
		if (*argv[0] == '?' && argc == 1) {
			bbscolorchange (m, "0B");
			tputs (Alsomenu);
			tputs (Alsomenu2);
			if (m->privs & SYSOP_CMD)
				tputs (Sysopmenu);
			bbscolorchange (m, "09");
			sprintf (buf, "%s/help.hlp", Helpdir);
			goto also;
		}
		return 0;
	}
#ifdef TUTOR
	if (Sinfo != -1) {
		log (m->user, logtutor, m->name);
		m->state = MBX_TUTOR;
		tutorserv (m->name, m, 1, m->usecolor, 0);
	} else {
#endif
		tprintf (Nosversion, Version, Compiled);
		tprintf ((BbsUsers == 1) ? Only : CurUsers, BbsUsers);
		sprintf (buf, "%s/sysinfo.hlp", Helpdir);
also:
		if ((fp = fopen (buf, READ_TEXT)) != NULLFILE) {
			(void) sendfile (fp, Curproc->output, ASCII_TYPE, 0);
			(void) fclose (fp);
		} else {
			if (argc > 1 && *argv[0] == 'h')
				tputs (nohelp);
		}
#ifdef TUTOR
	}
#endif
	return 0;
}



#ifdef MAILCMDS
extern char Badmsg[];
extern char Nomail[];



/* Move messages from current area to another */
int
dombmovemail (int argc, char *argv[], void *p)
{
int num, i;
int movebuf[NARG];
char *to;
struct mbx *m;
char buf[MBXLINE];
FILE *Mfile;		/* file to move to */
struct let *cmsg;
long size;
int thisone, wrcntr, sawempty;
struct let lt;
char *tmpbuf;
int msg, maxmsg;

	m = (struct mbx *) p;

	if (argc == 1) {
		char const *which;

		which = (m->stype == 'M') ? "move" : (m->stype == 'W') ? "write" : "copy";
		tprintf ("Syntax: M%c area - %s current message\n        M%c n1 [n2...] %s - %s message n1 (n2...)\n",
			 m->stype, which, m->stype, (m->stype == 'W') ? "filename" : "area", which);
		return 0;
	}
	if (m->mfile == NULLFILE) {
		tputs (Nomail);
		return 0;
	}
	if (argc == 2) {
		/* NO message #, use current message */
		num = 1;
		to = argv[1];
		movebuf[0] = m->current;
	} else {
		/* Scan all message # to move */
		num = 0;
		for (i = 1; i < argc - 1; i++) {
			tmpbuf = strchr (argv[i], '-');
			msg = atoi (argv[i]);
			if (tmpbuf == NULLCHAR)
				maxmsg = msg;
			else
				maxmsg = atoi (++tmpbuf);
			if (!msg || maxmsg < msg) {
				tprintf (Badmsg, argv[i]);
				continue;
			}
			for (; msg <= maxmsg; msg++) {
				if (msg > m->nmsgs)
					tprintf (Badmsg, msg);
				else
					movebuf[num++] = msg;
				if (num == NARG)
					break;
			}
			if (num == NARG)
				tprintf (onlyfirst, NARG);
		}
		to = argv[argc - 1];
	}
	if (m->stype != 'W')
		(void) nntp_name_expansion (to);

	/* Now try to lock the destination file, if not a write */
	if (m->stype != 'W')
		if (mlock (Mailspool, to) == -1) {
			tprintf (cannotlock, to);
			return 0;
		}
	/* open the destination file for appending */
	if (m->stype == 'W') {
		char *pp;

		pp = permtest (m->path, m->privs, to, STOR_CMD, NULLCHAR, 0);
		if (!pp)
			return 0;
		strncpy (buf, pp, MBXLINE);
		free (pp);
	} else
		sprintf (buf, "%s/%s.txt", Mailspool, to);
	if ((Mfile = fopen (buf, "a")) == NULLFILE) {
		tprintf ("Can't open '%s'\n", buf);
		if (m->stype != 'W')
			rmlock (Mailspool, to);
		return 0;
	}

	/* Okay, let's do the work */
	for (i = 0; i < num; i++) {
		thisone = movebuf[i];	/*lint !e644 */
		cmsg = &m->mbox[thisone];
		/* find start of this message */
		fseek (m->mfile, cmsg->start, 0);
		size = cmsg->size;
		(void) fflush (Mfile);
		lt.size = cmsg->size;
		lt.bid = cmsg->bid;
		lt.status = 0;
		lt.start = (long) filelength (fileno (Mfile));
		wrcntr = 0;
		sawempty = 0;

		/* now read this message */
		while (size > 0 && fgets (buf, MBXLINE, m->mfile) != NULLCHAR) {
			kwait (NULL);
			if (!sawempty && strlen (buf) == 1)	/* "\n" - dunno about DOS */
				sawempty = 1;
			if (m->stype == 'W' && !sawempty)	/* trim headers on "MW" */
				trimrightCR (buf);
			if (m->stype != 'W' || wrcntr > 2)
				fputs (buf, Mfile);
			wrcntr += 1;
			size -= (long) strlen (buf);
		}
#ifdef USERLOG
		if (m->stype == 'M' || m->stype == 'C') {
			updateCtl (to, &lt);
		}
#endif
		/* delete this message, if moving */
		if (m->stype == 'M') {
			cmsg->status |= BM_DELETE;
			cmsg->status &= ~BM_ONHOLD;
			statusCtl (m->area, "ctl", cmsg, thisone, 0);
			if (!issysarea (m->area))
				m->change |= CHG_DELETE;
		}
		tprintf (message, thisone, (m->stype == 'M') ? "moved" : (m->stype == 'W') ? "written" : "copied");
	}
	(void) fclose (Mfile);
	if (m->stype != 'W') {
#ifdef ALERTMONITOR
		alertarea (to);
#endif
		rmlock (Mailspool, to);
	}
	return 0;
}
#endif



static void
expertstat (struct mbx *m)
{
char const *p;
int32 flag;

	switch (m->stype) {
		case 'M':
			tprintf (moreafter, m->morerows);
			return;
		case 'A':
			p = areaadded;
			flag = m->sid & MBX_AREA;
			break;
		case 'N':
			p = nameadded;
			flag = m->sid & MBX_NRID;
			break;
		case 'S':
			p = newscanlogin;
			flag = m->sid & MBX_STATS;
			break;
		case 'G':
			p = usaANSI;
			flag = m->sid & MBX_GFX;
			break;
		case 'P':
			tprintf (permissionlevel, m->privs, m->privs);
			return;
#ifdef DELEGATE
		case 'D':
			display_delegation (m->name);
			p = fwddelegation;
			flag = m->sid & MBX_DELEGATE;
			break;
#endif
#ifdef LZW
		case 'C':
			p = usecompressed;
			flag = m->sid & MBX_TNOS;
			break;
#endif
		case 'X':
		case ' ':
			p = expertprompts;
			flag = m->sid & MBX_EXPERT;
			break;
		default:
			return;
	}
	tprintf ("%s: o%s\n", p, (flag) ? "n" : "ff");
	return;
}



static int
dombgetall (struct mbx *m)
{
char *args[2];
char buf[4];

	tprintf (usersettings, m->name);
	m->stype = 'M';			expertstat (m);
	m->stype = 'A';			expertstat (m);
	m->stype = 'N';			expertstat (m);
	m->stype = 'S';			expertstat (m);
	m->stype = 'G';			expertstat (m);
	m->stype = 'C';			expertstat (m);
#ifdef LZW
	m->stype = 'X';			expertstat (m);
#endif
	m->stype = 'P';			expertstat (m);
	strcpy (buf, "F");
	args[1] = buf;
	(void) dombget (2, args, m);
	strcpy (buf, "Si");
	args[1] = buf;
	(void) dombget (2, args, m);
#ifdef DELEGATE
	strcpy (buf, "D");
	args[1] = buf;
	(void) dombget (2, args, m);
#endif
	tputs (useSET);
	return 0;
}



int
dombget (int argc, char *argv[], void *p)
{
struct mbx *m;
int cnt;

	if (argc == 1)
		return (dombgetall (p));
	m = (struct mbx *) p;
	m->stype = (char) toupper (*argv[1]);
	if (m->stype != 'F' && strnicmp (argv[1], "Si", 2)) {
		expertstat (m);
		return 0;
	}
	sprintf (m->line, "%s/%s%s", (m->stype == 'F') ? FingerDir : Signature, m->name, (m->stype == 'S') ? ".sig" : "");
	tprintf (getheader, (m->stype == 'F') ? "Finger" : "Signature");
	cnt = DisplayFile (m->line, m->user);
	tprintf (gettrailer, (cnt) ? "End of" : "No", (m->stype == 'F') ? "Finger" : "Signature");
	return 0;
}



#ifdef MAILCMDS
int
doreply (int argc, char *argv[], void *p)
{
register struct mbx *m;

	m = (struct mbx *) p;
	m->stype = 'R';
	return (dosend (argc, argv, p));
}
#endif



int
douser (int argc, char *argv[], void *p)
{
register struct mbx *m;

	m = (struct mbx *) p;
	m->stype = 'L';
	return (dombusers (argc, argv, p));
}



#ifdef TIPMAIL
int
dombslip (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
register struct mbx *m;

	m = (struct mbx *) p;
	if (m->type != TIP) {
		tputs (noconfig);
		return (0);
	}
	if (m->privs & NO_SLIP) {
		tputs (Noperm);
		return 0;
	}
	suspendTipMail (m);
	return -2;
}
#endif



int
dombexpert (int argc, char *argv[], void *p)
{
struct mbx *m;
#ifdef LZW
struct usock *up;
#endif

	m = (struct mbx *) p;

	switch (m->stype) {
		case 'M':
			if (argc == 1) {
				tprintf (moreafter, m->morerows);
				return 0;
			} else {
				m->morerows = atoi (argv[1]);
			}
			break;
		case 'A':
			m->sid ^= MBX_AREA;
			break;
		case 'G':
			m->sid ^= MBX_GFX;
			m->usecolor ^= 1;
			break;
		case 'N':
			m->sid ^= MBX_NRID;
			break;
		case 'S':
			m->sid ^= MBX_STATS;
			break;
#ifdef DELEGATE
		case 'D':
			if (Mbdelegate) {
				m->sid ^= MBX_DELEGATE;
				if (m->sid & MBX_DELEGATE)
					setup_delegate (m);
				else
					clear_delegate (m);
			} else {
				tputs (delegationdisabled);
				m->sid &= ~MBX_DELEGATE;
				return 0;
			}
			break;
#endif
#ifdef LZW
		case 'C':
			m->sid ^= MBX_TNOS;
			up = itop (m->user);
			if ((m->sid & MBX_TNOS) || (up->zout != NULLLZW))
				togglelzw (m->user, m->sid & MBX_TNOS);
			break;
#endif
		case ' ':
		case 'X':
			m->sid ^= MBX_EXPERT;
			break;
		default:
			return 0;
	}
	m->update = 1;
	expertstat (m);
	return 0;
}



int
dombexpand (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
struct list *ap, *list;
char *arg, *orig, *newaddr, buf[LINELEN];
int loopindex;

	tprintf (expandsto, argv[1]);
	arg = argv[1];
	list = NULLLIST;
	orig = strdup (arg);
	/* rewrite address if possible */
	if ((newaddr = rewrite_address (arg, 0)) != NULLCHAR)
		if (strcmp (newaddr, arg) == 0) {
			free (newaddr);
			newaddr = NULLCHAR;
		} else {
			strncpy (buf, newaddr, LINELEN);
			arg = buf;
		}
	list = NULLLIST;
	(void) expandalias (&list, arg, orig);
	free (orig);
	if (list == NULLLIST) {
		tprintf ("nothing");
		return 0;
	}
	if (strcmp (list->val, arg) == 0 && list->next == NULLLIST)
		if (newaddr == NULLCHAR) {
			tprintf ("'%s'\n", arg);
			del_list (list);
			return 0;
		}
	ap = list;
	loopindex = 0;
	while (ap->next != NULLLIST) {
		if (!(loopindex++ % 2))
			tputc ('\n');
		tputs ("     ");
		tprintf ("%-32.32s", ap->val);
		ap = ap->next;
	}
	tprintf ("%s     %s\n", (loopindex % 2) ? "" : "\n", ap->val);
	del_list (list);
	free (newaddr);
	return 0;
}



#ifdef MBFWD
int
dombroute (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
{
char *arg, *newaddr;
#if 0
char buf[LINELEN];
#endif
int k, loopindex;

	arg = argv[1];
	if (!arg) {
		tputs (noaddress);
		return 1;
	}
	tprintf (addressroutes, argv[1]);
	/* rewrite address if possible */
	if ((newaddr = rewrite_address (arg, 0)) == NULLCHAR)
		newaddr = strdup (argv[1]);
	for (loopindex = 0, k = 0; k < Numfwds; k++)
		if (AREAlookup (newaddr, k)) {
			if (!(loopindex++ % 3))
				tputs ("\n   ");
			tprintf ("%-25.25s", MyFwds[k].name);
		}
	tputc ('\n');
	free (newaddr);
	return 0;
}
#endif



#ifdef RMAIL
int
dombrmail (int argc, char *argv[], void *p)
{
int orgchange;
struct mbx *m;
char *cp;

	m = (struct mbx *) p;
	if (!(m->sid & MBX_SID)) {	/* not available to BBSs */
		orgchange = m->change;
		m->change = -1;
		m->stype = 'C';
		cp = mallocw (strlen (argv[1]) + 8);
		sprintf (cp, "rmail@%s", argv[1]);
		argv[1] = cp;
		(void) dosend (argc, argv, p);
		m->change = orgchange;
		free (cp);
	}
	return 0;
}
#endif



#ifdef LZW
void
togglelzw (int soc, int mode)
{
struct usock *up;

	if ((up = itop (soc)) == NULLUSOCK)
		return;
	tprintf ("%c%c%c%c", IAC, COMPRESSED, (Lzwmode) ? 'f' : 'c', Lzwbits);
	usflush (soc);
	if (mode) {
		lzwinit (soc, Lzwbits, Lzwmode);
		up->zin->maxbits = -1;
	} else
		lzwfree (up);
	usflush (soc);
	if (socklen (soc, 0))	/* discard any remaining input */
		(void) recv_mbuf (soc, NULL, 0, NULLCHAR, 0);
}
#endif



int
dombset (int argc, char *argv[], void *p)
{
struct mbx *m;

	m = (struct mbx *) p;
	m->stype = (char) toupper (*argv[1]);
	if (!strnicmp (argv[1], "Si", 2))
		m->stype = '$';
	if (m->stype == 'F' || m->stype == '$')
		return (dombupload (argc, argv, p));
	else
		return (dombexpert (argc - 1, &argv[1], p));
}



#ifdef USERLOG

/* Routine for quickly checking an area for messages that are new to this user.
   This routine reads the area.ctl files, which contains a bid for each message.  */

static int
quickstat (register struct mbx *m, char *area)
{
long last, newlast, lastone;
char oldarea[64], buf[256];
FILE *fp;
register int retval = 0;

	last = m->lastread;
	newlast = m->newlastread;
	m->lastread = 0;
	strncpy (oldarea, m->area, 64);
	strncpy (m->area, area, 64);
	(void) strlwr (m->area);
	(void) nntp_name_expansion (m->area);
	/* only read last read message-id if current area is a public area
	 * and not 'help' or area starts with 'sys'
	 * read in all cases if a SYSOP
	 */

	if ((stricmp (area, "help") && isarea (area)) || !strnicmp (m->area, "sys", 3) || (m->privs & SYSOP_CMD))
		getlastread (m);
	kwait (NULL);
	lastone = m->lastread;
	m->lastread = last;
	m->newlastread = newlast;
	sprintf (buf, "%s/control/%s.ctl", Mailspool, m->area);
	if ((fp = fopen (buf, READ_BINARY)) != NULLFILE) {
		struct let lt;
		long size;
		size = (long) (filelength (fileno (fp)) - sizeof (struct let));

		fseek (fp, size, SEEK_SET);
		(void) fread (&lt, sizeof (struct let), 1, fp);

		(void) fclose (fp);
		if (lt.bid > lastone)
			retval = 1;
	}
	strncpy (m->area, oldarea, 64);
	kwait (NULL);
	return retval;
}



void
quickscan (struct mbx *m, int olduser, int usename)
{
char const *cp;
char *cp2;
FILE *fp;
char buf[MBXLINE];
char out[MBXLINE];
char const *initmsg;
int found = 0, length = 3;

	cp = Arealist;
	if ((m->privs & SYSOP_CMD) && (!access (AreaSlist, 0)))
		cp = AreaSlist;
	if ((fp = fopen (cp, READ_TEXT)) == NULLFILE)
		return;
	if (olduser)
		initmsg = unreadmessages;
	else
		initmsg = newmessages;
	strcpy (out, "   ");
	while (fgets (buf, MBXLINE, fp) != NULLCHAR) {
		kwait (NULL);
		if (isalnum (buf[0])) {	/* skip comments */
			if ((cp2 = strpbrk (buf, " \t\n\r")) != NULLCHAR)
				*cp2 = '\0';
			/*			statarea (m, buf, &total, &new, &hold, 0);
			if (new)	{		*/
			if (quickstat (m, buf)) {
				if (!found) {
					tprintf (initmsg, (usename) ? m->name : "You", (usename) ? "s" : "ve");
					found++;
				}
				length += (int) (strlen (buf) + 2);
				strcat (out, buf);
				strcat (out, "  ");
			}
			if (length > 70) {
				tputs (out);
				tputc ('\n');
				out[3] = 0;
				length = 3;
			}
		}
	}
	if (length != 3) {
		tputs (out);
		tputc ('\n');
	}
	(void) fclose (fp);
	if (!found)
		tprintf (nounreadmessages, (usename) ? m->name : "You", (usename) ? "is" : "are");
}
#endif



#if defined(FOQ_CMDS)
int
dombquote (int argc, char *argv[], void *p)
{
char *host, buf[8], *newargv[3], buf2[2];
struct mbx *m;

	m = (struct mbx *)p;
	host = Hostname;
	if (argc == 2)
		host = argv[1];
	buf2[0] = 't';
	buf2[1] = 0;
	newargv[0] = buf2;
	newargv[1] = host;
	sprintf (buf, "%d", IPPORT_QUOTE);
	newargv[2] = buf;
	SecureTelnet = 0;
	m->stype = '+';
	return dombtelnet (3, newargv, p);
}
#endif



static void
informGroup (
char *action OPTIONAL,
char *group,
char *user,
char *buf,
FILE *old,
FILE *new,
FILE *in
) {
char subj[68], *cp;

	sprintf (subj, inform_str, user, group);
	while (fgets (buf, 50, old) != NULLCHAR) {
		fputs (buf, new);
		if (*buf != ' ')
			break;
		if (buf[strlen (buf) - 2] == 'I') {
			if ((cp = strchr (&buf[1], ' ')) != NULLCHAR)
				*cp = 0;
			if (in)
				rewind (in);
			(void) rdaemon (in, NULLCHAR, NULLCHAR, &buf[1], subj, 'P', 0);
		}
	}
}



int
isgroup (char *group)
{
FILE *old;
register int len, retval = 0;
char buf[80];

	if (!group)
		return 0;
	len = (int) strlen (group);

	if ((old = fopen (Group, UPDATE_TEXT)) == NULLFILE)
		return 0;
	while (fgets (buf, 80, old) != NULLCHAR) {
		if (!strnicmp (buf, group, (unsigned) len) && buf[len] == ':') {
			retval = 1;
			break;
		}
	}
	(void) fclose (old);
	return retval;
}



int
groupcommand (
char *action,
char *group,
char *user,
int local,
FILE **out,
FILE *in
) {
FILE *new, *old;
char *newname, *cp;
int found = 0, retval = 0;
char which, buf[80], subj[68];
unsigned len;

	if ((old = fopen (Group, UPDATE_TEXT)) == NULLFILE)
		if ((old = fopen (Group, CREATE_TEXT)) == NULLFILE)
			return 0;
	newname = (char *) mallocw (strlen (Group) + 5);
	sprintf (newname, "%s.new", Group);
	if ((new = fopen (newname, CREATE_TEXT)) == NULLFILE) {
		(void) fclose (old);
		free (newname);
		return 0;
	}
	*action = (char) tolower (*action);
	if (*action == 'l') {	/* list groups */
		if (!local)
			fprintf (new, groupindex);
		while (fgets (buf, 50, old) != NULLCHAR) {
			if (*buf != ' ' && (cp = strchr (buf, ':')) != 0) {
				*cp++ = '\n';
				*cp = 0;
				if (!local)
					fputs (buf, new);
				else
					tputs (buf);
			}
		}
		if (!local) {
			rewind (new);
			strcpy (subj, "GROUP LISTING");
			(void) rdaemon (new, NULLCHAR, NULLCHAR, user, subj, 'P', 0);
		}
		retval = 1;
		goto confuzed;
	}
	if (group == NULLCHAR)
		goto confuzed;

	len = strlen (group);

	if (*action != 'a') {
		while (fgets (buf, 50, old) != NULLCHAR) {
			if (!strnicmp (buf, group, len) && buf[len] == ':') {
				found = 1;
				break;
			}
			fputs (buf, new);
		}
		if (!found)
			goto confuzed;
		if (*action != 'd')
			fputs (buf, new);
	}
	switch (tolower (*action)) {
		case 'a':
			fprintf (new, "%s:\n\n", group);
			break;
		case 'i':
			fprintf (new, " %s I\n", user);
			break;
		case 's':
			fprintf (new, " %s S\n", user);
			rip (buf);
			if (local)
				(void) DisplayFile (&buf[len + 2], Curproc->output);
			else
				*out = fopen (&buf[len + 2], READ_TEXT);
			informGroup (action, group, user, buf, old, new, in);
			break;
		case 'u':
			len = strlen (user);
			which = (char) ((strlen (action) < 3) ? 's' : tolower (action[2]));
			while (fgets (buf, 50, old) != NULLCHAR) {
				if (*buf != ' ') {
					fputs (buf, new);
					break;
				}
				if (strnicmp (&buf[1], user, (unsigned) len) ||
				    tolower (buf[strlen (buf) - 2]) != which)
					fputs (buf, new);
				else
					break;
			}
			break;
		case 'd':
			while (fgets (buf, 50, old) != NULLCHAR)
				if (*buf != ' ') {
					if (*buf != '\n')
						fputs (buf, new);
					break;
				}
			break;
		default:
			goto confuzed;
	}
	while (fgets (buf, 50, old) != NULLCHAR)
		fputs (buf, new);
	(void) fclose (new);
	(void) fclose (old);
	unlink (Group);
	(void) rename (newname, Group);
	free (newname);
	return 1;

confuzed:
	(void) fclose (new);
	(void) fclose (old);
	unlink (newname);
	free (newname);
	return retval;
}



int
dombgroup (int argc, char *argv[], void *p)
{
struct mbx *m;
char cmd;
int retval = 1;

	m = (struct mbx *) p;

	cmd = (char) tolower (*argv[1]);
	if (cmd != 's' && cmd != 'u' && cmd != 'l' && (m->privs == -1 || !(m->privs & SYSOP_CMD)))
		retval = 0;

	if (!retval || (cmd != 'l' && argc < 3) || !groupcommand (argv[1], (argc > 2) ? argv[2] : NULLCHAR, (argc > 3 && m->privs & SYSOP_CMD) ? argv[3] : m->name, 1, (FILE **) 0, (FILE *) 0))
		tputs ("Denied!\n");
	else
		tputs ("Approved!\n");
	return 0;
}



#ifdef TUTOR
extern int Stutor, Snews;

int
dombtutor (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
struct mbx *m;

	m = (struct mbx *) p;

	if (Stutor == -1) {
		tputs ("Tutorial server not enabled\n");
		return 0;
	}
	log (m->user, logtutor, m->name);
	m->state = MBX_TUTOR;
	tutorserv (m->name, m, 0, m->usecolor, 0);
	return 0;
}



int
dombnews (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
struct mbx *m;

	m = (struct mbx *) p;

	if (Snews == -1) {
		tputs (newsdisabled);
		return 0;
	}
	log (m->user, lognews, m->name);
	m->state = MBX_TUTOR;
	tutorserv (m->name, m, 2, m->usecolor, 0);
	return 0;
}
#endif



char *
wpage_exp (
char *to,
int hier OPTIONAL,
int exphome OPTIONAL		/* append and expand home bbs name ? */
) {
#if (defined(ASKHOME) || defined(WPAGES))
char *cp = (char *) -1;
char *cp2;
char origbbs[40];
#ifdef WPAGES
char buf[12];
#endif

	if (strchr (to, '%'))
		return (to);
#ifdef ASKHOME
	/* This section adds the HOME BBS to any messages to known users that do
	   not have "@bbs" portions of the TO address, except when user is a BBS	*/

	cp2 = strchr (to, '@');
	cp = NULLCHAR;
	if ((cp2 == NULLCHAR) && (cp = findhome (to)) != (char *) NULL) {
		if (cp != (char *) -1) {	/* home no here */
			sprintf (origbbs, "%s@%s", to, cp);
			free (to);
			free (cp);
			to = strdup (origbbs);
		}
	}
#endif /* ASKHOME */
#ifdef WPAGES
	/* This section looks up the destination bbs in the White Pages file, if user not a BBS
	   If found, the complete address is substituted	*/

#ifdef ASKHOME
	if (cp != (char *) -1) {	/* if home not here */
#else
	if (cp == (char *) -1) {
#endif
		cp2 = strchr (to, '@');
		if (cp2 == NULLCHAR) {	/* no dest BBS given */
			if ((cp2 = wpageCheck (to, 0, 0, 'X')) != NULLCHAR) {	/* found the bbs */
				free (to);
				(void) strlwr (cp2);
				to = cp2;
			}
			cp2 = strchr (to, '@');
		}
		if (hier && cp2 != NULLCHAR) {	/* we have a dest bbs, get correct, full haddress */
			cp = cp2++;
			if ((cp2 = wpageCheck (cp2, 1, 0, 'X')) != NULLCHAR) {
				*cp = '\0';
				sprintf (origbbs, "%s@%s", to, cp2);
				free (to);
				free (cp2);
				(void) strlwr (origbbs);
				to = strdup (origbbs);
			}
		}
	} else if (exphome) {
		(void) pax25 (buf, Mycall);
		if ((cp = strpbrk (buf, DIGI_IDS)) != NULLCHAR)
			*cp = '\0';	/* remove SSID */
		sprintf (origbbs, "%s@%s%s%s", to, buf,
			 (Mbhaddress != NULLCHAR) ? "." : "",
			 (Mbhaddress != NULLCHAR) ? Mbhaddress : "");
		free (to);
		to = strdup (origbbs);
	}
#endif /* WPAGES */
#endif /* ASKHOME || WPAGES */
	kwait (NULL);
	return (to);
}



#ifdef WPAGES
extern int MbWpages;

int
dombwpages (int argc, char *argv[], void *p)
{
struct mbx *m;
char buf[80], *cp, *cp2, origchar = 0;

	m = (struct mbx *) p;

	if (argc < 2) {
		tprintf (wp_syntax, m->privs & SYSOP_CMD ? " [@bbsname]" : "");
		return 0;
	}
	if (!(m->privs & SYSOP_CMD) && argc > 2) {
		tputs (sorry);
		return 0;
	}
	if (argc == 2) {	/* lookup */
		cp = wpageCheck (argv[1], 0, 0, 'X');
		if (!cp)
			tputs (notinwp);
		else {
			cp2 = wpage_exp (strdup (argv[1]), 1, 1);
			(void) strupr (cp2);
			tprintf (wplists2, cp2);
			free (cp2);
			free (cp);
		}
	} else {		/* else, setting it */
		if (argv[2][0] != '@')
			tputs (wp_sysopsyntax);
		else {
			if ((cp = strchr (argv[2], '.')) != NULLCHAR) {
				sprintf (buf, &argv[2][1]);
				(void) strupr (buf);
				wpageAdd (buf, 1, 1, 'U');
				origchar = *cp;
				*cp = 0;
			}
			sprintf (buf, "%s%s", argv[1], argv[2]);
			(void) strlwr (buf);
			wpageAdd (buf, 0, 1, 'U');
			if (cp && origchar)
				*cp = origchar;
			tputs (updating);
			tflush ();
			kwait (NULL);
			RenewWPages ();
		}
	}
	return 0;
}



static void
wpageUpdate (
FILE *fp,
char *string,
char *entry,
char *oldtime OPTIONAL,
int bbs,
int updateit,
char which
) {
time_t now;
char buf[LINELEN], *cp;
char compare[40];
int thesize, changed = 0;
int thelen;
long offset;

	/* parameters string and oldtime are not being used, yet */

	thesize = (bbs) ? 32 : 13;
	(void) time (&now);

	/* make a comparison string from 'string' */
	strncpy (buf, string, (unsigned) thesize);
	buf[thesize] = 0;
	if ((cp = strchr (buf, ' ')) != NULLCHAR)
		*cp = 0;

	/* make a comparison string from 'entry' */
	strncpy (compare, entry, (unsigned) thesize);
	compare[thesize] = 0;
	if ((cp = strchr (compare, ' ')) != NULLCHAR)
		*cp = 0;

	if (!bbs && (cp = strchr (buf, '.')) != NULLCHAR)
		*cp = 0;

	/* has this entry changed ? */
	if (stricmp (compare, buf))
		changed = 1;

	/* if we are updating it, take the NEW entry */
	if (updateit && which != 'G')
		entry = buf;

	/* write the old or new entry, with the new timestamp and close file */
	fprintf (fp, "%-*s %-14ld\n", thesize, entry, now);
	(void) fclose (fp);
	kwait (NULL);

	/* If we were updating the entry and the entry DID change..... */
	if (changed && which != 'T') {
		if (updateit) {
			/* now we update the temporary wpages */
			if (!bbs) {	/* no temp for wpagebbs */
				char buf2[LINELEN];

				if ((cp = strchr (buf, '@')) != NULLCHAR)
					thelen = (int) (cp - buf);
				else
					thelen = (int) strlen (buf);
				if ((fp = fopentmp (WhitePages, READ_TEXT)) != NULLFILE) {
					while (offset = ftell (fp), fgets (buf2, LINELEN - 1, fp)) {
						if (!strnicmp (buf2, string, (unsigned) thelen)) {
							fseek (fp, offset, SEEK_SET);
							break;
						}
					}
				} else
					fp = fopentmp (WhitePages, APPEND_TEXT);
				fprintf (fp, "%-*s %-14ld\n", thesize, buf, now);
				(void) fclose (fp);
				kwait (NULL);
			}
			/* record it in the daily WP Update temp file */
			if (bbs)
				add_WPUpdate (buf, buf, NULLCHAR, which);
			else {
				cp = strchr (buf, '@');
				if (cp)
					*cp++ = 0;
				add_WPUpdate (buf, cp, NULLCHAR, which);
			}
		}
	}
}



/* Returns a copy (strdup'ed) of existing entry if string is in whitepages file */
char *
wpageCheck (char *string, int bbs, int updateit, char which)
{
/* note: updateit is NEVER set except sometimes when called from wpageAdd() */
register FILE *fp;
char buf[LINELEN], *retval = NULLCHAR;
register char *cp;
int result, thelen;
int entrysize;
char const *fname;
int skipupdate = 0;

	if (!MbWpages || string == NULLCHAR || !*string)
		return (retval);
	if (!strnicmp (string, "sysop", 5))
		return (retval);
	entrysize = (bbs) ? WPAGEBBSENTRYSIZE : WPAGEENTRYSIZE;
	fname = (bbs) ? WhitePagesBBS : WhitePages;
	if ((cp = strchr (string, (bbs) ? '.' : '@')) != NULLCHAR)
		thelen = (int) (cp - string);
	else
		thelen = (int) strlen (string);
	result = searchfile (string, fname, buf, entrysize, 0, thelen);
	if (result == -1 && !updateit) {
		if ((fp = fopennew (fname, READ_TEXT)) != NULLFILE) {
			while (fgets (buf, LINELEN - 1, fp)) {
				if (!strnicmp (buf, string, (unsigned) thelen)) {
					result = 0;
					skipupdate = 1;
					break;
				}
			}
			(void) fclose (fp);
		}
	}
	if (result != -1) {	/* found it */
		if (((cp = strpbrk (buf, " \t")) != NULLCHAR))
			*cp = '\0';
		rip (buf);
		retval = strdup (buf);
		if (!skipupdate) {
			fp = fopen (fname, UPDATE_TEXT);
			if (fp != NULLFILE)	{
				fseek (fp, (long) (long) result * (long) entrysize, 0);
				wpageUpdate (fp, string, buf, &buf[(bbs) ? 33 : 14], bbs, updateit, (which != 'G') ? which : 'X');
				/* wpageUpdate does the (void) fclose(fp); */
			}
		}
	}
	return retval;
}



void
wpageAdd (char *entry, int bbs, int updateit, char which)
{
time_t now;
FILE *fp;
char *last, buf[14];

	if (!MbWpages || entry == NULLCHAR)
		return;
	if (!strnicmp (entry, "sysop", 5))
		return;
	last = wpageCheck (entry, bbs, updateit, which);
	if (!last) {
		(void) time (&now);
		if (!bbs) {
			strncpy (buf, entry, 13);
			buf[13] = 0;
			if ((last = strchr (buf, '.')) != NULLCHAR)
				*last = 0;
			entry = buf;
		}
		if ((fp = fopennew ((bbs) ? WhitePagesBBS : WhitePages, APPEND_TEXT)) != NULLFILE) {
			fprintf (fp, "%-*s %-14ld\n", (bbs) ? 32 : 13, entry, now);	/* Save h_addr in whitepages file */
			kwait (NULL);
			(void) fclose (fp);
		}
		if (bbs)
			add_WPUpdate (entry, entry, NULLCHAR, which);
		else {
			last = strchr (entry, '@');
			if (last)
				*last++ = 0;
			add_WPUpdate (entry, last, NULLCHAR, which);
		}
	} else
		free (last);
}
#endif



#ifdef MAILCMDS
/* Returns 1 if string is in history file or if 2 string appears to be a
 * message id generated by our system or 0 if not ours or in history file.
 */
int
msgidcheck (register char *string)
{
register char *cp;


	if (string == NULLCHAR)
		return 0;
	/* BID's that we have generated ourselves are not kept in the history
	 * file. Such BID's are in the nnnn_hhhh form, where hhhh is a part of
	 * our hostname, truncated so that the BID is no longer than 11
	 * characters.
	 */
	if ((cp = strchr (string, '_')) != NULLCHAR && *(cp + 1) != '\0' &&
	    strnicmp (cp + 1, Hostname, strlen (cp + 1)) == 0
	    && *((Hostname) + strlen (cp + 1)) == '.'
	    && (int) strspn (string, "0123456789") == (int) (cp - string))
		return 2;

	return (bid_check (string));
}
#endif



int
dombfdesc (int argc OPTIONAL, char *argv[], void *p)
{
struct mbx *m;
char *file, *cp, *nm;
FILE *fp;
int k;
#ifdef DOS_GETFILEATTR
unsigned attr;
char name[14];
#endif

	m = (struct mbx *) p;
	if ((file = permtest (m->path, m->privs, argv[1], RETR_CMD, NULLCHAR, 0)) == NULLCHAR) {
		tputs (Noperm);
		return 0;
	}
#ifdef DOS_GETFILEATTR
	(void) _dos_getfileattr (file, &attr);
#endif
	nm = strrchr (file, '/');
	if (nm == NULLCHAR)
		return 0;
	*nm++ = 0;
#ifdef DOS_GETFILEATTR
	if (attr & FA_DIREC) {
		sprintf (name, "%s/", nm);
		nm = name;
	}
#endif
	cp = pathname (file, index_dat);
	if ((fp = fopen (cp, APPEND_TEXT)) == NULLFILE) {
		tputs ("Can't open description file!\n");
		return 0;
	}
	fprintf (fp, "%-12s", nm);
	free (file);
	free (cp);
	tputs (filedesc);
	for (k = 1; k < 38; k++)
		tputc (uchar('0' + (k % 10)));
	tputc ('\n');
	while (mbxrecvline (m) != -1) {
		if (!strnicmp (m->line, "/ex", 3)) {
			(void) fclose (fp);
			return (0);
		}
		fprintf (fp, " %-37.37s\n", skipwhite (m->line));
	}
	(void) fclose (fp);
	return (-2);		/* socket closed here! */
}



#ifdef FILECMDS
int
dodownload (int argc, char *argv[], void *p)
{
struct mbx *m;
FILE *fp;
char *file;

	m = (struct mbx *) p;
	if (m->stype == 'I')
		return (dowhat (argc, argv, p));
	if (m->stype == 'E')
		return (dozap (argc, argv, p));

	if ((file = permtest (m->path, m->privs, argv[1], RETR_CMD, NULLCHAR, 0)) == NULLCHAR) {
		tputs (Noperm);
		mail_error (downloaddenied, m->name, cmd_line (argc, argv, m->stype));
		return 0;
	}
#ifdef UNIX
	if (!ftpsecuritycheck (file, m->privs, R_OK)) {
		tputs (Noperm);
		mail_error (downloaddenied, m->name, cmd_line (argc, argv, m->stype));
		return 0;
	}
#endif
	m->state = MBX_DOWNLOAD;
#ifdef TIPMAIL
#ifdef XMODEM
	if (m->stype == 'X') {
		if (m->type == TIP) {	/* xmodem send tip only */
			m->state = MBX_XMODEM_TX;
#ifdef MBXTDISC
			/* disable the mbox inactivity timeout timer - WG7J */
			stop_timer (&m->tdisc);
#endif
			(void) doxmodem ('S', file, m);
			return 0;
		} else {
			tputs (xmodemtip);
			return 0;
		}
	}
#endif
#endif
	if ((fp = fopen (file, (m->stype == 'U') ? READ_BINARY : READ_TEXT)) == NULLFILE)
		tprintf (cannotopen, file, SYS_ERRLIST(errno));
	else {
#ifdef MBXTDISC
		/* disable the mbox inactivity timeout timer - WG7J */
		stop_timer (&m->tdisc);
#endif
		if (m->stype == 'U') 	/* uuencode ? */
			(void) uuencode (fp, m->user, file);
		else
			(void) sendfile (fp, m->user, ASCII_TYPE, 0);
		if (fp != NULLFILE)
			(void) fclose (fp);
	}
	free (file);
	return 0;
}
#endif



int
dombupload (int argc, char *argv[], void *p)
{
struct mbx *m;
FILE *fp, *uufp = NULLFILE;
char *file;
#ifdef UNIX
int mode = 0x7fff;
#endif

	m = (struct mbx *) p;
	if (m->stype == 'S') {
		m->stype = 'L';
		return (dombusers (argc, argv, p));
	}
	if (m->stype == 'F') {
		sprintf (m->line, "%s/%s", FingerDir, m->name);
		file = strdup (m->line);
	} else if (m->stype == '$') {
		sprintf (m->line, "%s/%s.sig", Signature, m->name);
		file = strdup (m->line);
#ifdef DELEGATE
	} else if (m->stype == 'M') {
		sprintf (m->line, "%s/%s.del", Signature, m->name);
		file = strdup (m->line);
#endif
	} else if ((file = permtest (m->path, m->privs, argv[1], STOR_CMD, NULLCHAR, 0)) == NULLCHAR) {
		tputs (Noperm);
		mail_error (uploaddenied, m->name, cmd_line (argc, argv, m->stype));
		return 0;
	}
#ifdef UNIX
	if (!ftpsecuritycheck (file, m->privs, W_OK)) {
		tputs (Noperm);
		mail_error (uploaddenied, m->name, cmd_line (argc, argv, m->stype));
		return 0;
	}
#endif

#if defined(FILECMDS) && defined(TIPMAIL) && defined(XMODEM)
	if (m->stype == 'X') {
		if (m->type == TIP) {	/* xmodem receive tip only */
			m->state = MBX_XMODEM_RX;
#ifdef MBXTDISC
			/* disable the mbox inactivity timeout timer - WG7J */
			stop_timer (&m->tdisc);
#endif
			(void) doxmodem ('R', file, m);
			goto secureit;
		} else {
			tputs (xmodemtip);
			return 0;
		}
	}
#endif

	if ((fp = fopen (file, (m->stype == 'U') ? WRITE_BINARY : WRITE_TEXT)) == NULLFILE) {
		tprintf (cannotcreate, file, SYS_ERRLIST(errno));
		free (file);
		return 0;
	}

	if (m->stype == 'U')	{
		uufp = fp;
		fp = tmpfile ();
	}

	log (m->user, logupload, file);
	m->state = MBX_UPLOAD;
	tprintf ("Send %s,  %s", (m->stype == 'F') ? "personal information" : (m->stype == 'S') ? "signature information" : (m->stype == 'M') ? "personal message" : "file", Howtoend);
	for ( ; ; ) {
		if (mbxrecvline (m) == -1) {
			unlink (file);
			break;
		}
		if (*m->line == 0x01 || !stricmp (m->line, "/abort")) {	/* CTRL-A */
			unlink (file);
			tputs (MsgAborted);
			break;
		}
		if (*m->line == CTLZ || !strnicmp ("/ex", m->line, 3) || !stricmp (m->line, "."))
			break;
		fputs (m->line, fp);
#if !defined(UNIX) && !defined(__TURBOC__) && !defined(TNOS_68K)
		/* Needed only if the OS uses a CR/LF
		 * convention and putc doesn't do
		 * an automatic translation
		 */
		if (putc ('\r', fp) == EOF)
			break;
#endif
		if (putc ('\n', fp) == EOF)
			break;
	}
	if (m->stype == 'U')	{
#ifdef UNIX
		mode =
#else
		(void)
#endif
			uudecode (fp, uufp);
		(void) fclose (uufp);
	}

	(void) fclose (fp);

#if defined(FILECMDS) && defined(TIPMAIL) && defined(XMODEM)
secureit:
#endif

#ifdef UNIX
	if (CREATEsecure) {
		(void) chown (file, (uid_t) CREATEuid, (gid_t) CREATEgid);
		(void) chmod (file, (mode_t) (mode & CREATEmask));
	} else if (m->stype == 'U')
		(void) chmod (file, (mode_t) mode);	
#endif
	free (file);
	return 0;
}



void
bbsbump (
char *user,
int zap			/* if zap=1, remove entry, else just mark for bumping */
) {
int i;
struct mbx *m = NULLMBX;

	/* check the mailbox users */
	for (i = 0; i < NUMMBX; i++) {
		if ((m = Mbox[i]) != NULLMBX) {
			if (!stricmp (m->name, user))
				break;
		}
	}
	if (i != NUMMBX && m != NULLMBX) {
		if (zap)
			exitbbs (m);
		else
			m->privs |= EXCLUDED_CMD;
	}
}



int
dobump (int argc, char *argv[], void *p)
{
struct mbx *m;

	m = (struct mbx *) p;
	if (!(m->privs & SYSOP_CMD))
		tputs (sorry);
	else
		bbsbump (argv[1], ((argc > 2) && !strnicmp ("now", argv[2], 3)) ? 1 : 0);
	return 0;
}



int
dombencode (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
struct mbx *m;

	m = (struct mbx *) p;
	if (!(m->privs & SYSOP_CMD))
		tputs (sorry);
	else
		(void) doencode (0, (char **) 0, (void *) 0);
	return 0;
}



#ifdef FILECMDS

/* uuencode a file -- translated from C++; both versions copyright 1990
   by David R. Evans, G4AMJ/NQ0I
*/

static int
uuencode (FILE *infile, int s, char *infilename)
{
int n_read_so_far = 0, n_written_so_far = 0, in_chars, n, mode = 0755;
unsigned long cnt = 0;
unsigned char in[3], out[4], line[100];
#ifdef UNIX
struct stat stb;

	if (stat (infilename, &stb) != -1)
		mode = stb.st_mode & 0777;	/* get real file protection mode */
#endif
	usprintf (s, "begin %03o %s\n", mode, infilename);

	/* do the encode */
	for ( ; ; ) {
		in_chars = (int) fread (in, 1, 3, infile);
		out[0] = in[0] >> 2;
		out[1] = uchar (in[0] << 6);
		out[1] = out[1] >> 2;
		out[1] = out[1] | (in[1] >> 4);
		out[2] = uchar (in[1] << 4);
		out[2] = out[2] >> 2;
		out[2] = out[2] | (in[2] >> 6);
		out[3] = uchar (in[2] << 2);
		out[3] = out[3] >> 2;
		for (n = 0; n < 4; n++)
			out[n] += ' ';
		n_read_so_far += in_chars;
		for (n = 0; n < 4; n++)
			line[n_written_so_far++] = out[n];
		if (((in_chars != 3) || (n_written_so_far == 60)) && n_read_so_far > 0) {
			line[(n_read_so_far + 2) / 3 * 4] = '\0';

			usprintf (s, "%c%s\n", n_read_so_far + ' ', line);
			kwait (NULL);
			cnt += (unsigned long) n_read_so_far;
			n_read_so_far = 0;
			n_written_so_far = 0;
		}
		if (in_chars == 0)
			break;
	}
	if (usprintf (s, " \nend\nsize %lu\n", cnt) == EOF)
		return 1;
	return 0;
}


static int
uudecode (FILE *infile, FILE *outfile)
{
char line[100];
int header = 0;
int n_to_write, n_to_read, n_reads, n, m;
unsigned char b[4], out[3];
int mode = 0x7fff;

	rewind (infile);
	
	/* skip till the (begin) line is eaten */
	while (!header)	{
		(void) fgets(line, 100, infile);
		if (strstr (line, "begin"))	{
			header = 1;
			sscanf (line, "begin %03o", &mode);
		}
	}			

	/* decode the remainder of the file */
	while ((void) fgets (line, 100, infile), strncmp (line, "end", 3))	{
		n_to_write = (int)line[0] - 32;
		n_to_read = ((n_to_write + 2) / 3) * 4;
		if (strlen ((char *)line) - 2 < (unsigned int) n_to_read)	/* EOF apparently */
			n_to_read = 0;
		n_reads = n_to_read / 4;
		for (n = 0; n < n_reads; n++)	{
			for (m = 0; m < 4; m++)
				b[m] = (unsigned char) line[1 + 4 * n + m] - 32;

			/* now do the actual decode */
			out[0] = (unsigned char) (b[0] << 2) | ((b[1] & 0x30) >> 4);
			out[1] = ((b[1] & 0x0f) << 4) | ((b[2] & 0x3c) >> 2);
			out[2] = ((b[2] & 0x03) << 6) | (b[3]);
			if (n_to_write > 3)
				for (m = 0; m < 3; m++)
					fputc(out[m], outfile);
			else
				for (m = 0; m < n_to_write; m++)
					fputc (out[m], outfile);
			n_to_write -= m;
		}
	}
	return (mode);
}


#endif
#endif /* MAILBOX */
