#include "bbs.h"

#include "zmodem.h"

#define SLEEP_LOGOFF	2

short update_crc16(int cp, short crc);
long update_crc32(int cp, long crc);

extern BPTR fh;
extern char *SkipdFiles;
extern long zbaud, zbufsize, TMPBT;
extern int sendlen, doublebuffer, zresume, NumSkipd;
extern char *sendbuff, *zdiskbuff, *zbuffer, sbuff[];
extern ULONG STpos;

int tryzhdrtype;	        /* Header type to send corresponding to Last rx close */
int Zctlesc,start;		/* Don't escape control characters */
int transfering=0;
char txbuf[1024];

long chinseg;
char ZPathname[257];
long filesize;
long resumesize;
int Rxbuflen;
int Rxflags;

int Rxframeind;		        /* ZBIN ZBIN32, or ZHEX type of frame received */
int Rxtype;		            /* Type of header received */
int Rxcount;		            /* Count of data bytes received */
char Rxhdr[4];		        /* Received header */
char Txhdr[4];		        /* Transmitted header */
long Rxpos;		            /* Received file position */
long Txpos;		            /* Transmitted file position */
int Txfcs32;		            /* TRUE means send binary frames with 32 bit FCS */
int Crc32t;	                /* Display flag indicating 32 bit CRC being sent */
int Crc32;		            /* Display flag indicating 32 bit CRC being received */
char Attn[ZATTNLEN+1];	    /* Attention string rx sends to tx on err */
int lastsent;		        /* Last char we sent */
int zsrCDstat;

FILE *f;

//(RTS)
/*
 * ZMODEM protocol primitives
 * Chuck Forsberg Omen Technology Inc.
 *
 * Entry point Functions:
 *	zsbhdr(type, hdr) send binary header
 *	zshhdr(type, hdr) send hex header
 *	zgethdr(hdr, eflag) receive header - binary or hex
 *	zsdata(buf, len, frameend) send data
 *	zrdata(buf, len) receive data
 *	stohdr(pos) store position data in Txhdr
 *	long rclhdr(hdr) recover position offset from header
 */

static long rclhdr(char *hdr);
static void zputhex(int c), zmputs(char *s);
static void zsda32(char *buf, int length, int frameend);
static void zsdata(char *buf, int length, int frameend);
static void zshhdr(int type, char *hdr);
static void zsbh32(int type, char *hdr);
static void zsbhdr(int type, char *hdr);
static void stohdr(long pos), ackbibi(void), canit(void), putsec(void);

static int noxrd7(void), zdlread(void), zgethex(void), zrhhdr(char *hdr);
static int zrbhdr(char *hdr), zrbhdr32(char *hdr), zgethdr(char *hdr);
static int zrdat32(char *buf, int length), zrdata(char *buf, int length);
static int procheader(char *name), zsendfile(char *buf, int blen);
static int zsendfdata(void), getinsync(void), saybibi(void);
static int rzfile(void), rzfiles(void), tryz(void);
static int getzrxinit(void);

/*
 * Read a character from the modem line with timeout.
 *  Eat parity, XON and XOFF characters.
 */


static void ZErrDisplay(char *s)
{
 char temp[50];

 ZmodemStatPrint("[12;12H");
 ZmodemStatPrint("                             ");
 ZmodemStatPrint("[12;12H");
 ZmodemStatPrint(s);
 ZmodemStatPrint("[13;12H");
 strcpy(ZModemInfo.ZStat,s);
 ZModemInfo.ErrorPos=ZModemInfo.RecPos;
 sprintf(temp,"%ld    ",ZModemInfo.ErrorPos);
 ZmodemStatPrint(temp);
}


static void ZPrintClear(char *s)
{
 ZmodemStatPrint(s);
 ZmodemStatPrint("                             ");
}


static int noxrd7(void)
{
	register int c;

	for (;;) {
		if ((c = readbyte()) < 0)			return c;
		switch (c &= 0x7F)	{
			case XON:
			case XOFF:
				continue;
			default:
				if (Zctlesc && !(c & 0x60)) 	continue;
			case '\r':		case '\n':		case ZDLE:
				return c;
		}
	}
}

/*
 * Read a byte, checking for ZMODEM escape encoding
 *  including CAN*5 which represents a quick abort
 */
static int zdlread(void)
{
	register int c;

again:
	/* Quick check for non control characters */
	if ((c = readbyte()) & 0x60)		return c;
	switch (c)	{
		case ZDLE:			break;
		case 0x13:	case 0x93:	case 0x11:	case 0x91:
			goto again;
		default:
			if (Zctlesc && !(c & 0x60))		goto again;
			return c;
	}
again2:
	zsrCDstat=CheckCarrier();
	if(zsrCDstat==FALSE)		return CAN;
	if ((c = readbyte()) < 0)	return c;

	if (c == CAN && (c = readbyte()) < 0)	return c;
	if (c == CAN && (c = readbyte()) < 0)	return c;
	if (c == CAN && (c = readbyte()) < 0)	return c;
	switch (c) {
		case CAN:			return GOTCAN;
		case ZCRCE:		case ZCRCG:		case ZCRCQ:		case ZCRCW:
			return (c | GOTOR);
		case ZRUB0:			return 0x7F;
		case ZRUB1:			return 0xFF;
		case 0x13:	case 0x93:	case 0x11:	case 0x91:
			goto again2;
		default:
			if (Zctlesc && ! (c & 0x60))	goto again2;
			if ((c & 0x60) ==  0x40)		return(c ^ 0x40);
			break;
	}
	sprintf(sbuff, "Bad escape sequence %x", c);
	ZErrDisplay(sbuff);
	return ERROR;
}

