#include "global.h"
#include "ctype.h"
#include "commands.h"
#ifndef MSDOS
#include <time.h>
#endif
#include "bm.h"
#include "reject.h"
#ifdef FBBFWD
extern int MbForwarded;
extern int FWDpersonal, FWDbulletins, FWDpmulti;


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

extern int indexFwdBbs (char *name);
extern char *nntp_name_expansion (char *name);
void exitfwd (struct mbx * m);
static int fbbparse (struct fbbpacket * msglst, char *fbline, char *area);
static int fbbdofs (struct fbbpacket * msglst, struct fwd * f, int msgcnt);
static int dofbbrecv (struct fwd * f);
static int dofbbsend (struct fwd * f, int firstsend);
extern int msgidcheck (char *string);
static int fbbfwdthisarea (struct fwd *, char *, int *, int *);
static void sendEndOfFbBlock (struct fwd *, int, int *, int *, int);
static int nonascii (char *str);

extern int SYSOPprotect;

#ifdef CATALOG
#include "catalog.h"

#define CAT fbbfwd_catalog

#define lostremote	__STR(0)
#define receivedfrom	__STR(1)
#define badFS		__STR(2)
#define sendingmessageto __STR(3)
#define errorfbbdofscode __STR(4)
#define refusing	__STR(5)
#define deferring	__STR(6)
#define transferfailed	__STR(7)
#define transfersuccess	__STR(8)
#define receivecode	__STR(9)
#define receivedfrom2	__STR(10)
#define negotiationchecksum __STR(11)
#define checksumerr	__STR(12)
#define negotiationverified __STR(13)
#define receivedbaddata	__STR(14)
#define preparingFS	__STR(15)
#define badFAline	__STR(16)
#define failedrewrite	__STR(17)
#define failedmsgid	__STR(18)
#define sendingresponse	__STR(19)
#define erroropeningtemp __STR(20)
#define messagesreceived __STR(21)
#define sendingFto	__STR(22)
#define processingarea	__STR(23)
#define sendingto	__STR(24)
#define closingfwdfile	__STR(25)
#define nodatatosend	__STR(26)
#define unexpected	__STR(27)
#define errorgettingtemp __STR(28)
#define mailrefused	__STR(29)
#define mailsent	__STR(30)
#define fwdexitstr	__STR(31)
#define forwardingto	__STR(32)
#define deferincoming	__STR(33)
#define toomanyerr	__STR(34)
#define toomanyerr2	__STR(35)

#else /* CATALOG */

static const char lostremote[] = "FBBFWD: Lost the remote connection with %s...\n";
static const char receivedfrom[] = "FBBFWD: Received %s from %s\n";
static const char badFS[] = "FBB Error: Expected 'FS ' string. Received '%s' string";
static const char sendingmessageto[] = "FBBFWD: Sending message to %s\n";
static const char errorfbbdofscode[] = "FBBFWD: Error opening %s in fbbdofs() code.\n";
static const char refusing[] = "FBBFWD: Skipping message %s refused\n";
static const char deferring[] = "FBBFWD: Deferring message from %s\n";
static const char transferfailed[] = "FBBFWD: Transfer failed with %s......\n";
static const char transfersuccess[] = "FBBFWD: Transfer successful with %s\n";
static const char receivecode[] = "FBBFWD: This is the receive code for %s.\n";
static const char receivedfrom2[] = "FBBFWD: Received from %s - %s\n";
static const char negotiationchecksum[] = "FBBFWD: Negotiation checksum with %s failed! Terminating...\n";
static const char checksumerr[] = "*** CHECKSUM ERROR (CHECKSUM OF PROPOSALS IS WRONG.)\n";
static const char negotiationverified[] = "FBBFWD: Checksum of negotiation with %s verified\n";
static const char receivedbaddata[] = "FBBFWD: Received bad data from %s...terminating...\n";
static const char preparingFS[] = "FBBFWD: Preparing our FS response to %s.\n";
static const char badFAline[] = "FBBFWD: Mail deferred from %s... bad FA/FB line\n";
static const char failedrewrite[] = "FBBFWD: Mail refused from %s.... Failed rewrite check: %s\n";
static const char failedmsgid[] = "FBBFWD: Mail refused from %s..... Failed msgid check: %s\n";
static const char sendingresponse[] = "FBBFWD: Sending our response:%s to %s\n";
static const char erroropeningtemp[] = "FBBFWD: Error opening temp file: '%s'\n";
static const char messagesreceived[] = "FBBFWD: Messages received from %s....\n";
static const char sendingFto[] = "FBBFWD: Sending F> to %s\n";
static const char processingarea[] = "FBBFWD: Processing %s message area %s for %s.\n";
static const char sendingto[] = "FBBFWD: Sending ( to %s ) to %s";
static const char closingfwdfile[] = "FBBFWD: Closing .fwd file for %s.\n";
static const char nodatatosend[] = "FBBFWD: No Data to send. Sending FF to %s\n";
static const char unexpected[] = "FBB Error: Expected '+, -, or ='. Received \'%c\'.\n";
static const char errorgettingtemp[] =
	"FBBFWD: Error getting tmp file.\n"
	"FBBFWD: We're going to pass this file to sendmbmsg()\n"
	"FBBFWD: We were trying to open %s and failed.\n";
static const char mailrefused[] = "FBBFWD: bbs mail refused: %s from %s";
static const char mailsent[] = "PBBS mail sent: %s ";
static const char fwdexitstr[] = "FBBFWD: PBBS fwd exit: %s";
static const char forwardingto[] = "FBB %sing forwarding of PBBS mail to: %s ";
static const char deferincoming[] = "FBBFWD: Deferring message from %s (to: %s): incoming via %s\n";
static const char toomanyerr[] = "*** TOO MANY MESSAGES (5 MESSAGES PER PROPOSAL, MAXIMUM)\n";
static const char toomanyerr2[] = "FBBFWD: Too many messages received in a proposal from: %s\n";
#endif /* CATALOG */


