/* Derived from fbbfwd.c */

#include "global.h"
#ifdef XFWD
#include "ctype.h"
#include "commands.h"
#include "files.h"
#include "bm.h"
#include "reject.h"

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

extern int MbForwarded, SYSOPprotect;
extern int FWDpersonal, FWDbulletins, FWDpmulti;
extern char FWDCall[AXALEN];

extern int indexFwdBbs (char *name);
extern void importcmd (int mode, char *bbsname);
extern int bid_check (register char *string);
extern char *nntp_name_expansion (char *name);
extern void exitfwd (struct mbx *m);
static int xdosy (struct fbbpacket *msglst,struct fwd *f,int msgcnt);
static int doxrecv (struct fwd *f);
static int doxsend (struct fwd *f, int firstsend);
static int xfwdthisarea (struct fwd *, char *, int *, int *);
static void sendEndOfXBlock (struct fwd *, int, int *, int *);


#ifdef CATALOG
#include "catalog.h"

#define CAT xfwd_catalog

#define forwardingto	__STR(0)
#define nodatatosend	__STR(1)
#define closingfwdfile	__STR(2)
#define processingarea	__STR(3)
#define sendingto	__STR(4)
#define sendingSXto	__STR(5)
#define badSY		__STR(6)
#define lostremote	__STR(7)
#define receivedSYfrom	__STR(8)
#define transferfailed	__STR(9)
#define errorgettingtemp __STR(10)
#define errorxdosycode __STR(11)
#define sendingmessageto __STR(12)
#define refusing	__STR(13)
#define mailrefused	__STR(14)
#define transfersuccess	__STR(15)
#define mailsent	__STR(16)
#define receivecode	__STR(17)
#define sendingSY	__STR(18)
#define messagesreceived __STR(19)
#define receivedfrom	__STR(20)

#else /* CATALOG */
static const char forwardingto[] = "%sing X-forwarding of PBBS mail to: %s ";
static const char nodatatosend[] = "XFWD: No Data to send. Sending F> to %s\n";
static const char closingfwdfile[] = "XFWD: Closing .fwd file for %s.\n";
static const char processingarea[] = "XFWD: Processing %s message area %s for %s.\n";
static const char sendingto[] = "XFWD: Sending ( to %s ) to %s";
static const char sendingSXto[] = "XFWD: Sending SX Block of %d to %s\n";
static const char badSY[] = "XFWD Error: Expected 'SY ' string. Received '%s' string";
static const char lostremote[] = "XFWD: Lost the remote connection with %s...\n";
static const char receivedSYfrom[] = "XFWD: Received '%s' from %s\n";
static const char transferfailed[] = "XFWD: Transfer failed with %s......\n";
static const char errorgettingtemp[] =
	"XFWD: Error getting tmp file.\n"
	"XFWD: We're going to pass this file to sendmbmsg()\n"
	"XFWD: We were trying to open %s and failed.\n";
static const char errorxdosycode[] = "XFWD: Error opening %s in xdosy() code.\n";
static const char sendingmessageto[] = "XFWD: Sending message to %s\n";
static const char refusing[] = "XFWD: Skipping message %s refused\n";
static const char mailrefused[] = "XFWD: bbs mail refused: %s from %s";
static const char transfersuccess[] = "XFWD: Transfer successful with %s\n";
static const char mailsent[] = "PBBS mail sent: %s ";
static const char receivecode[] = "XFWD: This is the receive code for %s.\n";
static const char sendingSY[] = "XFWD: Sending our 'SY %d' response to %s.\n";
static const char messagesreceived[] = "XFWD: Messages received (%d) from %s....\n";
static const char receivedfrom[] = "XFWD: Received from %s - %s\n";
#endif /* CATALOG */


/* This code processes a SY packet from a remote system.
   Return = 0 means an error was detected.
            1 means an 'F>' or '>' was received from the remote system.
            2 means an 'Fx' (where x is something) was received.
*/