/* Decode two lower case hex digits into an 8 bit byte value */
static int zgethex(void)
{
	register int c, n;

	if ((c = noxrd7()) < 0)		return c;
	n = c - '0';
	if (n > 9)		            n -= ('a' - ':');
	if (n & ~0xF)	            return ERROR;
	if ((c = noxrd7()) < 0)		return c;

	c -= '0';
	if (c > 9)		c -= ('a' - ':');
	if (c & ~0xF)	return ERROR;
	c += (n<<4);
	return c;
}

/* Receive a hex style header (type and position) */
static int zrhhdr(char *hdr)
{
	register int c;
	register unsigned short crc;
	register int n;

	if ((c = zgethex()) < 0)		return c;
	Rxtype = c;
	crc = update_crc16(c, 0);

	for (n=4; --n >= 0; ++hdr)	{
		if ((c = zgethex()) < 0)
			return c;
		crc = update_crc16(c, crc);
		*hdr = c;
	}
	if ((c = zgethex()) < 0)
		return c;
	crc = update_crc16(c, crc);
	if ((c = zgethex()) < 0)
		return c;
	crc = update_crc16(c, crc);
	if (crc & 0xFFFF) 
	{
		ZErrDisplay("Bad CRC"); return ERROR;
	}
	switch ( c = readbyte()) 
	{
		case 0x8D:
		case 0xD:
		 	/* Throw away possible cr/lf */
			readbyte();
	}
	return Rxtype;
}

/* Receive a binary style header (type and position) */
static int zrbhdr(char *hdr)
{
	register int c, n;
	register unsigned short crc;

	if ((c = zdlread()) & ~0xFF)
		return c;
	Rxtype = c;
	crc = update_crc16(c, 0);

	for (n=4; --n >= 0; ++hdr) 
	{
		if ((c = zdlread()) & ~0xFF)
			return c;
		crc = update_crc16(c, crc);
		*hdr = c;
	}
	if ((c = zdlread()) & ~0xFF)
		return c;
	crc = update_crc16(c, crc);
	if ((c = zdlread()) & ~0xFF)
		return c;
	crc = update_crc16(c, crc);
	if (crc & 0xFFFF)
	{
		ZErrDisplay("Bad CRC");
		return ERROR;
	}
	return Rxtype;
}

/* Receive a binary style header (type and position) with 32 bit FCS */
static int zrbhdr32(char *hdr)
{
	register int c, n;
	register unsigned long crc;

	if ((c = zdlread()) & ~0xFF)
		return c;
	Rxtype = c;
	crc = 0xFFFFFFFFL; crc = update_crc32(c, crc);

	for (n=4; --n >= 0; ++hdr)
	{
		if ((c = zdlread()) & ~0xFF)
			return c;
		crc = update_crc32(c, crc);
		*hdr = c;
	}
	for (n=4; --n >= 0;)
	{
		if ((c = zdlread()) & ~0xFF)
			return c;
		crc = update_crc32(c, crc);
	}

	if (crc != 0xDEBB20E3L)
	{
		ZErrDisplay("Bad CRC");
		return ERROR;
	}
	return Rxtype;
}

/*
 * Read a ZMODEM header to hdr, either binary or hex.
 *  eflag controls local display of non zmodem characters:
 *	0:  no display
 *	1:  display printing characters only
 *	2:  display all non ZMODEM characters
 *   set Rxpos and return type of header.
 *   Otherwise return negative on error.
 *   Return ERROR instantly if ZCRCW sequence, for fast error recovery.
 */
static int zgethdr(char *hdr)
{
	register int c, n, cancount;

	n = 1400 + zbaud;	/* Max bytes before start of frame */
	Rxframeind = Rxtype = 0;

startover:
	cancount = 5;
again:
	/* Return immediate ERROR if ZCRCW sequence seen */
	switch (c = readbyte())
	{
		case RCDO:
		case TIMEOUT: {
			zsrCDstat=CheckCarrier();
 		if(zsrCDstat==FALSE)
	    			return NOCARRERROR;
			goto fifi;
		    }
		case CAN:
gotcan:
			if (--cancount <= 0) {
				c = ZCAN;
				goto fifi;
			}
			switch (c = readbyte())	{
				case TIMEOUT:
					goto again;
				case ZCRCW:
					c = ERROR;
				/* **** FALL THRU TO **** */
				case RCDO:
					goto fifi;
				default:
					break;
				case CAN:
					if (--cancount <= 0)
					{
						c = ZCAN; goto fifi;
					}
					goto again;
			}
	/* **** FALL THRU TO **** */
		default:
agn2:
			if ( --n == 0)
			{
				ZErrDisplay("Garbage count exceeded");
				return(ERROR);
			}
			goto startover;
		case ZPAD|0x80:		/* This is what we want. */
		case ZPAD:		/* This is what we want. */
			break;
	}
	cancount = 5;
splat:
	switch (c = noxrd7())
	{
		case ZPAD:
			goto splat;
		case RCDO:
		case TIMEOUT:
			goto fifi;
		default:
			goto agn2;
		case ZDLE:		/* This is what we want. */
			break;
	}

	switch (c = noxrd7())
	{
		case RCDO:
		case TIMEOUT:
			goto fifi;
		case ZBIN:
			Rxframeind = ZBIN;  Crc32 = FALSE;
			c =  zrbhdr(hdr);
			break;
		case ZBIN32:
			Crc32 = Rxframeind = ZBIN32;
			c =  zrbhdr32(hdr);
			break;
		case ZHEX:
			Rxframeind = ZHEX;  Crc32 = FALSE;
			c =  zrhhdr(hdr);
			break;
		case CAN:
			goto gotcan;
		default:
			goto agn2;
	}
	Rxpos = hdr[ZP3] & 0xFF;
	Rxpos = (Rxpos<<8) + (hdr[ZP2] & 0xFF);
	Rxpos = (Rxpos<<8) + (hdr[ZP1] & 0xFF);
	Rxpos = (Rxpos<<8) + (hdr[ZP0] & 0xFF);
fifi:
	switch (c)
	{
		case GOTCAN:
			c = ZCAN;
		/* **** FALL THRU TO **** */
		case ZNAK:
		case ZCAN:
		case ERROR:
		case TIMEOUT:
		case RCDO:
		/* **** FALL THRU TO **** */
		default:
			break;
	}
	return c;
}