/*
     *** Protocole Error (x) messages.
     0: Subject Packet does not start with a SOH (01) Byte.
     1: Checsum of message is wrong.
     2: Message could not be uncompressed.
     3: Received binary frame is not STX (02) or EOT (04).
     4: Checsum of proposals is wrong.
     5: Answer to proposals must start with "F" or "**".
     6: Answer to proposals must be "FS".
     7: More than 5 answers (with "+", "-" or "=") to proposals.
     8: Answer to proposal is not "+", "-" or "=".
     9: The number of answers does not match the number of proposals.
    10: More than 5 proposals have been received.
    11: The number of fields in a proposal is wrong (6 fields).
    12: Protocol command must be "FA", "FB", "F>", "FF" or "FQ".
    13: Protocol line starting with a letter which is not "F" or "*".
*/

/* FBB Fowarding comments:

   FBB Forwarding sends messages in groups instead of normal pbbs forwarding
   that sends everything. i.e. You send 5 messages, he sends 5 messages, you
   send 5 messages, he sends 5 messages, etc.

   In our case... we send up to 5 messages from a forwarding area and then
   let the other system send us messages. When we get to the end of the
   messages in an area, we may send less than 5 messages. When we're done
   with the area... the other system will send us messages and we'll switch
   to another message area and start sending messages again. So... he may
   send 5 messages each time, and we might send 1 message each time... it just
   depends on how many messages are in each area. Hope that makes sense.
*/


void
FBBerror (int x, int y, struct mbx *m)
{
	tprintf ("*** Protocole Error (%d)\n", x);
	log (y, "Sent: *** Protocole Error (%d)", x);
	if (FBBtrace)
		tcmdprintf ("FBBFWD: Receive Protocol Error %d: %s\n", x, fwd_bbsname (m));
}



/* This code was moved for the FBBFWD stuff to compile without warning messages. */
void 
exitfwd (struct mbx *m)
{
	if (m->state != MBX_TRYING)
		log (m->user, fwdexitstr, fwd_bbsname (m));	/* N5KNX: log exits */
	if (FBBtrace) {
		tcmdprintf (fwdexitstr, fwd_bbsname (m));
		tcmdprintf ("\n");
	}
}



static int
nonascii (char *str)
{
	if (!str)
		return 1;
	for ( ; *str; str++)
		if (!isascii (*str))
			return 1;
	return 0;
}



/* This code converts/parses a FBB FB type message to a fbbpacket structure.
	   Here's an example FBB message header packet.
	   This one happens to contain 3 message headers.
		FA B KF5MG USA NOS 34535_KF5MG 298
		FA B KF5MG USA NOS 34537_KF5MG 303
		FA B KF5MG USA NOS 34541_KF5MG 309
 */
static int 
fbbparse (struct fbbpacket *msglst, char *fbline, char *area OPTIONAL)
{
char *cp;
char *atbbs;
char *to;
unsigned int len;

#ifdef REJECT
	/* first, before we strip off the 'FA/FB', lets pass the string to reject() */
	msglst->reject = reject (fbline, 1, 0);
#endif
	/* FB/FA */
	/* At present... FA and FB are treated equally. The FBB Specs say that 'FA' is an */
	/*    ASCII message and 'FB' is a BINARY file. 'FB' is not supported by FBB at    */
	/*    right now ( or rather is treated as a 'FA' type message tranfer ).          */
	if ((cp = strtok (fbline, " ")) == NULLCHAR)
		return 0;
	if (strlen (cp) < sizeof (msglst->fbbcmd))
		strncpy (msglst->fbbcmd, cp, 3);

	/* Message type ( P, B, or T ) */
	if ((cp = strtok (NULLCHAR, " ")) == NULLCHAR)
		return 0;
	msglst->type = cp[0];

	/* From id ( Userid only ) */
	cp = strtok (NULLCHAR, " ");
	free (msglst->from);
	msglst->from = (cp) ? strdup (cp) : cp;
	if (nonascii (cp))
		msglst->reject = REJ_REJECT;
	
	/* hostname or flood-area ( USA, WW, KF5MG.#DFW.TX.USA.NOAM, etc.) */
	len = 2;
	cp = strtok (NULLCHAR, " ");
	if (cp)	{
		atbbs = strdup (cp);
		len += strlen (cp);
	} else
		atbbs = cp;

	/* To: id */
	cp = strtok (NULLCHAR, " ");
	if (cp)	{
		to = strdup (cp);
		len += strlen (cp);
	} else
		to = cp;

	/* set msglst->to equal to the 'to id' '@' 'hostname' */
	free (msglst->to);

	if (to && atbbs)	{
		msglst->to = mallocw (len);
		strcpy (msglst->to, to);
		strcat (msglst->to, "@");
		strcat (msglst->to, atbbs);
	} else
		msglst->to = NULLCHAR;

	if (nonascii (msglst->to))
		msglst->reject = REJ_REJECT;

	/* Bid ( Lame - aka standard - ax.25 PBBS message id ) */
	cp = strtok (NULLCHAR, " ");
	free (msglst->messageid);
	msglst->messageid = (cp) ? strdup (cp) : cp;
	if (nonascii (msglst->messageid))
		msglst->reject = REJ_REJECT;

	/* Size ( At present, we don't do much with this. ) */
	cp = strtok (NULLCHAR, " ");
	msglst->size = (cp) ? atoi (cp) : -1;

	free (to);
	free (atbbs);

	if (msglst->size < 0)
		return 0;

	return 1;
}



/* This code processes a FS packet from a remote system.
   A '+' means the message is accepted.
     '-' means the message is rejected.
     '=' means the message is deferred.
                   i.e. This is wanted.... but can't be received at this time.

   RC =  0 means an error was detected.
         1 means an 'FF' was received from the remote system.
         2 means an 'Fx' (where x is something) was received.
*/

