#include "fido.h"
#include <xfbuf.h>
#include <ascii.h>
#include "fidomem.h"
#include <filexfer.h>
#include "proto.h"

/* Main FidoNet machine; FidoNet Standards Committee (FSC001-7)
states shown as comments. */

/* Internal protocol type numbers */

#define FSC001 1	/* Vanilla FSC001 FidoNet */
#define WAZOO 2		/* Wazoo */

/* Globals used, declared elsewhere: */

/* extern int filemode;	*/	/* file transfer protocol in use */
/* extern int pktnum;	*/	/* incoming packet file number/name */

/* Table of product code names */

struct {
	char code;		/* code number, */
	char *name;		/* its name */

} products[] = {
	0x00,"Fido/FidoNet",	/* (hard-coded number check in recv_hello) */
	0x01,"Rover",
	0x02,"SEAdog",
	0x04,"Slick/150",
	0x05,"Opus",
	0x06,"Dutchie",
	0x08,"Tabby",
	0x0A,"Wolf/68k",
	0x0B,"QMM",
	0x0C,"FrontDoor",
	0x11,"MailMan",
	0x12,"OOPS",
	0x13,"GS-Point",
	0x14,"BGMail",
	0x19,"BinkScan",
	0x1A,"D'Bridge",
	0x1B,"BinkleyTerm",
	0x1C,"Yankee",
	0x1E,"Daisy",
	0x1F,"Polar Bear",
	0x20,"The-Box",
	0x21,"STARgate/2",
	0x22,"TMail",
	0x23,"TCOMMail",
	0x24,"Bananna",
	0x25,"RBBSMail",
	0x26,"Apple-Netmail",
	0x27,"Chameleon",
	0x28,"Majik Board",
	0x29,"QMail",
	0x2A,"Point And Click",
	0x2B,"Aurora Three Bundler",
	0x2C,"FourDog",
	0x2D,"MSG-PACK",
	0x2E,"AMAX",
	0x2F,"Domain Communication System",
	0x30,"LesRobot",
	0x31,"Rose",
	0x32,"Paragon",
	0x33,"BinkleyTerm/oMMM/ST",
	0x34,"StarNet",
	0x35,"ZzyZx",
	0x36,"QEcho",
	0x37,"BOOM",
	0x38,"PBBS",
	0x39,"TrapDoor",
	0x3A,"Welmat",
	0x3B,"NetGate",
	0x3C,"Odie",
	0x3D,"Quick Gimme",
	0x3E,"dbLink",
	0x3F,"TosScan",
	0x40,"Beagle",
	0x41,"Igor",
	0x42,"TIMS",
	0x43,"Isis",
	0x44,"AirMail",
	0x45,"XRS",
	0x46,"Juliet Mail System",
	0x47,"Jabberwocky",
	0x48,"XST",
	0x49,"MailStorm",
	0x4A,"BIX-Mail",
	0x4B,"IMAIL",
	0x4C,"FTNGate",
	0x4D,"RealMail",
	0x4E,"Lora-CBIS",
	0x4F,"TDCS",
	0x50,"InterMail",
	0x51,"RFD",
	0x52,"Yuppie!",
	0x53,"EMMA",
	0x54,"QBoxMail",
	0x55,"Number 4",
	0x56,"Number 5",
	0x57,"GSBBS",
	0x58,"Merlin",
	0x59,"TPCS",
	0x5A,"Raid",
	0x5B,"Outpost",
	0x5C,"Nizze",
	0x5D,"Armadillo",
	0x5E,"rfmail",
	0x5F,"Msgtoss",
	0x60,"InfoTex",
	0x61,"GEcho",
	0x62,"CDEhost",
	0x63,"Pktize",

	255,"Product Code #%d"	/* see recv_hello() */
};

/* Elapsed time counters. The total elpased counter is somewhat useful; the
file transfer time counter is just so we can make impressive numbers in
the log file to please people. (I suppose it measures something.) */

static long elapsed_time;		/* total connect time */
static long transfer_time;		/* file transfer time only */
static int h1;				/* connect time timer */
static int h2;				/* actual file transfer time timer */
static int mail_packets,mail_files;	/* packets/files recieved */

/* FidoNet receiver. */

