/*
 *	Simple mail user interface for KA9Q IP/TCP package.
 *	A.D. Barksdale Garbee II, aka Bdale, N3EUA
 *	Copyright 1986 Bdale Garbee, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 *	Copyright 1987 1988 Dave Trulli NN2Z, All Rights Reserved.
 *	Permission granted for non-commercial copying and use, provided
 *	this notice is retained.
 *
 *	Ported to NOS at 900120 by Anders Klemets SM0RGV.
 *
 *  Userlogging, 'RM' and 'KM' implementation,
 *  more-prompts for all types
 *  920307 and later, by Johan. K. Reinalda, WG7J/PA3DIS
 *  920719 and later, by Brian A. Lantz, KO4KS
 */
#include "global.h"
#include "ctype.h"
#include "commands.h"
#include "files.h"
#ifdef MSDOS
#include <io.h>
#else
#include <time.h>
#endif
#include "ftpserv.h"
#include "smtp.h"
#include "proc.h"
#include "usock.h"
#include "telnet.h"
#include "mailbox.h"
#include "bm.h"
#include "color.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: bmutil.c,v 1.41 2000/05/09 16:48:24 brian Exp $";
#endif

#ifndef UNIX
#define		SETVBUF
#else
#include <sys/stat.h>
#endif


#ifdef SETVBUF
#define		MYBUF	1024
#endif

static void readareas (const char *name);
void freeareas (void);
static int is_area2 (const char *name);

extern int mbx_data (struct mbx * m, struct list * cclist, char *extra, int returnreceipt);
extern int dombroute (int argc, char *argv[], void *p);
extern void updateCtl (const char *who, struct let * info);
extern int makeBBSbid (char *bid, int bidlen, char *line);

#ifdef SAMCALLB
extern char *cb_lookname (char *);
#endif

#ifdef MAILCMDS
char Badmsg[] = "Invalid Message number %d\n";
static char Badrange[] = "Invalid Message range '%d - %d'\n";
char Nomail[] = "No messages\n";
static char NoMsgs[] = "No message numbers given\n";
static char anymore[] = "More(N=no)? ";
static int readnotes (struct mbx * m, int already);
static long isnewmail (struct mbx * m);
static int initnotes (struct mbx * m);
static void noteheader (struct mbx * m, int usebid);
static void close_notes (int a, void *b, void *c);
static void mfclose (struct mbx * m);
#endif

int lockit (struct mbx * m);
extern char *nntp_name_expansion (char *name);
static void dodeleting (struct mbx * m, int msg);
static int is_area (const char *name, const char *listfile);
char *mblookname (struct mbx * m, char *str);
void notifynewmail (struct mbx * m);
void backEmUp (int len);
#ifdef SAMCALLB
void leadingCaps (char *str, int others);
#endif

static char logdelmsg[] = "Msg %d in area '%s' made %s by '%s'";

const char hitEnter[] = "Hit enter to continue";

extern int MBnoReturnReceipt;


FILE *subdir_fopen (char *name, const char *mode);

FILE *
subdir_fopen (char *name, const char *mode)
{
FILE *fp;
char *cp;

#undef ENOENT
#define ENOENT 2

	if ((fp = fopen (name, mode)) != (FILE *) 0)
		return fp;
	if ((errno == ENOENT) && !strchr (mode, 'r')) {
		cp = &name[1];
		while ((cp = strpbrk (cp, "/\\")) != (char *) 0) {
			*cp = 0;
			if (access (name, 0))
				if (mkdir (name, 0777))
					return ((FILE *) 0);
			*cp++ = '/';
		}
		fp = fopen (name, mode);
		return fp;	/* NULL if STILL not opened */
	}
	return ((FILE *) 0);
}



#ifdef MAILBOX
#ifdef MAILCMDS

static int
initnotes (struct mbx *m)
{
char buf[256];
int wasit = 0;
#ifdef USERLOG
register struct let *cmsg;
int i;
#endif

	if (m->mfile != NULLFILE) {
		(void) fclose (m->mfile);
		wasit = m->nmsgs;
	}
	sprintf (buf, "%s/%s.txt", Mailspool, m->area);
	if ((m->mfile = subdir_fopen (buf, READ_TEXT)) == NULLFILE)
		return 0;
	m->mboxsize = (long) filelength (fileno (m->mfile));
	if (!(m->sid & MBX_SID) && !stricmp (m->area, m->name))	/* our private mail area */
		m->mysize = m->mboxsize;
	m->nmsgs = 0;
	m->change &= CHG_SPECIAL;
	m->newmsgs = 0;
	m->newmsgsonentry = 0;
	m->anyread = 0;
	/* Allocate space for reading messages */
	(void) readnotes (m, wasit);
#ifdef USERLOG
	m->current = 0;		/*reset it*/
	if (m->nmsgs)
		for (cmsg = &m->mbox[1], i = 1; i <= m->nmsgs; i++, cmsg++)
			if ((cmsg->status & BM_READ) == 0) {
				m->current = i;	/* first new message */
				break;
			}
	/* start at one if no new messages */
	if ((m->current == 0) && m->nmsgs)
		m->current++;
#endif
	return 0;
}



/* readnotes assumes that ifile is pointing to the first
 * message that needs to be read.  For initial reads of a
 * notesfile, this will be the beginning of the file.  For
 * rereads when new mail arrives, it will be the first new
 * message.
 */
static int
readnotes (register struct mbx *m, int already)
{
register FILE *fp;
char mailbox[128];
long size;
register int i, isit;
register struct let *cmsg;
struct let *new;

	m->newmsgs = m->hmsgs = 0;
	sprintf (mailbox, "%s/control/%s.ctl", Mailspool, m->area);
	if ((fp = subdir_fopen (mailbox, READ_BINARY)) != NULLFILE) {
#if !defined(TNOS_68K)
		m->stdoutbuf = mallocw (MYBUF);
		(void) setvbuf (fp, m->stdoutbuf, _IOFBF, MYBUF);
#endif
		size = (long) filelength (fileno (fp));
		new = (struct let *) mallocw ((unsigned long) size + sizeof (struct let));

		(void) fread (&new[1], (unsigned) size, 1, fp);
		(void) fclose (fp);
		if (already && !strcmp (m->area, m->name))
			memcpy (new, m->mbox, (unsigned) (already + 1) * sizeof (struct let));

#if !defined(TNOS_68K)
		free (m->stdoutbuf);
		m->stdoutbuf = 0;
#endif
		free (m->mbox);
		m->mbox = new;
		m->nmsgs = (int) (size / (long) sizeof (struct let));

		isit = issysarea (m->area);
		for (cmsg = &m->mbox[1], i = 1; i <= m->nmsgs; i++, cmsg++)
			if (isit) {
				if ((cmsg->bid > m->lastread) && !(cmsg->status & BM_DELETE))
					m->newmsgs++;
				else
					cmsg->status |= BM_READ;
				if (cmsg->status & BM_ONHOLD)
					m->hmsgs++;
			} else {
				if (!(cmsg->status & BM_READ))
					m->newmsgs++;
				if (cmsg->status & BM_ONHOLD)
					m->hmsgs++;
			}
	} else
		m->nmsgs = 0;

	m->newmsgsonentry = m->newmsgs;
	return 0;
}



/* Display the header to a listing */
static void
noteheader (struct mbx *m, int usebid)
{
char *area, *cp;

	area = strdup (m->area);
	while ((cp = strchr (area, '/')) != NULLCHAR)
		*cp = '.';
	bbscolorcls (m);
	tputs ("\nMail area: ");
	bbscolorchange (m, "09");
	tputs (area);
	bbscolorchange (m, "0B");
	tprintf ("  %d ", m->nmsgs);
	bbscolorchange (m, "0F");
	tprintf ("message%s -  ", m->nmsgs == 1 ? " " : "s ");
	bbscolorchange (m, "0B");
	tprintf ("%d ", m->newmsgs);
	bbscolorchange (m, "0F");
	tprintf ("new\n\nStat  #        TO         FROM %s SUBJECT\n", (usebid) ? "MESSAGE ID   " : " DATE    SIZE");
	free (area);
}