static int xdosy (msglst, f, msgcnt)
struct fbbpacket *msglst;
struct fwd *f;
int msgcnt;
{
int i, k;
struct mbx *m;
int incnt;
char txtfile[80], *cp, *cp2;
char tmp[AXBUF];

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

eatprompt:
	/* Get the SY line. */
	if (recvline (m->user, (unsigned char *)m->line, MBXLINE) == -1 ) {
		if (Xtrace)
			tcmdprintf (lostremote, fwd_bbsname(m));
		return 0;
	}

	/* remove any line-end characters. */
	rip (m->line);

	if (Xtrace)
		tcmdprintf (receivedSYfrom, m->line, fwd_bbsname(m));

	/* Make sure we got a "SY " line. Anything else is an error. */
	if (strnicmp (m->line, "SY ", 3) != 0) {
		if (!m->fwdbbs->eatenanyprompts && !strcmp (">", m->line))	{
			m->fwdbbs->eatenanyprompts = 1;
			tcmdprintf ("XFWD: Eating a stray '>' (only allowed once) for %s\n", fwd_bbsname(m));
			goto eatprompt;
		}
		tprintf (badSY, m->line);
		tputs (".\n");
		if (Xtrace)	{
			tcmdprintf(badSY, m->line);
			tcmdprintf(" from %s\n", fwd_bbsname(m));
		}
		return 0;
	}

	m->fwdbbs->eatenanyprompts = 1;
	incnt = atoi (&m->line[3]);
	/* Check to see if we got too many responses. */
	/* The next check makes sure we got enough of the right responses. */
	if(incnt > msgcnt)
		return 0;

	/* Validate SY line. Clean up unused entries. */
	for (i = 0; i < XMAXMSGS; i++) {
		msglst[i].accept = fbbNO;
		if (i >= msgcnt) {
			/* Zero out and free rest of the FS structure. */
			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;
		}
	}

	/* process the requested bid in the SY block */
	for (i = 0; i < incnt; i++)	{
		if (recvline (m->user, (unsigned char *)m->line, MBXLINE) == -1) {
			if (Xtrace)
				tcmdprintf (transferfailed, fwd_bbsname(m));
			return 0;
		}
		rip (m->line);
		for (k = 0; k < msgcnt; k++) {
			if (!stricmp (m->line, msglst[k].bid))	{
				msglst[k].accept = fbbYES;
				break;
			}
		}
	}

	/* Now open the tempfile */
	(void) tmpnam (txtfile);

	if ((m->quickfile = fopen (txtfile, "w+b")) == NULLFILE) {
		tcmdprintf (errorgettingtemp, txtfile);
		if (Xtrace)
			log (m->user, errorxdosycode, txtfile);
		return 0;
	}
	
	/* The SY line is OK. Send the requested messages. */
	for (i = 0; i < msgcnt; i++) {
		kwait (NULL);
		MbForwarded++;

		if(msglst[i].accept == fbbYES) {
			/* Message is wanted. We need to send it.
			   Do not update the X-Forwarded Header yet. X-fwd 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. */

			if (Xtrace)
				tcmdprintf (sendingmessageto, fwd_bbsname(m));
			changearea (f->m, msglst[i].area, (int) 0);

			/* build From: line */
			strncpy (m->line, msglst[i].sline, MBXLINE);
			cp = strchr (m->line, '<');
			if (cp == NULLCHAR)	/* shouldn't ever happen */
				continue;
			cp += 2;
			cp2 = strchr (cp, ' ');
			if (cp2 != NULLCHAR)	/* also shouldn't ever happen */
				*cp2 = 0;
			fprintf (m->quickfile, "%s%s", Hdrs[FROM], cp);

			strncpy (m->line, msglst[i].bid, MBXLINE);
			cp = strpbrk (m->line, "_-");
			if (cp)	{
				*cp = '@';
				fprintf (m->quickfile, "%s", cp);
			}

			/* build Message-Id: line */
			fprintf (m->quickfile, "\n%s<%lu@%s>\n", Hdrs[MSGID], m->mbox[msglst[i].number].bid,
				(*FWDCall) ? pax25(tmp,FWDCall) : pax25(tmp,Mycall));

			/* build To: line */
			strncpy (m->line, msglst[i].sline, MBXLINE);
			cp = strchr (&m->line[3], '@');
			if (cp == NULLCHAR)	{
				cp = strchr (&m->line[3], ' ');
				if (cp != NULLCHAR)
					*cp = 0;
				fprintf (m->quickfile, "%s%s\n", Hdrs[TO], &m->line[3]);
			} else	{
				*cp = 0;
				if (*(cp-1) == ' ')
					*(cp-1) = 0;
				if (*++cp == ' ')
					++cp;
				cp2 = strchr (cp, ' ');
				if (cp2 != NULLCHAR)
					*cp2 = 0;
				fprintf (m->quickfile, "%s%s@%s\n", Hdrs[TO], &m->line[3], cp);
			}
			
			/* now the msgtype line */
			fprintf (m->quickfile, "%s%c\n", Hdrs[MSGTYPE], msglst[i].sline[1]);
			/* and the X-BID line */
			if (msglst[i].sline[1] == 'B' || ((msglst[i].sline[1] == 'P') && (!stricmp (msglst[i].area, "sysop") || !stricmp (&m->line[3], "sysop"))))
				fprintf (m->quickfile, "%s%s\n", Hdrs[XBID], msglst[i].bid);
			/* and finally the subject */
			fprintf (m->quickfile, "%s%s\n\n", Hdrs[SUBJECT], msglst[i].subject);

			/* Prepare the text of the message. */
			sendmbmsg(f->m, msglst[i].number, msglst[i].bid);
			fprintf (m->quickfile, "/EX\n");
			f->sentThisArea++;

		} else {
			/* 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. */
			if (Xtrace)
				tcmdprintf (refusing, fwd_bbsname(m));
			/* 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));
		}
	}

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

	/* And Send it. */
	if (incnt)
		(void) send_lzhuf(f, txtfile);
	else
		unlink (txtfile);

	/* 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 (Xtrace)
			tcmdprintf (transferfailed, fwd_bbsname(m));
		return 0;
	} else if (toupper(*m->line) == 'F' || *m->line == '>'){	/* only mark completed, if no garbage came in */
		if (Xtrace)
			tcmdprintf (transfersuccess, fwd_bbsname(m));
		/* We got a response back, so we can update our sent and unwanted 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((!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;
		}
	}

	if (strnicmp (m->line,"F>", 2) == 0 || *m->line == '>')
		return 1;
	else
		return 2;
}


/* returns 0 = okay, 1 = aborted, 2 = invalid data, 3 = lost connection, 4 = recv 'F>' */
static int doxrecv(struct fwd *f)
{
int msgsize = 0;
int i, k;
char tempname[80], *cp;
struct fbbpacket *msglst;
struct mbx *m;
int incnt = 0, mycnt = 0;
int16 inchecksum, mychecksum;
int retval = 0;
#ifdef REJECT
int rej;
#endif

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

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

	if (!f->numtrans) {
		if (recvline (m->user, (unsigned char *)m->line, MBXLINE) == -1) {
			if (Xtrace)
				tcmdprintf (lostremote, fwd_bbsname(m));
			return 3;
		}
		rip (m->line);
		if (Xtrace)
			tcmdprintf (receivedfrom, fwd_bbsname (m), m->line);
		(void) strupr(m->line);
		if (!strnicmp ("F>", m->line, 2))
			return 4;
		if (strnicmp ("SX ", m->line, 3))
			return 2;
		incnt = atoi (&m->line[3]);
	} else	{
		incnt = f->numtrans;
		f->numtrans = 0;
	}

	for (i = 0; i < XMAXMSGS; i++)
		msglst[i].accept = fbbNO;

 	/* Loop till we have received a all X blocks. */
	for (k = 0; k < incnt; k++) {
		if (recvline (m->user, (unsigned char *)m->line, MBXLINE) == -1) {
			if (Xtrace)
				tcmdprintf (lostremote, fwd_bbsname(m));
			return 3;
		}
		kwait (NULL);

		if (cutofffwding (m))
			return 3;

		/* strip any trailing NL characters. */
		rip(m->line);

		if (Xtrace)
			tcmdprintf (receivedfrom, fwd_bbsname(m), m->line);
		log (m->user, "XFWD %s: %s", fwd_bbsname(m), m->line);

#ifdef REJECT
		rej = reject (m->line, 1, 1);
		if (rej != REJ_REJECT && rej != REJ_DEFER)
#endif
			if (!bid_check (m->line))
				strncpy (msglst[mycnt++].bid, m->line, 15);

	}


	if (Xtrace)
		tcmdprintf (sendingSY, mycnt, fwd_bbsname(m));
	tprintf ("SY %d\n", mycnt);
	for (k = 0; k < mycnt; k++)
		tprintf ("%s\n", msglst[k].bid);
	usflush(m->user);

	if (mycnt)	{
		/* receive the SS block, with the compressed data size */
		if (recvline (m->user, (unsigned char *)m->line, MBXLINE) == -1) {
			if (Xtrace)
				tcmdprintf (lostremote, fwd_bbsname(m));
			return 3;
		}
		if (Xtrace)
			tcmdprintf (receivedfrom, fwd_bbsname(m), m->line);
		msgsize = atoi (&m->line[3]);
		cp = skipnonwhite (&m->line[3]);
		cp = skipwhite (cp);
		inchecksum = (int16) atol (cp);

		/* The remote system will start sending the message data. */
		/* Set up a temp name to receive the file. */
		sprintf (tempname, "%s/%s.imp", IMPORTDir, m->name);

		/* Receive the messages and uncompress them. */
		strncpy (f->iFile, tmpnam(NULL), FWDFILELEN);
		strncpy (f->oFile, tempname, FWDFILELEN);
		if (recv_lzhuf(f, msgsize, &mychecksum) == 0)
               		return 1;
                if (inchecksum != mychecksum)	{
       	        	if (Xtrace)
				tcmdprintf ("XFWD: Checksum error with %s: received %u, calculated %u\n", fwd_bbsname(m), inchecksum, mychecksum);
			retval = 1;
			unlink (f->oFile);
		} else
			importcmd (0, m->name);
		/* remove the other file */
		unlink (f->iFile);

		if (Xtrace && !retval)
			tcmdprintf (messagesreceived, mycnt, fwd_bbsname(m));
	}
	return retval;
}


/****************************************************************************************/
/* This code is called when we've 25 X-Blocks, or we don't have any more                */
/* messages to send.                                                                    */
/****************************************************************************************/
static void sendEndOfXBlock (f, msgcnt, Xok, Xdone)
struct fwd *f;
int msgcnt, *Xok, *Xdone;
{
int rc;
struct mbx *m;
struct fbbpacket *msglst;

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

	if (Xtrace)
		tcmdprintf (sendingSXto, msgcnt, fwd_bbsname(m));
	tprintf ("SX %d\n", msgcnt);
	for (rc = 0; rc < msgcnt; rc++)
		tprintf ("%s\n", msglst[rc].bid);

	/* Process an incoming SY and receive messages. */
	rc = xdosy(msglst, f, msgcnt);
	if (!rc) {
		*Xok   = FALSE;
		*Xdone = TRUE;
	} else if (rc == 1) {
		*Xok   = TRUE;
		*Xdone = FALSE;
	} else if (rc == 2) {
		*Xok   = TRUE;
		*Xdone = TRUE;
	}
}


/****************************************************************************************/
/* This code is called for each message area we forward to.                             */
/****************************************************************************************/
static int xfwdthisarea (f, area, Xok, Xdone)
struct fwd *f;
char *area;
int *Xok, *Xdone;
{
char line[MBXLINE];
int err = 0;
int msgcnt = 0;
int Tmsgcnt = 0;
int theindex = 0;
int changed = 0;
int rc, i;
long bid;
long curpos;
long tempsize;
char *pszBid;
long msgsize = 0;
#if 0
int passes = 0;
#endif

struct let *cmsg;
struct mbx *m;
struct fbbpacket *msglst;

/* for makecl() */
int bulletin;
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 (Xtrace && FWDareatrace)
		tcmdprintf (processingarea, bulletin ? "Public  " : "Private ", area, fwd_bbsname(m));

	/* Initialize variables. */
	*Xok = TRUE;
	*Xdone = FALSE;

	if (bulletin && (!FWDbulletins || !m->fwdbbs->bulletins))	{
		if (Xtrace)
			tcmdprintf ("Skipping any bulletins in area '%s' for %s\n", area, m->fwdbbs->name);
		return Tmsgcnt;
	}
	if (!bulletin && (!FWDpersonal || !m->fwdbbs->personals))	{
   		if (Xtrace)
			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 = 0L;                           /* set 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 != '*' && *line != '!' && *line != '-') {	/* '*' means this has been done          */
									/* '!' means this is blocked             */
						                 	/* '-' means this was sent to an ALT BBS */
			if ((pszBid = strpbrk(&line[1], " \t")) != NULLCHAR)	/* get the bid. */
				*pszBid++ = '\0';
			else
				continue;				/* improperly formatted */
			if (!stricmp (area, &line[1]))  {		/* is this the correct area?    */
				bid = atol (pszBid);			/* get the message number       */
				if (!changed)   {			/* are we in the right area...  */
					m->area[0] = 0;			/* force a reload of index      */
					changearea(m, area, (int) 0);	/*    if not... go there now... */
					changed = 1;			/*       and update the flag....*/
				}
				/* 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 (Xtrace)
						tcmdprintf ("XFWD: 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         */
					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 command line ok, store it. */
							/* Copy SEND line for message log. */
							free (msglst[msgcnt].sline);
							(void) strupr (msglst[msgcnt].sline = strdup (line2));
							msglst[msgcnt].fwdfileindex = curpos;
							msglst[msgcnt].fwdfile = f->fwdfile;

							/* store the message type */
							msglst[msgcnt].type = msglst[msgcnt].sline[1];

							/* 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);

							if (Xtrace)
								tcmdprintf (sendingto, fwd_bbsname(m), line2);

							msgcnt++;
							msgsize += m->mbox[theindex].size;
							/* If we have filled our SX Block */
							if (msgcnt >= XMAXMSGS || (m->fwdbbs && m->fwdbbs->fbbsize && msgsize > m->fwdbbs->fbbsize)) {
								sendEndOfXBlock (f, msgcnt, Xok, Xdone);
								Tmsgcnt += msgcnt;
								msgcnt = 0;
								msgsize = 0;
							}
						}
					}
				}
			}
		} /* 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 (*Xdone)
			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 messages... send them now. */
	if (msgcnt) {
		sendEndOfXBlock (f, msgcnt, Xok, Xdone);
		Tmsgcnt += msgcnt;
	}

	return Tmsgcnt;
}


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

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

	/* figure out name of the user. */
	sprintf (name, "%s/%s", Mailspool, m->name);
	(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);

	/* 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))	{
			Xok = 0;
			break;
		}

		f->sentThisArea = 0;
		Tmsgcnt += xfwdthisarea (f, fwdarea->name, &Xok, &Xdone);
		if (Xdone || 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 (!Xok)	{	/* 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 (Xtrace)
		tcmdprintf (closingfwdfile, fwd_bbsname(m));

	/* See if we can remove the .fwd file. */
	fwdlockit (m->name);
	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 0
			break;