void recv_mail(sync_char)
char sync_char;			/* character that got us here, else 0 */
{
int i,n;
int mode;			/* FSC001, WAZOO */
char name[SS];
struct _node d;			/* who we are connected to */
char pwd[9];			/* session password */

/* R0, WaitCxD, and R1, WaitBaud, are done before we even
get here, in fidonet(), by answer(). */

	gtod(text); 
	lprintf(0," * %s\377",lastconnect());	/* log baud rate */
	clprintf(SM+30,&text[11]);
	if (fbit(FBIT_NLG)) timelog(1);		/* log net connection */
	totl_process= 0;
	set_abort(0);				/* trap carrier loss here, */
	if (was_abort()) {
		stop_tick();			/* stop the timers */
		discon();			/* hang up, */
		close_up();			/* then close any dangling files */
		if (totl_process) clprintf(SM+31); /* "carrier loss..." */

		n= nearestn(elapsed_time,1000L);/* convert mS to sec */
		clprintf(SM+32,n / 60, n % 60);
		rename_pkt();			/* rename the packet files */
		set_doscode();			/* set possible DOS errorlevel */
		rept_stack();			/* stack usage report */
		return;
	}
	cd_flag= 0;				/* enable carrier-loss detection */
	limit= 1;				/* limit to sync */
	clr_clk();				/* reset the clock, */
	start_tick();				/* start the tickers */

/* If we were invoked from Fido (crash mail) then the "Whack CR" phase 
is complete. */

	if (! sync_char) mail_warning();	/* whack return, etc */

/* R2, WaitTSYNC. For Wazoo, check session password, if any. */

	mode= recv_sync(sync_char);		/* determine protocol, */
	if (mode == WAZOO) {			/* if WAZOO, */
		recv_hello(&d,pwd);		/* get senders HELLO packet */
		if (find_ndat(&d) != -1) {	/* if a password specified */
			if (!chk_pwd(pwd)) 	/* check session password */
				frc_abort();
		}
		re_wazoo_sync();		/* do basic YOOHOO/ENQ */
		send_hello(ndat.pwd);		/* then send ours */
	}

/* R3 - R5 in recv_pkt() */

	if (! recv_pkt(mode,text,textsize)) 
		frc_abort();			/* (R4 error exit) */

/* R6, ChkFiles: we don't. */

/* R7, AllowPickup. 'n' is the NODELIST.NTC record number for this node, or
an error code; -1 node not in nodelist; -2 no FidoNet event running (therefore
no packets at all); -3 bad/missing FSC-0001 packet header. */

	n= 0;					/* PICKUP/mail exists == no error */
	if (mode == FSC001) {
		n= who_from(&d,pwd);		/* get senders ID from packet */
		if (n < 0) n= -3;		/* (flag with unique value) */
		if (find_ndat(&d) != -1) {	/* if a password specified */
			if (!chk_pwd(pwd)) 	/* check session password */
				frc_abort();
		}
	}

/* Generate values for 'n'. Somewhat random logic. */

	if ((fido.event == -1) && (n >= 0)) 	/* no event, error not flagged */
		n= -2;				/* flag no pickup */

	if (n >= 0) n= find_ntc(&d);		/* check if .NTC record exists */

	switch (n) {
		case -1: clprintf(SM+37,str_node(&d)); break; /* no mail waiting */
		case -2: clprintf(SM+33); break;	/* no event running */
		case -3: break;				/* (message in who_from()) */

		default:				/* .NTC recd exists */
			if (ntc.bits & NTC_SUCCESS) {
				clprintf(SM+35,str_node(&d)); /* "already picked up" */
				n= -1;			/* dont do it again */

			} else clprintf(SM+36,str_node(&d)); /* "mail ready to pickup" */
			break;
	}

/* S3 WaitClear/SendSync */

/* Next we go into the pickup phase. If there is mail for this node unsent they
get to pick it up now. "(n >= 0)" tells send_pkt() to send the files/packets, 
else it will transmit the list of files "" to satisfy the protocol logic. File-
requests are sent after the packets or dummies. */

	if (mode == FSC001) {
		clr_clk();
		limit= 1;			/* bound it timewise */
		re_tsync();			/* do TSYNC again */
		limit= 0;			/* revoke the limit */
	}

/* States S4 - S6 in send_pkt(). */

	i= send_pkt(&ntc,(n >= 0),mode,text,textsize); /* send possible packets, files */
	if (i > 0) {
		ntc.bits |= NTC_PD;		/* if mail sent (ie. picked up) */
		if (n >= 0) pktcompl(n);	/* mark as complete */

	} else if (i < 0) 			/* error */
		clprintf(SM+38);		/* "error sending packet ..." */

	delay(500);				/* delay for telco */
	frc_abort();				/* error trap exit */
}

/* Check the password just received from the remote against the possible
required session password. Return true if all is OK. */

static chk_pwd(pwd)
char *pwd;
{
	if (*ndat.pwd) {
		stoupper(ndat.pwd);		/* make same case */
		stoupper(pwd);
		if (! same(pwd,ndat.pwd)) {
			if (! *pwd) clprintf(SM+39);	/* "missing password" */
			else clprintf(SM+40,pwd);	/* "wrong password" */
			return(0);
		}
	}
	return(1);
}

/* Do YOOHOO/ENQ sync between HELLO packets. */

static re_wazoo_sync() {
int i,c;

	for (i= 1; i <= 5; i++) {
		clprintf(SM+41);		/* WV5: Resync */
		modout(YOOHOO);			/* send YOOHOO */
again:;		switch (modin(1000)) {
			case ENQ: return;	/* get ENQ response */
			case -1: break;		/* timeout out; missed it? */
			default: goto again;	/* garbage, ignore */
		}
	}
	clprintf(SM+42);			/* WV5: Failed resync */
	frc_abort();
}			

/* Perform the basic TSYNC process, before the FSC001 line turnaround. */

static re_tsync() {
int c;

	clprintf(SM+84);				/* FS3: Waiting for clear line again */
	while (modin(50) != -1) limitchk();		/* 1/2 sec quiet line, */

	while (1) {
		clprintf(SM+44);			/* FS3: Sending TSYNC again */
		modout(TSYNC);
		limitchk();

		while ((c= modin(300)) != -1) {
			limitchk();			/* enforce time limits */
			switch (c) {			/* process input */
				case NAK:		/* XMODEM checksum */
				case 'C': 		/* XMODEM CRC */
					return;

				case -1: break;		/* ignore timeouts */
			}
		}
	}
}

/* Determine who the system is that just dropped off the packet. Set the
node number, and return -1 if error or not found. */

static who_from(d,pwd)
struct _node *d;
char *pwd;
{
char buff[SS],buff2[SS];
struct _pkthdr pkthdr;
char *cp;
int i,n;

	d-> zone= d-> net= d-> number= d-> point= 0; /* clear it first */
	sprintf(buff,0,"%d.IN",pktnum);		/* packet file just received */
	makefname(buff2,buff);
	i= open(buff2,0);			/* open the packet, */
	if (i == -1) {
		clprintf(SM+45);
		return(-1);			/* no packet! */
	}
	n= read(i,&pkthdr,sizeof(struct _pkthdr));
	close(i);
	if (n != sizeof(struct _pkthdr)) {	/* short packet! */
		clprintf(SM+46);
		return(-1);
	}
	d-> number= pkthdr.orig_number;		/* make a real node */
	d-> net= pkthdr.orig_net;		/* structure from the */
	d-> zone= id.zone;			/* packet horror */
	strcpy(pwd,pkthdr.pwd);			/* session password */
	return(0);
}

/* States R3 - R5. We are connected to an incoming call; receive the mail 
packet and any attached files. Return the number of packet(s) and file(s)
received, or zero if a transfer protocol failure. */