static int zrdat32(char *buf, int length)
{
	register int c, d;
	register unsigned long crc;
	register char *end;

	crc = 0xFFFFFFFFL;  Rxcount = 0;  end = buf + length;
	while (buf <= end)
	{
		if ((c = zdlread()) & ~0xFF)
		{
crcfoo:
			switch (c)
			{
				case GOTCRCE:
				case GOTCRCG:
				case GOTCRCQ:
				case GOTCRCW:
					crc = update_crc32(d=c, crc);
					if ((c = zdlread()) & ~0xFF)
						goto crcfoo;
					crc = update_crc32(c, crc);
					if ((c = zdlread()) & ~0xFF)
						goto crcfoo;
					crc = update_crc32(c, crc);
					if ((c = zdlread()) & ~0xFF)
						goto crcfoo;
					crc = update_crc32(c, crc);
					if ((c = zdlread()) & ~0xFF)
						goto crcfoo;
					crc = update_crc32(c, crc);
					if (crc != 0xDEBB20E3L)
					{
						ZErrDisplay("Bad CRC");
						return ERROR;
					}
					Rxcount = length - (end - buf);
					return d;
				case GOTCAN:
					ZErrDisplay("Sender Canceled");
					return ZCAN;
				case TIMEOUT:
					zsrCDstat=CheckCarrier();
					if(zsrCDstat==FALSE) {
						return ERROR;
					}
					ZErrDisplay("TIMEOUT");
					return c;
				default:
					ZErrDisplay("Bad data subpacket");
					return c;
			}
		}
		*buf++ = c;
		crc = update_crc32(c, crc);
	}
	ZErrDisplay("Data subpacket too long");
	return ERROR;
}

/*
 * Receive array buf of max length with ending ZDLE sequence
 *  and CRC.  Returns the ending character or error code.
 *  NB: On errors may store length+1 bytes!
 */
static int zrdata(char *buf, int length)
{
	register int c, d;
	register unsigned short crc;
	register char *end;

	if (Rxframeind == ZBIN32)
		return zrdat32(buf, length);

	crc = Rxcount = 0;  end = buf + length;
	while (buf <= end)
	{
		if ((c = zdlread()) & ~0xFF)
		{
crcfoo:
			switch (c)
			{
				case GOTCRCE:
				case GOTCRCG:
				case GOTCRCQ:
				case GOTCRCW:
					crc = update_crc16(d=c, crc);
					if ((c = zdlread()) & ~0xFF)
						goto crcfoo;
					crc = update_crc16(c, crc);
					if ((c = zdlread()) & ~0xFF)
						goto crcfoo;
					crc = update_crc16(c, crc);
					if (crc & 0xFFFF)
					{
						ZErrDisplay("Bad CRC");
						return ERROR;
					}
					Rxcount = length - (end - buf);
					return d;
				case GOTCAN:
					ZErrDisplay("Sender Canceled");
					return ZCAN;
				case TIMEOUT:
					zsrCDstat=CheckCarrier();
					if(zsrCDstat==FALSE) {
						return ERROR;
					}
					ZErrDisplay("TIMEOUT");
					return c;
				default:
					ZErrDisplay("Bad data subpacket");
					return c;
			}
		}
		*buf++ = c;
		crc = update_crc16(c, crc);
	}
	ZErrDisplay("Data subpacket too long");
	return ERROR;
}


/* Recover a long integer from a header */
static long rclhdr(char *hdr)
{
	register long l;

	l = (hdr[ZP3] & 0xFF);
	l = (l << 8) | (hdr[ZP2] & 0xFF);
	l = (l << 8) | (hdr[ZP1] & 0xFF);
	l = (l << 8) | (hdr[ZP0] & 0xFF);
	return l;
}

/*
 * Send character c with ZMODEM escape sequence encoding.
 *  Escape XON, XOFF. Escape CR following @ (Telenet net escape)
 */
void zsendbyte(int c)
{
	/* Quick check for non control characters */
	if (c & 0x60)
		sendbyte(lastsent = c);
	else 
	{
		switch (c &= 0xFF) 
		{
			case ZDLE:
				sendbyte(ZDLE);
				sendbyte(lastsent = (c ^= 0x40));
				break;
			case 0xD:
			case 0x8D:
				if (!Zctlesc && (lastsent & 0x7F) != '@')
					goto sendit;
			/* **** FALL THRU TO **** */
			case 0x10:
			case 0x11:
			case 0x13:
			case 0x90:
			case 0x91:
			case 0x93:
				sendbyte(ZDLE);
				c ^= 0x40;
sendit:
				sendbyte(lastsent = c);
				break;
			default:
				if (Zctlesc && ! (c & 0x60)) 
				{
					sendbyte(ZDLE);
					c ^= 0x40;
				}
				sendbyte(lastsent = c);
		}
	}
}

/* Send a byte as two hex digits */
static void zputhex(int c)
{
	static char digits[] = "0123456789abcdef";

	sendbyte(digits[(c&0xF0)>>4]);
	sendbyte(digits[(c)&0xF]);
}

/*
 * Send a string to the modem
 */
static void zmputs(char *s)
{
	while(*s)
		sendbyte(*s++);
}