/* list headers of a notesfile a message */
/* Rearranged display - WG7J */
int
dolistnotes (int argc, char *argv[], void *p)
{
struct mbx *m;
struct let *cmsg;
char *cp, *s;
char smtp_date[SLINELEN], smtp_from[SLINELEN];
char smtp_subject[SLINELEN], tstring[LINELEN], type;
char smtp_to[SLINELEN], smtp_bid[30];
int start, stop;
long size;
int c, usemore = 0, lin;
int usebid = 0;
int headerseen = 0;
#if 0
char *area;
#endif
#ifdef USERLOG
long msgid;
#endif

	m = (struct mbx *) p;

	/*If this user doesn't have read-permissions,
	 *we're not going to let him list anything - WG7J    */
	if (m->privs & NO_READCMD) {
		tputs (Noperm);
		return 0;
	}
	if (m->mfile == NULLFILE) {
		if (!(m->privs & ALL_AREAS))
			tputs (Nomail);
		return 0;
	}
	if (m->stype == 'B') {
		usebid = 1;
		m->stype = ' ';
	}
	if (m->stype == '$')
		usebid = 1;

	if ((m->stype == 'S' || m->stype == '$' || m->stype == '>' || m->stype == '<') && argc == 1) {
		tputs ("Search criterium needed!\n");
		return 0;
	}
	if ((lin = m->morerows) != 0)
		usemore = 1;	/* Display More prompt */

#if 0
	noteheader (m, usebid);
#endif

	stop = m->nmsgs;
	if (m->stype == 'L') {	/* LL (List Latest) command */
		if (argc > 1)
			start = stop - atoi (argv[1]) + 1;
		else
			start = stop;
		if (start < 1)
			start = 1;
	} else {
		if ((m->stype == 'A') || (m->stype == '>') ||
		    (m->stype == '<') || (m->stype == 'S') ||
		    (m->stype == '$') || (m->stype == 'H') ||
		    (m->stype == 'M')) {
			start = 1;
			stop = m->nmsgs;
		} else {
			if (argc > 1)
				start = atoi (argv[1]);
			else
#ifdef USERLOG
#if 0
				start = m->nmsgs - m->newmsgsonentry + 1;
#else
				start = (m->newmsgsonentry) ? m->current : (m->nmsgs - m->newmsgsonentry + 1);
#endif
#else
				start = 1;
#endif
			if (argc > 2)
				stop = atoi (argv[2]);
		}
	}
	if (stop > m->nmsgs)
		stop = m->nmsgs;
	if (start < 1 || start > stop) {
		if (!(m->privs & ALL_AREAS)) {
			if ((m->stype == ' ') || (m->stype == 'M'))
				tputs ("No new mail.\n");
			else
				tputs ("Invalid range.\n");
		}
		return 0;
	}
	if (m->sid & MBX_HTTP)
		tprintf ("<PRE>\n");
	for (cmsg = &m->mbox[start]; start <= stop; start++, cmsg++) {
		*smtp_date = '\0';
		*smtp_from = '\0';
		*smtp_subject = '\0';
		*smtp_to = '\0';
		*smtp_bid = '\0';
		type = ' ';
		fseek (m->mfile, cmsg->start, 0);
		size = cmsg->size;
		/* Be a little less selfish - WG7J */
		kwait (NULL);

#ifdef USERLOG
		/* We need to get the id from the last message listed !
		 * m->mbox[i].start (ie cmsg->start) points to the 'From ' line
		 * next are the 'Received....' and 'ID...' lines
		 * These are thes line added by our smtp server.
		 * following the 'AA' is the number that we want ! - WG7J
		 */
#if 0
		if (start == stop) {
#endif
			/*The 'From ' line*/
			(void) fgets (tstring, sizeof (tstring), m->mfile);
			size -= (long) strlen (tstring);
			/*The 'Received' line*/
			(void) fgets (tstring, sizeof (tstring), m->mfile);
			size -= (long) strlen (tstring);
			/*The 'ID' line*/
			(void) fgets (tstring, sizeof (tstring), m->mfile);
			size -= (long) strlen (tstring);
			/* find id number */
#ifdef nope
			if ((cmsg->bid > m->lastread) && (cmsg->bid > m->newlastread))
				m->newlastread = cmsg->bid;
#else
			if ((cp = strstr (tstring, "AA")) != NULLCHAR) {
				/*what follows is the message-number*/
				msgid = atol (cp + 2);
				if ((msgid > m->lastread) && (msgid > m->newlastread))
					m->newlastread = msgid;
			}
#endif
#if 0
		}
#endif
#endif

		while (size > 0 && fgets (tstring, sizeof (tstring), m->mfile) != NULLCHAR) {
			kwait (NULL);
			if (*tstring == '\n')	/* end of header */
				break;
			size -= (long) strlen (tstring);
			rip (tstring);
			/* handle continuation later */
			if (*tstring == ' ' || *tstring == '\t')
				continue;
			switch (htype (tstring)) {
				case FROM:
					cp = getaddress (tstring, 0);
					strncpy (smtp_from, cp != NULLCHAR ? cp : "", 8);
					if ((cp = strchr (smtp_from, '@')) != NULLCHAR)
						*cp = '\0';	/* get rid of @-host or @-bbs field */
					break;
				case SUBJECT:
					trimright (tstring);
					cp = skipwhite (&tstring[9]);
					strncpy (smtp_subject, cp, 35);
					break;
				case MSGID:
					strncpy (smtp_bid, &tstring[12], 29);
					break;
				case DATE:
					if ((cp = strchr (tstring, ',')) == NULLCHAR)
						cp = &tstring[6];
					else
						cp++;
					/* skip spaces */
					while (*cp == ' ')
						cp++;
					if (strlen (cp) < 17)
						break;	/* not a valid length */
					s = smtp_date;
					/* copy day */
					if (atoi (cp) < 10 && *cp != '0')
						*s++ = ' ';
					else
						*s++ = *cp++;
					*s++ = *cp++;

					*s++ = ' ';
					*s = '\0';
					while (*cp == ' ')
						cp++;
					strncat (s, cp, 3);	/* copy month */
#ifdef use_time
					cp += 3;
					while (*cp == ' ')
						cp++;
					/* skip year */
					while (isdigit (*cp))
						cp++;
					/* copy time */
					strncat (s, cp, 6);	/* space hour : min */
#endif
					break;
				case BBSTYPE:
					type = tstring[16];
					break;
				case TO:
					strncpy (smtp_to, &tstring[4], 13);
					break;
				case NOHEADER:
				default:
					break;
			}
		}
		if ((m->stype == 'M') && (cmsg->status & BM_READ))
			continue;
		if (m->stype == ' ' || m->stype == 'L' || m->stype == 'M' ||
		 m->stype == 'A' || (type == m->stype && m->stype != ' ') ||
		    ((m->stype == 'H') && (cmsg->status & BM_ONHOLD)) ||
		    ((m->stype == 'S') && (strstr (strlwr (smtp_subject), argv[1]) != NULLCHAR)) ||
		    ((m->stype == '<') && (strstr (strlwr (smtp_from), argv[1]) != NULLCHAR)) ||
		    ((m->stype == '$') && (strstr (strlwr (smtp_bid), argv[1]) != NULLCHAR)) ||
		    ((m->stype == '>') && (strstr (strlwr (smtp_to), argv[1]) != NULLCHAR))) {
			lin--;
			if ((cmsg->status & BM_DELETE) && !(m->privs & SYSOP_CMD))
				strcpy (smtp_subject, "[DELETED]");
			if (!(headerseen++ % 50))
				noteheader (m, usebid);
			bbscolorchange (m, "07");
			if (m->sid & MBX_HTTP)
				tprintf ("<A HREF=\"/bbs/message/%s/%d.html\">", m->area, start);
			tprintf ("%c%c%c%c%3d ", (start == m->current ? '>' : ' '),
				 (cmsg->status & BM_DELETE ? 'D' :
				  cmsg->status & BM_ONHOLD ? 'H' : ' '),
				 (cmsg->status & BM_READ ? 'Y' : 'N'),
				 (cmsg->status & BM_PERMANENT) ? 'P' : ' ',
				 start);
			bbscolorchange (m, "0E");
			tprintf ("%13.13s ", smtp_to);
			bbscolorchange (m, "0C");
			tprintf ("%8.8s ", smtp_from);
			bbscolorchange (m, "07");
			if (usebid) {
				char *tmpbid, *bidptr = smtp_bid;

				if (*bidptr == '<')
					bidptr++;
				if ((tmpbid = strchr (bidptr, '@')) != NULLCHAR) {
					if (strstr (tmpbid, ".bbs") || strcmp (tmpbid, "@hamradio"))
						*tmpbid++ = 0;
					else {
						*tmpbid = '_';
						tmpbid = strchr (tmpbid, '.');
						if (tmpbid)
							*tmpbid = 0;
					}
				}
				(void) strupr (bidptr);
				tprintf ("%-13.13s ", bidptr);
			} else {
				tprintf ("%-7.7s ", smtp_date);
				tprintf ("%5ld ", cmsg->size);
			}
			bbscolorchange (m, "0E");
			tprintf ("%.34s\n", smtp_subject);
			if (m->sid & MBX_HTTP)
				tprintf ("</A>");
#ifdef STATS_AREA
			STATS_addarea (0, 1, m->area);
#endif
		}
		/* More prompting added - WG7J */
		if (usemore && lin == 0) {
			if (m->type == TELNET || m->type == TIP)
				c = tkeywait ("--More--", 0, m->linemode);
			else	/* For AX.25 and NET/ROM connects - WG7J */
				c = mykeywait (anymore, m);
#ifdef MBXTDISC
			start_timer (&m->tdisc);
#endif
			if (c == -1 || c == 'q' || c == 'Q' || c == 'n' || c == 'N')
				break;
#ifdef TNOS_68K
			if (c == '\l' || c == '\r')
#else
			if (c == '\n' || c == '\r')
#endif
				lin = 1;
			else
				lin = m->morerows;

		}
	}
	if (m->sid & MBX_HTTP)
		tprintf ("</PRE>\n");
	return 0;
}