static recv_pkt(mode,buff,bufsize)
int mode;			/* type of transfer */
char *buff;			/* work buffer */
unsigned bufsize;
{
char pktname[SS];		/* name of incoming packet file */
char flname[SS];		/* name of incoming file attach list */
char *cp,w[SS];
int i,n;

	++pktnum;				/* choose the number FIRST */
	sprintf(w,0,"%d.IN",pktnum);		/* incoming packet name */
	makefname(pktname,w);
	sprintf(w,0,"%d.FLI",pktnum);
	makefname(flname,w);			/* incoming file attaches */
	kill_req();				/* kill old recv'd .REQ files */

	clprintf(SM+47);			/* FR3: Receiving Mail Packet */

	*buff= NUL;				/* received file name */
	limit= 0;				/* no time limit */
	flush(0);				/* flush any garbage, */

	switch (mode) {
		case WAZOO: n= recv_wazoo(pktname,flname,buff,bufsize); break;
		case FSC001: n= recv_fsc001(pktname,flname,buff,bufsize); break;
		default: n= 0; break;
	}
	rename_pkt();				/* rename packet files */
	return(n);
}

/* FSC001: Receive a packet and attached files. */

static recv_fsc001(pktname,flname,buff,bufsize)
char *pktname;			/* name of incoming packet file */
char *flname;			/* name of incoming file attach list */
char *buff;			/* work buffer */
unsigned bufsize;
{
char *cp,w[SS];
int i;

	filemode= fbit(FBIT_FSC001) ? FSC001X : XMODEMC;/* selectable purity */
	transfer_time= 0L;				/* reset the timer, */
	xrecv(pktname,buff,bufsize,filemode);		/* put packet into default dir */
	tally_xfer();					/* tally transfer time */

/* R4, XRecEnd */

	clprintf(SM+48,totl_files,totl_bytes);		/* FR4 received packets */
	if (totl_errors) {
		clprintf(SM+49);			/* error */
		return(0);
	}
	++mail_packets;					/* global packet received */
	delay(100);					/* delay for EOTs to settle */
	flush(0);					/* flush rubbish */

/* R5, RecFiles */

	clprintf(SM+50);				/* FR5: Receiving (possible) attached files */
	*buff= NUL;					/* list of uploaded files */
	makefname(w,"");				/* upload path */
	filemode= (fbit(FBIT_DIETIFNA) ? DIETIFNA : TELINKC); /* sloppy or merely loose */
	if (fbit(FBIT_FSC001)) filemode= FSC001T;	/* selectable purity */
	transfer_time= 0L;
	xrecv(w,buff,bufsize,filemode);			/* attached files into FidoNet area */
	tally_xfer();

/* R6, ChkFiles; success checked by caller */

	clprintf(SM+51,totl_files,totl_bytes);
	if (totl_files) {				/* save list of received */
		i= append(flname);			/* files for later */
		if (i == -1) {
			clprintf(SM+52,flname);
			return(0);
		}
		write(i,buff,strlen(buff));
		close(i);
		mail_files += totl_files;		/* global files received */
	}
	return(totl_errors == 0);
}

/* WAZOO: Receive packet and attached files. This could use either ZMODEM or
DIETIFNA depending on the contents of the hello packet. */

static recv_wazoo(pktname,flname,buff,bufsize)
char *pktname;			/* name of incoming packet file */
char *flname;			/* name of incoming file attach list */
char *buff;			/* work buffer */
unsigned bufsize;
{
int i;
char w[SS],name[SS],*p,*cp,*sp;
int packets;

	clprintf(SM+53);				/* WRx: Receiving packets and/or files */
	makefname(name,"");				/* get upload path */
	*buff= NUL;					/* nothing in the file list */

	transfer_time= 0L;
	if (filemode == ZMODEM) i= zrecv(name,buff,bufsize);
	else i= xrecv(name,buff,bufsize,filemode);	/* receive packet & files, */
	tally_xfer();

/* Now see what we just received: arbitrarily consider .PKT files "packets" 
and all others "files". (Packets don't get listed in the files list.) */

	i= append(flname);				/* make attached file list */
	if (i == -1) {
		clprintf(SM+52,flname);
		return(0);
	}
	packets= 0;
	for (cp= buff; *cp; cp= next_arg(cp)) {
		cpyarg(name,cp);			/* work on one at a time */
		stoupper(name);				/* force upper case */
		for (p= name; *p && (*p != '.'); ++p);	/* find the extention */
		if (same(p,".PKT")) {
			++packets;			/* .PKT is a packet */
			--totl_files;			/* and not a "file" */
			++mail_packets;			/* global packet count */

		} else if (same(p,".REQ")) {		/* don't count */
			/* na da zip */			/* file requests at all */

		} else {
			write (i,name,strlen(name));	/* else its just a file */
			write(i," ",1);
			++mail_files;			/* global file count */
		}
	}
	close(i);
	rename_pkt();					/* rename incoming packets */

	clprintf(SM+54,packets,totl_files,totl_bytes);
	if (totl_errors) {
		clprintf(SM+55);
		return(0);
	}
	return(1);					/* success even if 0 packets */
}

/* Receiver synchronization and protocol determination. If WAZOO is off, then
it simply waits for a TSYNC.

If WAZOO is on, it will return WAZOO immediately when a YOOHOO is received. 
If a TSYNC is received however, it looks an additional 10 seconds to see
if a YOOHOO follows. 

We are passed the initial sync character to check when invoked from Fido. */