static void zsda32(char *buf, int length, int frameend)
{
	register int c;
	register unsigned long crc;

	crc = 0xFFFFFFFFL;
	for (;--length >= 0; ++buf)
	{
		c = *buf & 0xFF;
		if (c & 0x60)
			sendbyte(lastsent = c);
		else
			zsendbyte(c);
		crc = update_crc32(c, crc);
	}
	sendbyte(ZDLE); sendbyte(frameend);
	crc = update_crc32(frameend, crc);

	crc = ~crc;
	for (length=4; --length >= 0;)
	{
		zsendbyte((int)crc);
		crc >>= 8;
	}
}

/*
 * Send binary array buf of length length, with ending ZDLE sequence frameend
 */
static void zsdata(char *buf, int length, int frameend)
{
	register unsigned short crc;

	if (Crc32t)
		zsda32(buf, length, frameend);
	else
	{
		crc = 0;
		for (;--length >= 0; ++buf)
		{
			zsendbyte(*buf); crc = update_crc16(*buf, crc);
		}
		sendbyte(ZDLE); sendbyte(frameend);
		crc = update_crc16(frameend, crc);

		crc = update_crc16(0, crc);
		crc = update_crc16(0, crc);
		zsendbyte(crc>>8); zsendbyte(crc);
	}
	if (frameend == ZCRCW)
		sendbyte(XON);
}

/* Send ZMODEM binary header hdr of type type */
static void zsbh32(int type, char *hdr)
{
	register int n;
	register unsigned long crc;

	sendbyte(ZBIN32);  zsendbyte(type);
	crc = 0xFFFFFFFFL; crc = update_crc32(type, crc);

	for (n=4; --n >= 0; ++hdr)
	{
		crc = update_crc32(*hdr, crc);
		zsendbyte(*hdr);
	}
	crc = ~crc;
	for (n=4; --n >= 0;)
	{
		zsendbyte((int)crc);
		crc >>= 8;
	}
}

/* Send ZMODEM HEX header hdr of type type */
static void zshhdr(int type, char *hdr)
{
	register int n;
	register unsigned short crc;

	sendbyte(ZPAD); sendbyte(ZPAD); sendbyte(ZDLE); sendbyte(ZHEX);
	zputhex(type);
	Crc32t = 0;

	crc = update_crc16(type, 0);
	for (n=4; --n >= 0; ++hdr)
	{
		zputhex(*hdr); crc = update_crc16(*hdr, crc);
	}
	crc = update_crc16(0, crc);
	crc = update_crc16(0, crc);
	zputhex(crc>>8); zputhex(crc);

	/* Make it printable on remote machine */
	sendbyte(0xD); sendbyte(0x8A);
	/*
	 * Uncork the remote in case a fake XOFF has stopped data flow
	 */
	if (type != ZFIN && type != ZACK)
		sendbyte(XON);
}

/* Send ZMODEM binary header hdr of type type */
static void zsbhdr(int type, char *hdr)
{
	register int n;
	register unsigned short crc;

	sendbyte(ZPAD); sendbyte(ZDLE);

	if (Crc32t=Txfcs32)
		zsbh32(type, hdr);
	else
	{
		sendbyte(ZBIN); zsendbyte(type); crc = update_crc16(type, 0);

		for (n=4; --n >= 0; ++hdr)
		{
			zsendbyte(*hdr);
			crc = update_crc16(*hdr, crc);
		}
		crc = update_crc16(0, crc);
		crc = update_crc16(0, crc);
		zsendbyte(crc>>8);
		zsendbyte(crc);
	}
}

/* Store long integer pos in Txhdr */
static void stohdr(long pos)
{
	Txhdr[ZP0] = pos;
	Txhdr[ZP1] = pos>>8;
	Txhdr[ZP2] = pos>>16;
	Txhdr[ZP3] = pos>>24;
}

/*
 * Ack a ZFIN packet, let byegones be byegones
 */
static void ackbibi(void)
{
	register int n;

	stohdr(0L);
	for (n=3; --n>=0; )
	{
		purgeline();
		zshhdr(ZFIN, Txhdr);
		switch (readbyte())
		{
			case 'O':
				readbyte();	/* Discard 2nd 'O' */
				return;
			case RCDO:
				return;
			case TIMEOUT:
			default:
				break;
		}
	}
}

static void canit(void)
{
	static char canistr[] =
	{
	 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
	};

	zmputs(canistr);
}


/*
 * Putsec writes the n characters of buf to receive file fh
 * remember to put in write err checking in here... then longjmp..
 */
static void putsec(void)
{
	WriteAsync(zbuffer, chinseg);
	if(zbuffer == zdiskbuff)
		{
		zbuffer += 10240L;
		}
	else
		{
		zbuffer = zdiskbuff;
		}
	chinseg = 0L;
}

/*
 * Receive a file with ZMODEM protocol
 *  Assumes file name frame is in buffer
 */