/*  save msg on stream - if noheader set don't output the header */
int
msgtofile (
struct mbx *m,
int msg,
FILE * tfile,	/* already open for write */
int noheader,
int numbered
) {
char tstring[LINELEN];
long size;
int lineno = 0;

	if (m->mfile == NULLFILE) {
		tputs (Nomail);
		return -1;
	}
	fseek (m->mfile, m->mbox[msg].start, 0);
	size = m->mbox[msg].size;

	if (noheader) {
		/* skip header */
		while (size > 0 && fgets (tstring, sizeof (tstring) - 1, m->mfile) != NULLCHAR) {
			size -= (long) strlen (tstring);
			kwait (NULL);
			if (*tstring == '\n')
				break;
		}
	}
	while (size > 0 && fgets (tstring, sizeof (tstring) - 1, m->mfile)
	       != NULLCHAR) {
		kwait (NULL);
		size -= (long) strlen (tstring);
		if (numbered)
			fprintf (tfile, "%02d) ", ++lineno);
		fputs (tstring, tfile);
		if (ferror (tfile)) {
			tputs ("Error writing mail file\n");
			return -1;
		}
	}

	return 0;
}



static void
dodeleting (struct mbx *m, int msg)
{
	switch (m->stype) {
		case 'U':
			m->mbox[msg].status &= ~BM_DELETE;
			tprintf ("Msg %d Un-Killed.\n", msg);
			break;
		case 'A':
			m->mbox[msg].status &= ~BM_ONHOLD;
			tprintf ("Msg %d Available.\n", msg);
			log (m->user, logdelmsg, msg, m->area, "available", m->name);
			break;
		case 'H':
			m->mbox[msg].status |= BM_ONHOLD;
			tprintf ("Msg %d Held.\n", msg);
			log (m->user, logdelmsg, msg, m->area, "held", m->name);
			break;
		case 'T':
			m->mbox[msg].status &= ~BM_PERMANENT;
			tprintf ("Msg %d Temporary.\n", msg);
			log (m->user, logdelmsg, msg, m->area, "temporary", m->name);
			break;
		case 'P':
			m->mbox[msg].status |= BM_PERMANENT;
			tprintf ("Msg %d Permanent.\n", msg);
			log (m->user, logdelmsg, msg, m->area, "permanent", m->name);
			break;
		default:
			if (m->mbox[msg].status & BM_PERMANENT)
				tprintf ("Can't kill message %d - Message is permanent!\n", msg);
			else {
				m->mbox[msg].status |= BM_DELETE;
				m->mbox[msg].status &= ~BM_ONHOLD;
				tprintf ("Msg %d Killed.\n", msg);
				if (!issysarea (m->area))
					m->change |= CHG_DELETE;
			}
			break;
	}
	statusCtl (m->area, "ctl", &m->mbox[msg], msg, 0);
}



/* dodelmsg - delete message in current notesfile */
/* Modified to allow the 'KM' command. 920307 -  WG7J */
/* also handles holsing/releasing messages - KO4KS */
int
dodelmsg (int argc, char *argv[], void *p)
{
struct mbx *m;
int msg = 0, i = 0;
char *myargv[NARG];
int myargc = 0;
int maxmsg;
struct let *cmsg;
char *tmpbuf;
int start, end;

	m = (struct mbx *) p;

	/* If this user doesn't have read-permissions,
	 * we're not going to let him kill/hold anything;
	 * allow anyone to kill/hold messages in areas
	 * who's names start with 'nts' - WG7J
	 */
	/* Check if we have permission to delete others mail */
	if ((m->privs & NO_READCMD) || (m->privs & NO_SENDCMD) ||
	    (!(m->privs & FTP_WRITE) &&
	     stricmp (m->area, m->name) &&
	     strnicmp (m->area, "nts", 3))) {
		tputs (Noperm);
		return 0;
	}
	if ((m->stype == 'A' || m->stype == 'H' || m->stype == 'P' || m->stype == 'T') && !(m->privs & SYSOP_CMD)) {
		tputs (Noperm);
		return 0;
	}
	if (m->stype == 'S') {
		if (argc > 2) {
			myargc = msg = atoi (argv[1]);
			i = atoi (argv[2]);
		}
		if ((argc < 3) || !msg || !i)
			tputs (NoMsgs);
		else {
			for (; myargc <= i; myargc++) {
				m->mbox[myargc].status |= BM_DELETE;
				m->mbox[myargc].status &= ~BM_ONHOLD;
				statusCtl (m->area, "ctl", &m->mbox[myargc], myargc, 0);
			}
			if (!issysarea (m->area))
				m->change |= CHG_DELETE;
			tprintf ("Msgs %d to %d Killed.\n", msg, i);
		}
		return 0;
	}
	if (m->mfile == NULLFILE) {
		tputs (Nomail);
		return 0;
	}
	/*If this is the KM command, setup myargv[]
	 *to contain up to NARG message numbers - WG7J
	 */
	if (m->stype == 'M') {
		myargc = 1;
		/* scan all messsages to find read ones */
		maxmsg = min (m->nmsgs, NARG - 1);
		for (i = 1; i <= maxmsg; i++) {
			cmsg = &m->mbox[i];
			if (cmsg->status & BM_READ) {	/*found a read msg!*/
				tmpbuf = mallocw (18);	/*allocate space for the new argument*/
				myargv[myargc++] = itoa (i, tmpbuf, 10);
			}
		}
		if (myargc == 1) {
			tputs (NoMsgs);
			return 0;
		}
		argc = myargc;
	} else {
		if (argc == 1) {
			if (m->stype != 'A') {
				msg = m->current;
				dodeleting (m, msg);
				return 0;
			} else {
				for (i = 1; i <= m->nmsgs; i++) {
					cmsg = &m->mbox[i];
					if (cmsg->status & BM_ONHOLD) {
						cmsg->status &= ~BM_ONHOLD;
						statusCtl (m->area, "ctl", cmsg, i, 0);
					}
				}
				tputs ("All Messages Available.\n");
				return 0;
			}
		}
		/*simply point to the old arguments*/
		for (i = 1; i < argc; i++)
			myargv[i] = argv[i];
	}

	/* See if x - y format was used and use a for i = x to y loop */
	if (argc > 2 && myargv[2][0] == '-') {		/*lint !e644 */
		start = atoi (myargv[1]);
		if (myargv[2][1])
			end = atoi (&myargv[2][1]);
		else
			end = atoi (myargv[3]);
		if (start < 0 || start > m->nmsgs || end < 0 || end > m->nmsgs)
			tprintf (Badrange, start, end);
		else
			for (i = start; i <= end; i++) {
				msg = i;
				dodeleting (m, msg);
			}	/* endfor */
	} else
		for (i = 1; i < argc; ++i) {
			tmpbuf = strchr (myargv[i], '-');	/* N5KNX: allow from-to msg specification */
			msg = atoi (myargv[i]);
			if (tmpbuf == NULLCHAR)
				maxmsg = msg;
			else
				maxmsg = atoi (++tmpbuf);
			if (maxmsg < msg) {
				tprintf (Badmsg, myargv[i]);
				continue;
			}
			for (; msg <= maxmsg; msg++) {
				if (msg < 0 || msg > m->nmsgs) {
					tprintf (Badmsg, msg);
					continue;
				}
				dodeleting (m, msg);
			}
		}
	/* If this was 'KM'
	 * free the memory allocated for myargv[] - WG7J
	 */
	if (m->stype == 'M') {
		for (i = 1; i < argc; i++)
			free (myargv[i]);
	}
	return 0;
}