static recv_sync(sync_char)
int sync_char;		/* char already received else 0 */
{
FLAG got_tsync;
long start;

/* R2, WaitTSYNC */

	clprintf(SM+56);				/* FR2/WVx: Waiting for sync */
	got_tsync= 0;

	while (1) {
		if (! sync_char) sync_char= modin(10);	/* get a character */
		switch (sync_char) {
			case YOOHOO: 
				if (! fbit(FBIT_WAZOO)) break;
				clprintf(SM+57);	/* FR2/WV1: Got YOOHOO */
				modout(ENQ);		/* acknowledge it */
				return(WAZOO);

			case TSYNC: 
				if (! got_tsync) {
					got_tsync= 1;	/* remember we got it */
					start= millisec;/* reset the counter */
				}
				clprintf(SM+58);	/* FR2/WV2: Got TSYNC */
				if (fbit(FBIT_WAZOO)) {
					clprintf(SM+59);/* "but trying for YOOHOO" */
					break;
				}
				clprintf(FM+1);		/* cr/lf */
				return(FSC001);

			case CR: 
				clprintf(SM+60);	/* (Sender still in FS2) */
				modout(CR);		/* try to get it in sync */
				modout(' ');
				break;
		}

/* If we received a TSYNC earlier, and six seconds has elapsed, it is not
likely we're going to get a YOOHOO; return FSC001 selected. */

		if (got_tsync && (millisec - start > 6000L)) {
			clprintf(SM+61);		/* "FR2/WV2: Honoring previous TSYNC" */
			return(FSC001);
		}
		limitchk();
		sync_char= 0;				/* must read one */
	}

}

/* Display a warning to human callers. (Also satisfies the mailers 
desire for CRs returned after "Whacking Return") */

static mail_warning() {
char date[SS],buff[257]; /* NOTE WIERD SIZE */
int i,f;

	gtod(date);
	sprintf(buff,0,"\r\r\r\r%s, %s, %s (Fido/FidoNet %d%c)\r\n",
	    fido.name,str_node(&id),date,FIDOVER,FILEVER + 96);
	wbuff(buff);					/* copy to modem no echo */


	maketname(buff,"mailevent.bbs");		/* name in text path */
	f= open(buff,0);
	if (f == -1) wbuff(string(CM+102));		/* "sorry call back later" */

	else {						/* else message file */
		while (i= read(f,buff,sizeof(buff) - 1)) {
			buff[i]= NUL;			/* (terminate string) */
			wbuff(buff);
		}
		close(f);
	}
}

/* Write a string to the modem, none of the usual caller niceties. */

static wbuff(s)
char *s;
{
	while (*s) modout(*s++);
}


/* FSC001 Sender. Passed is the record number of the node we're to call. */

void send_mail(recd)
int recd;
{
char *cp,date[SS];
int i,n,f;
int mode;
FLAG connect;

struct _node d;
char pwd[9];

	totl_process= 0;
	connect= 0;				/* not connected yet */
	set_abort(0);
	if (was_abort()) {
		stop_tick();
		discon();			/* disconnect */
		close_up();			/* close open files */
		if (totl_process) clprintf(SM+31); /* "carrier loss..." */
		if (connect) {			/* timer allocated upon connect */
			++ntc.connects;		/* we connected */
			n= nearestn(elapsed_time,1000L); /* round to nearest sec */
			ntc.time += n;
			clprintf(SM+32,n / 60,n % 60); /* "total connect time" */
		}
		if (ntc.bits & NTC_SUCCESS) 	/* if packets sent, */
			pktcompl(recd);		/* kill the files */
		else put_ntc(recd);		/* else just update NTC */
		set_doscode();			/* possible DOS errorlevel */
		rept_stack();			/* stack usage report */
		return;
	}
	start_tick();				/* start the timers */

/* S0 SendInit, S1 WaitCxD combined */

	gtod(date);				/* "calling ..." */
	clprintf(SM+64,str_node(&ndat.node),ndat.name,ndat.phone,&date[11]);
	++ntc.tries;				/* log another try, */
	++ntc.totl_tries;

	elapsed_time= 0L;			/* start counting from offhook */
	setbaud(maxbaud);			/* set max. dialing rate */

	i= dial(ndat.phone);			/* dial the number, */
	lprintf(0," * %s\377",lastconnect());	/* log the connect attempt */
	switch (i) {
		case 0: frc_abort(); break;	/* no connection */
		case 1: break;			/* connected now */
		default: 			/* -1 == error */
			ntc.tries= dial_tries + 1; frc_abort(); break;
	}
	cd_flag= 0;				/* enable carrier-loss detection */
	connect= 1;				/* we are now online */
	delay(200);				/* let modems settle */

/* End S0/S1 */

	clr_clk();				/* reset clock, */
	limit= 1;				/* failsafe on sync */
	flush(0);				/* flush buffers */

/* S2 WhackCRs */

	clprintf(SM+65);			/* FS2: Initial synchronization */
	for (n= 20; --n;) {			/* limited tries for baud test */
		modout(CR);
		delay(20);			/* get one back, */
		modout(' ');			/* send CR space until we */
		delay(20);
		modout(ETX);			/* ^C to kill welcomes */
		while (1) {
			i= modin(200);		/* get a character */
			if (i == CR) break;	/* either CR */
			if (i == -1) break;	/* or time out */
		}
		if (i == CR) break;		/* in sync now */
	}
	if (n == 0) {
		clprintf(SM+66);		/* (Initial synchronization failed) */
		frc_abort();
	}
	delay(20);				/* delay 1/5th second */
	modout(ETX);				/* ^C to kill welcomes */

/* S3 WaitClear/SendSync */

	clr_clk();
	limit= 1;				/* one minute limit, */
	mode= send_sync();			/* clear line/sync */
	if (mode == WAZOO) {
		send_hello(ndat.pwd);		/* send our HELLO packet */
		if (recv_sync(0) != mode) frc_abort();
		recv_hello(&d,pwd);
		if (!same_node(&d,&ntc.node)) 	/* note alias, etc */
			clprintf(SM+67,str_node(&d));

		if (!chk_pwd(pwd)) {		/* check session password */
			ntc.tries= dial_tries + 1; /* force error */
			frc_abort();
		}
	}

/* States S4 - S6 in send_pkt(). Generate filenames based upon the node
address we dialed from the nodelist. */

	limit= 0;				/* now no time limit */
	kill_req();				/* kill old .REQ file */
	i= send_pkt(&ntc,1,mode,text,textsize);	/* -1, 0, 1, 2 */
	if (i > 0) {
/* (S4 success) */
		ntc.bits |= NTC_SUCCESS;

	} else if (i < 0) {
/* (S4 failure) */
		clprintf(SM+38);		/* "(Error sending packet or file(s))" */
		frc_abort();
	}

/* All/any mail/files have been sent. If either PICKUP is enabled, or we
sent a .REQ file (and therefore are expecting incoming files) turn the line
around to receive them. (If neither, then simply terminate.) The called node
can also hang up now to terminate the session without error. */

/* S8 TryPickup */

	if ((i == 2) || (ntc.bits & NTC_PU)) {		/* pickup or filerequest */
		clprintf(SM+69);			/* FS8: Attempting Mail Pickup */
		if (mode == FSC001) {
			clr_clk();			/* connect time so far */
			limit= 1;			/* 1 min. to sync, etc */
			recv_sync(0);			/* wait for TSYNC again */
		}
		n= recv_pkt(mode,text,textsize);	/* get packets and files */
	}
	delay(500);				/* end S8 */
	frc_abort();				/* do termination code */
}