#endif
		}
	}

	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 (Xtrace)
			tcmdprintf ("XFWD: Relooping on .fwd file for %s - 1st scan since last aborted session\n", fwd_bbsname(m));
		fwdunlockit (m->name);
		goto checkloop;
	}
   
	/* one last check, to avoid deleting a file that JUST added a record */
	if (isnewprivmail (m, "fwd") > 0)	{
		if(Xtrace)
			tcmdprintf ("XFWD: Relooping on .fwd file for %s - new messages received\n", fwd_bbsname(m));
		fwdunlockit (m->name);
		goto checkloop;
	}

	(void) fclose (f->fwdfile);
	if (!err)
		(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 X-forwarding. */
int
doxfwd(argc,argv,p)
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 X_fwd;

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

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

	/* First verify that this is a system that supports X-forwarding. */
	if(!(f.m->sid & MBX_XFWD)) {
		tputs ("Huh?\n");
		usflush (m->user);
		return -1;
	}

	/* Get memory for the msglst array. */
	f.msglst = (struct fbbpacket *)callocw ((unsigned)XMAXMSGS, sizeof(struct fbbpacket));
	m->msglst = msglst = f.msglst;

	NeedData = FALSE;
	Done     = FALSE;
	X_fwd   = FALSE;

	/* See if we were called because we received a SX from the remote system. */
	if (!argc) {
		f.m->state = MBX_FORWARD; /* We are in send mode. */
		FBBRdone   = FALSE;
		NeedData   = TRUE;
		X_fwd     = TRUE;
	} else if(argv[0][0] == 'S' && m->stype == 'X') {
		if (argc != 2) {
			tputs ("Huh?\n");
			usflush (m->user);
			Done = TRUE;
		} else {
			f.numtrans = (char) atoi (argv[1]);
			/* indicate that we are in receive mode. */
			f.m->state = MBX_REVFWD;
			FBBRdone = FALSE;
		}
	} else {
		/* Must have received a F> from the remote system. */
		f.m->state = MBX_FORWARD; /* We are in send mode. */
		FBBRdone = TRUE;
	}


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

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

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

			NeedData = TRUE;
			/* Receive data from remote system. Process SX block. */
			rc = doxrecv (&f);
			if (rc == 4)	{	/* Remote system sent us a F> */
				if (X_fwd)
					Done = TRUE;
				else	/* Change status. */
					f.m->state = MBX_FORWARD;
				FBBRdone = TRUE;
			} else if (rc)
				Done = TRUE;
			if (Done != TRUE)	{
				tputs ((X_fwd == TRUE) ? "F>\n" : ">\n");
				if (Xtrace)
					tcmdprintf ("XFWD: Sending '%s>' to %s\n",
						(X_fwd == TRUE) ? "F" : "", fwd_bbsname(m));
				usflush (m->user);
			}
		} else {
			if (Xtrace)
				tcmdprintf ("XFWD: Sending data to %s.\n", fwd_bbsname(m));

			NeedData = FALSE;
			/* Change status. */
			rc = doxsend (&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 disconnect.
				 */
					break;
				} else {
					/* Tell them that we do not have any data for them. */
					if(Xtrace)
						tcmdprintf (nodatatosend, fwd_bbsname(m));
					tputs ("F>\n");
					/* Change status. */
					f.m->state = MBX_REVFWD;
					usflush (m->user);
					NeedData = TRUE;
				}
			}
		}
	} /* endwhile */

	/* free anything in the msglst array. */
	for (i = 0; i < XMAXMSGS ; i++) {
		free (msglst[i].to);
		free (msglst[i].from);
		free (msglst[i].messageid);
		free (msglst[i].sline);
		msglst[i].to = msglst[i].from = msglst[i].messageid = msglst[i].sline = NULLCHAR;
	}

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

	usflush (m->user);
	releasesubchannel (m);	/* just in case */
	if (X_fwd)	{
		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 XFWD */