struct clparms {
	int nostatus, nodelete;
	long *hostsize;
	long _hostsize;
	char name[20];		/* Name of remote station */
	char area[64];		/* name of current mail area */
	FILE *mfile;		/* mail data file pointer */
	struct let *mbox;
	int nmsgs;		/* number of messages in this mail box */
	int isbbs;
};


static void
close_notes (int a OPTIONAL, void *b, void *c OPTIONAL)
{
struct clparms *cl;
struct let *cmsg;
char *line;
char tstring[LINELEN], buf[256];
char buf2[256];
long size, diff;
FILE *nfile;
int i, nextisBID, numwritten = 0;
int foundstatus, firstIDline;
#if 0
char *cp;
long msgid;
#endif

	cl = (struct clparms *) b;
	line = tstring;
	sprintf (buf, "%s/control/%s.ctl", Mailspool, cl->area);
	(void) remove (buf);

	(void) fclose (cl->mfile);
	sprintf (buf, "%s/%s.txt", Mailspool, cl->area);
	sprintf (buf2, "%s/%s.bak", Mailspool, cl->area);
	(void) remove (buf2);	/* delete it here, just in case! */
	(void) rename (buf, buf2);
	if ((cl->mfile = subdir_fopen (buf2, READ_TEXT)) == NULLFILE) {
		rmlock (Mailspool, cl->area);
		free (cl);
		return;
	}
	if ((nfile = subdir_fopen (buf, WRITE_TEXT)) == NULLFILE) {
		(void) fclose (cl->mfile);
		rmlock (Mailspool, cl->area);
		free (cl);
		return;
	}
	/* copy tmp file back to notes file */
	for (cmsg = &cl->mbox[1], i = 1; i <= cl->nmsgs; i++, cmsg++) {
		fseek (cl->mfile, cmsg->start, 0);
		kwait (NULL);
		cmsg->start = ftell (nfile);
		size = cmsg->size;
		diff = 0;
		foundstatus = 0;
		/* It is not possible to delete messages if nodelete is set */
		if ((cmsg->status & BM_DELETE) && !cl->nodelete)
			continue;
		nextisBID = 0;
		firstIDline = 0;
		/* copy the header */
		while (size > 0 && fgets (line, LINELEN, cl->mfile) != NULLCHAR) {
			kwait (NULL);	/* can cause problems if exiting NOS */
#if 0
			if (!firstIDline && nextisBID && (cp = strstr (line, "AA")) != NULLCHAR) {
				/*what follows is the message-number*/
				msgid = atol (cp + 2);
#else
			if (!firstIDline && nextisBID && strstr (line, "AA") != NULLCHAR) {
#endif
				nextisBID = 0;
				firstIDline = 1;
			}
			if (!strncmp (line, Hdrs[RECEIVED], strlen (Hdrs[RECEIVED])))
				nextisBID = 1;
			if (!strncmp (line, Hdrs[STATUS], strlen (Hdrs[STATUS])))
				foundstatus = 1;
			size -= (long) strlen (line);
			if (*line == '\n') {
#ifdef notagain
				if (cmsg->status & BM_FORWARDED) {
					fprintf (nfile, "%s%s\n", Hdrs[XFORWARD],
						 cl->name);
					diff += (strlen (Hdrs[XFORWARD]) + strlen (cl->name) + 1);
				}
#endif
				if (!foundstatus && (cmsg->status & BM_READ) != 0 && !cl->nostatus) {
					fprintf (nfile, "%sR\n", Hdrs[STATUS]);
					diff += (long) (strlen (Hdrs[STATUS]) + 2);
				}
				fprintf (nfile, "\n");
				break;
			}
			fputs (line, nfile);
			kwait (NULL);	/* can cause problems if exiting NOS */
		}
		kwait (NULL);
		while (size > 0 && fgets (line, LINELEN, cl->mfile) != NULLCHAR) {
			kwait (NULL);	/* can cause problems if exiting NOS */
			if (!cl->nostatus && !strncmp (line, Hdrs[RRECEIPT], strlen (Hdrs[RRECEIPT]))
			    && !strcmp (cl->name, cl->area) && (cmsg->status & BM_RRECEIPT)) {
				fputs ("Return-Receipt-Sent\n", nfile);
				diff -= (long) strlen (line);
				diff += 20;
			} else
				fputs (line, nfile);
			size -= (long) strlen (line);
			kwait (NULL);	/* dont want no damaged files */
			if (ferror (nfile)) {
				(void) fclose (nfile);
				(void) fclose (cl->mfile);
				rmlock (Mailspool, cl->area);
				free (cl);
				return;
			}
		}
		cmsg->status &= BM_READ;
		cmsg->size += diff;
		updateCtl (cl->area, cmsg);
		(void) fflush (nfile);
		numwritten++;
	}
	if (!cl->isbbs && !stricmp (cl->name, cl->area))
		*cl->hostsize = ftell (nfile);	/* Update the size of our hosts' mailbox */
	/* potientially dangerous! */
	/* remove a zero length file */
	if (!numwritten) {
		(void) fclose (nfile);
		(void) unlink (buf);
	} else
		(void) fclose (nfile);
	(void) fclose (cl->mfile);
	(void) remove (buf2);	/* leave it around, for now! NOT! */
	rmlock (Mailspool, cl->area);
	free (cl->mbox);
	free (cl);
	kwait (NULL);
	return;
}



/* close the temp file while coping mail back to the mailbox */
int
closenotes (struct mbx *m, int exiting)
{
int nostatus, nodelete = 0;
struct clparms *cl;

	if (m->mfile == NULLFILE || !*m->area)
		return 0;
	nostatus = issysarea (m->area);

	/* Allow any user to delete from area names starting with 'nts' */
	if (!strnicmp (m->area, "nts", 3))
		nostatus = 0;
	if (!(m->change & CHG_NORMAL) || nostatus) {	/* no changes were made (or bulletin) */
		mfclose (m);
		m->mboxsize = 0;
		return 0;
	}
	/* If this area is not our own private message area, then we will not add a
	 * Status line to indicate that the message has been read.
	 */
	if (strcmp (m->area, m->name)) {
		nostatus = 1;
		/* Don't delete messages from public message areas unless you are
		 * a SYSOP.
		 */
		nodelete = !(m->privs & SYSOP_CMD);
	}
	/* Allow any user to delete from area names starting with 'nts' */
	if (!strnicmp (m->area, "nts", 3))
		nodelete = 0;
	/* If not a SYSOP, not an "nts" area, not our area, AND deletions, must be BBS forward */
	if (m->change & CHG_DELETE) {
		nodelete = 0;
		log (-1, "PBBS forwarding rewrite of personal area '%s' by %s ", m->area, m->name);
	}
	if (nostatus && nodelete) {
		mfclose (m);
		m->mboxsize = 0;
		return 0;
	}
	scanmail (m);
	if (lockit (m))
		return -1;
	cl = (struct clparms *) mallocw (sizeof (struct clparms));

	cl->nodelete = nodelete;
	cl->nostatus = nostatus;
	if (!exiting)
		cl->hostsize = &m->mysize;
	else	{
		cl->_hostsize = m->mysize;
		cl->hostsize = &cl->_hostsize;
	}
	cl->isbbs = (m->sid & MBX_SID);
	strncpy (cl->name, m->name, 20);
	strncpy (cl->area, m->area, 64);
	cl->mfile = m->mfile;
	cl->nmsgs = m->nmsgs;
	cl->mbox = m->mbox;
	(void) newproc ("Closing notefile", 2560, close_notes, 0, cl, 0, 0);
	kwait (NULL);
	m->mfile = NULLFILE;
	m->mbox = (struct let *) 0;
	mfclose (m);
	m->mboxsize = 0;
	return 0;
}

#endif /* MAILCMDS */



/* Returns 1 if name is in the given area file, 0 otherwise */
static int
is_area (const char *name, const char *listfile)
{
char buf[LINELEN], *cp;
FILE *fp;

	if ((fp = fopen (listfile, READ_TEXT)) == NULLFILE)
		return 0;
	while (fgets (buf, sizeof (buf), fp) != NULLCHAR) {
		/* The first word on each line is all that matters */
		if (isalnum (buf[0])) {	/* skip comments */
			if ((cp = strpbrk (buf, " \t\n")) != NULLCHAR)
				*cp = '\0';
			(void) nntp_name_expansion (buf);
			if (stricmp (name, buf) == 0) {	/* found it */
				(void) fclose (fp);
				return 1;
			}
		}
	}
	(void) fclose (fp);
	return 0;
}



/* Returns 1 if name is a public message Area, 0 otherwise */
int
isarea (const char *name)
{
	return (is_area2 (name));
}



/* Returns 1 if name is a public message Area, 0 otherwise */
int
issysarea (const char *name)
{
	return (is_area (name, AreaSlist));
}



#ifdef MAILCMDS
int
lockit (struct mbx *m)
{
int c, cnt = 0;

	while (mlock (Mailspool, m->area)) {
		(void) kpause (1000L);	/* Wait one second */
		if (++cnt == 10) {
			char buf[80];

			sprintf (buf, "Mail file '%s' is busy, Abort or *Retry ? ", m->area);
			cnt = 0;
			c = tkeywait (buf, 1, m->linemode);
			if (c == 'A' || c == 'a' || c == EOF) {
				mfclose (m);
				return 1;
			}
		}
	}
	return 0;
}



/* read the next message or the current one if new */
int
doreadnext (int argc OPTIONAL, char *argv[]OPTIONAL, void *p)
{
struct mbx *m;
char buf[14], *newargv[2], buf2[6];

	m = (struct mbx *) p;
	if (m->mfile == NULLFILE)
		return 0;
	if ((m->mbox[m->current].status & BM_READ) != 0) {
		if (m->current == 1 && m->anyread == 0)
			;
		else if (m->current < m->nmsgs)
			m->current++;
		else {
			tputs ("Last message\n");
			return 0;
		}
	}
	sprintf (buf, "%d", m->current);
	strcpy (buf2, "read");
	newargv[0] = buf2;
	newargv[1] = buf;
	return doreadmsg (2, newargv, p);
}

#endif



#ifdef SAMCALLB
void
leadingCaps (char *str, int others)
{
char *cp;

	cp = str;
	(void) strlwr (cp);
	if (!others) {
		if ((cp = strchr (cp, ' ')) != 0)
			(void) strupr (cp);
		return;
	}
	do {
		cp = strchr (cp, ' ');
		if (cp != NULL) {
			cp = skipwhite (cp);
			*cp = (char) toupper (*cp);
		}
	} while (cp);
}
#endif


char *
mblookname (struct mbx *m, char *str)
{
	if ((m != NULLMBX) && (m->realname) && (*m->realname == '(') && (m->realname[1] != ')'))
		return (strdup (m->realname));

	if (strchr (str, '('))
		return (0);	/* already has a name */
#ifdef SAMCALLB
	return (cb_lookname (str));
#else
	return (0);
#endif
}



#ifdef MAILCMDS

extern int MbRead;

/* display message on the crt given msg number */
/* Modified to allow the 'RM' command, 920307 - WG7J */
int
doreadmsg (int argc, char *argv[], void *p)
{
struct mbx *m;
register int c, col, lin;
unsigned char buf[MAXBUF + 2], *cp, *cp2;
char *subj = 0, *theto = 0;
int msg, cnt, i, usemore = 0, verbose, mbxheader, pathcol = 5;
int header, lastheader;
long size;
char *myargv[NARG];
int myargc;
int maxmsg;
struct let *cmsg;
char *tmpbuf;
char *returnreceipt;
#ifdef USERLOG
long msgid;
#endif

	m = (struct mbx *) p;

	/*Check for read-permissions - WG7J */
	if (m->privs & NO_READCMD) {
		tputs (Noperm);
		return 0;
	}
#ifdef MBFWD
	if (m->stype == 'O')
		return (dombroute (argc, argv, p));
#endif
	if ((m->mfile == NULLFILE) || (m->nmsgs == 0)) {
		if (!(m->privs & ALL_AREAS))
			tputs (Nomail);
		return 0;
	}
	if ((lin = m->morerows) != 0)
		usemore = 1;	/* Display More prompt */

	/*If this is the RM or VM command, setup myargv[]
	 *to contain up to NARG message numbers - WG7J
	 */
	if (m->stype == 'M') {
		myargc = 1;
		if (!m->newmsgs) {
			tputs (Nomail);
			return 0;
		}
		/* scan all messsages to find unread ones */
		maxmsg = min (m->nmsgs, m->current + NARG - 2);
		for (i = m->current; i <= maxmsg; i++) {
			cmsg = &m->mbox[i];
			if (!(cmsg->status & BM_READ)) {	/*found an unread msg!*/
				tmpbuf = mallocw (18);	/*allocate space for the new argument*/
				myargv[myargc++] = itoa (i, tmpbuf, 10);
			}
		}
		argc = myargc;
	} else {
		/*simply point to the old arguments*/
		for (i = 1; i < argc; i++)
			myargv[i] = argv[i];
	}
	if (argc == 1) {
		tputs ("Usage: Read/Verbose #\n");
		return 0;
	}
	m->state = MBX_READ;
	for (i = 1; i < argc; ++i) {
		msg = atoi (myargv[i]);		/*lint !e644 */
		if (msg < 1 || msg > m->nmsgs) {
			if (!(m->privs & ALL_AREAS))
				tprintf (Badmsg, msg);
			goto iamdone;
		}
		MbRead++;
		fseek (m->mfile, m->mbox[msg].start, 0);
#ifdef USERLOG
		/* Check the ID number of this message and
		 * adjust new lastread count, if needed - WG7J
		 */
		(void) fgets ((char *) buf, MAXBUF + 2, m->mfile);	/* the 'From ' line */
		(void) fgets ((char *) buf, MAXBUF + 2, m->mfile);	/* the 'Received: ' line */
		(void) fgets ((char *) buf, MAXBUF + 2, m->mfile);	/* the ' ID' line */
		/* find id number */
		if ((cp = (unsigned char *) strstr ((char *) buf, "AA")) != (unsigned char *) 0) {
			/*what follows is the message-number*/
			msgid = atol ((char *) cp + 2);
			if ((msgid > m->lastread) && (msgid > m->newlastread))
				m->newlastread = msgid;
		}
		fseek (m->mfile, m->mbox[msg].start, 0);
#endif
#ifdef nope			/* was USERLOG */
		if ((m->mbox[msg].bid > m->lastread) && (m->mbox[msg].bid > m->newlastread))
			m->newlastread = m->mbox[msg].bid;
#endif

		m->anyread = 1;
		returnreceipt = 0;
		size = m->mbox[msg].size;
		m->current = msg;
		header = NOHEADER;
		mbxheader = 0;
		if ((*argv[0] == 'v') || (m->stype == 'H'))
			verbose = 1;	/* display all header lines */
		else
			verbose = 0;

		m->inmessage = 1;
		if (m->stype != 'M')
			bbscolorcls (m);
		bbscolorchange (m, "07");
		if (m->sid & MBX_HTTP)
			tprintf ("<PRE>\n");
		tprintf ("Message #%d %s\n", msg,
			 m->mbox[msg].status & BM_ONHOLD ? "[On Hold - Awaiting Review of SYSOP]" :
			 m->mbox[msg].status & BM_DELETE ? "[Deleted]" : "");

		if ((m->mbox[msg].status & (BM_ONHOLD | BM_DELETE)) && !(m->privs & SYSOP_CMD))
			continue;

		/* When you have sysop privs,
	         * only mark your own private area as read and changed.
	         * other areas, only mark as read, NOT changed !
	         * for regular users, simply mark all as read and changed.
	         * That way sysops can read other's mail without
	         * marking stuff read that really wasn't read by
	         * the right person !
	         * for regular users, simply mark as read.
	         * 910312 - WG7J
	         */
		if (!(m->mbox[msg].status & BM_READ)) {
			m->mbox[msg].status |= BM_READ;
			/* regular users */
			if (!issysarea (m->area) && (!(m->privs & SYSOP_CMD) || ((m->privs & SYSOP_CMD) && !strcmp (m->name, m->area))))	/*sysops*/
				m->change |= CHG_READ;
			m->newmsgs--;
		}
		--lin;
		col = 0;
		while (!feof (m->mfile) && size > 0) {
			for (col = 0; col < MAXBUF;) {
				c = getc (m->mfile);
				size--;
				if (feof (m->mfile) || size == 0)	/* end this line */
					break;
				if (c == '\t') {
					cnt = col + 8 - (col & 7);
					if (cnt >= MAXBUF)	/* end this line */
						break;
					while (col < cnt)
						buf[col++] = ' ';
				} else {
					if (c == '\n')
						break;
					buf[col++] = uchar (c);
				}
			}
			if (col < MAXBUF)
				buf[col++] = '\n';
			buf[col] = '\0';
			if (mbxheader > 0) {
				/* Digest R: lines and display as a Path: line */
				if (strnicmp ((char *) buf, "R:", 2) != 0 || (cp = (unsigned char *) strchr ((char *) buf, '@')) == (unsigned char *) 0) {
					tputc ('\n');
					bbscolorchange (m, "0B");
					mbxheader = -1;	/* don't get here again */
					verbose = 1;
				} else {
					if (*(++cp) == ':')
						++cp;
					for (cp2 = cp; isalnum (*cp2); ++cp2)
						;
					*cp2 = '\0';
					if (mbxheader++ == 1) {
						bbscolorchange (m, "07");
						tputs (Hdrs[PATH]);
						pathcol = 5;
						--lin;
					} else {
						tputc ('!');
						if (++pathcol + (int) strlen ((char *) cp) > MAXCOL - 3) {
							tputs ("\n      ");
							pathcol = 5;
							--lin;
						}
					}
					tputs ((char *) cp);
					pathcol += (int) strlen ((char *) cp);
					++lin;	/* to allow for not printing it later */
				}
			}
			if (col == 1 && !mbxheader) {
				bbscolorchange (m, "0B");
				/* last header line reached */
				if (!verbose)
					mbxheader = 1;
			}
			if (verbose)
				/*				tputs (buf);	*/
				(void) colorprintf (NULLCHAR, m->usecolor, buf);
			if (!strncmp (Hdrs[RRECEIPT], (char *) buf, strlen (Hdrs[RRECEIPT])))
				returnreceipt = strdup ((char *) &buf[strlen (Hdrs[RRECEIPT])]);
			if (!verbose && !mbxheader) {
				lastheader = header;
				if (!isspace (*buf))
					header = htype ((char *) buf);
				else
					header = lastheader;
				switch (header) {
					case SUBJECT:
						subj = strdup ((char *) &buf[strlen (Hdrs[SUBJECT]) - 4]);
						tputs ((char *) buf);
						break;
					case MSGID:
#ifdef MBFWD
						/* give a "traditional BBS" version to the user */
						{
							char bid[LINELEN];

							(void) makeBBSbid (bid, sizeof (bid), (char *) buf);
							(void) strupr (bid);
							tprintf ("%s%s\n", Hdrs[MSGID], bid);
						}
						break;
#endif
					case TO:
						theto = strdup ((char *) &buf[strlen (Hdrs[TO])]);
						/* fall through */
					case FROM:
						{
							char *nm;

							rip ((char *) buf);
							tputs ((char *) buf);
							nm = strchr ((char *) buf, ' ');
							if (nm && (nm = mblookname (NULLMBX, ++nm)) != 0) {
								tputs (nm);
								free (nm);
							}
							tputc ('\n');
						}
						break;
					case CC:
					case DATE:
					case REPLYTO:
					case APPARTO:
					case ERRORSTO:
					case XMAILGROUP:
					case ORGANIZATION:
						tputs ((char *) buf);
						break;
					case XBBSHOLD:
						if (m->privs & SYSOP_CMD)
							tputs ((char *) buf);
						break;
					default:
						++lin;
				}
			}
			col = 0;
			if (usemore && --lin <= 0) {
				if (m->type == TELNET || m->type == TIP)
					c = tkeywait ("--More--", 0, m->linemode);
				else	/* For AX.25 and NET/ROM connects - WG7J */
					c = mykeywait (anymore, m);
#ifdef MBXTDISC
				start_timer (&m->tdisc);
#endif
				lin = m->morerows;
				if (c == -1 || c == 'q' || c == 'Q')
					break;
#ifdef TNOS_68K
				if (c == '\l' || c == '\r')
#else
				if (c == '\n' || c == '\r')
#endif
					lin = 1;
			}
		}		/* while !eof */
#ifdef TUTOR
		{
			char savbuf[MBXLINE + 1];

			memcpy (savbuf, m->line, MBXLINE + 1);
			sprintf (m->line, "msgend %d", msg);
			mbscripthook (m, "msgend.sys");
			memcpy (m->line, savbuf, MBXLINE + 1);
		}
#endif
		if (returnreceipt && !MBnoReturnReceipt && !(m->mbox[msg].status & BM_RRECEIPT)) {
			struct mbx *mm;

			m->mbox[msg].status |= BM_RRECEIPT;
			rip (returnreceipt);	/* strip off <CR> */
			tprintf ("[Sending return receipt to '%s']\n", returnreceipt);
			mm = (struct mbx *) callocw (1, sizeof (struct mbx));

			mm->tomsgid = mm->date = mm->origbbs = mm->tofrom = NULLCHAR;
			mm->origto = returnreceipt;
			if (!subj)
				subj = strdup ("    No subject");
			mm->subject = subj;
			mm->realname = strdup ("()");
			rip (subj);	/* strip off <CR> */
			subj[0] = subj[1] = 'R';
			subj[2] = ':';
			subj[3] = ' ';
			mm->stype = 'P';
			strncpy (mm->name, m->name, 20);
			if ((mm->tfile = tmpfile ()) != NULLFILE) {
				struct list *cclist = NULLLIST;
				char fullfrom[MBXLINE];

				(void) addlist (&cclist, returnreceipt, 0, returnreceipt);
				sprintf (fullfrom, "%s@%s", m->name, Hostname);
				(void) mbx_data (mm, NULLLIST, NULLCHAR, 0);
				if (!theto)
					theto = strdup ("unknown addressee");
				rip (theto);	/* strip off the <CR> */
				fprintf (mm->tfile, "Message received - Originally addressed to: '%s'\n", theto);
				fseek (mm->tfile, 0L, 0);	/* rewind file */
				(void) queuejob (mm->tfile, Hostname, cclist, fullfrom);
				(void) fclose (mm->tfile);
				del_list (cclist);
				smtptick (NULL);	/* wake up SMTP to send mail */
			}
			free (mm->realname);
			free (mm);
			free (returnreceipt);
		}
		free (subj);
		free (theto);
		m->inmessage = 0;
#ifdef STATS_AREA
		STATS_addarea (1, 1, m->area);
#endif
	}			/* for argc */
      iamdone:
	/* If this was 'RM' or 'VM',
	 * free the memory allocated for myargv[] - WG7J
	 */
	if (m->stype == 'M') {
		for (i = 1; i < argc; i++)
			free (myargv[i]);
	}
	if (m->sid & MBX_HTTP)
		tprintf ("</PRE>\n");
	return 0;
}



/* Set up m->to when replying to a message. The subject is returned in
 * m->line.
 */
int
mbx_reply (
int argc,
char *argv[],
struct mbx *m,
struct list **cclist,	/* Pointer to buffer for pointers to cc recipients */
char **rhdr	/* Pointer to buffer for extra reply headers */
) {
char subject[MBXLINE], *msgid = NULLCHAR, *date = NULLCHAR;
char *cp;
int msg, lastheader, header = NOHEADER, k;
long size;

	/* 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;
	subject[0] = '\0';

	if (argc == 1)
		msg = m->current;
	else
		msg = atoi (argv[1]);
	if (m->mfile == NULLFILE) {
		if (m->sid & MBX_SID)
			tputs ("NO - ");
		tputs (Nomail);
		return 0;
	}
	if (msg < 1 || msg > m->nmsgs) {
		if (m->sid & MBX_SID)
			tputs ("NO - ");
		tprintf (Badmsg, msg);
		return -1;
	}
	fseek (m->mfile, m->mbox[msg].start, 0);
	size = m->mbox[msg].size;
	m->current = msg;
	while (size > 0 && fgets (m->line, MBXLINE - 1, m->mfile) != NULLCHAR) {
		size -= (long) strlen (m->line);
		if (m->line[0] == '\n')	/* end of header */
			break;
		rip (m->line);
		lastheader = header;
		if (!isspace (m->line[0])) {
			header = htype (m->line);
			lastheader = NOHEADER;
		}
		switch (header) {
			case SUBJECT:
				trimright (m->line);
				if (strlen (m->line) > 11 && !strnicmp (&m->line[9], "Re:", 3))
					strncpy (subject, &m->line[9], MBXLINE);
				else	{
					strcpy (subject, "Re: ");
					strncpy (&subject[4], &m->line[9], MBXLINE - 4);
				}
				break;
			case FROM:
				trimright (m->line);
				if (m->to == NULLCHAR && (cp = getaddress (m->line, 0)) !=
				    NULLCHAR)
					m->to = strdup (cp);
				break;
			case REPLYTO:
				if ((cp = getaddress (m->line, 0)) != NULLCHAR) {
					free (m->to);
					m->to = strdup (cp);
				}
				break;
			case MSGID:
				trimright (m->line);
				free (msgid);
				msgid = strdup (&m->line[12]);
				break;
			case DATE:
				free (date);
				date = strdup (&m->line[6]);
				break;
#ifdef notdef
				/* don't want a reply back to myself - WG7J */
			case TO:
			case APPARTO:
#endif
			case CC:
				/* Get addresses on To, Cc and Apparently-To lines */
				cp = m->line;
				k = (int) (strlen (cp) + 1);
				m->line[min(k, MBXLINE)] = '\0';	/* add extra null at end */
				for (;;) {
					if ((cp = getaddress (cp, lastheader == header || cp != m->line)) == NULLCHAR)
						break;
					(void) addlist (cclist, cp, 0, cp);
					/* skip to next address, if any */
					cp += strlen (cp) + 1;
				}
				break;
			default:
				break;
		}
	}
	if (msgid != NULLCHAR || date != NULLCHAR) {
		*rhdr = mallocw (LINELEN);
		sprintf (*rhdr, "In-Reply-To: your message ");
		if (date != NULLCHAR) {
			sprintf (m->line, "of %s.\n", date);
			strcat (*rhdr, m->line);
			if (msgid != NULLCHAR)
				strcat (*rhdr, "             ");
		}
		if (msgid != NULLCHAR) {
			sprintf (m->line, "%s\n", msgid);
			strcat (*rhdr, m->line);
		}
		free (msgid);
		free (date);
	}
	strncpy (m->line, subject, MBXLINE);
	return 0;
}