/* States S4 - S6: send mail to the current node; return -1 if failure, 0 if
nothing sent, or 1 for success, 2 .REQ file sent.  Note that .OUT files get 
renamed on the fly to .PKT files by the protocol driver. */

static send_pkt(ntc,sendpkt,mode,buff,bufsize)
struct _ntc *ntc;		/* pointer to NTC record we're sending to */
int sendpkt;			/* 1 == send packets (NTC is valid) */
int mode;			/* protocol to use */
char *buff;			/* work buffer */
unsigned bufsize;
{
char b[SS];
char pktname[SS];		/* name of outgoing packet file */
char flname[SS];		/* name of (possible) file attach list */
char reqname[SS];		/* name of (possible) file request list */
int i,n,f;
long mn;

	*reqname= *pktname= *flname= NUL;
	if (sendpkt) {
		sprintf(b,0,"%d.OUT",ntc-> recd);
		makeuname(pktname,b);		/* normal packet name */
		mn= millisec * millisec;	/* make a funny name */
		mn= (mn >> 16) | (mn << 16);	/* swap halfs */
		mn &= 0xffffffL;		/* keep in 8 digits or less! */
		sprintf(b,0,"=%08lu.PKT",mn);	/* millisecond timer as filename */
		strcat(pktname,b);		/* add "=nnnnn.PKT" for rename */
		sprintf(b,0,"%d.FLO",ntc-> recd);
		makeuname(flname,b);
		sprintf(b,0,"%04x%04x.REQ",ntc-> node.net,ntc-> node.number);
		makeuname(reqname,b);
	}

/* Build the list of files to send in the in-memory buffer. This consists
of the packet filename, the attached files, and any requested files. */

	strcpy(buff,pktname);			/* put the packet on the list */
	if (*buff) strcat(buff," ");		/* plus a separator */
	n= strlen(buff);
	bufsize -= n;				/* account for space used, */

	f= open(flname,0);			/* read in file list, */
	if (f != -1) {
		n += read(f,&buff[n],bufsize);	/* append the list of files */
		close(f);
		buff[n]= NUL;			/* terminate it */
		bufsize -= n;			/* how much room left */
	}

/* If files were requested (xxxxyyyy.REQ file received in previous phase)
then we add the list of requested files to the list of files to transmit. */

	if ((mode != FSC001) || !fbit(FBIT_FSC001)) {/* do file req. for all */
		do_freq(buff,bufsize);		/* except non-pure FSC001 */
	}

/* 'filemode' is set by recv_hello(). */

	switch (mode) {
		case WAZOO: n= send_wazoo(buff,sendpkt); break;
		case FSC001: n= send_fsc001(buff,sendpkt); break;
		default: n= -1; break;
	}

/* If there are no errors, kill the packet file and file attach 
list. (Saves disk space & clutter.) If success, we check to see if there was
a .REQ file for this node. If so, we can assume it was sent -- and can
expect to receive the attached files next phase. Flag the fact that we sent
a .REQ file. */

	if ((n > 0) && sendpkt) {
		delete(pktname);			/* done with packet */
		delete(flname);				/* and file list */

		i= open(reqname,0);			/* if .REQ file */
		if (i != -1) {				/* was actually sent, */
			close(i);
			delete(reqname);		/* kill it */
			n= 2;				/* flag for return */
		}
	}
	return(n);
}

/* Append the file request list (if any) to the file attach list (if any). To
do this, we read each line of the .REQ file, look up each word in the 
FILEREQ.INI file, and put the resulting replacement text onto the file list. */

