You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
729 lines
19 KiB
C
729 lines
19 KiB
C
/*
|
|
* binkleyforce -- unix FTN mailer project
|
|
*
|
|
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "confread.h"
|
|
#include "logger.h"
|
|
#include "util.h"
|
|
#include "io.h"
|
|
#include "session.h"
|
|
#include "prot_common.h"
|
|
#include "prot_zmodem.h"
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* States of our Zmodem sending protocol */
|
|
/* ------------------------------------------------------------------------- */
|
|
typedef enum ztxstates {
|
|
ZTX_START, /* Start sending (init, send "rz\r") */
|
|
ZTX_RQINIT, /* Send ZRQINIT frame */
|
|
ZTX_RQINITACK, /* Wait for answer on ZRQINIT frame */
|
|
ZTX_NEXTFILE, /* Prepare next file for sending */
|
|
ZTX_FINFO, /* Send file's ZFINFO frame with data */
|
|
ZTX_FINFOACK, /* Wait for answer on our ZFINFO + .. */
|
|
ZTX_STARTDATA, /* Start sending of data (send ZDATA?) */
|
|
ZTX_DATA, /* Send new portion of data blocks */
|
|
ZTX_READCHECK, /* Check - we got some chars */
|
|
ZTX_CRCWACK, /* Wait for CRCW ack */
|
|
ZTX_CRCQACK, /* Wait for CRCQ ack */
|
|
ZTX_EOF, /* Send ZEOF frame with our file length */
|
|
ZTX_EOFACK, /* Wait for answer on our ZEOF */
|
|
ZTX_FIN, /* Send ZFIN to finish sending */
|
|
ZTX_FINACK /* Wait for answer on sent ZFIN */
|
|
} e_ztxstates;
|
|
|
|
static void zmodem_add_empty_packet(s_protinfo *pi)
|
|
{
|
|
struct stat st;
|
|
s_filelist **ptrl;
|
|
s_packet pkt;
|
|
char tmpname[] = "/tmp/bfXXXXXX";
|
|
char *p_tmpname;
|
|
|
|
if( (p_tmpname = mktemp(tmpname)) == NULL )
|
|
{
|
|
logerr("cannot generate temp. file name for packet from \"%s\"", tmpname);
|
|
return;
|
|
}
|
|
|
|
memset(&pkt, '\0', sizeof(s_packet));
|
|
|
|
pkt.dest = state.node.addr;
|
|
pkt.orig = *state.handshake->remote_address(state.handshake);
|
|
|
|
if( pkt_createpacket(p_tmpname, &pkt) )
|
|
{
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
logerr("cannot create packet for address %s",
|
|
ftn_addrstr(abuf, pkt.dest));
|
|
return;
|
|
}
|
|
|
|
if( stat(p_tmpname, &st) )
|
|
{
|
|
logerr("cannot stat created packet \"%s\"", p_tmpname);
|
|
unlink(p_tmpname);
|
|
return;
|
|
}
|
|
|
|
for( ptrl = &pi->filelist; *ptrl; ptrl = &((*ptrl)->next) )
|
|
{ /* EMPTY LOOP */ }
|
|
|
|
(*ptrl) = (s_filelist *)xmalloc(sizeof(s_filelist));
|
|
memset(*ptrl, '\0', sizeof(s_filelist));
|
|
(*ptrl)->fname = xstrcpy(p_tmpname);
|
|
(*ptrl)->type = TYPE_NETMAIL;
|
|
(*ptrl)->action = ACTION_FORCEUNLINK;
|
|
(*ptrl)->size = st.st_size;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Send files with Z-Modem protocol */
|
|
/* Files to transfer stored in pi->syslist structure */
|
|
/* Return: */
|
|
/* PRC_SUCCESSFUL: All files received or transfer was finished cleanly */
|
|
/* PRC_ERROR: Error at sending something(include carrier lost) */
|
|
/* PRC_ERROR: Error at receiving something(include carrier lost) */
|
|
/* PRC_MERROR: Misc. errors (i.e. incompatibilies, failures, etc) */
|
|
/* PRC_TRIESOUT: Very many tries doing something without result */
|
|
/* PRC_REMOTEDEAD: For a long time no real information transfered */
|
|
/* PRC_REMOTEABORT: Transfer was aborted by remote (i.e. five CAN, etc..) */
|
|
/* PRC_LOCALBORT: Transfer was aborted by local (i.e. CPS too low, etc..) */
|
|
/* ------------------------------------------------------------------------- */
|
|
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 && pi->send->fp )
|
|
p_tx_fclose(pi);
|
|
txtries = 0;
|
|
txstate = p_tx_fopen(pi) ? ZTX_FIN : ZTX_FINFO;
|
|
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! */
|
|
/* 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)
|
|
&& fseek(pi->send->fp, Z_Rxpos, 0) )
|
|
{
|
|
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 )
|
|
{
|
|
/* 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 && pi->send->fp )
|
|
p_tx_fclose(pi);
|
|
|
|
if( txbuf )
|
|
free(txbuf);
|
|
|
|
return(rc);
|
|
}
|
|
|