#ifdef USERLOG

/*get the last message listed/read
 *from the areaname.USR file
 *keeps track for each user.
 *February '92, WG7J
 */
void
getlastread (struct mbx *m)
{
FILE *Alog;
char buf[256];
char *cp;
int found = 0;

	m->lastread = m->newlastread = 0L;

	sprintf (buf, "%s/users/%s.usr", Mailspool, m->area);
	if ((Alog = subdir_fopen (buf, "r+")) == NULLFILE) {
		/* USR file doesn't exist, create it */
		if ((Alog = subdir_fopen (buf, "w")) == NULLFILE)
			return;
		/* Add this user as first one */
		sprintf (buf, "%s 0\n", m->name);
		fputs (buf, Alog);
		(void) fclose (Alog);
		return;
	}
	/*Find user in the usr file for this area*/
	for (;;) {
		if (fgets (buf, sizeof (buf), Alog) == NULLCHAR)
			break;
		if ((cp = strchr (buf, ' ')) != NULLCHAR)	{
			*cp = '\0';
			if (!stricmp (m->name, buf)) {
				/*found user*/
				cp++;
				while (*cp == ' ')	/*skip blanks*/
					cp++;
				m->lastread = atol (cp);
				found = 1;
				break;
			}
		}
	}
	if (!found) {
		/*Add user*/
		sprintf (buf, "%s 0\n", m->name);
		fputs (buf, Alog);
	}
	(void) fclose (Alog);
	return;
}