static int rzfile(void)
{
	register int c, n;
	long rxbytes;

	start=0;
	if (procheader(zbuffer) == ERROR)
		return(tryzhdrtype = ZSKIP);

	n = 10; rxbytes = resumesize;

	for (;;)
	{
		chinseg = 0L;
		stohdr(rxbytes);
		zshhdr(ZRPOS, Txhdr);
nxthdr:
		if(check_abort()) { putsec(); return ERROR; }

		switch (c = zgethdr(Rxhdr))
		{
			default:
			case ZNAK:
			case TIMEOUT:
				putsec();
				zsrCDstat=CheckCarrier();
				if(zsrCDstat==FALSE) {
					return ERROR;
				}
				if ( --n < 0)
				{
					return ERROR;
				}
			case ZFILE:
				zrdata(zbuffer, 1024);
				continue;
			case ZEOF:
				putsec();
				if (rclhdr(Rxhdr) != rxbytes)
					goto nxthdr;
					/*
					 * Ignore eof if it's at wrong place - force
					 *  a timeout because the eof might have gone
					 *  out before we sent our zrpos.
					 */

				throughput(rxbytes-resumesize);
				++Online_NFiles;
				if(!FreeDFlag)
					{
					++DONF;
					}
				Aclose();
				return c;
			case ERROR:	/* Too much garbage in header search error */
				putsec();
				if ( --n < 0)
				{
					return ERROR;
				}
				zmputs(Attn);
				continue;
			case ZSKIP:
				putsec();
				Aclose();
				return c;
			case ZDATA:
				if(!start)
					{
					throughput(0L);
					start=1;
					}
				if (rclhdr(Rxhdr) != rxbytes)
				{
					if ( --n < 0)
						return ERROR;
					putsec();
					zmputs(Attn);
					continue;
				}
moredata:
				if(check_abort()) { putsec(); return ERROR; }

				if(chinseg >= ((doublebuffer) ? 8192L : 16384L))
					{
					putsec();
					}

				switch (c = zrdata(zbuffer+chinseg, 1024))
				{
					case ZCAN:
						putsec();
						return ERROR;
					case ERROR:	/* CRC error */
						putsec();
						if ( --n < 0)
						{
							ZErrDisplay("Too many errors");
							return ERROR;
						}
						zmputs(Attn);
						continue;
					case TIMEOUT:
						putsec();
						zsrCDstat=CheckCarrier();
						if(zsrCDstat==FALSE)
							{
							return ERROR;
							}
						if ( --n < 0)
						{
							ZErrDisplay("Too many errors");
							return ERROR;
						}
						continue;
					case GOTCRCW:
						n = 10;
						chinseg += Rxcount;
						rxbytes += Rxcount;
						stohdr(rxbytes);
						zshhdr(ZACK, Txhdr);
						sendbyte(XON);
						sprintf(sbuff, "[5;12H%ld  ",rxbytes);
						ZmodemStatPrint(sbuff);
						sprintf(sbuff, "[6;12H%ld%%  ",(ZModemInfo.Filesize?((100*rxbytes)/ZModemInfo.Filesize):0));
						ZmodemStatPrint(sbuff);
						itp(rxbytes-resumesize,1);
						ZModemInfo.RecPos=rxbytes;
						goto nxthdr;
					case GOTCRCQ:
						n = 10;
						chinseg += Rxcount;
						rxbytes += Rxcount;
						stohdr(rxbytes);
						zshhdr(ZACK, Txhdr);
						sprintf(sbuff, "[5;12H%ld  ",rxbytes);
						ZmodemStatPrint(sbuff);
						sprintf(sbuff, "[6;12H%ld%%  ",(ZModemInfo.Filesize?((100*rxbytes)/ZModemInfo.Filesize):0));
						ZmodemStatPrint(sbuff);
						itp(rxbytes-resumesize,1);
						ZModemInfo.RecPos=rxbytes;
						goto moredata;
					case GOTCRCG:
						n = 10;
						chinseg += Rxcount;
						rxbytes += Rxcount;
						sprintf(sbuff, "[5;12H%ld  ",rxbytes);
						ZmodemStatPrint(sbuff);
						sprintf(sbuff, "[6;12H%ld%%  ",(ZModemInfo.Filesize?((100*rxbytes)/ZModemInfo.Filesize):0));
						ZmodemStatPrint(sbuff);
						itp(rxbytes-resumesize,1);
						ZModemInfo.RecPos=rxbytes;
						goto moredata;
					case GOTCRCE:
						n = 10;
						chinseg += Rxcount;
						rxbytes += Rxcount;
						sprintf(sbuff, "[5;12H%ld  ",rxbytes);
						ZmodemStatPrint(sbuff);
						sprintf(sbuff, "[6;12H%ld%%  ",(ZModemInfo.Filesize?((100*rxbytes)/ZModemInfo.Filesize):0));
						ZmodemStatPrint(sbuff);
						itp(rxbytes-resumesize,1);
						ZModemInfo.RecPos=rxbytes;
						goto nxthdr;
				}
		}
	}
}

static int zmodemreceive(char *flname) 	/* NEED pathname!!! */
{
	register c;

	RecFileNames[0]='\0';
	*(SkipdFiles+(NumSkipd*32))='\0';
	strcpy(ZPathname, flname);

	tryzhdrtype = ZRINIT;
	Zctlesc = FALSE;

	if (c=tryz())	{
		if (c == ZCOMPL)			return TRUE;
		if (c == ERROR) 			goto fubar;
		if (c == NOCARRERROR)		return FALSE;
		if (c == ZFILE)	{
			if (c = rzfiles())
				goto fubar;
			return TRUE;
		}
	}
	return FALSE;

fubar:
	canit();
	Aclose();
	return FALSE;
}

/*
 * Initialize for Zmodem receive attempt, try to activate Zmodem sender
 *  Handles ZSINIT  frame
 *  Return ZFILE if Zmodem filename received, -1 on error,
 *   ZCOMPL if transaction finished,  else 0
 */