static do_freq(buff,buflen)
char *buff;		/* where to append filenames */
unsigned buflen;	/* how much room in it */
{
char req[SS];		/* name that the remote requests */
char word[SS];		/* name in the FILEREQ list */
char actual[SS];	/* actual pathname we send for that */
char fname[SS];		/* searched-for full pathname */
struct _xfbuf xfbuf;
char ln[SS],*cp,*p;
FLAG ok,found;
int which,i,n;

/* Note that for backwards compatibility, we check specifically for "not
within window" return code -1, not just < 0; -2 means there is no FILE REQUEST
event in the table; we want that to default to always-enabled. */

	for (which= 0; which < 2; which++) {	/* check for a .REQ file */
		switch (which) {
			case 0: sprintf(word,0,"%04x%04x.REQ",id.net,id.number); break;
			case 1:	sprintf(word,0,"%04x%04x.REQ",altid.net,altid.number); break;
		}
		makefname(ln,word);		/* .REQ file is in netfile area */
		n= open(ln,0);			/* see if it exists */
		if (n != -1) break;		/* yup! */
	}
	if (n == -1) return;			/* nothing to do */

	if (til_sched('Z',0,0) == -1) {		/* if file request DISabled */
		close(n);			/* we wont be using this */
		clprintf(SM+70);		/* (File Request not enabled now) */
		maketname(req,"nofreq.bbs");	/* send this file instead */
		strcat(buff,req);
		switch (which) {
			case 0: sprintf(req,0,"=%04x%04x.FRQ",id.net,id.number); break;
			case 1:	sprintf(req,0,"=%04x%04x.FRQ",altid.net,altid.number); break;
		}
		strcat(buff,req);		/* but name it this */
		return;
	}

/* (...) */

	i= open("FILEREQ.INI",0);		/* and the translate file */
	if (i == -1) {
		clprintf(SM+71,"\"FILEREQ.INI\""); /* cant find it */
		close(n);
		return;
	}

/* Pull out one line (one requested file(s)) of the .REQ file and clean it up. */

	newdelim(" \t");			/* spaces and tabs are whitespace */
	while (rline(n,ln,sizeof(ln))) {	/* process .REQ line by line */
		cp= skip_delim(ln);		/* skip leading garbage */
		if (*cp == ';') continue;	/* ignore comments (Binkley!) */
		cpyarg(req,strip_path(word,cp));/* copy minus any path */
		stoupper(req);			/* make upper case, */
		clprintf(SM+72,req);		/* log the requested name, */

/* Simply ignore "*.*" */

		if (same(req,"*.*")) {
			clprintf(SM+74);	/* not requestable */
			continue;
		}

/* REQ is the name the remote requested. We have to look that up in the 
FILEREQ file and see if it's allowed and what file to send for that name. 
The FILEREQ file lines can specify either (1) "pathname" or (2) "filename=
pathname". First we string-compare on the first (or only) word on the
line for a match. If (1) then we use the name as-is, else (2) we use the
name that follows the =. Paths are only used locally and are stripped off. */

		ok= 0;				/* requested file not found */
		lseek(i,0L,0);			/* start of the file, */
		while (rline(i,ln,sizeof(ln))) {/* read from FILEREQ file */
			found= 0;
			cp= ln;
			if (*cp == ';') continue; /* ignore comments */
			cp= skip_delim(cp);	/* skip leading delimiters */
			stoupper(cp);		/* all upper case */

			cpyarg(word,cp);	/* word to match */
			for (p= word; *p; ++p)	/* see if an "=" follows */
				if (*p == '=') break;
			if (*p == '=') *p++= NUL; /* if "=NAME" separate them */
			else p= word;		/* else use same name */

			cpyarg(actual,p);	/* orig. or replacement, w/path */
			cpyarg(word,strip_path(ln,word)); /* name only */

/* WORD	is the requestable filename; the left half of a FILE=PATHNAME, or the
	filename portion of a PATHNAME shorthand line, or null from a
	DIRECTORY line (ie. no filename).

ACTUAL	is the full pathname, from the right half of a FILE=PATHNAME, or
	the full pathname of a PATHNAME shorthand line, or the directory from
	a DIRECTORY line. 
*/

/* Check to see if (1) this line is a directory, and if so, (2) if the
requested file exists within it. */

			if (isdir(actual)) {		/* if a directory ... */
				strcpy(word,actual);
				fixdir(word);		/* clean it up */
				strip_path(actual,word);/* make full pathname */
				strcat(actual,req);	/* our path + req */

				xfbuf.s_attrib= 0;
				found= _find(actual,0,&xfbuf);

			} else found= _fmatch(req,word); /* ... else is a file */

			if (found) {
				if ((strlen(actual) + 1) < buflen) {
					if (ok) clprintf(0," + ");
					else clprintf(0," --> ");
					clprintf(0," \"%s\"\r\n",actual);
					strcat(buff,actual);
					buflen -= strlen(actual) + 1;
					strcat(buff," ");	/* that "+ 1" */

				} else clprintf(SM+73);		/* too many! */
				ok= 1;
			}
		}
		if (! ok) clprintf(SM+74);		/* not requestable */
	}
	newdelim(" \t;,\n\r");				/* arg delimiter list, */
	close(i);
	close(n);
}

/* FSC001: Send packet and any attached files. Returns -1 for error, or 1 for 
sucessful packet/file send. */

static send_fsc001(buff,ispacket)
char *buff;			/* list of files to send */
int ispacket;			/* 1 == actual packet (else dummy) */
{
int n,f;
char pktname[SS];

/* S4 SendMail */

	limit= 0;
	clprintf(SM+75);			/* FS4: Sending Packet */
	cpyarg(pktname,buff);			/* first filename is packet */

	filemode= fbit(FBIT_FSC001) ? FSC001X : XMODEMC;/* selectable purity */
	transfer_time= 0L;				/* restart xfer time counter */
	xsend(pktname,"",0,filemode);		/* *.OUT in default dir */
	tally_xfer();				/* tally total time, bytes, files */

/* S5, CheckMail */

	if (totl_errors) {
		clprintf(SM+76);		/* (Error sending packet) */
		return(-1);			/* terminate if nothing sent */
	}
	clprintf(SM+77,totl_bytes);		/* FS5 */
	delay(100);				/* delay 1 sec to let rcvr finish */

/* S6 SendFiles. This is zero or more files. */

	clprintf(SM+78);			/* FS6: attached files and requests */
	buff= next_arg(buff);			/* skip (sent) packet file */

	filemode= (fbit(FBIT_DIETIFNA) ? DIETIFNA : TELINKC); 
	if (fbit(FBIT_FSC001)) filemode= FSC001T; /* selectable purity */
	transfer_time= 0L;			/* once again ... */
	xsend(buff,"",0,filemode);		/* relative to default dir */
	tally_xfer();				/* tally transfer */

/* S7, CheckFile. */

	if (totl_errors) {
		clprintf(SM+79);		/* FS7: failed */
		return(-1);
	}
	clprintf(SM+80,totl_files,totl_bytes);	/* FS7: complete */

/* We can simply assume "packet sent"; the first XMODEM send either sends
any existing packet, or it doesn't. */

	return(1);
}

/* WAZOO: Send the packet and any attached files. Returns -1 for error, 0 for
no packets sent, or 1 for success. */