/* Write the new last read id number to the USR file - WG7J
 * only update if this is not a bbs,
 * current area is a public area and not 'help',
 * or anything that starts with 'sys',
 * and a new message was actually listed/read
 */
void
setlastread (struct mbx *m)
{
FILE *Alog, *tfile;
char buf[256];
char tmpname[80];
char *cp;
int doit = 0;

	if ((m->sid & MBX_SID) || (m->newlastread <= m->lastread))
		return;

	/*	if(issysarea(m->area))
		if ((m->privs & MBX_SYSOP) || (stricmp(m->area, "help")))
			doit = 1;	*/
	if (isarea (m->area) && strcmp (m->area, "help"))
		doit = 1;
	if ((m->privs & SYSOP_CMD) && issysarea (m->area))
		doit = 1;

	if (doit) {

#ifdef notdef
		tprintf ("SETLAST: %d\n", m->newlastread);
#endif


		sprintf (buf, "%s/users/%s.usr", Mailspool, m->area);

		/* Rename the USR file to a tempfile */
#ifdef UNIX
		strncpy (tmpname, buf, 75);
		strcat (tmpname, ".bak");
#else
		(void) tmpnam (tmpname);
#endif
		if (rename (buf, tmpname))
			/* Can't rename ??? */
			return;

		if ((Alog = subdir_fopen (buf, "w")) == NULLFILE) {
			/* can't creat new USR file ???*/
			(void) rename (tmpname, buf);	/* try to undo the damage */
			return;
		}
		if ((tfile = subdir_fopen (tmpname, "r")) == NULLFILE)
			/* can't open renamed file ??? */
			return;

		/*Write all users back, but update this one!*/
		while (fgets (buf, sizeof (buf), tfile) != NULLCHAR) {
			if ((cp = strchr (buf, ' ')) != NULLCHAR)
				*cp = '\0';
			if (!stricmp (m->name, buf)) {
				/*found this user*/
				sprintf (buf, "%s %lu\n", m->name, m->newlastread);
			} else if (cp)
				*cp = ' ';	/* restore the space !*/
			fputs (buf, Alog);
		}
		(void) fclose (tfile);
		unlink (tmpname);
		(void) fclose (Alog);
	}
	return;
}