static int tryz(void)
{
	register int c, n;

	for (n=10; --n>=0; )
	{
		if(check_abort()) return ERROR;
		zsrCDstat=CheckCarrier();
		if(zsrCDstat==FALSE)
			{
			return NOCARRERROR;
			}

		/* Set buffer length (0) and capability flags */
		stohdr(0L);
		Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
		zshhdr(tryzhdrtype, Txhdr);
		if (tryzhdrtype == ZSKIP)	/* Don't skip too far */
			tryzhdrtype = ZRINIT;
again:
		switch (zgethdr(Rxhdr))
		{
			case NOCARRERROR:
				return NOCARRERROR;
			case ZRQINIT:
				continue;
			case ZEOF:
				continue;
			case TIMEOUT:
				continue;
			case ZFILE:
				tryzhdrtype = ZRINIT;
				c = zrdata(zbuffer, 1024);
				if (c == GOTCRCW)
					return ZFILE;
				zshhdr(ZNAK, Txhdr);
				goto again;
			case ZSINIT:
				Zctlesc = TESCCTL & Rxhdr[ZF0];
				if (zrdata(Attn, ZATTNLEN) == GOTCRCW)
				{
					stohdr(1L);
					zshhdr(ZACK, Txhdr);
					goto again;
				}
				zshhdr(ZNAK, Txhdr);
				goto again;
			case ZFREECNT:
				stohdr(1000000L);
				zshhdr(ZACK, Txhdr);
				goto again;
			case ZCOMMAND:
				return ERROR;
			case ZCOMPL:
				goto again;
			default:
				continue;
			case ZFIN:
				ackbibi();
				return ZCOMPL;
			case ZCAN:
				return ERROR;
		}
	}
	return 0;
}

/*
 * Receive 1 or more files with ZMODEM protocol
 */
static int rzfiles(void)
{
	register int c;

	for (;;) {

		if(check_abort()) return ERROR;
		zsrCDstat=CheckCarrier();
		if(zsrCDstat==FALSE)			return ERROR;

		switch (c = rzfile()) {
			case ZEOF:
			case ZSKIP:
				switch (tryz()) 	{
					case ZCOMPL:		return OK;
					default:	       return ERROR;
					case ZFILE:	      break;
				}
				continue;
			default:
				return c;
			case ERROR:
				return c;
		}
	}
}

static int CheckInPlayPens(char *s)
{
 BPTR lock1,lock2;
 UWORD loop;

 loop=0;

 do {
	    if(loop==Cmds->AcLvl[LVL_NODE_NUMBER]) {
		    loop++;
	    }
	    sprintf(GSTR1,"%snode%ld",Cmds->BBSLoc,loop);
	    if(lock1=Lock(GSTR1,ACCESS_READ)) {
		    sprintf(GSTR1,"%sNode%ld/PLAYPEN/%s",Cmds->BBSLoc,loop,s);
		    if(lock2=Lock(GSTR1,ACCESS_READ)) {
			    UnLock(lock2);
			    UnLock(lock1);
			    return(1);
			}
               else if(IoErr()!=205) { UnLock(lock1); return(1); }
		    UnLock(lock1);
		}
	    loop++;
	} while(lock1!=NULL);
 return(0);
}

/*
 * Process incoming file information header
 */
static int procheader(char *name)
{
	register char *p;
	int stats;
	unsigned long seconds;
	char fullname[257],string[200];
	BPTR l;

	strcpy(ZModemInfo.FileName,name);
	sprintf(sbuff, "[2;12H%s",name);
	ZPrintClear("[2;12H");
	ZmodemStatPrint(sbuff);
	ZErrDisplay("Processing Incoming Header");

	stats=Check_For_File(name);
	if(stats==0)		stats=CheckInPlayPens(name);
	if(stats!=0)	{
		ZErrDisplay("File Exists!");
//1NOV92
		strcpy(SkipdFiles+(NumSkipd*32),name);
		NumSkipd+=1;
          *(SkipdFiles+(NumSkipd*32))='\0';
		return ERROR;
	}
     SendMasterUpload(name);

	strcpy(fullname, ZPathname);
	strcat(fullname, name);
	if((strlen(RecFileNames)+strlen(name)+1)<1024) {
		strcat(RecFileNames,name);
		strcat(RecFileNames," ");
	}
	else		return(ERROR);

	filesize = 0L;
	p = name + 1 + strlen(name);
	if (*p) sscanf(p, "%ld", &filesize);

	l = Lock(fullname, ACCESS_READ);
	if (l) UnLock(l);
 else zresume = FALSE;

	if(!Aopen(fullname, (long)((zresume) ? MODE_OLDFILE : MODE_NEWFILE))) {
		ZErrDisplay("File OPEN Error!");
		return ERROR;
	}
	Seek(fh, 0L, OFFSET_END);
	resumesize = Seek(fh, 0L, OFFSET_CURRENT);
	TMPBT=filesize;

	if(zresume)
		sprintf(string,"\tResuming %-12s %ld bytes [%6d]\n",name,filesize,resumesize);
	else {
		sprintf(string,"\tUploading %-12s %ld bytes\n",name,filesize);
          }
	CallersLog(string);
	UDLog(string);

	sprintf(sbuff, "[3;12H%ld     ",filesize);
	ZModemInfo.Filesize=filesize;
	ZmodemStatPrint(sbuff);

	seconds = (((filesize-resumesize)/(long)(zbaud/10L))*100L)/95L;
	if(resumesize) {
		sprintf(sbuff, "[7;12H%ld     ", resumesize);
		ZmodemStatPrint(sbuff);
		ZModemInfo.ResumePos=resumesize;
	}
	else	ZModemInfo.ResumePos=0L;

	sprintf(sbuff, "[4;12H%ld mins %ld secs  (%d) ",seconds/60L, seconds % 60L,zbaud);
	ZmodemStatPrint(sbuff);
	ZModemInfo.ApxTime=seconds;

	ZErrDisplay("ZDATA");
	return OK;
}

/* flname = full file name(s) */