static int 
fbbdofs (struct fbbpacket *msglst, struct fwd *f, int msgcnt)
{
int i;
struct mbx *m;
int anysent = 0;
#ifdef FBBCMP
char txtfile[80];
#endif

	m = f->m;
	/* Send any data in our buffer. */
	usflush (m->user);

eatprompt:
	/* Get the FS line. */
	if (recvline (m->user, (unsigned char *) m->line, MBXLINE) == -1) {
		if (FBBtrace)
			tcmdprintf (lostremote, fwd_bbsname (m));
		return 0;
	}
	/* remove any line-end characters. */
	if (m->family == AF_INET && *m->line == '\n' && m->line[1] == 0)
		goto eatprompt;		/* hack for FBB 'telnet' */
	rip (m->line);
	(void) strupr (m->line);

	if (FBBtrace)
		tcmdprintf (receivedfrom, m->line, fwd_bbsname (m));

	/* Make sure we got a "FS " line. Anything else is an error. */
	if (strnicmp (m->line, "FS ", 3) != 0) {
		if (!m->fwdbbs->eatenanyprompts && !strcmp (">", m->line))	{
			m->fwdbbs->eatenanyprompts = 1;
			tcmdprintf ("FBBFWD: Eating a stray '>' (only allowed once) for %s\n", fwd_bbsname(m));
			goto eatprompt;
		}
		tprintf (badFS, m->line);
		tputs (".\n");
		if (FBBtrace) {
			tcmdprintf ("FBBFWD: ");
			tcmdprintf (badFS, m->line);
			tcmdprintf (" from %s\n", fwd_bbsname (m));
		}
		return 0;
	}
	m->fwdbbs->eatenanyprompts = 1;
	/* Check to see if we got too many responses. */
	/* The next check makes sure we got enough of the right responses. */
	if ((int) strlen (m->line) > (msgcnt + 3))
		return 0;

	/* Validate FS line. Clean up unused entries. */
	for (i = 0; i < FBBMAXMSGS; i++) {
		if (i < msgcnt) {
			if ((m->line[3 + i] != '+') &&
			    (m->line[3 + i] != '-') &&
			    (m->line[3 + i] != '=')) {
				tprintf (unexpected, m->line[3 + i]);
				return 0;
			}
		} else {
			/* Zero out and free rest of the FS structure. */
			msglst[i].accept = 0;
			free (msglst[i].to);
			msglst[i].to = NULLCHAR;
			free (msglst[i].from);
			msglst[i].from = NULLCHAR;
			free (msglst[i].messageid);
			msglst[i].messageid = NULLCHAR;
			free (msglst[i].sline);
			msglst[i].sline = NULLCHAR;
#ifdef FBBCMP
			free (msglst[i].rewrite_to);
			msglst[i].rewrite_to = NULLCHAR;
#endif
		}
	}

	/* The FS line is OK. Send the '+' messages and delete the '-' messages. */
	for (i = 0; i < msgcnt; i++) {
		kwait (NULL);
		rip (msglst[i].sline);	/* pretty up log. */

		MbForwarded++;

		if (m->line[3 + i] == '+') {
			/* Message is wanted. We need to send it.
			   Do not update the X-Forwarded Header yet. FBB does not tell us
			   if it received the message or not. We have to send all our
			   messages and then see if the connect is still there. If we get
			   a valid FBB Response, then we can assume that the messages were
			   delivered and update the X-Forwarded flag.
			 */

			anysent++;
			f->sentThisArea++;
			if (FBBtrace)
				tcmdprintf (sendingmessageto, fwd_bbsname (m));
			msglst[i].accept = fbbYES;
			changearea (f->m, msglst[i].area, (int) 0);

#ifdef FBBCMP
			if (f->m->sid & MBX_FBBCMP) {
				/* Open a tmpfile for sendmbmsg to place       */
				(void) tmpnam (txtfile);

				if ((m->quickfile = fopen (txtfile, "w+b")) == NULLFILE) {
					tcmdprintf (errorgettingtemp, txtfile);
					if (FBBtrace)
						log (m->user, errorfbbdofscode, txtfile);
					return 0;
				}
				/* Prepare the text of the message. */
				sendmbmsg (f->m, msglst[i].number, msglst[i].bid);

				/* close the file. send_yapp() will re-open it. */
				(void) fclose (m->quickfile);
				m->quickfile = NULLFILE;

				/* And Send it. */
				(void) send_yapp (f, txtfile, msglst[i].subject);
			} else
#endif
			{
				/* Send a subject line. */
				tprintf ("%s\n", msglst[i].subject);
				/* Send the text of the message. */
				sendmbmsg (f->m, msglst[i].number, msglst[i].bid);

				/* Send a ctrl-z ... that is OCTAL(32) */
				tputs ("\032\n");
				usflush (m->user);

				/* remove the files */
				unlink (f->iFile);
				unlink (f->oFile);
			}
		} else if (m->line[3 + i] == '-') {
			/* The remote system does not want this message.
			   Go ahead and mark the msg as so. If the link goes
			   down, we still want to mark this message as unwanted.
			   One the link goes back up, we do not want to ask the
			   remote system again about this because it already
			   told us no. This is handeled differently than '+'
			   messages.
			 */
			if (FBBtrace)
				tcmdprintf (refusing, fwd_bbsname (m));
			msglst[i].accept = fbbNO;
			/* mark message as forwarded or deleted. */
			mark_forwarded (msglst[i].fwdfile, msglst[i].fwdfileindex, '*');
			m->change |= CHG_READ;

			log (m->user, mailrefused, msglst[i].sline, fwd_bbsname (m));
		} else if (m->line[3 + i] == '=') {
			if (FBBtrace)
				tcmdprintf (deferring, fwd_bbsname (m));
			mark_forwarded (msglst[i].fwdfile, msglst[i].fwdfileindex, '=');
			msglst[i].accept = fbbDEFER;
		}
	}

	if (!anysent)
		goto goback;
		
	/* Now we will get a line from the remote system to make sure that the
	   messages were received ok.
	 */
	usflush (m->user);
	if (recvline (m->user, (unsigned char *) m->line, MBXLINE) == -1) {
		/* There was a problem. We did not get a response from the remote
		   system after we sent our messages.
		 */
		if (FBBtrace)
			tcmdprintf (transferfailed, fwd_bbsname (m));
		return 0;
	} else if (!anysent || toupper (*m->line) == 'F') {	/* only mark completed, if no garbage came in */
		if (anysent) {
			(void) strupr (m->line);
			if (FBBtrace)
				tcmdprintf (transfersuccess, fwd_bbsname (m));
		}
		/* We got a response back, so we can update our '+' and '-' messages. */
		for (i = 0; i < msgcnt; i++) {
			if (msglst[i].accept == fbbYES) {
				/* mark message as sent. */
				mark_forwarded (msglst[i].fwdfile, msglst[i].fwdfileindex, '*');
				m->change |= CHG_READ;

				log (m->user, mailsent, msglst[i].sline);
#ifdef STATS_MSG
				STATS_addmsg (1, 1);
#endif
#ifdef STATS_TFC
				STATS_addtfc (3, 1);
#endif
#ifdef STATS_FWD
				STATS_addfwd (1, 1, m->name);
#endif
			}
			if (msglst[i].accept == fbbYES || msglst[i].accept == fbbNO) {
				if ((!isarea (msglst[i].area) || msglst[i].type != 'B') && (stricmp (msglst[i].area, "sysop") || !SYSOPprotect) && (!FWDpmulti)) {
					m->mbox[msglst[i].number].status |= BM_DELETE;
					statusCtl (msglst[i].area, "ctl", &m->mbox[msglst[i].number], msglst[i].number, 0);
					m->change |= CHG_DELETE;
				}
			}
			free (msglst[i].to);
			msglst[i].to = NULLCHAR;
			free (msglst[i].from);
			msglst[i].from = NULLCHAR;
			free (msglst[i].messageid);
			msglst[i].messageid = NULLCHAR;
			free (msglst[i].sline);
			msglst[i].sline = NULLCHAR;
#ifdef FBBCMP
			free (msglst[i].rewrite_to);
			msglst[i].rewrite_to = NULLCHAR;
#endif
		}
	}
	/* Change forward direction */
goback:
	m->state = MBX_REVFWD;
	if (strnicmp (m->line, "FF", 2) == 0)
		return 1;
	else {
		if (!anysent)
			m->line[0] = 0;
		return 2;
	}
}



