Exemple #1
0
/*
 * Receive a file with ZMODEM protocol
 *  Assumes file name frame is in secbuf
 */
static int rzfile(void)
{
	int c, n;
	long last_rxbytes = 0;
	long not_printed = 0;

	last_bps = 0;
	Eofseen = FALSE;

	n = 20;
	rxbytes = 0l;

	if (procheader(secbuf) == ERROR) {
		return (tryzhdrtype = ZSKIP);
	}


	for (;;) {
		stohdr(rxbytes);
		zshhdr(ZRPOS, Txhdr);
	      nxthdr:
		switch (c = zgethdr(Rxhdr, 0)) {
		default:
			vfile("lrzfile: zgethdr returned %d", c);
			return ERROR;
		case ZNAK:
		case TIMEOUT:
			if (--n < 0) {
				vfile("lrzfile: zgethdr returned %d", c);
				return ERROR;
			}
		case ZFILE:
			zrdata(secbuf, MAX_BLOCK);
			continue;
		case ZEOF:
			if (rclhdr(Rxhdr) != rxbytes) {
				/*
				 * Ignore eof if it's at wrong place - force
				 *  a timeout because the eof might have gone
				 *  out before we sent our zrpos.
				 */
				errors = 0;
				goto nxthdr;
			}
			if (Verbose > 1) {
				int minleft = 0;
				int secleft = 0;
				last_bps = (rxbytes / timing(0));
				if (last_bps > 0) {
					minleft =
					    (Bytesleft -
					     rxbytes) / last_bps / 60;
					secleft =
					    ((Bytesleft - rxbytes) /
					     last_bps) % 60;
				}
				fprintf(stderr,
					"\rBytes Received: %7ld/%7ld   BPS:%-6d                   \r\n",
					rxbytes, Bytesleft, last_bps);
			}
			closeok = 1;
			if (closeit()) {
				tryzhdrtype = ZFERR;
				vfile("lrzfile: closeit returned <> 0");
				return ERROR;
			}
			vfile("lrzfile: normal EOF");
			return c;
		case ERROR:	/* Too much garbage in header search error */
			if (--n < 0) {
				vfile("lrzfile: zgethdr returned %d", c);
				return ERROR;
			}
			zmputs(Attn);
			continue;
		case ZSKIP:
			closeit();
			vfile("lrzfile: Sender SKIPPED file");
			return c;
		case ZDATA:
			if (rclhdr(Rxhdr) != rxbytes) {
				if (--n < 0) {
					return ERROR;
				}
				zmputs(Attn);
				continue;
			}
		      moredata:
			if (Verbose > 1
			    && (not_printed > 7
				|| rxbytes >
				last_bps / 2 + last_rxbytes)) {
				int minleft = 0;
				int secleft = 0;
				last_bps = (rxbytes / timing(0));
				if (last_bps > 0) {
					minleft =
					    (Bytesleft -
					     rxbytes) / last_bps / 60;
					secleft =
					    ((Bytesleft - rxbytes) /
					     last_bps) % 60;
				}
				fprintf(stderr,
					"\rBytes Received: %7ld/%7ld   BPS:%-6d ETA %02d:%02d  ",
					rxbytes, Bytesleft, last_bps,
					minleft, secleft);
				last_rxbytes = rxbytes;
				not_printed = 0;
				if (nodeinf != -1 && ((time(0) - lup) > 5)) {
					lseek(nodeinf, 0, SEEK_SET);
					nin.ddn_bpsrate = last_bps;
					sprintf(nin.ddn_activity,
						"UL: %-34.34s", myname);
					write(nodeinf, &nin,
					      sizeof(struct
						     DayDream_NodeInfo));
					lup = time(0);
				}
			} else if (Verbose)
				not_printed++;
			switch (c = zrdata(secbuf, MAX_BLOCK)) {
			case ZCAN:
				vfile("lrzfile: zgethdr returned %d", c);
				return ERROR;
			case ERROR:	/* CRC error */
				if (--n < 0) {
					vfile
					    ("lrzfile: zgethdr returned %d",
					     c);
					return ERROR;
				}
				zmputs(Attn);
				continue;
			case TIMEOUT:
				if (--n < 0) {
					vfile
					    ("lrzfile: zgethdr returned %d",
					     c);
					return ERROR;
				}
				continue;
			case GOTCRCW:
				n = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				stohdr(rxbytes);
				zshhdr(ZACK, Txhdr);
				xsendline(XON);
				goto nxthdr;
			case GOTCRCQ:
				n = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				stohdr(rxbytes);
				zshhdr(ZACK, Txhdr);
				goto moredata;
			case GOTCRCG:
				n = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				goto moredata;
			case GOTCRCE:
				n = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				goto nxthdr;
			}
		}
	}
}
Exemple #2
0
/*
 * 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)
{
	int c, n;
	int cmdzack1flg;

	for (n = Zmodem ? 15 : 5; --n >= 0;) {
		/* Set buffer length (0) and capability flags */
		stohdr(0L);
		Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO | CANBRK;
		if (Zctlesc)
			Txhdr[ZF0] |= TESCCTL;	/* TESCCTL == ESCCTL */
		zshhdr(tryzhdrtype, Txhdr);
		if (tryzhdrtype == ZSKIP)	/* Don't skip too far */
			tryzhdrtype = ZRINIT;	/* CAF 8-21-87 */
	      again:
		switch (zgethdr(Rxhdr, 0)) {
		case ZRQINIT:
			continue;
		case ZEOF:
			continue;
		case TIMEOUT:
			continue;
		case ZFILE:
			zconv = Rxhdr[ZF0];
			zmanag = Rxhdr[ZF1];
			ztrans = Rxhdr[ZF2];
			tryzhdrtype = ZRINIT;
			c = zrdata(secbuf, MAX_BLOCK);
			mode(3);
			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(getfree());
			zshhdr(ZACK, Txhdr);
			goto again;
		case ZCOMMAND:
			cmdzack1flg = Rxhdr[ZF0];
			if (zrdata(secbuf, MAX_BLOCK) == GOTCRCW) {
				if (Verbose) {
					fprintf(stderr,
						"lrz: remote requested command\n");
					fprintf(stderr, "lrz: %s\n",
						secbuf);
				}
				if (Verbose)
					fprintf(stderr,
						"lrz: not executed\n");
				zshhdr(ZCOMPL, Txhdr);
				return ZCOMPL;
			}
			goto again;
		case ZCOMPL:
			goto again;
		default:
			continue;
		case ZFIN:
			ackbibi();
			return ZCOMPL;
		case ZCAN:
			return ERROR;
		}
	}
	return 0;
}
Exemple #3
0
/*
 * 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: can be ymodem.
 */