static int zmodemsend(char *flname)
{
	int rc,y;
	char *r, *fn;
	char string[200];
	unsigned long seconds;
	struct FileInfoBlock *FBlock;
	BPTR FLock;

	if(getzrxinit()==ERROR) return FALSE;

 sendmorefiles:
	fn = &ZPathname[0];
	strcpy(ZPathname, flname);

	if((FLock=Lock(fn,ACCESS_READ))==NULL)	return FALSE;

	if((FBlock=(struct FileInfoBlock *)AllocDosObject(DOS_FIB,NULL))==NULL) {
		UnLock(FLock);
		return FALSE;
	}
	if(Examine(FLock,FBlock)) {
		strcpy(ZModemInfo.FileName,FBlock->fib_FileName);
		if(FBlock->fib_Comment[0]=='F')	{
 		FreeDFlag=1;
		}
		else{
			FreeDFlag=0;
		}
	} else {
		UnLock(FLock);
		FreeDosObject(DOS_FIB,FBlock);
		return FALSE;
	}
	UnLock(FLock);
	FreeDosObject(DOS_FIB,FBlock);

	strcpy(string,Conference_Location);
     if(IsFromIcon(string,"FREEDOWNLOADS"))
     {
		FreeDFlag=1;
	}
	if(!(fh = Open(fn, MODE_OLDFILE))) return FALSE;

	r = fn + strlen(fn);
	while (r>=ZPathname && *r!='/' && *r!=':') r--;

	strlwr(++r);
	strcpy(txbuf,r);

	/* VERY TEMP */
	Seek(fh, 0L, OFFSET_END);
	filesize = Seek(fh, 0L, OFFSET_BEGINNING);

	sprintf(string, "%ld %lo %o", filesize, 0L, 0L);
	strcpy(txbuf+strlen(txbuf)+1,string);

	y=strlen(string);

	TMPBT=filesize;
	if(FreeDFlag)
		strcpy(sbuff,"Free ");
	else
		sbuff[0]='\0';

	sprintf(string,"\t%sDownloading %-12s %ld bytes\n",sbuff,flname,filesize);
	(void)CallersLog(string);
	(void)UDLog(string);
(void)SendMasterDownload(ZModemInfo.FileName);
	ZPrintClear("[2;12H");
	sprintf(sbuff, "[2;12H%s",ZModemInfo.FileName);
	ZmodemStatPrint(sbuff);

	sprintf(sbuff, "[3;12H%ld     ", filesize);
	ZmodemStatPrint(sbuff);
	ZModemInfo.Filesize=filesize;

	seconds = (filesize/(zbaud/10L));
	sprintf(sbuff, "[4;12H%ld mins %ld secs  (%d) ",
                seconds/60L, seconds % 60L,zbaud);
        ZModemInfo.ApxTime=seconds;
	ZmodemStatPrint(sbuff);

	rc = zsendfile(txbuf, (int)(2+y+strlen(txbuf)));
	Close(fh);

	switch(rc)	{
		case ERROR:			canit();		return FALSE;
		case OK:		case ZSKIP:
		default:
			++Online_NFiles;
			if(!FreeDFlag)
				++DONF;

			flname = flname + strlen(flname)+1;
			if(*flname) goto sendmorefiles;
			saybibi();
			return TRUE;
	}
return(0);
}

/*
 * Get the receiver's init parameters
 */
static int getzrxinit(void)
{
	int n;

	zmputs("rz\r");
	stohdr(0L);
	zshhdr(ZRQINIT, Txhdr);

	for (n=10; --n>=0; ) {
		if(check_abort()) return ERROR;

		switch (zgethdr(Rxhdr)) {
			case ZCHALLENGE:	/* Echo receiver's challenge numbr */
				stohdr(Rxpos);
				zshhdr(ZACK, Txhdr);
				continue;
			case ZCOMMAND:		/* They didn't see out ZRQINIT */
				stohdr(0L);
				zshhdr(ZRQINIT, Txhdr);
				continue;
			case ZRINIT:
				Rxflags = 0xFF & Rxhdr[ZF0];
				Txfcs32 = (Rxflags & CANFC32);
				Zctlesc |= Rxflags & TESCCTL;
				Rxbuflen = (0xFF & Rxhdr[ZP0])+((0xFF & Rxhdr[ZP1])<<8);

				return OK;
			case ZCAN:				return ERROR;
			case TIMEOUT:
				zsrCDstat=CheckCarrier();
				if(zsrCDstat==FALSE)
					return ERROR;

				stohdr(0L);
				zshhdr(ZRQINIT, Txhdr);
				continue;
			case ZRQINIT:
				if (Rxhdr[ZF0] == ZCOMMAND)
					continue;
			default:
				zshhdr(ZNAK, Txhdr);
				continue;
		}
	}
	return ERROR;
}

/* Send file name and related info */
static int zsendfile(char *buf, int blen)
{
	register int c, n;

	for (n=5; --n>=0;)	{
		if(check_abort()) return ERROR;

		Txhdr[ZF0] = 0; 	/* file conversion request */
		Txhdr[ZF1] = 0; 	/* file management request */
		Txhdr[ZF2] = 0; 	/* file transport request */
		Txhdr[ZF3] = 0;
		zsbhdr(ZFILE, Txhdr);
		zsdata(buf, blen, ZCRCW);
again:
		c = zgethdr(Rxhdr);
		switch (c)	{
			case ZRINIT:
				while ((c = readbyte()) > 0)
					if (c == ZPAD)
						goto again;
			case ZCAN:		case ZABORT:	case ZFIN:
				return ERROR;
			case ZSKIP:		return c;
			case ZRPOS:
				Seek(fh, Rxpos, OFFSET_BEGINNING);
				STpos=Rxpos;
				Txpos = Rxpos;
				purgeline();
				return zsendfdata();
			case TIMEOUT:

				zsrCDstat=CheckCarrier();
				if(zsrCDstat==FALSE) return ERROR;
                    return ERROR; 
			default:
				continue;
		}
	}
	return ERROR;
}