#endif /*USERLOG*/



void
scanmail (struct mbx *m)
{				/* Get any new mail */
	if (!isnewmail (m))
		return;
	if (lockit (m))
		return;
	(void) initnotes (m);
	rmlock (Mailspool, m->area);
}



/* Check the current mailbox to see if new mail has arrived.
 * Returns the difference in size.
 */
static long
isnewmail (struct mbx *m)
{
char buf[256];

	sprintf (buf, "%s/%s.txt", Mailspool, m->area);
	return fsize (buf) - m->mboxsize;
}



/* Check if the private mail area has changed */
long
isnewprivmail (struct mbx *m, const char *ext)
{
long cnt;
char buf[256];

	sprintf (buf, "%s/%s.%s", Mailspool, m->name, ext);
	cnt = m->mysize;
	m->mysize = fsize (buf);
	return m->mysize - cnt;	/* != 0 not more than once */
}



void
notifynewmail (struct mbx *m)
{				/* not a BBS, but from within ANY area */
	if (!(m->sid & MBX_SID) && (isnewprivmail (m, "txt") > 0L)) {
		tprintf ("\007You have new mail in your personal area (%s). Please Kill when read!\n", m->name);
		usflush (Curproc->output);
	}
}

#endif
#endif



/* This function returns the length of a file. The proper thing would be
 * to use stat(), but it fails when using DesqView together with Turbo-C
 * code.
 */