int tryz(void)
{
    int	    c, n;
    int	    cmdzack1flg;

    if (protocol != ZM_ZMODEM)
	return 0;

    for (n = zmodem_requested ?15:10; --n >= 0; ) {
	/*
	 * Set buffer length (0) and capability flags
	 */
	Syslog('z', "tryz attempt %d", n);
	stohdr(0L);
	Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
	if (Zctlesc)
	    Txhdr[ZF0] |= TESCCTL;
	zshhdr(tryzhdrtype, Txhdr);
	if (tryzhdrtype == ZSKIP)       /* Don't skip too far */
	    tryzhdrtype = ZRINIT;	/* CAF 8-21-87 */
again:
	switch (zgethdr(Rxhdr)) {
	    case ZRQINIT:   continue;
	    case ZEOF:	    continue;
	    case TIMEOUT:   Syslog('z', "Zmodem: tryz() timeout attempt %d", n);
			    continue;
	    case ZFILE:	    zconv = Rxhdr[ZF0];
			    if (!zconv) {
				Syslog('z', "*** !zconv %d", zconv);
				zconv = ZCBIN;
			    }
			    zmanag = Rxhdr[ZF1];
			    ztrans = Rxhdr[ZF2];
			    tryzhdrtype = ZRINIT;
			    c = zrdata(secbuf, MAXBLOCK);
			    io_mode(0, 3);
			    if (c == GOTCRCW) {
				Syslog('z', "tryz return ZFILE");
				return ZFILE;
			    }
			    zshhdr(ZNAK, Txhdr);
			    goto again;
	    case ZSINIT:    /* this once was:
			     * Zctlesc = TESCCTL & Rxhdr[ZF0];
			     * trouble: if rz get --escape flag:
			     * - it sends TESCCTL to sz, 
			     *   get a ZSINIT _without_ TESCCTL (yeah - sender didn't know), 
			     *   overwrites Zctlesc flag ...
			     * - sender receives TESCCTL and uses "|=..."
			     * so: sz escapes, but rz doesn't unescape ... not good.
			     */
			    Zctlesc |= TESCCTL & Rxhdr[ZF0];
			    if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
				stohdr(1L);
				zshhdr(ZACK, Txhdr);
				goto again;
			    }
			    zshhdr(ZNAK, Txhdr);
			    goto again;
	    case ZFREECNT:  stohdr(getfree());
			    zshhdr(ZACK, Txhdr);
			    goto again;
	    case ZCOMMAND:  cmdzack1flg = Rxhdr[ZF0];
			    if (zrdata(secbuf, MAXBLOCK) == GOTCRCW) {
				if (cmdzack1flg & ZCACK1)
				    stohdr(0L);
				else
				    Syslog('+', "Zmodem: request for command \"%s\" ignored", printable(secbuf,-32));
				stohdr(0L);
				do {
				    zshhdr(ZCOMPL, Txhdr);
				} while (++errors<20 && zgethdr(Rxhdr) != ZFIN);
				return ackbibi();
			    }
			    zshhdr(ZNAK, Txhdr); 
			    goto again;
	    case ZCOMPL:    goto again;
	    case ZRINIT:    Syslog('z', "tryz: got ZRINIT");
			    return TERROR;
	    case ZFIN:	    /* do not beleive in first ZFIN */
			    ackbibi(); 
			    return ZCOMPL;
	    case TERROR:
	    case HANGUP:
	    case ZCAN:	    return TERROR;
	    default:	    continue;
	}
    }
    return 0;
}
Exemple #4
0
/*
 * Receive a file with ZMODEM protocol
 *  Assumes file name frame is in secbuf
 */