/* Send the data in the file */
static int zsendfdata(void)
{
	register int c, e, n;
	int newcnt, goodblks = 0, goodneeded = 1;
	int blklen, maxblklen;

	blklen = maxblklen = 1024;
	if(Rxbuflen && Rxbuflen < maxblklen)
		blklen = maxblklen = Rxbuflen;
	throughput(0L);

somemore:
	if(checkline())
	{
waitack:
		switch (c = getinsync())
		{
			default:
			case ZCAN:
				return ERROR;
			case ZSKIP:
				return c;
			case ZACK:
				break;
			case ZRPOS:
				blklen = (blklen>>1 > 64) ? blklen>>1 : 64;
				goodblks = 0;
				goodneeded = (goodneeded<<1 < 64) ? goodneeded<<1 : 64;
				break;
			case ZRINIT:
				return OK;
		}

		while (checkline())
		{
			switch (readbyte())
			{
				case CAN:
				case ZPAD:
					ZErrDisplay("ERROR Received");
					goto waitack;
			}
		}
	}

	newcnt = Rxbuflen;
	stohdr(Txpos);
	zsbhdr(ZDATA, Txhdr);

	do
	{
		if(check_abort()) { waitwrite(); return ERROR; }

		n = Read(fh, txbuf, (long)blklen);
		if (n < blklen)
			e = ZCRCE;
		else if (Rxbuflen && (newcnt -= n) <= 0)
			e = ZCRCW;
		else
			e = ZCRCG;

		sendbuff = zbuffer;	/* tell sendbyte() to buffer the bytes in buffer */
		sendlen = 0;		/* sendlen is byte buffer count */
		zsdata(txbuf, n, e);
		sendbuff = 0L;

		while(checkline())
		{
			switch (readbyte())
			{
				case CAN:
				case ZPAD:
					purgewrite();
					ZErrDisplay("ERROR Received");
					goto waitack;
			}
		}

		waitwrite();
		zsrCDstat=CheckCarrier();
		if(zsrCDstat==FALSE)
			{
			return ERROR;
			}
		sendbuffer(zbuffer, sendlen);
		if(zbuffer == zdiskbuff)
			zbuffer += 10240L;
		else
			zbuffer = zdiskbuff;

		Txpos += n;

		sprintf(sbuff, "[5;12H%ld     ", Txpos);
		ZmodemStatPrint(sbuff);
		sprintf(sbuff, "[6;12H%ld%%  ", (ZModemInfo.Filesize?((100*Txpos)/ZModemInfo.Filesize):0));
		ZmodemStatPrint(sbuff);
		itp(Txpos-STpos,0);
		ZModemInfo.RecPos=Txpos;

		if(blklen < maxblklen && ++goodblks > goodneeded)
		{
			blklen = (blklen<<1 < maxblklen) ? blklen<<1 : maxblklen;
			goodblks = 0;
			goodneeded = (goodneeded>>1 > 1) ? goodneeded>>1 : 1;
		}

		if (e == ZCRCW)
		{
			waitwrite();
			goto waitack;
		}

	} while(e == ZCRCG);

	waitwrite();

	for (;;)
	{
		if(check_abort()) return ERROR;

		stohdr(Txpos);
		zsbhdr(ZEOF, Txhdr);
		switch(getinsync())
		{
			case ZACK:
				continue;
			case ZRPOS:
				ZErrDisplay("ERROR Received");
				goto somemore;
			case ZRINIT:
				throughput(Txpos-STpos);
				return OK;
			case ZSKIP:
				return c;
			default:
				return ERROR;
		}
	}
}

/*
 * Respond to receiver's complaint, get back in sync with receiver
 */
static int getinsync(void)
{
	register int c, n;

	for (n = 5; --n>=0;)
	{
		c = zgethdr(Rxhdr);
		purgeline();
		switch(c)
		{
			case ZCAN:
			case ZABORT:
			case ZFIN:
				return ERROR;
			case ZRPOS:
				/* ************************************* */
				/*  If sending to a buffered modem, you  */
				/*   might send a break at this point to */
				/*   dump the modem's buffer.		 */
				Seek(fh, Rxpos, OFFSET_BEGINNING);
				Txpos = Rxpos;
				return c;
			case ZACK:
				if (Txpos == Rxpos)
					return c;
				continue;
			case ZRINIT:
			case ZSKIP:
				return c;
			case TIMEOUT:
				zsrCDstat=CheckCarrier();
				if(zsrCDstat==FALSE)		return ERROR;
				continue;
			case ERROR :
			default:
				zsbhdr(ZNAK, Txhdr);
				continue;
		}
	}
	return ERROR;
}

/* Say "bibi" to the receiver, try to do it cleanly */
static int saybibi(void)
{
	int n;

	for (n = 5; --n>=0;)
	{
		stohdr(0L);		/* CAF Was zsbhdr - minor change */
		zshhdr(ZFIN, Txhdr);	/*  to make debugging easier */
		switch (zgethdr(Rxhdr))
		{
			case ZFIN:
				sendbyte('O'); sendbyte('O');	return OK;
			case ZCAN:			return ERROR;
			case TIMEOUT:
				continue;
		}
	}
	return ERROR;
}
int startzmodem(char *fname,int flg)
{
 int x;
 transfering = TRUE;
 if(flg){
//     return(zmodemsend(fname));      /* do zmodem send    */
       x = zmodemsend(fname);      /* do zmodem send    */

 } else   {
//     return(zmodemreceive(fname));   /* do zmodem receive */
//(RTS) added to stop that back flow bullshit
       x = zmodemreceive(fname);   /* do zmodem receive */
//       return(x);
 }
/*if(!CheckCarrier())
{
   DropDTR(); Delay(25L); IntDoReset(Sopt->OffHook); Delay(5L);
}*/
transfering=FALSE;
return(x);
}