static int 
dofbbrecv (struct fwd *f)
{
int msgcnt;
int FBBok;
int FBBdone;
int i, ii;
int FirstTime;
int thechecksum = 0;
char FBBresp[9];
char *cp;
int retval;
struct fbbpacket *msglst;
struct mbx *m;
#ifdef FBBCMP
char tempname[80];
#endif

	msglst = f->msglst;
	m = f->m;

	msgcnt = 0;

	FBBdone = FALSE;
	FirstTime = TRUE;

	if (FBBtrace)
		tcmdprintf (receivecode, fwd_bbsname (m));

	/* Loop till we have received a F> or 5 FB blocks. */
	for (;;) {
		if (FirstTime) {
			/* Change the Firsttime flag and skip the read. */
			FirstTime = FALSE;
			if (m->line[0] == 0) {
				if (recvline (m->user, (unsigned char *) m->line, MBXLINE) == -1) {
					if (FBBtrace)
						tcmdprintf (lostremote, fwd_bbsname (m));
					break;
				}
				(void) strupr (m->line);
			}
		} else if (recvline (m->user, (unsigned char *) m->line, MBXLINE) == -1) {
			if (FBBtrace)
				tcmdprintf (lostremote, fwd_bbsname (m));
			break;
		}
		(void) strupr (m->line);

		kwait (NULL);

		if (cutofffwding (m))
			return 3;

		/* Start off  with FBBok = FALSE.
		   If we get a FB or F> we will reset it to TRUE and
		   continue on. If we get something else we will exit.
		 */
		FBBok = FALSE;

		/* strip any trailing NL characters. */
		if (m->family == AF_INET && *m->line == '\n' && m->line[1] == 0)
			continue;	/* hack for FBB 'telnet' */
		rip (m->line);

		if (FBBtrace)
			tcmdprintf (receivedfrom2, fwd_bbsname (m), m->line);

		/* calculate the checksum BEFORE converting to lowercase */
		if (!strnicmp (m->line, "fb", 2) || !strnicmp (m->line, "fa", 2)) {
			cp = m->line;
			while (*cp)
				thechecksum += *cp++;
			thechecksum += 13;
		}
		/* lowercase command. */
		(void) strlwr (m->line);

		if (strncmp (m->line, "ff", 2) == 0)
			/* End of FBB Packet */
			return 2;

		if (strncmp (m->line, "fq", 2) == 0)
			/* End of FBB Packet */
			return 3;

		if (strncmp (m->line, "f>", 2) == 0) {
			/* End of FBB Packet */

			if (strlen (m->line) > 2) {	/* we have a received checksum */
				int tempint;

				sscanf (&m->line[3], "%x", &tempint);
				thechecksum %= 256;
				if (thechecksum)
					thechecksum = 256 - thechecksum;
				if (tempint != thechecksum) {
					if (FBBtrace)
						tcmdprintf (negotiationchecksum, fwd_bbsname (m));
#if 1
					tprintf (checksumerr);
					break;
#endif
				} else if (FBBtrace)
					tcmdprintf (negotiationverified, fwd_bbsname (m));
			}
			FBBdone = TRUE;
			break;
		}
		if (strncmp (m->line, "fb", 2) == 0 || strncmp (m->line, "fa", 2) == 0) {
			/* Another message */
			if (msgcnt > FBBMAXMSGS) {
				/* Too many in fact. We should only get 5 (FBBMAXMSGS) msgs. */
				tprintf (toomanyerr);
				if (FBBtrace)
					tcmdprintf (toomanyerr2, fwd_bbsname (m));
				break;
			} else {
				log (m->user, "PBBS %s: %s", fwd_bbsname(m), m->line);
				if (fbbparse (&msglst[msgcnt], m->line, m->area))	{
					msgcnt++;
					FBBok = TRUE;
				}
			}
		}
		/* If we did not reset the FBBok flag,
		   something is wrong and we need to exit. */
		if (!FBBok) {
			if (FBBtrace)
				tcmdprintf (receivedbaddata, fwd_bbsname (m));
			break;
		}
	}

	/* We have exited the FB....F> loop.
	   If FBBdone = TRUE we are ok... otherwise we received bad data.
	 */
	if ((!FBBdone))
		return 0;

	if (FBBtrace)
		tcmdprintf (preparingFS, fwd_bbsname (m));

	/* FB....F> data looks ok. Now process it and build our FS line. */
	strcpy (FBBresp, "FS ");
	/* Check each message in the list to see if we want it or not. */
	for (i = 0; i < msgcnt; i++) {
		kwait (NULL);
		/* allow zero length messages */
		if (msglst[i].size < 0) {
			/* The size is in error. The FA/FB line is probably bad. We'll defer it for now */
			msglst[i].accept = fbbDEFER;
			if (FBBtrace)
				tcmdprintf (badFAline, fwd_bbsname (m));
		} else
			/* first check the msgid. If that works, run it through the rewrite file. */
		if (!msgidcheck (msglst[i].messageid)) {
			msglst[i].accept = fbbYES;
			kwait (NULL);

			if (msglst[i].reject == REJ_REJECT) {
				msglst[i].accept = fbbNO;
				if (FBBtrace) {
					(void) strupr (msglst[i].messageid);
					tcmdprintf (failedmsgid, fwd_bbsname (m), msglst[i].messageid);
				}
			} else if (msglst[i].reject == REJ_DEFER) {
				msglst[i].accept = fbbDEFER;
				if (FBBtrace)
					tcmdprintf ("FBBFWD: Deferring message\n");
				log (m->user, "FBBFWD: Deferring message\n");
			} else if ((cp = rewrite_address (msglst[i].to, 0)) != NULLCHAR) {
				/* See if this is on the reject list. */
				if (!strcmp (cp, "refuse")) {
					msglst[i].accept = fbbNO;
					if (FBBtrace) {
						(void) strupr (msglst[i].to);
						tcmdprintf (failedrewrite, fwd_bbsname (m), msglst[i].to);
					}
				}
				/* See if this is on the defer list. */
				if (!strcmp (cp, "defer")) {
					msglst[i].accept = fbbDEFER;
					if (FBBtrace)
						tcmdprintf ("FBBFWD: Deferring message\n");
					log (m->user, "FBBFWD: Deferring message\n");
				}
				free (cp);
			}
			/* This section checks the other FBB-style sessions, to see if this
			   message is already coming in via another connection. If so, it is
			   deferred till later............ Only checked if still OK */
			if (msglst[i].accept == fbbYES) {
				for (ii = 0; ii < NUMMBX; ii++) {
					if (Mbox[ii] != NULLMBX && Mbox[ii]->msglst && Mbox[ii] != m) {
						int k;

						for (k = 0; k < FBBMAXMSGS; k++)
							if (Mbox[ii]->msglst[k].accept == fbbYES && Mbox[ii]->msglst[k].messageid && !strcmp (Mbox[ii]->msglst[k].messageid, msglst[i].messageid)) {
								msglst[i].accept = fbbDEFER;
								if (FBBtrace)
									tcmdprintf (deferincoming, fwd_bbsname (m), msglst[i].to, Mbox[ii]->name);
								log (m->user, deferincoming, fwd_bbsname (m), msglst[i].to, Mbox[ii]->name);
								break;
							}
						if (msglst[i].accept == fbbDEFER)
							break;
					}
				}
			}
		} else {
			msglst[i].accept = fbbNO;
			if (FBBtrace) {
				(void) strupr (msglst[i].messageid);
				tcmdprintf (failedmsgid, fwd_bbsname (m), msglst[i].messageid);
			}
		}

		switch (msglst[i].accept) {
			case fbbNO:
				FBBresp[i + 3] = '-';
				break;
			case fbbYES:
				FBBresp[i + 3] = '+';
				break;
			case fbbDEFER:
				FBBresp[i + 3] = '=';
				break;
			case fbbUNKNOWN:
			default:
				tcmdprintf ("Programing error.... msglst.accept = 0 and that is not valid.\n");
				FBBresp[i + 3] = '=';
				break;
		}		/* endswitch */
	}
	/* Add a null to the end of the string. */
	FBBresp[i + 3] = '\0';

	if (FBBtrace)
		tcmdprintf (sendingresponse, FBBresp, fwd_bbsname (m));

	/* Now we can send it. */
	tprintf ("%s\n", FBBresp);
	usflush (m->user);

	/* The remote system will start sending the messages.
	   Loop through the message list.
	 */

	for (i = 0; i < msgcnt; i++) {
		if (msglst[i].accept == fbbYES) {
#ifdef FBBCMP
			if (f->m->sid & MBX_FBBCMP) {
#if 0
				log (m->user, "FBBFWD: Processing new FBB Compressed Message.");
#endif

				/* Set up a temp name to receive the file. */
				(void) tmpnam (tempname);

				/* Receive the message and uncompress it. */
				strncpy (f->iFile, tmpnam (NULL), FWDFILELEN);
				strncpy (f->oFile, tempname, FWDFILELEN);
				if (recv_yapp (f) == 0)
					return 0;

				if ((m->quickfile = fopen (f->oFile, "rb")) == NULLFILE) {
					if (FBBtrace)
						tcmdprintf (erroropeningtemp, f->oFile);
					log (m->user, erroropeningtemp, f->oFile);
					return 0;
				}
			}
#endif
			kwait (NULL);
			sprintf (m->line, "s%c %s < %s \"$%s\"", msglst[i].type,
				 msglst[i].to, msglst[i].from, msglst[i].messageid);
			/* upper case the line */
			(void) strupr (m->line);

			/* Call mbx_parse to parse the send command. */
			retval = mbx_parse (m);
#ifdef FBBCMP
			if (m->quickfile) {
				(void) fclose (m->quickfile);
				m->quickfile = NULLFILE;
			}
#endif
			if (retval < 0) {
				if (FBBtrace)
					tcmdprintf (lostremote, fwd_bbsname (m));
				return 0;
			}
			/* remove the files */
			unlink (f->iFile);
			unlink (f->oFile);
		}
	}

	if (FBBtrace)
		tcmdprintf (messagesreceived, fwd_bbsname (m));
#ifdef FBBCMP
	if (m->quickfile) {
		(void) fclose (m->quickfile);
		m->quickfile = NULLFILE;
	}
#endif
	return 1;
}



