diff --git a/src/sbbs3/zmodem.c b/src/sbbs3/zmodem.c index 0167cd9ecc21e976dac57d233a32970067638a18..d6d4ad042317712884dd13904057d1e965996e3e 100644 --- a/src/sbbs3/zmodem.c +++ b/src/sbbs3/zmodem.c @@ -77,6 +77,7 @@ static BOOL is_cancelled(zmodem_t* zm) return(zm->cancelled=zm->is_cancelled(zm->cbdata)); return(zm->cancelled); } + int zmodem_data_waiting(zmodem_t* zm, unsigned timeout) { if(zm->data_waiting) @@ -491,10 +492,15 @@ int zmodem_send_data16(zmodem_t* zm, uchar subpkt_type,unsigned char * p, size_t * send a data subpacket using crc 16 or crc 32 as desired by the receiver */ -int zmodem_send_data(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t l) +int zmodem_send_data_subpkt(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t l) { int result; + if(subpkt_type == ZCRCW || subpkt_type == ZCRCE) /* subpacket indicating 'end-of-frame' */ + zm->frame_in_transit=FALSE; + else /* other subpacket (mid-frame) */ + zm->frame_in_transit=TRUE; + if(!zm->want_fcs_16 && zm->can_fcs_32) { if((result=zmodem_send_data32(zm, subpkt_type,p,l))!=0) return result; @@ -510,6 +516,17 @@ int zmodem_send_data(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t return result; } +int zmodem_send_data(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t l) +{ + if(!zm->frame_in_transit) { /* Start of frame, include ZDATA header */ + lprintf(zm,LOG_DEBUG,"send_data: start of frame, offset %u" + ,zm->current_file_pos); + zmodem_send_pos_header(zm, ZDATA, zm->current_file_pos, /* Hex? */ FALSE); + } + + return zmodem_send_data_subpkt(zm, subpkt_type, p, l); +} + int zmodem_send_pos_header(zmodem_t* zm, int type, int32_t pos, BOOL hex) { uchar header[5]; @@ -561,9 +578,9 @@ int zmodem_send_zskip(zmodem_t* zm) return zmodem_send_pos_header(zm, ZSKIP, 0L, /* Hex? */ TRUE); } -int zmodem_send_zeof(zmodem_t* zm) +int zmodem_send_zeof(zmodem_t* zm, uint32_t pos) { - return zmodem_send_pos_header(zm, ZEOF, zm->current_file_size, /* Hex? */ TRUE); + return zmodem_send_pos_header(zm, ZEOF, pos, /* Hex? */ TRUE); } @@ -857,16 +874,13 @@ int zmodem_recv_data(zmodem_t* zm, unsigned char* p, size_t maxlen, unsigned* l, subpkt_type = zmodem_recv_data16(zm, p, maxlen, l); } - if(subpkt_type==FALSE) - return(FALSE); - - if(subpkt_type==TIMEOUT) - return(TIMEOUT); - + if(subpkt_type <= 0) /* e.g. TIMEOUT, FALSE */ + return(subpkt_type); + lprintf(zm,LOG_DEBUG,"recv_data received subpacket-type: %s" ,chr(subpkt_type)); - switch (subpkt_type) { + switch(subpkt_type) { /* * frame continues non-stop */ @@ -893,7 +907,7 @@ int zmodem_recv_data(zmodem_t* zm, unsigned char* p, size_t maxlen, unsigned* l, return ENDOFFRAME; } - lprintf(zm,LOG_WARNING,"Invalid subpacket-type: %s",chr(subpkt_type)); + lprintf(zm,LOG_WARNING,"Received invalid subpacket-type: %s", chr(subpkt_type)); return FALSE; } @@ -1391,13 +1405,12 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint32_t pos, uint32_t* sent) size_t n; uchar type; unsigned buf_sent=0; + unsigned subpkts_sent=0; if(sent!=NULL) *sent=0; - fseek(fp,pos,SEEK_SET); - - zmodem_send_pos_header(zm, ZDATA, pos, /* Hex? */ FALSE); + fseek(fp,zm->current_file_pos=pos,SEEK_SET); /* * send the data in the file @@ -1422,36 +1435,41 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint32_t pos, uint32_t* sent) CANOVIO bit, or sets a buffer size, the sender uses the ZCRCW to allow the receiver to write its buffer before sending more data. ***/ + /* Note: we always use ZCRCW for the first data subpacket of a frame */ if(!zm->can_overlap_io || zm->no_streaming - || (zm->recv_bufsize && buf_sent+n>=zm->recv_bufsize)) { + || (zm->recv_bufsize && buf_sent+n>=zm->recv_bufsize) + || subpkts_sent==0) { type=ZCRCW; buf_sent=0; } - if((uint32_t)ftell(fp) >= zm->current_file_size || n==0) /* can't use feof() here! */ + if(zm->current_file_pos >= zm->current_file_size || n==0) /* can't use feof() here! */ type = ZCRCE; + /* Note: No support for sending ZCRCQ data sub-packets here */ + if(zmodem_send_data(zm, type, zm->tx_data_subpacket, n)!=0) return(TIMEOUT); + zm->current_file_pos += n; + subpkts_sent++; + if(type == ZCRCW || type == ZCRCE) { - int ack; lprintf(zm,LOG_DEBUG,"Sent end-of-frame (%s sub-packet)", chr(type)); - if(type==ZCRCW) { /* ZACK expected */ - lprintf(zm,LOG_DEBUG,"Waiting for ZACK"); while(is_connected(zm)) { + int ack; if((ack = zmodem_recv_header(zm)) != ZACK) return(ack); if(is_cancelled(zm)) return(ZCAN); - if(zm->rxd_header_pos == (uint32_t)ftell(fp)) + if(zm->rxd_header_pos == zm->current_file_pos) break; lprintf(zm,LOG_WARNING,"ZACK for incorrect offset (%lu vs %lu)" - ,zm->rxd_header_pos, ftell(fp)); + ,zm->rxd_header_pos, zm->current_file_pos); } } @@ -1462,13 +1480,13 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint32_t pos, uint32_t* sent) buf_sent+=n; - if((uint32_t)ftell(fp) >= zm->current_file_size) { - lprintf(zm,LOG_DEBUG,"send_from: end of file (%ld)", zm->current_file_size ); + if(zm->current_file_pos >= zm->current_file_size) { + lprintf(zm,LOG_DEBUG,"send_from: end of file reached at offset %ld", zm->current_file_size); return ZACK; } if(n==0) { lprintf(zm,LOG_ERR,"send_from: read error %d at offset %lu" - ,ferror(fp), ftell(fp) ); + ,ferror(fp), zm->current_file_pos); return ZACK; } @@ -1479,16 +1497,22 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint32_t pos, uint32_t* sent) while(zmodem_data_waiting(zm, zm->consecutive_errors ? 1000:0) && !is_cancelled(zm) && is_connected(zm)) { - int type; + int rx_type; int c; lprintf(zm,LOG_DEBUG,"Back-channel traffic detected:"); if((c = zmodem_recv_raw(zm)) < 0) return(c); if(c == ZPAD) { - type = zmodem_recv_header(zm); - lprintf(zm,LOG_DEBUG,"Received header: %s",chr(type)); - if(type != TIMEOUT && type != ZACK) { - return type; + /* ZMODEM.DOC: + FULL STREAMING WITH SAMPLING + If one of these characters (CAN or ZPAD) is seen, an + empty ZCRCE data subpacket is sent. + */ + zmodem_send_data(zm, ZCRCE, NULL, 0); + rx_type = zmodem_recv_header(zm); + lprintf(zm,LOG_DEBUG,"Received back-channel header: %s", chr(rx_type)); + if(rx_type != TIMEOUT && rx_type != ZACK) { + return rx_type; } } else lprintf(zm,LOG_DEBUG,"Received: %s",chr(c)); @@ -1503,9 +1527,6 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint32_t pos, uint32_t* sent) if(zm->block_size > zm->max_block_size) zm->block_size = zm->max_block_size; } - - if(type == ZCRCW || type == ZCRCE) /* end-of-frame */ - zmodem_send_pos_header(zm, ZDATA, ftell(fp), /* Hex? */ FALSE); } lprintf(zm,LOG_DEBUG,"send_from: returning unexpectedly!"); @@ -1654,7 +1675,11 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti p += strlen(p) + 1; - do { + for(attempts=0;;attempts++) { + + if(attempts > zm->max_errors) + return(FALSE); + /* * send the header and the data */ @@ -1662,8 +1687,10 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti lprintf(zm,LOG_DEBUG,"Sending ZFILE header block: '%s'" ,zm->tx_data_subpacket+strlen(zm->tx_data_subpacket)+1); - zmodem_send_bin_header(zm,zfile_frame); - zmodem_send_data(zm,ZCRCW,zm->tx_data_subpacket,p - zm->tx_data_subpacket); + if(zmodem_send_bin_header(zm,zfile_frame)!=0) + continue; + if(zmodem_send_data_subpkt(zm,ZCRCW,zm->tx_data_subpacket,p - zm->tx_data_subpacket)!=0) + continue; /* * wait for anything but an ZACK packet @@ -1698,7 +1725,9 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti type = zmodem_recv_header(zm); } - } while(type != ZRPOS); + if(type == ZRPOS) + break; + } zm->transfer_start_time = time(NULL); zm->transfer_start_pos = 0; @@ -1729,12 +1758,18 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti if(sent != NULL) *sent += sent_bytes; + if(type == ZSKIP) { + zm->file_skipped=TRUE; + lprintf(zm,LOG_WARNING,"File skipped by receiver at offset: %lu", pos); + break; + } + pos += sent_bytes; if(type == ZACK) /* success */ break; - lprintf(zm,LOG_ERR,"%s at offset: %lu", chr(type), pos); + lprintf(zm,LOG_ERR,"Received %s at offset: %lu", chr(type), pos); if(zm->block_size == zm->max_block_size && zm->max_block_size > ZBLOCKLEN) zm->max_block_size /= 2; @@ -1760,6 +1795,11 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti lprintf(zm,LOG_WARNING,"Invalid ZRPOS offset: %lu", zm->rxd_header_pos); } + if(pos >= zm->current_file_size) { + lprintf(zm,LOG_INFO,"End of file reached at offset: %lu", pos); + break; + } + } while(type == ZRPOS || type == ZNAK || type==TIMEOUT); @@ -1767,7 +1807,7 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti if(sent!=NULL) lprintf(zm,LOG_DEBUG,"Sent %lu total bytes", *sent); - if(type==ZACK) { + if(type==ZACK || type==ZRPOS) { /* * file sent. send end of file frame * and wait for zrinit. if it doesnt come then try again @@ -1776,7 +1816,7 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti for(attempts=0;attempts<=zm->max_errors && !is_cancelled(zm) && is_connected(zm);attempts++) { lprintf(zm,LOG_INFO,"Sending End-of-File (ZEOF) frame (%u of %u)" ,attempts+1, zm->max_errors+1); - zmodem_send_zeof(zm); + zmodem_send_zeof(zm, pos); if(zmodem_recv_header(zm)==ZRINIT) { success=TRUE; break; @@ -2057,7 +2097,7 @@ unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, uint32_t offset) break; if(i!=ENDOFFRAME) { if(i>0 && !zm->local_abort) - lprintf(zm,LOG_ERR,"%s at offset: %lu", chr(i), ftell(fp)); + lprintf(zm,LOG_ERR,"Received %s at offset: %lu", chr(i), ftell(fp)); errors++; } } @@ -2105,7 +2145,7 @@ int zmodem_recv_file_frame(zmodem_t* zm, FILE* fp) zm->block_size = n; if(zm->progress!=NULL) - zm->progress(zm->cbdata,ftell(fp)); + zm->progress(zm->cbdata, ftell(fp)); if(is_cancelled(zm)) return(ZCAN); diff --git a/src/sbbs3/zmodem.h b/src/sbbs3/zmodem.h index 342e8eeaa5da6126c0a8b0135d8f361e69d2b3a4..cdcbe04c419175e28ba8ce9c79d9e7d9838b07f6 100644 --- a/src/sbbs3/zmodem.h +++ b/src/sbbs3/zmodem.h @@ -228,6 +228,7 @@ typedef struct { char current_file_name[MAX_PATH+1]; uint32_t current_file_size; + uint32_t current_file_pos; time_t current_file_time; unsigned current_file_num; unsigned total_files; @@ -240,7 +241,7 @@ typedef struct { int receive_32bit_data; int use_crc16; int32_t ack_file_pos; /* file position used in acknowledgement of correctly */ - /* received data subpackets */ + /* received data subpackets */ int last_sent; @@ -253,6 +254,7 @@ typedef struct { BOOL local_abort; BOOL file_skipped; BOOL no_streaming; + BOOL frame_in_transit; unsigned recv_bufsize; /* Receiver specified buffer size */ int32_t crc_request; unsigned errors;