long
fsize (char *name)
{
long cnt;
FILE *fp;

	if ((fp = subdir_fopen (name, READ_TEXT)) == NULLFILE)
		return -1L;
	fseek (fp, 0L, 2);
	cnt = ftell (fp);
	/*	cnt = filelength (fileno(fp)); */
	(void) fclose (fp);
	return cnt;
}



#ifdef MAILCMDS
/* close the temporary mail file */
static void
mfclose (struct mbx *m)
{
	if (m->mfile != NULLFILE)
		(void) fclose (m->mfile);
	m->mfile = NULLFILE;
#ifdef SETVBUF
	free (m->stdoutbuf);
	m->stdoutbuf = NULLCHAR;
#endif
}

#endif



/* erase a previous prompt via backspaces */
void
backEmUp (int len)
{
int i;

	/* Get rid of the prompt */
	for (i = len; i != 0; i--)
		tputc ('\b');
	for (i = len; i != 0; i--)
		tputc (' ');
	for (i = len; i != 0; i--)
		tputc ('\b');
	tflush ();
}



/* Print prompt and read one character, telnet version */
int
tkeywait (
const char *prompt,	/* Optional prompt */
int flush,	/* Flush queued input? */
int linemode	/* Negotiate line mode? */
) {
int c, cl, oldimode, oldomode;

	if (flush && socklen (Curproc->input, 0) != 0)
		(void) recv_mbuf (Curproc->input, NULL, 0, NULLCHAR, 0);	/* flush */
	if (prompt == NULLCHAR)
		prompt = hitEnter;
	tprintf ("%s", prompt);
	/* hack - handle Unix telnet clients correctly */
	if (linemode)
		tprintf ("%c%c%c%c%c%c%c", IAC, SB, TN_LINEMODE, 1, 2, IAC, SE);
	tprintf ("%c%c%c", IAC, WILL, TN_ECHO);
	usflush (Curproc->output);

	/* discard the response */

	oldimode = sockmode (Curproc->input, SOCK_BINARY);
	oldomode = sockmode (Curproc->output, SOCK_BINARY);

	while ((c = rrecvchar (Curproc->input)) == IAC) {
		c = rrecvchar (Curproc->input);
		if (c == SB) {
			if ((c = rrecvchar (Curproc->input)) == EOF)
				break;
			cl = c;
			c = rrecvchar (Curproc->input);
			while ((c != EOF) && !(cl == IAC && c == SE)) {
				cl = c;
				c = rrecvchar (Curproc->input);
			}
		} else if (c > 250 && c < 255)
			(void) rrecvchar (Curproc->input);
	}

	(void) sockmode (Curproc->output, oldomode);
	(void) sockmode (Curproc->input, oldimode);

	backEmUp ((int) strlen (prompt));
	tprintf ("%c%c%c", IAC, WONT, TN_ECHO);
	if (linemode)
		tprintf ("%c%c%c%c%c%c%c", IAC, SB, TN_LINEMODE, 1, 1, IAC, SE);
	usflush (Curproc->output);
	return c;
}



/* Print -more- prompt and read reply,
 * AX.25 and NETROM version - WG7J
 * <CR> is taken as a 'Yes'
 */
int
mykeywait (char *prompt, struct mbx *m)
{
	tputs (prompt);
	usflush (Curproc->output);
	if (recvline (m->user, (unsigned char *) m->line, MBXLINE) == -1) {
		return -1;
	}
	/* Only 'N' or 'n' really matters */
	if ((*m->line == 'N') || (*m->line == 'n'))
		return -1;
	return 0;
}



struct areas {
	struct areas *next;
	char *name;
};
static struct areas *Areas = NULL;



static void
readareas (const char *name)
{
FILE *fp;
struct areas *ca = (struct areas *) 0, *na;
char *cp, buf[FILE_PATH_SIZE];

	if ((fp = fopen (name, READ_TEXT)) == NULL)
		return;

	while (fgets (buf, sizeof (buf), fp) != NULL) {
		if (buf[0] == '#' || buf[0] == '-')	/* comment line */
			continue;
		cp = buf;
		/* get first token on line */
		while (*cp) {
			if (*cp == '\n' || *cp == ' ' || *cp == '\t') {
				*cp = '\0';
				break;
			}
			cp++;
		}
		if (*buf) {
			na = (struct areas *) mallocw (sizeof (struct areas));

			na->name = strdup (buf);
			na->next = NULL;
			if (Areas == NULL)
				Areas = na;
			else if (ca)
				ca->next = na;
			ca = na;
		}
	}
	(void) fclose (fp);
}



static int
is_area2 (const char *name)
{
#ifdef MAILCMDS
struct areas *a;

	if (!Areas)
		readareas (Arealist);

	a = Areas;

	while (a) {
		if (stricmp (a->name, name) == 0)
			return 1;
		a = a->next;
	}
#endif
	return 0;
}



void
freeareas (void)
{
struct areas *a;

	while (Areas) {
		a = Areas;
		Areas = Areas->next;
		if (a->name)	/* should always be, but just in case */
			free (a->name);
		free (a);
	}
}