/****************************************************************************************/
/* This code is called when we've prepared 5 FB Blocks, or we don't have any more       */
/* messages to send.                                                                    */
/****************************************************************************************/
static void 
sendEndOfFbBlock (struct fwd *f, int msgcnt, int *FBBok, int *FBBdone, int thechecksum)
{
int rc;
struct mbx *m;
struct fbbpacket *msglst;

	/* point pointers to their proper location. */
	msglst = f->msglst;
	m = f->m;

	if (FBBtrace)
		tcmdprintf (sendingFto, fwd_bbsname (m));
	/* Send a end-of FB Block flag */
	thechecksum %= 256;
	if (thechecksum)
		thechecksum = 256 - thechecksum;
	tprintf ("F> %02X\n", thechecksum);

	/* Process an incoming FS and receive messages. */
	rc = fbbdofs (msglst, f, msgcnt);
	if (!rc) {
		*FBBok = FALSE;
		*FBBdone = TRUE;
	} else if (rc == 1) {
		*FBBok = TRUE;
		*FBBdone = FALSE;
	} else if (rc == 2) {
		*FBBok = TRUE;
		*FBBdone = TRUE;
	}
}



/****************************************************************************************/
/* This code is called for each message area we forward to.                             */
/****************************************************************************************/
static int 
fbbfwdthisarea (struct fwd *f, char *area, int *FBBok, int *FBBdone)
{
char line[MBXLINE + 1];
int err = 0;
int msgcnt = 0;
int Tmsgcnt = 0;
int theindex = 0;
int rc, i;
long bid;
long curpos;
long tempsize;
int thechecksum = 0;
char *pszBid;
long msgsize = 0;
struct let *cmsg;
struct mbx *m;
struct fbbpacket *msglst;
int bulletin;		/* for makecl() */
char *newto = NULLCHAR;
char line2[64];
char thebid[LINELEN];
char subject[LINELEN];
struct arealist *a;

	/* point pointers to their proper location. */
	msglst = f->msglst;
	m = f->m;

	/* public area? */
	bulletin = isarea (area);

	if (FBBtrace && FWDareatrace)
		tcmdprintf (processingarea, bulletin ? "Public  " : "Private ", area, fwd_bbsname (m));

	/* Initialize variables. */
	*FBBok = TRUE;
	*FBBdone = FALSE;

	if (bulletin && (!FWDbulletins || !m->fwdbbs->bulletins)) {
		if (FBBtrace)
			tcmdprintf ("Skipping any bulletins in area '%s' for %s\n", area, m->fwdbbs->name);
		return Tmsgcnt;
	}
	if (!bulletin && (!FWDpersonal || !m->fwdbbs->personals)) {
		if (FBBtrace)
			tcmdprintf ("Skipping any personal messages in area '%s' for %s\n", area, m->fwdbbs->name);
		return Tmsgcnt;
	}
	/* check if there are any messages in this area
	 * that need to be forwarded.
	 */
	rewind (f->fwdfile);		/* rewind forward file...       */
	curpos = ftell (f->fwdfile);	/* get file position            */
	/* read each line in the .fwd file. exit loop on error or no more lines */
	while (!err && fgets (line, MBXLINE, f->fwdfile) != NULLCHAR) {
		(void) fflush (f->fwdfile);
		kwait (NULL);		/* Give control back to system. */
		if (*line == ' ') {	/* '*' means this has been done          */
					/* '!' means this is blocked             */
					/* '-' means this was sent to an ALT BBS */
					/* '=' means this has been deferred this session */
			if ((pszBid = strpbrk (&line[1], " \t")) == NULLCHAR)
				continue;	/* no bid, error! */
			/* get the bid. */
			*pszBid++ = '\0';
			if (!stricmp (area, &line[1])) {		/* is this the correct area?    */
				bid = atol (pszBid);			/* get the message number       */
				changearea (m, area, (int) 0);	/* if not... go there now...    */

				/* Set the 'found message flag' to false. */
				theindex = 0;
				/* for each message in the .txt file... */
				for (cmsg = &m->mbox[1], i = 1; i <= m->nmsgs; i++, cmsg++)
					/* find the message we're supposed to forward. */
					if ((bid == cmsg->bid) && !(cmsg->status & BM_DELETE)) {
						/* this is the message... set the flag and break. */
						theindex = i;
						break;
					}
				if (theindex && m->fwdbbs && m->fwdbbs->maxsize && cmsg->size > m->fwdbbs->maxsize) {
					if (FBBtrace)
						tcmdprintf ("FBBFWD: Deferring large message to %s, allowed=%ld, size=%ld\n", m->fwdbbs->name, m->fwdbbs->maxsize, cmsg->size);
				} else {
					/* if we didn't find the message... it must have been deleted. We can   */
					/* mark the message as forwarded so we don't try it again. This is just */
					/* done for performance reasons.                                        */
					/* Also, if found, block it. Then if an I/O error occurs, this message  */
					/* will be skipped until all others have a chance to get passed         */
					if (!theindex || (cmsg->status & BM_ONHOLD) == 0)
						mark_forwarded (f->fwdfile, curpos, (!theindex) ? '*' : '!');

					/* if the 'found message flag' is set.... */
					if (theindex && !(cmsg->status & (BM_ONHOLD | BM_DELETE))) {
						newto = NULLCHAR;	/* just in case */
						if (m->fwdbbs)	/* just in case */
							for (a = m->fwdbbs->areas; a; a = a->next)
								if (!stricmp (area, a->name))
									newto = a->forceaddr;
						/* Prepare the FB line. */
						bulletin = isarea (area);	/* reset - just in case */
						rc = makecl (m, theindex, newto, line2, subject, thebid, &bulletin);
						if (rc == -2)	{	/* delete the message */
							mark_forwarded (f->fwdfile, curpos, '*');
							if ((!isarea (area) || m->stype != 'B') && (stricmp (area, "sysop") || !SYSOPprotect)) {
								m->mbox[i].status |= BM_DELETE;
								statusCtl (area, "ctl", &m->mbox[i], i, 0);
								m->change |= CHG_DELETE;
							}
						} else if (rc != -1) {	/* If FB line ok, send it. */
							/* Copy FB line for message log. */
							free (msglst[msgcnt].sline);
							(void) strupr (msglst[msgcnt].sline = strdup (line2));
							msglst[msgcnt].fwdfileindex = curpos;
							msglst[msgcnt].fwdfile = f->fwdfile;

							/* Keep track of the subject.    */
							free (msglst[msgcnt].subject);
							msglst[msgcnt].subject = strdup (subject);

							/* Keep track of the message area */
							free (msglst[msgcnt].area);
							msglst[msgcnt].area = strdup (area);

							/* Keep track of message number in area. */
							msglst[msgcnt].number = i;

							/* Keep track of makecl() modified bid. */
							strncpy (msglst[msgcnt].bid, &thebid[1], 15);

							/* uppercase the FB line and send it. */
							(void) strupr (line2);
							tprintf ("%s", line2);
							{
								char *cpp = line2;

								while (*cpp != '\n')
									thechecksum += *cpp++;
								thechecksum += 13;
							}
							if (FBBtrace)
								tcmdprintf (sendingto, fwd_bbsname (m), line2);

							if (fbbparse (&msglst[msgcnt], line2, m->area))	{
								msgcnt++;
								msgsize += m->mbox[theindex].size;
							}
							/* If we have filled our FB Block */
							if (msgcnt >= FBBMAXMSGS || (m->fwdbbs && m->fwdbbs->fbbsize && msgsize > m->fwdbbs->fbbsize)) {
								sendEndOfFbBlock (f, msgcnt, FBBok, FBBdone, thechecksum);
								thechecksum = 0;
								Tmsgcnt += msgcnt;
								msgcnt = 0;
								msgsize = 0;
							}
						}
					}
				}
			}	/* end if() */
		}		/* end if() */
		/* If we got an error from dofs() or the response
		   from dofs() was 2, we are done with this message
		   transfer. If we got a 1 from dofs() we can send
		   more messages now so we do not need to exit.
		 */
		if (*FBBdone)
			break;
		curpos = ftell (f->fwdfile);	/* get file position            */
		tempsize = m->mysize;
		if (isnewprivmail (m, "fwd") > 0) {
			m->mysize = tempsize;
			break;
		}
	}			/* end while() */

	/* If we have any left over FB messages... send them now. */
	if (msgcnt) {
		sendEndOfFbBlock (f, msgcnt, FBBok, FBBdone, thechecksum);
		Tmsgcnt += msgcnt;
	}
	return Tmsgcnt;
}