int rzfile(void)
{
    int	c, n;

    Eofseen=FALSE;
    rxbytes = 0l;
    if ((c = procheader(secbuf))) {
	return (tryzhdrtype = c);
    }

    n = 20;

    for (;;) {
	stohdr(rxbytes);
	zshhdr(ZRPOS, Txhdr);
nxthdr:
	switch (c = zgethdr(Rxhdr)) {
	    default:	    Syslog('z', "rzfile: Wrong header %d", c);
			    if ( --n < 0) {
				Syslog('+', "Zmodem: wrong header %d", c);
				return TERROR;
			    }
			    continue;
	    case ZCAN:	    Syslog('+', "Zmodem: sender CANcelled");
			    return TERROR;
	    case ZNAK:	    if ( --n < 0) {
				Syslog('+', "Zmodem: Got ZNAK");
				return TERROR;
			    }
			    continue;
	    case TIMEOUT:   if ( --n < 0) {
				Syslog('z', "Zmodem: TIMEOUT");
				return TERROR;
			    }
			    continue;
	    case ZFILE:	    zrdata(secbuf, MAXBLOCK);
			    continue;
	    case ZEOF:	    if (rclhdr(Rxhdr) != rxbytes) {
				/*
				 * Ignore eof if it's at wrong place - force
				 *  a timeout because the eof might have gone
				 *  out before we sent our zrpos.
				 */
				errors = 0;
				goto nxthdr;
			    }
			    if (closeit(1)) {
				tryzhdrtype = ZFERR;
				Syslog('+', "Zmodem: error closing file");
				return TERROR;
			    }
			    fout = NULL;
			    Syslog('z', "rzfile: normal EOF");
			    return c;
	    case HANGUP:    Syslog('+', "Zmodem: Lost Carrier");
			    return TERROR;
	    case TERROR:    /* Too much garbage in header search error */
			    if (--n < 0) {
				Syslog('+', "Zmodem: Too many errors");
				return TERROR;
			    }
			    zmputs(Attn);
			    continue;
	    case ZSKIP:	    Modtime = 1;
			    closeit(1);
			    Syslog('+', "Zmodem: Sender SKIPPED file");
			    return c;
	    case ZDATA:	    if (rclhdr(Rxhdr) != rxbytes) {
				if ( --n < 0) {
				    Syslog('+', "Zmodem: Data has bad address");
				    return TERROR;
				}
				zmputs(Attn);  
				continue;
			    }
moredata:
			    Nopper();
			    alarm_on();
			    switch (c = zrdata(secbuf, MAXBLOCK)) {
				case ZCAN:	Syslog('+', "Zmodem: sender CANcelled");
						return TERROR;
				case HANGUP:	Syslog('+', "Zmodem: Lost Carrier");
						return TERROR;
				case TERROR:	/* CRC error */
						if (--n < 0) {
						    Syslog('+', "Zmodem: Too many errors");
						    return TERROR;
						}
						zmputs(Attn);
						continue;
				case TIMEOUT:	if ( --n < 0) {
						    Syslog('+', "Zmodem: TIMEOUT");
						    return TERROR;
						}
						continue;
				case GOTCRCW:	n = 20;
						putsec(secbuf, Rxcount);
						rxbytes += Rxcount;
						stohdr(rxbytes);
						PUTCHAR(XON);
						zshhdr(ZACK, Txhdr);
						goto nxthdr;
				case GOTCRCQ:	n = 20;
						putsec(secbuf, Rxcount);
						rxbytes += Rxcount;
						stohdr(rxbytes);
						zshhdr(ZACK, Txhdr);
						goto moredata;
				case GOTCRCG:	n = 20;
						putsec(secbuf, Rxcount);
						rxbytes += Rxcount;
						goto moredata;
				case GOTCRCE:	n = 20;
						putsec(secbuf, Rxcount);
						rxbytes += Rxcount;
						goto nxthdr;
			    }
	}
    }
}
Exemple #5
0
/* ------------------------------------------------------------------------- */
int tx_zmodem(s_protinfo *pi, bool caller)
{
	int   startblk  = 64;  /* Initial Zmodem block size                  */
	int   minblk    = 64;  /* Minimal Z-protocol block size              */
	int   maxblk    = 1024;/* Maximal Z-protocol block size              */
	int   blocklen  = 0;   /* Length of transmitted blocks               */
	int   goodblk   = 0;   /* How many blocks we sent w/o ZRPOS'tion :)  */
	int   txwindow  = 0;   /* Tranmitter window size (0 means streaming) */
	int   newcnt    = 0;   /* Count free bytes in receiver's buffer      */
	int   rxbuflen  = 0;   /* Receiver's max buffer length               */
	int   rxlastpos = 0;   /* Receiver's last reported offset            */
	int   beenhere  = 0;   /* How many times we've been ZRPOS'd same place */
	long  bytescnt  = 0;   /* Received bytes(current offset)             */
	long  lastsync  = 0;   /* Last offset to which we got a ZRPOS        */
	char  zconv     = 0;   /* Local ZMODEM file conversion request       */
	char  zmanag    = 0;   /* Local ZMODEM file management request       */
	char  ztrans    = 0;   /* Local ZMODEM file translation request      */
	char  zexten    = 0;   /* Local ZMODEM file extended options         */
	char *txbuf     = NULL;/* Buffer with ZMAXBLOCKLEN size              */
	int   zrinitcnt = 0;   /* Count received ZRINITs                     */
	int   rxflags1  = 0;
	int   rxflags2  = 0;
	int   txtries   = 0;
	int   junkcnt   = 0;
	int   initacked = 0;   /* TRUE when at least one ZRQINIT was sent    */
	                       /* after first ZRINIT was received            */
	int   rc        = 0;   /* Our return code                            */
	int   dtype, n;
	int   ftype;
	char  c, *p;
	long unsigned crc32;
	enum  ztxstates txstate;
	time_t deadtimer;
	
	log("start %s send", Protocols[state.handshake->protocol]);
	DEB((D_PROT, "start %s send", Protocols[state.handshake->protocol]));
	
	/* Set time transfer started at */
	if( pi->start_time == 0 )
		pi->start_time = time(NULL);
	
	txbuf      = (char *)xmalloc(ZMAXBLOCKLEN+1);
	zconv      = ZCBIN;
	maxblk     = (state.handshake->protocol == PROT_ZMODEM) ? 1024 : 8192;
	
	/* Set initial block size (default is 128b) */
	if( (startblk = conf_number(cf_zmodem_start_block_size)) > 0 )
	{
		if( startblk%64 || startblk > maxblk || startblk < 64 )
			startblk = 256;
	} else 
		startblk = 256;
	
	blocklen  = startblk;
	txwindow  = conf_number(cf_zmodem_tx_window);
	Z_Rxwait  = ZWAITTIME;
	Z_Rxtout  = ZRXTIMEOUT;
	txstate   = ZTX_START;

	timer_set(&deadtimer, ZDEADTIMER);
	
	setalarm(Z_Rxtout);
	
	/*
	 * At zmodem batches send empty netmail packet
	 * if no real outgoing traffic available
	 */
	if( !pi->send_left_size && conf_boolean(cf_zmodem_send_dummy_pkt) )
		zmodem_add_empty_packet(pi);
	
	while(1)
	{
		if( timer_expired(deadtimer) )
		{
			log("brain dead! (abort)");
			gotoexit(PRC_LOCALABORTED);
		}
		
		if( txstate == ZTX_RQINIT || txstate == ZTX_FINFO
		 || txstate == ZTX_EOF    || txstate == ZTX_FIN )
		{
#ifdef DEBUG
			if( txtries ) DEB((D_PROT, "tx_zmodem: try #%d", txtries));
#endif			
			if( ++txtries > ZMAXTRIES )
			{
				log("out of tries");
				gotoexit(PRC_LOCALABORTED);
			}
		}
		
		switch(txstate) {
		case ZTX_START:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_START"));
			if( PUTSTR("rz\r") < 0 )
				gotoexit(PRC_ERROR);
			txtries = 0;
			txstate = ZTX_RQINIT;
			break;
			
		case ZTX_RQINIT:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_RQINIT"));
			stohdr(Z_Txhdr, 0L);
			if( zshhdr(ZRQINIT, Z_Txhdr) < 0 )
				gotoexit(PRC_ERROR);
			setalarm(Z_Rxtout);
			txstate = ZTX_RQINITACK;
			break;
			
		case ZTX_NEXTFILE:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_NEXTFILE"));
			if (pi->send) p_tx_fclose(pi);
			txtries = 0;
			txstate = p_tx_fopen(pi, NULL) ? ZTX_FIN : ZTX_FINFO;
			log("nextfile next state: %d", txstate);
			break;

		case ZTX_FINFO:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_FINFO"));
			
			zrinitcnt = 0;
			
			strnxcpy(txbuf, pi->send->net_name, ZMAXFNAME);
			
			p = txbuf + strlen(txbuf) + 1; 
			sprintf(p, "%ld %lo %lo 0 %ld %ld",
				(long)pi->send->bytes_total, (long)pi->send->mod_time,
				(long)pi->send->mode, (long)pi->send_left_num,
				(long)pi->send_left_size);
			
			DEB((D_PROT, "tx_zmodem: send \"%s\\000%s\"", txbuf, p));
			
			Z_Txhdr[ZF0] = zconv;	/* file conversion request */
			Z_Txhdr[ZF1] = zmanag;  /* file management request */
			Z_Txhdr[ZF2] = ztrans;  /* file transport request  */
			Z_Txhdr[ZF3] = zexten;
			
			if( zsbhdr(ZFILE, Z_Txhdr) < 0 )
				gotoexit(PRC_ERROR);
			if( zsdata(txbuf, (p - txbuf) + strlen(p), ZCRCW, 0) < 0 )
				gotoexit(PRC_ERROR);
			
			setalarm(Z_Rxtout);
			txstate = ZTX_FINFOACK;
			break;
			
		case ZTX_STARTDATA:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_STARTDATA"));
			
			newcnt   = rxbuflen;
			junkcnt  = 0;
			
			stohdr(Z_Txhdr, pi->send->bytes_sent);
			if( zsbhdr(ZDATA, Z_Txhdr) < 0 )
				gotoexit(PRC_ERROR);
			
			txstate = ZTX_DATA;
			break;
			
		case ZTX_DATA:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_DATA"));
			
			timer_set(&deadtimer, ZDEADTIMER);
			setalarm(Z_Rxtout); /* Remove annoing timeouts! */
			
			if( (n = p_tx_readfile(txbuf, blocklen, pi)) < 0 )
			{
				/* error occured, remote wait for DATA */
				/* so send null ZCRCE data subpacket   */
				if( zsdata(txbuf, 0, ZCRCE, 0) < 0 )
					gotoexit(PRC_ERROR);
				txstate = ZTX_NEXTFILE;
				break;
			}
		
			if( pi->send->eofseen )
				dtype = ZCRCE;
			else if( junkcnt > 6 )
				dtype = ZCRCW;
			else if( bytescnt == lastsync )
				dtype = ZCRCW;
			else if( rxbuflen && (newcnt -= n) <= 0 )
				dtype = ZCRCW;
			else if( txwindow && (bytescnt - rxlastpos + n) >= txwindow )
				dtype = ZCRCQ;
			else
				dtype = ZCRCG;

			if( (rc = p_info(pi, 0)) )
				gotoexit(rc);
			
			if( zsdata(txbuf, n, dtype, pi->send->bytes_sent) < 0 )
				gotoexit(PRC_ERROR);
			
			if( ++goodblk > 5 && blocklen*2 <= maxblk )
			{
				goodblk = 0;
				blocklen *= 2;
				DEB((D_PROT, "tx_zmodem: new blocklen = %ld byte(s)", blocklen));
			}
			
			bytescnt = pi->send->bytes_sent += n;
			
			if( dtype == ZCRCW )
			{
				junkcnt = 0;
				setalarm(Z_Rxtout);
				txstate = ZTX_CRCWACK;
				break;
			}
			else if( dtype == ZCRCQ )
			{
				junkcnt = 0;
				setalarm(Z_Rxtout);
				txstate = ZTX_CRCQACK;
				break;
			}
			else if( dtype == ZCRCE )
			{
				txtries = 0;
				txstate = ZTX_EOF;
				break;
			}
			
			if( CHARWAIT(0) )
			{
				while( (rc = GETCHAR(1)) != ZTIMER )
				{
					if( rc < 0 )
					{
						gotoexit(PRC_ERROR);
					}
					else if( rc == CAN || rc == ZPAD )
					{
						DEB((D_PROT, "tx_zmodem: got ZPAD or CAN!"));
						setalarm(Z_Rxtout);
						txstate = ZTX_READCHECK;
						break;
					}
					else if( rc == XOFF || rc == (XOFF|0200) )
					{
						DEB((D_PROT, "tx_zmodem: got XOFF"));
						if( GETCHAR(5) < 0 )
							gotoexit(PRC_ERROR);
						break;
					}
					else if( rc == XON  || rc == (XON|0200) )
					{
						DEB((D_PROT, "tx_zmodem: got XON"));
					}
					else
					{
						junkcnt++;
						DEB((D_PROT, "tx_zmodem: got JUNK = 0x%x (junkcnt = %d)",
							rc, junkcnt));
					}
				} /* end of while( rc != ZTIMER ) */
			} /* end of if( CHARWAIT(0) ) */
			break;
		
		case ZTX_EOF:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_EOF"));
			
			stohdr(Z_Txhdr, pi->send->bytes_sent);
			if( zsbhdr(ZEOF, Z_Txhdr) < 0 )
				gotoexit(PRC_ERROR);
			
			setalarm(Z_Rxtout);
			txstate = ZTX_EOFACK;
			break;
			
		case ZTX_FIN:
			DEB((D_PROT, "tx_zmodem: entering state ZTX_FIN"));
			
			stohdr(Z_Txhdr, 0L);
			if( zshhdr(ZFIN, Z_Txhdr) < 0 )
				gotoexit(PRC_ERROR);
			
			setalarm(Z_Rxtout);
			txstate = ZTX_FINACK;
			break;
			
		default:
			/* Ignore them all */
			break;
		} /* end of switch(txstate) */
	
		if( txstate != ZTX_START  && txstate != ZTX_RQINIT
		 && txstate != ZTX_FINFO  && txstate != ZTX_DATA
		 && txstate != ZTX_EOF    && txstate != ZTX_FIN )
		{
			switch( ftype = zgethdr(Z_Rxhdr) ) {
			case ZCAN:
				gotoexit(PRC_REMOTEABORTED);
				break;
				
			case ZHANGUP:
			case ZEXIT:
				gotoexit(PRC_ERROR);
				break;
				
			case ZTIMER:
				log("time out");
				
				if( txstate == ZTX_READCHECK )
					zsdata(txbuf, 0, ZCRCE, 0);
				
				switch(txstate) {
				case ZTX_RQINITACK: txstate = ZTX_RQINIT;    break;
				case ZTX_FINFOACK:  txstate = ZTX_FINFO;     break;
				case ZTX_READCHECK: txstate = ZTX_STARTDATA; break;
				case ZTX_CRCWACK:   txstate = ZTX_STARTDATA; break;
				case ZTX_CRCQACK:   txstate = ZTX_STARTDATA; break;
				case ZTX_EOFACK:    txstate = ZTX_EOF;       break;
				case ZTX_FINACK:    txstate = ZTX_FIN;       break;
				default:            break;
				}
				break;
				
			case ZERROR:
			case ZCRCERR:
				/* NAK them all! */
				stohdr(Z_Txhdr, 0L);
				if( zshhdr(ZNAK, Z_Txhdr) < 0 )
					gotoexit(PRC_ERROR);
				break;
				
			case ZRQINIT:
				if( txstate == ZTX_RQINITACK )
				{
					if( Z_Rxhdr[0] == ZCOMMAND )
						break;
					
					stohdr(Z_Txhdr, 0L);
					if( zshhdr(ZNAK, Z_Txhdr) < 0 )
						gotoexit(PRC_ERROR);
					
					txstate = ZTX_RQINIT;
				}
				else if( txstate == ZTX_FINFOACK )
				{
					/* remote is sender - abort */
					log("zmodem: remote is sender");
					gotoexit(PRC_LOCALABORTED);
				}
				break;
				
			case ZRINIT:
				if( txstate == ZTX_RQINITACK )
				{
					if( initacked == 0 )
					{
						/* Be sure ack first ZRINIT */
						stohdr(Z_Txhdr, 0L);
						if( zshhdr(ZRQINIT, Z_Txhdr) < 0 )
							gotoexit(PRC_ERROR);
						initacked = 1;
					}
					
					/* Get receiver's options */
					rxflags1  = (0377 & Z_Rxhdr[ZF0]);
					rxflags2  = (0377 & Z_Rxhdr[ZF1]);
					Z_Txfcs32 = (rxflags1 & CANFC32);
					Z_Ctlesc |= (rxflags1 & TESCCTL);
					rxbuflen  = (0377 & Z_Rxhdr[ZP0]);
					rxbuflen += ((0377 & Z_Rxhdr[ZP1])<<8);
					
					/* No ZCRCQ if remote doesn't indicate */
					/* FDX ability                         */ 
					if( !(rxflags1 & CANFDX) )
						txwindow = 0;
				
					DEB((D_PROT, "tx_zmodem: Z_Txfcs32 = %d Z_Ctlesc = %d",
						Z_Txfcs32, Z_Ctlesc));
					DEB((D_PROT, "tx_zmodem: rxbuflen = %d blocklen = %d",
						rxbuflen, blocklen));
					DEB((D_PROT, "tx_zmodem: txwindow = %u",
						txwindow));
				
					txstate   = ZTX_NEXTFILE;
				}
				else if( txstate == ZTX_FINFOACK )
				{
					/* Possible they didn't see */
					/* our file information     */
					if( ++zrinitcnt > 2 )
						txstate = ZTX_FINFO;
				}
				else if( txstate == ZTX_READCHECK
				      || txstate == ZTX_CRCQACK
				      || txstate == ZTX_CRCWACK )
				{
					if( txstate == ZTX_READCHECK
					 || txstate == ZTX_CRCQACK )
						zsdata(txbuf, 0, ZCRCE, 0);
					
					/* Assume file normaly sent ? */
					log("assume file normaly sent");
					
					pi->send->status = FSTAT_SUCCESS;
					txstate = ZTX_NEXTFILE;
				}
				else if( txstate == ZTX_EOFACK )
				{
					/* ok, send next */
					pi->send->status = FSTAT_SUCCESS;
					txstate = ZTX_NEXTFILE;
				}
				else if( txstate == ZTX_FINACK )
				{
					/* Possible we should ignore  */
					/* first ZRINIT. Because they */
					/* didn't see our first ZFIN  */
					/* But I'm soo lazy .. :))    */
					txstate = ZTX_FIN;
				}
				break;
				
			case ZACK:
				if( txstate == ZTX_CRCWACK )
				{
					rxlastpos = Z_Rxpos;
					if( pi->send->bytes_sent == Z_Rxpos )
						txstate = ZTX_STARTDATA;
				}
				else if( txstate == ZTX_READCHECK
				      || txstate == ZTX_CRCQACK )
				{
					rxlastpos = Z_Rxpos;
					txstate   = ZTX_DATA;
				}
				break;
				
			case ZSKIP:
				if( txstate == ZTX_FINFOACK
				 || txstate == ZTX_READCHECK
				 || txstate == ZTX_CRCQACK
				 || txstate == ZTX_CRCWACK
				 || txstate == ZTX_EOFACK )
				{
					if( txstate == ZTX_READCHECK
					 || txstate == ZTX_CRCQACK )
						zsdata(txbuf, 0, ZCRCE, 0);
					
					if( txstate == ZTX_READCHECK )
						CLEAROUT();
					
					pi->send->status = FSTAT_SKIPPED;
					log("remote side skipped file");
					
					txstate = ZTX_NEXTFILE;
				}
				break;
				
			case ZFIN:
				/* BUG!BUG!BUG!BUG!BUG!BUG!BUG!BUG!BUG! */
				/* BUG!BUG!BUG!BUG!BUG!BUG!BUG!BUG!BUG! */
				log(" BUG!BUG!BUG!BUG!BUG!BUG!BUG!BUG!BUG! ");
				if( txstate == ZTX_FINACK )
				{
					if( PUTSTR("OO") == 0 )
						FLUSHOUT();
					gotoexit(PRC_NOERROR);
				}
				break;
				
			case ZRPOS:
				if( txstate == ZTX_FINFOACK
				 || txstate == ZTX_READCHECK
				 || txstate == ZTX_CRCQACK
				 || txstate == ZTX_CRCWACK
				 || txstate == ZTX_EOFACK )
				{
					rxlastpos = Z_Rxpos;
					
					/* Clear modem buffers */
					/* if( txstate != FINFOACK ) SENDBREAK(); */
					if( txstate == ZTX_READCHECK ) CLEAROUT();

					if( txstate == ZTX_READCHECK
					 || txstate == ZTX_CRCQACK )
					{
						if( zsdata(txbuf, 0, ZCRCE, 0) < 0 )
							gotoexit(PRC_ERROR);
					}
					
					/* Reset EOF flag! */
					pi->send->eofseen = FALSE;
					
					/* Check pos */
					if( (Z_Rxpos || txstate != ZTX_FINFOACK)
					 && p_tx_rewind(pi, Z_Rxpos) )
					{
						logerr("can't send file from requested position");
						/* Open next file for send */
						txstate = ZTX_NEXTFILE;
						break;
					}
					
					if( txstate == ZTX_FINFOACK )
					{
						if( Z_Rxpos )
						{
							log("resyncing at offset %d", Z_Rxpos);
							pi->send->bytes_skipped = Z_Rxpos;
						}
					}
					else if( txstate == ZTX_READCHECK
					      || txstate == ZTX_CRCWACK
					      || txstate == ZTX_CRCQACK )
					{
						goodblk = 0;
						if( lastsync >= Z_Rxpos && ++beenhere > 4 )
							if( blocklen > minblk )
							{
								blocklen /= 2;
								DEB((D_PROT, "tx_zmodem: falldown to %ld BlockLen", blocklen));
							}
					}
					
					lastsync = bytescnt = pi->send->bytes_sent = Z_Rxpos;
					
					if( txstate == ZTX_FINFOACK )
						--lastsync;
					
					txstate = ZTX_STARTDATA;
				}
				break;
				
			case ZNAK:
				switch(txstate) {
				case ZTX_RQINITACK: txstate = ZTX_RQINIT; break;
				case ZTX_FINFOACK:  txstate = ZTX_FINFO;  break;
				case ZTX_EOFACK:    txstate = ZTX_EOF;    break;
				case ZTX_FINACK:    txstate = ZTX_FIN;    break;
				default:            break;
				}
				break;
				
			case ZCRC:
				if( txstate == ZTX_FINFOACK )
				{
					log(" Send file's CRC-32 ");
					crc32 = 0xFFFFFFFFL;
					
					while( ((c = getc(pi->send->fp)) != EOF) && --Z_Rxpos )
						crc32 = updcrc32(c, crc32);
					
					crc32 = ~crc32;
					
					clearerr(pi->send->fp); /* Clear EOF */
					fseek(pi->send->fp, 0L, 0);
					
					stohdr(Z_Txhdr, crc32);
					if( zsbhdr(ZCRC, Z_Txhdr) < 0 )
						gotoexit(PRC_ERROR);
				}
				break;
				
			case ZCHALLENGE:
				if( txstate == ZTX_RQINITACK )
				{
					/* Echo receiver's challenge number */
					stohdr(Z_Txhdr, Z_Rxpos);
					if( zshhdr(ZACK, Z_Txhdr) < 0 )
						gotoexit(PRC_ERROR);
					txstate = ZTX_RQINIT;
				}
				break;
				
			case ZCOMMAND:
				if( txstate == ZTX_RQINITACK )
				{
					txstate = ZTX_RQINIT;
				}
				break;

			case ZABORT:
				log("remote requested for session abort");
				stohdr(Z_Txhdr, 0L);
				if( zshhdr(ZFIN, Z_Txhdr) < 0 )
					gotoexit(PRC_ERROR);
				gotoexit(PRC_REMOTEABORTED);
				break;
				
			case ZFERR:
				if( txstate == ZTX_FINFOACK
				 || txstate == ZTX_READCHECK
				 || txstate == ZTX_CRCWACK
				 || txstate == ZTX_CRCQACK
				 || txstate == ZTX_EOFACK )
				{
					if( txstate == ZTX_READCHECK
					 || txstate == ZTX_CRCQACK )
					{
						if( zsdata(txbuf, 0, ZCRCE, 0) < 0 )
							gotoexit(PRC_ERROR);
					}

					pi->send->status = FSTAT_REFUSED;
					log("remote side refused file");
					
					txstate = ZTX_NEXTFILE;
				}
				break;
				
			default:
				log("got unexpected frame %d", ftype);
				break;
			} /* end of switch(hdr) */
		} /* end of if */
	} /* end of while */
	
exit:
	DEB((D_PROT, "tx_zmodem: SEND exit = %d", rc));
	
	setalarm(0);
	
	if (pi->send) p_tx_fclose(pi);
	
	if( txbuf ) {
		free(txbuf);
		txbuf = NULL;
	}
	
	
	return(rc);
}