static send_wazoo(buff,ispacket)
char *buff;			/* list of files to send */
int ispacket;			/* 1 == includes packet (else dummy) */
{
int packets;

	clprintf(SM+81);			/* WSx: Sending packets and files */

/* filemode is set by recv_hello(); it will be indeterminate if no hello packet
is received, but we won't have gotten this far without one ... */

	transfer_time= 0L;
	if (filemode == ZMODEM) zsend(buff,"",0);/* send packet plus files */
	else xsend(buff,"",0,filemode);
	tally_xfer();				/* log transfer */

	packets= 0;
	if (totl_files && ispacket) {		/* if a packet was in the list */
		++packets;			/* adjust the counts */
		--totl_files;
	}
	clprintf(SM+82,packets,totl_files,totl_bytes);
	if (totl_errors) {
		clprintf(SM+83);
		return(-1);
	}
	return(packets ? 1 : 0);		/* 0 == no packets sent */
}

/* Perform send synchronization. If FSC001 is on and WAZOO is off, we issue
a single TSYNC and return. (This is strictly FSC001-9 compatibility.) 
Otherwise if WAZOO is off, we issue a TSYNC every 2 seconds, looking for the
NAK/'C' we'd get from the receivers XMODEM module.

If WAZOO is on, then we issue a YOOHOO before each TSYNC, and look for
an ENQ response indicating WAZOO. If we get a response to the TSYNC, then
defer deciding for 10 seconds, while we look for a response to the YOOHOO, 
in case the first one was lost. */

static send_sync() {
long start;
FLAG got_response;
int c;

	clprintf(SM+84);				/* FS3: Waiting for clear line */
	while (modin(50) != -1) limitchk();		/* 1/2 sec quiet line, */

/* Issue sync character(s). */

	if (fbit(FBIT_FSC001)) {
		clprintf(SM+85);			/* FS3: Sending TSYNC */
		modout(TSYNC);
		return(FSC001);				/* thats all for this mode */
	}
	got_response= 0;

	while (1) {
		limitchk();				/* check time limits! */
		if (fbit(FBIT_WAZOO)) {
			clprintf(SM+86);		/* WS2: Send YOOHOO/TSYNC */
			modout(YOOHOO);
			delay(1);			/* delay 10 mS tween chars */

		} else clprintf(SM+85);			/* FS3: Sending TSYNC */

		modout(TSYNC);				/* always a TSYNC */

/* Now look for a response to the sync character(s) just sent. If we get
an ENQ and WAZOO is enabled, return WAZOO immediately. If we get a response
to the TSYNC, wait until 10 seconds goes by (in case we get an ENQ later.) */

/* (Process characters until we get a timeout; this prevents this routine
from sending out one YOOHOO/TSYNC for each character received; we only want
to send one out evry few seconds.) */

		while ((c= modin(300)) != -1) {
			limitchk();			/* enforce time limits */

			switch (c) {			/* process input */
				case ENQ: if (fbit(FBIT_WAZOO)) {
						clprintf(SM+88); /* WS4: WAZOO Protocol */
						return(WAZOO);
					}
					break;

/* If WAZOO is on, and we get a (presumed) TSYNC response, simply flag it
and wait for 10 more secs to elapse. */

				case 0x00:		/* SEAdog? */
				case 0xff:		/* SEAdog? */
				case NAK:		/* XMODEM checksum */
				case 'C': 		/* XMODEM CRC */
					if (! fbit(FBIT_WAZOO)) return(FSC001);
					if (! got_response) {
						got_response= 1; /* we got a response */
						start= millisec; /* start counting */
						clprintf(SM+89); /* FS3: Got TSYNC, checking for YOOHOO */
					}
					break;

				case -1: break;		/* ignore timeouts */

				case TSYNC & 0x7f:	/* probably BBS echo */
				case YOOHOO & 0x7f:	/* probably BBS echo */
					clprintf(SM+90);
					break;
			}
		}
		if (got_response && (millisec - start > 8000L)) {
			clprintf(SM+91);		/* FS3: Honoring previous TSYNC */
			return(FSC001);
		}
	}
}

/* Send a WAZOO HELLO packet, get confirmation; abort if error. */

static send_hello(pwd)
char *pwd;		/* password */
{
struct _wazoo wz;
char *cp;
unsigned n,i,crc;

	clprintf(SM+92);			/* SHx: Sending HELLO packet */
	for (cp= (char *) &wz, i= sizeof(struct _wazoo); i--;) *cp++= 0;

	wz.signal= 0x6f;
	wz.version= 1;
	wz.rev= FIDOVER;			/* program version */
	wz.ver= FILEVER;			/* and revision */
	strcpy(wz.name,fido.name);		/* system name */
	strcpy(wz.password,pwd);		/* session password */
	wz.zone= id.zone;			/* who we are */
	wz.net= id.net;
	wz.node= id.number;
	wz.point= id.point;
	wz.bits= WAZ_DIETIFNA | WAZ_ZMODEM | WAZ_FREQ;

/* Do this til sucess or timeout. */

	for (n= 10; n--;) {
		clrcrc();
		modout(0x1f);			/* SH2 header */
		cp= (char *) &wz; 		/* ready to send, */
		for (i= 0; i < 128; i++) {
			modout(*cp);		/* SH2 output the data, */
			updcrc(*cp++);		/* generate CRC-16 */
		}
		flush(0);			/* flush out garbage, */
		crc= fincrc();			/* send CRC bytes, */
		modout(crc >> 8);		/* SH4 MS byte! */
		modout(crc);			/* SH4 then LS byte, */

again:;		switch (i= modin(600)) {	/* SH5 get the result, */
			case ACK: return(1);	/* got it OK */
			case ENQ: goto again;	/* out of sync */
			case '?': clprintf(SM+93); break; /* SH5: Try again */
			case -1: clprintf(SM+94); break;
			default: clprintf(SM+95,i); break;
		}
		limitchk();			/* enforce time limit */
	}
	clprintf(SM+96);			/* (Send HELLO failed) */
	frc_abort();				/* hard failure */
}

