#include <fido.h>
#include <xfbuf.h>
#include <ascii.h>
#include <\mailer\fidomem.h>
#include <filexfer.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 */
*/

/* 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 xfer_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 */

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

chk_pwd(p)
char *p;
{
	if (*p) {
		stoupper(p);				/* make same case */
		stoupper(pwd);
		if (! same(p,pwd)) {
			if (! *p) 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 */
			}
		}
	}
}

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

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

struct _node d;
char opwd[9];

	totl_process= 0;
	connect= 0;				/* not connected yet */
	set_abort(0);
	if (was_abort()) {
		chkabort();			/* check manual abort */
		stop_tick();
		discon();			/* disconnect */
		close_up();			/* close open files */
		if (totl_process) clprintf(SM+31); /* "carrier loss..." */
		if (connect) {			/* timer allocated upon connect */
			++connects;		/* we connected */
			n= nearestn(elapsed_time,1000L); /* round to nearest sec */
			clprintf(SM+32,n / 60,n % 60); /* "total connect time" */
		}
		chkabort();
		return;
	}
	start_tick();				/* start the timers */

/* S0 SendInit, S1 WaitCxD combined */

	gtod(date);				/* "calling ..." */
	clprintf(0," * Calling %s (tel: %s) at %s\r\n",
	    str_node(&node),phone,&date[11]);
	++tries;				/* log another try, */

	elapsed_time= 0L;			/* start counting from offhook */
	setbaud(maxbaud);			/* set dialing rate, */
	i= dial(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 */
			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(pwd);		/* send our HELLO packet */
		if (recv_sync(0) != mode) frc_abort();
		recv_hello(&d,opwd);
		if (!same_node(&d,&node)) 	/* note alias, etc */
			clprintf(SM+67,str_node(&d));

		if (!chk_pwd(opwd)) {		/* check session password */
			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 */
	i= send_pkt(1,mode,text,textsize);
	if (i > 0) {
/* (S4 success) */
		doscode= fido.filecode;		/* flag as good send */

	} else if (i < 0) {
/* (S4 failure) */
		clprintf(SM+38);		/* "(Error sending packet or file(s))" */
	}
	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.  Note that .OUT files get renamed on the fly 
to .PKT files by the protocol driver. */

static send_pkt(sendpkt,mode,buff,bufsize)
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 */
int i,n,f;
long mn;

	*pktname= *flname= NUL;
	if (sendpkt) {
		strcpy(pktname,"PACKET.OUT");
		mn= millisec * millisec;	/* make a funny name */
		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 */
		strcpy(flname,"FILELIST.OUT");
	}

/* 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 */
	}

/* '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 ((n > 0) && sendpkt) {
		delete(pktname);			/* done with packet */
		delete(flname);				/* and file list */
	}
	return(n);
}

/* FSC001: Send packet and any attached files. */

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 */
	xfer_time= 0L;				/* restart xfer time counter */
	xsend(pktname,"",0,filemode);		/* *.OUT in default dir */

/* 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 */
	xfer_time= 0L;				/* once again ... */
	xsend(buff,"",0,filemode);		/* relative to default dir */

/* 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. */

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

/* fliemode 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 ... */

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

	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);			/* 0 == no packets sent */
}

/* 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 */
	}

}

/* 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") */
}

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

static start_tick() {

	elapsed_time= xfer_time= 0L;
	h2= set_tick(&xfer_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;
}