/* This code is used to figure out what messages need to be sent. */
static int 
dofbbsend (struct fwd *f, int firstsend)
{
int err;
int Tmsgcnt;
int FBBdone = 0;
int FBBok = 0;
long pos;
int areasused;
int equals, bang;
char name[256];
struct arealist *fwdarea;
struct mbx *m;

	/* point pointers to their proper location. */
	m = f->m;

	/* figure out name of the user. */
	sprintf (name, "%s/%s", Mailspool, m->name);

	/* What does this do? */
	(void) nntp_name_expansion (name);

	/* Save data */
	strncpy (f->savefsline, m->line, MBXLINE);

	/* figure out .fwd file name and see if it exist. It it doesn't exist...   */
	/* .... we have nothing for this user.                                     */
	strcat (name, ".fwd");
	(void) strlwr (name);
	if ((f->fwdfile = fopen (name, UPDATE_TEXT)) == NULLFILE)
		/* the file doesn't exist. This should only occur with a reverse forward request. */
		return 2;

	/* Check to see if we have any info to send and make sure it is the
	   right time to send data.
	 */
	if (!f->m->fwdbbs && fwdinit (f->m, 1) == -1)
		/* The bbs is not in the forward.bbs file or it is the wrong time to send.
		   Return a 0 to indicate that we have no data for the remote system.
		 */
		return 0;

	/* Now grab a subchannel slot, if needed. It is available, or fwdinit()
	   would have returned -1. */
	(void) checksubchannel (m, 1);

	/* Check to see if we have already read the list of areas to send
	   for this bbs.

	   Since FBB calls this code for each block of 5 messages, we
	   want to reduce the number of times we read the foward.bbs
	   file to pull out the forward area info.
	 */

	/* restore data. */
	strncpy (m->line, f->savefsline, MBXLINE);

checkloop:
	kwait (NULL);
	/* Initialize the fwdarea pointer. This is the list of areas we forward to. */
	fwdarea = m->fwdbbs->areas;

	/* for each area in the fwdarea list do.... */
	err = 0;
	Tmsgcnt = 0;
	areasused = 0;
	(void) isnewprivmail (m, "fwd");	/* first time, just sets m->mysize */
	while (!err && fwdarea) {
		kwait (NULL);
		if (cutofffwding (m)) {
			FBBok = 0;
			break;
		}
		f->sentThisArea = 0;
		Tmsgcnt += fbbfwdthisarea (f, fwdarea->name, &FBBok, &FBBdone);
		if (FBBdone || Tmsgcnt)
			break;
		if (isnewprivmail (m, "fwd") > 0)
			goto checkloop;
		if (f->sentThisArea)	/* if we got something in this area */
			areasused += 1;
		fwdarea = fwdarea->next;	/* Do next area. */
	}			/* end while() */

	if (!FBBok) {
		/* We had an error. */
		(void) fclose (f->fwdfile);
		return 3;
	}
	/* this next piece (and the sections above that affect areasused), make
	   this go BACK to check for messages added into earlier areas. This
	   prevents us from disconnecting if some new info JUST came in. */
	if (areasused)
		goto checkloop;

	if (FBBtrace)
		tcmdprintf (closingfwdfile, fwd_bbsname (m));

	kwait (NULL);
	fwdlockit (m->name);
	/* See if we can remove the .fwd file. */
	rewind (f->fwdfile);
	equals = 0;
	bang = 0;
	while (pos = ftell (f->fwdfile), fgets (f->line, MBXLINE, f->fwdfile) != NULLCHAR) {
		kwait (NULL);
		if (*f->line == '=') {
			mark_forwarded (f->fwdfile, pos, '!');
			equals++;
			err = 1;
		} else if (*f->line == '!') {
			mark_forwarded (f->fwdfile, pos, ' ');
			bang++;
			err = 1;
		} else if (*f->line != '*' && *f->line != '-') 		/* if not already done */
			err = 1;
	}

	if (firstsend && !Tmsgcnt && bang && !equals) {
		/* we didn't transfer any messages, there seems to have been all
		   messages marked with '!', so we will re-scan */
		if (FBBtrace)
			tcmdprintf ("FBBFWD: Relooping on .fwd file for %s - 1st scan since last aborted session\n", fwd_bbsname (m));
		fwdunlockit (m->name);
		firstsend = 0;
		goto checkloop;
	}

	/* one last check, to avoid deleting a file that JUST added a record */
	if (!FBBdone && isnewprivmail (m, "fwd") > 0) {
		if (FBBtrace)
			tcmdprintf ("FBBFWD: Relooping on .fwd file for %s - new messages received\n", fwd_bbsname (m));
		fwdunlockit (m->name);
		goto checkloop;
	}
	(void) fclose (f->fwdfile);
	if (!err)	{
		if (FBBtrace)
			tcmdprintf ("FBBFWD: Removing completed .fwd file for %s\n", fwd_bbsname (m));
		(void) remove (name);
	}
	fwdunlockit (m->name);
	kwait (NULL);

	if (!Tmsgcnt)
		/* We had no data. */
		return 2;
	return 1;
}