/* Receive a WAZOO HELLO packet. Return the node address and session password
of the node we are connected to. This will not return if there is an 
unrecoverable error. */

static recv_hello(d,pwd)
struct _node *d;	/* returned node address */
char *pwd;		/* returned password */
{
struct _wazoo wz;
unsigned crc,i,n;
int tries;
char *cp;

	for (tries= 10; --tries;) {
		clrcrc();				/* start the CRC */
		clprintf(SM+97);			/* RHx: Receive HELLO packet */
		while (1) {
			i= modin(100);			/* RH3 watch for 0x1f */
			if (i == 0x1f) break;		/* RH3 got header */
			if (i == YOOHOO) {		/* ERROR IN STATE TABLE */
				clprintf(SM+98);	/* (Sender still in WS4/WV5) */
				modout(ENQ);		/* RH2 */
			}
			limitchk();
		}
		cp= (char *) &wz; 
		for (i= 0; i < 128; i++) {		/* RH5 get the HELLO packet */
			n= modin(1000);
			if (n == -1) break;		/* timeout in packet */
			*cp++= n;
			updcrc(n);
		}
		if (n != -1) updcrc(n= modin(1000));	/* RH6 get CRC bytes */
		if (n != -1) updcrc(n= modin(1000));	/* RH6 (unless we timed out */
		if ((n == -1) || chkcrc()) modout('?');	/* RH7 no sense waiting) */
		else break;				/* RH8 got the HELLO packet OK */

		limitchk();				/* enforce time limit */
	}
	if (! tries) frc_abort();			/* never got one! */

	modout(ACK);					/* RH8 acknowledge, */
	modout(YOOHOO);					/* RH8 */

/* We got the HELLO packet from the other side; all we do at this
point is use DIETIFNA if the other end can't do ZMODEM. */

	filemode= ZMODEM;
	if (!(wz.bits & WAZ_ZMODEM)) filemode= DIETIFNA;

	d-> zone= wz.zone;
	d-> net= wz.net;
	d-> number= wz.node;
	d-> point= wz.point;

/* The password might be (1) all 8 characters, (2) space filled or (3) null
filled. Whatever, copy only printable characters. */

	for (i= 0, cp= pwd; i < 8;) {
		if (wz.password[i] <= ' ') break;
		*cp++= wz.password[i++];
	}
	*cp= NUL;

	clprintf(SM+99,str_node(d),wz.name);		/* connected to ... */
	for (i= 0; products[i].code != 255; i++) {	/* look for product code */
		if (wz.product == products[i].code) break; /* or 255 (unknown) */
	}

/* Print the name of the program we're connected to, or its number if
it's not in the table. */

	clprintf(0,"   (");				/* nicely formatted */
	clprintf(0,products[i].name,wz.product);	/* name or number, */
	if (products[i].code == 0) 			/* if Fido, */
		clprintf(0," %d%c)\r\n",wz.rev,wz.ver + 96); /* number letter ("12S") */
	else clprintf(0," %d.%d)\r\n",wz.rev,wz.ver);	/* else number.number ("4.51") */
}

/* Rename any .PKT files to .IN so that we can unpack them later. This is 
needed only after a WAZOO session, where we have no control 
over incoming filenames. */

static rename_pkt() {
char name1[SS],name2[SS];
char buff[SS];
struct _xfbuf xfbuf;
int i;

	i= xfbuf.s_attrib= 0;
	makefname(buff,"*.PKT");		/* search for these */
	while (_find(buff,i++,&xfbuf)) {	/* rename them */
		sprintf(name2,0,"%d.IN",pktnum++);
		makefname(name1,name2);		/* new .IN name */
		makefname(name2,xfbuf.name);	/* current .PKT name */
		delete(name1);			/* kill any existing, */
		rename(name2,name1);		/* rename */
	}
}


/* Mail was sent or picked up; mark the NTC record as complete and
kill the packets and messages. */

void pktcompl(n)
int n;
{
char *cp,buff[SS],name[SS];

	ntc.bits |= NTC_SUCCESS;
	put_ntc(n);				/* record success, */

	sprintf(buff,0,"%d.OUT",ntc.recd);	/* kill the files */
	makeuname(name,buff);
	delete(name);

	sprintf(buff,0,"%d.FLO",ntc.recd);
	makeuname(name,buff);
	delete(name);

	clprintf(SM+100,str_node(&ntc.node));
	markmsg(&ntc.node);			/* mark/kill the messages */
}	

/* Kill any existing .REQ file. */

static kill_req() {
char name1[SS],name2[SS];

	sprintf(name1,0,"%04x%04x.REQ",id.net,id.number);
	makefname(name2,name1);			/* file request list name */
	delete(name2);				/* kill it before we start */

	sprintf(name1,0,"%04x%04x.REQ",altid.net,altid.number);
	makefname(name2,name1);
	delete(name2);
}

/* Start the total elapsed time and file transfer time tickers. */

static start_tick() {

	elapsed_time= transfer_time= 0L;
	h2= set_tick(&transfer_time);
	h1= set_tick(&elapsed_time);		/* start a timer */

	if ((h1 | h2) == -1) {			/* timer failure */
		clprintf(SM+101);
		frc_abort();
	}
}

/* Stop the tickers. */

static stop_tick() {

	clr_tick(h1); h1= -1;			/* release any timer */
	clr_tick(h2); h2= -1;
}

/* If packets or files were received, set doscode so that we exit to DOS
after this connect; file-errorlevel overrides mail-errorlevel. */

set_doscode() {

	if ((doscode == 0) || (doscode > 2)) {
		if (mail_packets || mail_files) doscode= mailcode;
		if (mail_files && filecode) doscode= filecode;
	}
}

/* Tally up the result of this transfer in the NTC record. */

static tally_xfer() {

	ntc.xfer_time += nearestn(transfer_time,1000L);/* tally silly time counter */
	ntc.files += totl_files;		/* files actually sent */
	ntc.bytes += totl_bytes;		/* tally bytes transferred */
}