/* This is the main entry point for FBB forwarding. */
int
dofbbfwd (int argc, char *argv[], void *p)
{
struct fwd f;
struct mbx *m;
struct fbbpacket *msglst;
int i;
int firstsend = 1;
int Done;
int rc;
int FBBRdone = FALSE;	/* Receiving system has no more data. */
int NeedData;
int FBBfwd;
int sendFQ = 0;

	f.m = (struct mbx *) p;
	m = f.m;

	log (f.m->user, forwardingto, (argc) ? "Incom" : "Outgo", m->name);

	/* First verify that this is a FBB type system. */
	if (!(f.m->sid & MBX_FBBFWD)) {
		tprintf ("Huh?\n");
		usflush (m->user);
		return -1;
	}
	/* Get memory for the msglst array. */
	f.msglst = (struct fbbpacket *) callocw ((unsigned) FBBMAXMSGS, sizeof (struct fbbpacket));

	m->msglst = msglst = f.msglst;

	NeedData = FALSE;
	Done = FALSE;
	FBBfwd = FALSE;

	/* See if we were called because we received a FB from the remote system. */
	if (argc == 0) {
		f.m->state = MBX_FORWARD;	/* We are in send mode. */
		FBBRdone = FALSE;
		NeedData = TRUE;
		FBBfwd = TRUE;
	} else if (strnicmp (argv[0], "FA", 2) == 0 || strnicmp (argv[0], "FB", 2) == 0) {
		if (argc != 7) {
			tprintf ("Huh?\n");
			usflush (m->user);
			Done = TRUE;
		} else {
			sprintf (f.m->line, "FA %s %s %s %s %s %s", argv[1], argv[2],
				argv[3], argv[4], argv[5], argv[6]);
			/* indicate that we are in receive mode. */
			f.m->state = MBX_REVFWD;
			FBBRdone = FALSE;
		}
	} else {
		/* Must have received a FF from the remote system. */
		f.m->state = MBX_FORWARD;	/* We are in send mode. */
		FBBRdone = TRUE;
	}

	if (FBBtrace)
		tcmdprintf ("FBBFWD: %sing forwarding started with %s.\n", (argc) ? "Incom" : "Outgo", fwd_bbsname (m));

	while (!Done) {
		kwait (NULL);
		if (f.m->state == MBX_REVFWD) {
			if (FBBtrace)
				tcmdprintf ("FBBFWD: Receiving data from %s.\n", fwd_bbsname (m));

			if (NeedData)		/* null out the line. */
				f.m->line[0] = 0;

			/* Receive data from remote system. Process FB....F> block. */
			rc = dofbbrecv (&f);
			if (rc == 0)		/* An error occured. */
				Done = TRUE;
			else if (rc == 2)	/* Remote system sent us a FF */
				FBBRdone = TRUE;
			else if (rc == 3) {
				sendFQ = 1;
				Done = TRUE;
			}
			/* Change status. */
			f.m->state = MBX_FORWARD;
			smtptick (NULL);	/* wake SMTP to send that mail */
		} else {
			if (FBBtrace)
				tcmdprintf ("FBBFWD: Sending data to %s.\n", fwd_bbsname (m));

			NeedData = FALSE;
			/* Change status. */
			rc = dofbbsend (&f, firstsend);
			firstsend = 0;
			if (rc == 3)		/* An error occured. */
				break;
			if ((rc == 0) || (rc == 2)) {
				/* We had no data for remote system. */
				if (FBBRdone) {
					/* They have no more data for us....
					   So we break out of this loop and send our FQ and disconnect.
					 */
					sendFQ = 1;
					break;
				} else {
					/* Tell them that we do not have any data for them. */
					if (FBBtrace)
						tcmdprintf (nodatatosend, fwd_bbsname (m));
					tprintf ("FF\n");
					usflush (m->user);
					NeedData = TRUE;
				}
			}
			/* Change status. */
			f.m->state = MBX_REVFWD;
		}
	}			/* endwhile */

	/* free anything in the msglst array. */
	for (i = 0; i < FBBMAXMSGS; i++) {
		free (msglst[i].to);
		free (msglst[i].from);
		free (msglst[i].messageid);
		free (msglst[i].sline);
#ifdef FBBCMP
		free (msglst[i].rewrite_to);
		msglst[i].rewrite_to = NULLCHAR;
#endif
		msglst[i].to = msglst[i].from = msglst[i].messageid = msglst[i].sline = NULLCHAR;
	}

	/* Now free the msglst array. */
	free (msglst);

	if (sendFQ) {
		if (FBBtrace)
			tcmdprintf ("FBBFWD: Sending our FQ to %s...\n", fwd_bbsname (m));

		/* We're done. Send our FQ and we're out of here. */
		tprintf ("FQ\n");
	}
	usflush (m->user);
	releasesubchannel (m);	/* just in case */
	if (FBBfwd) {
		exitfwd (m);
		return 0;
	} else {
		/* This section marks the last fwd session time */
		if ((i = indexFwdBbs (fwd_bbsname (m))) != NUMFWDBBS) {
			MyFwds[i].laston = time (NULL);
			MyFwds[i].lastactivity = time (NULL);
		}
		m->state = MBX_CMD;
		return domboxbye (0, NULL, m);
	}
}

#endif /* ifdef FBBFWD */
