--- sbbs/src/sbbs3/zmodem.c 2018/04/24 16:41:23 1.1 +++ sbbs/src/sbbs3/zmodem.c 2018/04/24 16:42:32 1.1.1.2 @@ -2,7 +2,7 @@ /* Synchronet ZMODEM Functions */ -/* $Id: zmodem.c,v 1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: zmodem.c,v 1.1.1.2 2018/04/24 16:42:32 root Exp $ */ /******************************************************************************/ /* Project : Unite! File : zmodem general Version : 1.02 */ @@ -39,10 +39,14 @@ #include "sexyz.h" #include "telnet.h" -#define ENDOFFRAME 2 -#define FRAMEOK 1 -#define TIMEOUT -1 /* rx routine did not receive a character within timeout */ -#define INVHDR -2 /* invalid header received; but within timeout */ +#define ENDOFFRAME 2 +#define FRAMEOK 1 +#define TIMEOUT -1 /* rx routine did not receive a character within timeout */ +#define INVHDR -2 /* invalid header received; but within timeout */ +#define ABORTED -3 /* Aborted *or* disconnected */ +#define SUBPKTOVERFLOW -4 /* Subpacket received more than block length */ +#define CRCFAILED -5 /* Failed CRC comparison */ +#define INVALIDSUBPKT -6 /* Invalid Subpacket Type */ #define ZDLEESC 0x8000 /* one of ZCRCE; ZCRCG; ZCRCQ or ZCRCW was received; ZDLE escaped */ #define BADSUBPKT 0x80 @@ -56,6 +60,9 @@ static int lprintf(zmodem_t* zm, int lev if(zm->lputs==NULL) return(-1); + if(zm->log_level != NULL) + if(level > *zm->log_level) + return 0; va_start(argptr,fmt); vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); @@ -77,6 +84,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) @@ -89,35 +97,42 @@ static char *chr(int ch) static char str[25]; switch(ch) { - case TIMEOUT: return("TIMEOUT"); - case ZRQINIT: return("ZRQINIT"); - case ZRINIT: return("ZRINIT"); - case ZSINIT: return("ZSINIT"); - case ZACK: return("ZACK"); - case ZFILE: return("ZFILE"); - case ZSKIP: return("ZSKIP"); - case ZCRC: return("ZCRC"); - case ZNAK: return("ZNAK"); - case ZABORT: return("ZABORT"); - case ZFIN: return("ZFIN"); - case ZRPOS: return("ZRPOS"); - case ZDATA: return("ZDATA"); - case ZEOF: return("ZEOF"); - case ZPAD: return("ZPAD"); - case ZCAN: return("ZCAN"); - case ZDLE: return("ZDLE"); - case ZDLEE: return("ZDLEE"); - case ZBIN: return("ZBIN"); - case ZHEX: return("ZHEX"); - case ZBIN32: return("ZBIN32"); - case ZRESC: return("ZRESC"); - case ZCRCE: return("ZCRCE"); - case ZCRCG: return("ZCRCG"); - case ZCRCQ: return("ZCRCQ"); - case ZCRCW: return("ZCRCW"); - - } - if(ch>=' ' && ch<='~') + case TIMEOUT: return("TIMEOUT"); + case ABORTED: return("ABORTED"); + case SUBPKTOVERFLOW: return "Subpacket Overflow"; + case CRCFAILED: return "CRC Failure"; + case INVALIDSUBPKT: return "Invalid Subpacket"; + case ZRQINIT: return("ZRQINIT"); + case ZRINIT: return("ZRINIT"); + case ZSINIT: return("ZSINIT"); + case ZACK: return("ZACK"); + case ZFILE: return("ZFILE"); + case ZSKIP: return("ZSKIP"); + case ZCRC: return("ZCRC"); + case ZNAK: return("ZNAK"); + case ZABORT: return("ZABORT"); + case ZFIN: return("ZFIN"); + case ZRPOS: return("ZRPOS"); + case ZDATA: return("ZDATA"); + case ZEOF: return("ZEOF"); + case ZFERR: return("ZFERR"); + case ZPAD: return("ZPAD"); + case ZCAN: return("ZCAN"); + case ZDLE: return("ZDLE"); + case ZDLEE: return("ZDLEE"); + case ZBIN: return("ZBIN"); + case ZHEX: return("ZHEX"); + case ZBIN32: return("ZBIN32"); + case ZRESC: return("ZRESC"); + case ZCRCE: return("ZCRCE"); + case ZCRCG: return("ZCRCG"); + case ZCRCQ: return("ZCRCQ"); + case ZCRCW: return("ZCRCW"); + + } + if(ch<0) + sprintf(str,"%d",ch); + else if(ch>=' ' && ch<='~') sprintf(str,"'%c' (%02Xh)",(uchar)ch,(uchar)ch); else sprintf(str,"%u (%02Xh)",(uchar)ch,(uchar)ch); @@ -129,44 +144,58 @@ static char* frame_desc(int frame) static char str[25]; if(frame==TIMEOUT) - return("TIMEOUT"); + return "TIMEOUT"; if(frame==INVHDR) - return("Invalid Header"); + return "Invalid Header"; - if(frame&BADSUBPKT) - strcpy(str,"BAD "); - else - str[0]=0; + if(frame==ABORTED) + return "Aborted"; - switch(frame&~BADSUBPKT) { - case ZRQINIT: strcat(str,"ZRQINIT"); break; - case ZRINIT: strcat(str,"ZRINIT"); break; - case ZSINIT: strcat(str,"ZSINIT"); break; - case ZACK: strcat(str,"ZACK"); break; - case ZFILE: strcat(str,"ZFILE"); break; - case ZSKIP: strcat(str,"ZSKIP"); break; - case ZNAK: strcat(str,"ZNAK"); break; - case ZABORT: strcat(str,"ZABORT"); break; - case ZFIN: strcat(str,"ZFIN"); break; - case ZRPOS: strcat(str,"ZRPOS"); break; - case ZDATA: strcat(str,"ZDATA"); break; - case ZEOF: strcat(str,"ZEOF"); break; - case ZFERR: strcat(str,"ZFERR"); break; - case ZCRC: strcat(str,"ZCRC"); break; - case ZCHALLENGE: strcat(str,"ZCHALLENGE"); break; - case ZCOMPL: strcat(str,"ZCOMPL"); break; - case ZCAN: strcat(str,"ZCAN"); break; - case ZFREECNT: strcat(str,"ZFREECNT"); break; - case ZCOMMAND: strcat(str,"ZCOMMAND"); break; - case ZSTDERR: strcat(str,"ZSTDERR"); break; - default: - sprintf(str,"Unknown (%08X)", frame); - break; - } + if(frame >= 0 && (frame&BADSUBPKT)) { + strcpy(str,"BAD "); + switch(frame&~BADSUBPKT) { + case ZRQINIT: strcat(str,"ZRQINIT"); break; + case ZRINIT: strcat(str,"ZRINIT"); break; + case ZSINIT: strcat(str,"ZSINIT"); break; + case ZACK: strcat(str,"ZACK"); break; + case ZFILE: strcat(str,"ZFILE"); break; + case ZSKIP: strcat(str,"ZSKIP"); break; + case ZNAK: strcat(str,"ZNAK"); break; + case ZABORT: strcat(str,"ZABORT"); break; + case ZFIN: strcat(str,"ZFIN"); break; + case ZRPOS: strcat(str,"ZRPOS"); break; + case ZDATA: strcat(str,"ZDATA"); break; + case ZEOF: strcat(str,"ZEOF"); break; + case ZFERR: strcat(str,"ZFERR"); break; + case ZCRC: strcat(str,"ZCRC"); break; + case ZCHALLENGE: strcat(str,"ZCHALLENGE"); break; + case ZCOMPL: strcat(str,"ZCOMPL"); break; + case ZCAN: strcat(str,"ZCAN"); break; + case ZFREECNT: strcat(str,"ZFREECNT"); break; + case ZCOMMAND: strcat(str,"ZCOMMAND"); break; + case ZSTDERR: strcat(str,"ZSTDERR"); break; + default: + sprintf(str,"Unknown (%08X)", frame); + break; + } + } else + sprintf(str,"%d",frame); return(str); } +ulong frame_pos(zmodem_t* zm, int type) +{ + switch(type) { + case ZRPOS: + case ZACK: + case ZEOF: + case ZDATA: + return(zm->rxd_header_pos); + } + + return 0; +} /* * read bytes as long as rdchk indicates that @@ -179,10 +208,19 @@ void zmodem_recv_purge(zmodem_t* zm) } /* + * Flush the output buffer + */ +void zmodem_flush(zmodem_t* zm) +{ + if(zm->flush!=NULL) + zm->flush(zm); +} + +/* * transmit a character. * this is the raw modem interface */ - +/* Returns 0 on success */ int zmodem_send_raw(zmodem_t* zm, unsigned char ch) { int result; @@ -339,6 +377,8 @@ int zmodem_send_hex_header(zmodem_t* zm, if(type!=ZACK && type!=ZFIN) result=zmodem_send_raw(zm, XON); + zmodem_flush(zm); + return(result); } @@ -350,7 +390,7 @@ int zmodem_send_bin32_header(zmodem_t* z { int i; int result; - unsigned long crc; + uint32_t crc; lprintf(zm,LOG_DEBUG,"send_bin32_header: %s", chr(*p)); @@ -428,9 +468,9 @@ int zmodem_send_bin_header(zmodem_t* zm, int zmodem_send_data32(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t l) { int result; - unsigned long crc; + uint32_t crc; - lprintf(zm,LOG_DEBUG,"send_data32: %s (%u bytes)", chr(subpkt_type), l); +// lprintf(zm,LOG_DEBUG,"send_data32: %s (%u bytes)", chr(subpkt_type), l); crc = 0xffffffffl; @@ -491,10 +531,15 @@ int zmodem_send_data16(zmodem_t* zm, uch * 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; @@ -507,10 +552,23 @@ int zmodem_send_data(zmodem_t* zm, uchar if(subpkt_type == ZCRCW) result=zmodem_send_raw(zm, XON); + zmodem_flush(zm); + return result; } -int zmodem_send_pos_header(zmodem_t* zm, int type, long pos, BOOL hex) +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, (uint32_t)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]; @@ -526,42 +584,41 @@ int zmodem_send_pos_header(zmodem_t* zm, return zmodem_send_bin_header(zm, header); } -int zmodem_send_ack(zmodem_t* zm, long pos) +int zmodem_send_ack(zmodem_t* zm, int32_t pos) { return zmodem_send_pos_header(zm, ZACK, pos, /* Hex? */ TRUE); } -int zmodem_send_nak(zmodem_t* zm) -{ - return zmodem_send_pos_header(zm, ZNAK, 0, /* Hex? */ TRUE); -} - int zmodem_send_zfin(zmodem_t* zm) { unsigned char zfin_header[] = { ZFIN, 0, 0, 0, 0 }; + lprintf(zm,LOG_NOTICE,"Finishing Session (Sending ZFIN)"); return zmodem_send_hex_header(zm,zfin_header); } -int zmodem_abort_receive(zmodem_t* zm) +int zmodem_send_zabort(zmodem_t* zm) { - lprintf(zm,LOG_WARNING,"Aborting receive"); + lprintf(zm,LOG_WARNING,"Aborting Transfer (Sending ZABORT)"); return zmodem_send_pos_header(zm, ZABORT, 0, /* Hex? */ TRUE); } int zmodem_send_znak(zmodem_t* zm) { - return zmodem_send_pos_header(zm, ZNAK, zm->ack_file_pos, /* Hex? */ TRUE); + lprintf(zm,LOG_INFO,"Sending ZNAK"); + return zmodem_send_pos_header(zm, ZNAK, 0, /* Hex? */ TRUE); } int zmodem_send_zskip(zmodem_t* zm) { + lprintf(zm,LOG_INFO,"Sending ZSKIP"); 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); + lprintf(zm,LOG_INFO,"Sending End-of-File (ZEOF) frame (pos=%lu)", pos); + return zmodem_send_pos_header(zm, ZEOF, pos, /* Hex? */ TRUE); } @@ -577,8 +634,17 @@ int zmodem_send_zeof(zmodem_t* zm) int zmodem_recv_raw(zmodem_t* zm) { int c; + unsigned attempt; - if((c=zm->recv_byte(zm->cbdata,zm->recv_timeout)) < 0) + for(attempt=0;attempt<=zm->recv_timeout;attempt++) { + if((c=zm->recv_byte(zm->cbdata,1 /* second timeout */)) >= 0) + break; + if(is_cancelled(zm)) + return(ZCAN); + if(!is_connected(zm)) + return(ABORTED); + } + if(attempt>zm->recv_timeout) return(TIMEOUT); if(c == CAN) { @@ -614,26 +680,27 @@ int zmodem_rx(zmodem_t* zm) * will be received. */ - while(is_connected(zm)) { + while(is_connected(zm) && !is_cancelled(zm)) { - while(TRUE) { - if((c = zmodem_recv_raw(zm)) < 0) - return(c); - - switch (c) { + do { + switch(c = zmodem_recv_raw(zm)) { case ZDLE: break; case XON: case XON|0x80: case XOFF: case XOFF|0x80: + lprintf(zm,LOG_WARNING,"rx: dropping flow ctrl char: %s" + ,chr(c)); continue; default: /* * if all control characters should be escaped and * this one wasnt then its spurious and should be dropped. */ - if(zm->escape_ctrl_chars && (c & 0x60) == 0) { + if(zm->escape_ctrl_chars && (c >= 0) && (c & 0x60) == 0) { + lprintf(zm,LOG_WARNING,"rx: dropping unescaped ctrl char: %s" + ,chr(c)); continue; } /* @@ -642,25 +709,24 @@ int zmodem_rx(zmodem_t* zm) return c; } break; - } + } while(!is_cancelled(zm)); /* * ZDLE encoded sequence or session abort. * (or something illegal; then back to the top) */ - while(TRUE) { - if((c = zmodem_recv_raw(zm)) < 0) - return(c); - - if(c == XON || c == (XON|0x80) || c == XOFF || c == (XOFF|0x80) || c == ZDLE) { - /* - * these can be dropped. - */ - continue; - } + while(!is_cancelled(zm)) { - switch (c) { + switch(c=zmodem_recv_raw(zm)) { + case XON: + case XON|0x80: + case XOFF: + case XOFF|0x80: + case ZDLE: + lprintf(zm,LOG_WARNING,"rx: dropping escaped flow ctrl char: %s" + ,chr(c)); + continue; /* * these four are really nasty. * for convenience we just change them into @@ -680,11 +746,16 @@ int zmodem_rx(zmodem_t* zm) case ZRUB1: return 0xff; default: + if(c < 0) + return c; + if(zm->escape_ctrl_chars && (c & 0x60) == 0) { /* * a not escaped control character; probably * something from a network. just drop it. */ + lprintf(zm,LOG_WARNING,"rx: dropping unescaped ctrl char: %s" + ,chr(c)); continue; } /* @@ -703,10 +774,10 @@ int zmodem_rx(zmodem_t* zm) } /* - * not reached. + * not reached (unless cancelled). */ - return 0; + return ABORTED; } /* @@ -726,8 +797,8 @@ int zmodem_rx(zmodem_t* zm) int zmodem_recv_data32(zmodem_t* zm, unsigned char * p, unsigned maxlen, unsigned* l) { int c; - unsigned long rxd_crc; - unsigned long crc; + uint32_t rxd_crc; + uint32_t crc; int subpkt_type; lprintf(zm,LOG_DEBUG,"recv_data32"); @@ -737,16 +808,18 @@ int zmodem_recv_data32(zmodem_t* zm, uns do { c = zmodem_rx(zm); - if(c == TIMEOUT) { - return TIMEOUT; - } - if(c < 0x100 && *l < maxlen) { - crc = ucrc32(c,crc); - *p++ = c; - (*l)++; - continue; - } - } while(c < 0x100); + if(c < 0) + return c; + + if(c > 0xff) + break; + + if(*l >= maxlen) + return SUBPKTOVERFLOW; + crc = ucrc32(c,crc); + *p++ = c; + (*l)++; + } while(1); subpkt_type = c & 0xff; @@ -762,7 +835,7 @@ int zmodem_recv_data32(zmodem_t* zm, uns if(rxd_crc != crc) { lprintf(zm,LOG_WARNING,"CRC32 ERROR (%08lX, expected: %08lX) Bytes=%u, subpacket-type=%s" ,rxd_crc, crc, *l, chr(subpkt_type)); - return FALSE; + return CRCFAILED; } lprintf(zm,LOG_DEBUG,"GOOD CRC32: %08lX (Bytes=%u, subpacket-type=%s)" ,crc, *l, chr(subpkt_type)); @@ -786,15 +859,18 @@ int zmodem_recv_data16(zmodem_t* zm, reg do { c = zmodem_rx(zm); - if(c == TIMEOUT) { - return TIMEOUT; - } - if(c < 0x100 && *l < maxlen) { - crc = ucrc16(c,crc); - *p++ = c; - (*l)++; - } - } while(c < 0x100); + if(c < 0) + return c; + + if(c > 0xff) + break; + + if(*l >= maxlen) + return SUBPKTOVERFLOW; + crc = ucrc16(c,crc); + *p++ = c; + (*l)++; + } while(1); subpkt_type = c & 0xff; @@ -806,7 +882,7 @@ int zmodem_recv_data16(zmodem_t* zm, reg if(rxd_crc != crc) { lprintf(zm,LOG_WARNING,"CRC16 ERROR (%04hX, expected: %04hX) Bytes=%d" ,rxd_crc, crc, *l); - return FALSE; + return CRCFAILED; } lprintf(zm,LOG_DEBUG,"GOOD CRC16: %04hX (Bytes=%d)", crc, *l); @@ -838,16 +914,13 @@ int zmodem_recv_data(zmodem_t* zm, unsig 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, SUBPKTOVERFLOW, CRCFAILED */ + 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 */ @@ -874,9 +947,9 @@ int zmodem_recv_data(zmodem_t* zm, unsig 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; + return INVALIDSUBPKT; } BOOL zmodem_recv_subpacket(zmodem_t* zm, BOOL ack) @@ -885,7 +958,7 @@ BOOL zmodem_recv_subpacket(zmodem_t* zm, type=zmodem_recv_data(zm,zm->rx_data_subpacket,sizeof(zm->rx_data_subpacket),NULL,ack); if(type!=FRAMEOK && type!=ENDOFFRAME) { - zmodem_send_nak(zm); + zmodem_send_znak(zm); return(FALSE); } @@ -898,9 +971,8 @@ int zmodem_recv_nibble(zmodem_t* zm) c = zmodem_rx(zm); - if(c == TIMEOUT) { + if(c < 0) return c; - } if(c > '9') { if(c < 'a' || c > 'f') { @@ -908,7 +980,7 @@ int zmodem_recv_nibble(zmodem_t* zm) * illegal hex; different than expected. * we might as well time out. */ - return TIMEOUT; + return -1; } c -= 'a' - 10; @@ -919,7 +991,7 @@ int zmodem_recv_nibble(zmodem_t* zm) * illegal hex; different than expected. * we might as well time out. */ - return TIMEOUT; + return -1; } c -= '0'; } @@ -935,15 +1007,13 @@ int zmodem_recv_hex(zmodem_t* zm) n1 = zmodem_recv_nibble(zm); - if(n1 == TIMEOUT) { + if(n1 < 0) return n1; - } n0 = zmodem_recv_nibble(zm); - if(n0 == TIMEOUT) { + if(n0 < 0) return n0; - } ret = (n1 << 4) | n0; @@ -971,8 +1041,8 @@ BOOL zmodem_recv_bin16_header(zmodem_t* for(n=0;nrxd_header[i] = c; @@ -1018,17 +1087,15 @@ void zmodem_recv_hex_header(zmodem_t* zm c = zmodem_recv_hex(zm); - if(c == TIMEOUT) { - return; - } + if(c < 0) + return FALSE; rxd_crc = c << 8; c = zmodem_recv_hex(zm); - if(c == TIMEOUT) { - return; - } + if(c < 0 ) + return FALSE; rxd_crc |= c; @@ -1038,6 +1105,7 @@ void zmodem_recv_hex_header(zmodem_t* zm } else { lprintf(zm,LOG_WARNING,"CRC16 ERROR: 0x%hX, expected: 0x%hX", rxd_crc, crc); + return FALSE; } /* @@ -1050,14 +1118,16 @@ void zmodem_recv_hex_header(zmodem_t* zm */ zmodem_rx(zm); /* drop LF */ } + + return TRUE; } BOOL zmodem_recv_bin32_header(zmodem_t* zm) { int c; int n; - unsigned long crc; - unsigned long rxd_crc; + uint32_t crc; + uint32_t rxd_crc; lprintf(zm,LOG_DEBUG,"recv_bin32_header"); @@ -1065,9 +1135,8 @@ BOOL zmodem_recv_bin32_header(zmodem_t* for(n=0;nrxd_header[n] = c; } @@ -1140,25 +1209,27 @@ int zmodem_recv_header_raw(zmodem_t* zm, c = zmodem_rx(zm); - if(c == TIMEOUT) { - lprintf(zm,LOG_WARNING,"recv_header_raw: TIMEOUT"); - return c; - } - switch (c) { case ZBIN: - zmodem_recv_bin16_header(zm); + if(!zmodem_recv_bin16_header(zm)) + return INVHDR; zm->receive_32bit_data = FALSE; break; case ZHEX: - zmodem_recv_hex_header(zm); + if(!zmodem_recv_hex_header(zm)) + return INVHDR; zm->receive_32bit_data = FALSE; break; case ZBIN32: - zmodem_recv_bin32_header(zm); + if(!zmodem_recv_bin32_header(zm)) + return INVHDR; zm->receive_32bit_data = TRUE; break; default: + if(c < 0) { + lprintf(zm,LOG_WARNING,"recv_header_raw: %s", chr(c)); + return c; + } /* * unrecognized header style */ @@ -1211,7 +1282,7 @@ int zmodem_recv_header_raw(zmodem_t* zm, break; } -#if 0 /*def _DEBUG */ +#if 0 /* def _DEBUG */ lprintf(zm,LOG_DEBUG,"recv_header_raw received header type: %s" ,frame_desc(frame_type)); #endif @@ -1223,49 +1294,81 @@ int zmodem_recv_header(zmodem_t* zm) { int ret; - ret = zmodem_recv_header_raw(zm, FALSE); - - if(ret == TIMEOUT) - lprintf(zm,LOG_WARNING,"recv_header TIMEOUT"); - else if(ret == INVHDR) - lprintf(zm,LOG_WARNING,"recv_header detected an invalid header"); - else - lprintf(zm,LOG_DEBUG,"recv_header returning: %s (pos=%ld)" - ,frame_desc(ret), zm->rxd_header_pos); + switch(ret = zmodem_recv_header_raw(zm, FALSE)) { + case TIMEOUT: + lprintf(zm,LOG_WARNING,"recv_header TIMEOUT"); + break; + case INVHDR: + lprintf(zm,LOG_WARNING,"recv_header detected an invalid header"); + break; + default: + lprintf(zm,LOG_DEBUG,"recv_header returning: %s (pos=%lu)" + ,frame_desc(ret), frame_pos(zm, ret)); - if(ret==ZCAN) - zm->cancelled=TRUE; + if(ret==ZCAN) + zm->cancelled=TRUE; + else if(ret==ZRINIT) + zmodem_parse_zrinit(zm); + break; + } return ret; } int zmodem_recv_header_and_check(zmodem_t* zm) { - int type; + int type=ABORTED; - while(is_connected(zm)) { + while(is_connected(zm) && !is_cancelled(zm)) { type = zmodem_recv_header_raw(zm,TRUE); - if(type != INVHDR && (type&BADSUBPKT) == 0) { + if(type == TIMEOUT) + break; + + if(type != INVHDR && (type&BADSUBPKT) == 0) break; - } zmodem_send_znak(zm); } + lprintf(zm,LOG_DEBUG,"recv_header_and_check returning: %s (pos=%lu)" + ,frame_desc(type), frame_pos(zm, type)); + + if(type==ZCAN) + zm->cancelled=TRUE; + return type; } -BOOL zmodem_get_crc(zmodem_t* zm, long length, ulong* crc) +BOOL zmodem_request_crc(zmodem_t* zm, int32_t length) { + zmodem_recv_purge(zm); zmodem_send_pos_header(zm,ZCRC,length,TRUE); - if(!zmodem_data_waiting(zm,zm->crc_timeout*1000)) + return TRUE; +} + +BOOL zmodem_recv_crc(zmodem_t* zm, uint32_t* crc) +{ + int type; + + if(!zmodem_data_waiting(zm,zm->crc_timeout)) { + lprintf(zm,LOG_ERR,"Timeout waiting for response (%u seconds)", zm->crc_timeout); return(FALSE); - if(zmodem_recv_header(zm)!=ZCRC) + } + if((type=zmodem_recv_header(zm))!=ZCRC) { + lprintf(zm,LOG_ERR,"Received %s instead of ZCRC", frame_desc(type)); return(FALSE); + } if(crc!=NULL) *crc = zm->crc_request; - return(TRUE); + return TRUE; +} + +BOOL zmodem_get_crc(zmodem_t* zm, int32_t length, uint32_t* crc) +{ + if(zmodem_request_crc(zm, length)) + return zmodem_recv_crc(zm, crc); + return FALSE; } void zmodem_parse_zrinit(zmodem_t* zm) @@ -1301,7 +1404,7 @@ int zmodem_get_zrinit(zmodem_t* zm) zmodem_send_raw(zm,'\r'); zmodem_send_hex_header(zm,zrqinit_header); - if(!zmodem_data_waiting(zm,zm->init_timeout*1000)) + if(!zmodem_data_waiting(zm,zm->init_timeout)) return(TIMEOUT); return zmodem_recv_header(zm); } @@ -1336,14 +1439,23 @@ int zmodem_send_zrinit(zmodem_t* zm) return zmodem_send_hex_header(zm, zrinit_header); } +/* Returns ZFIN on success */ int zmodem_get_zfin(zmodem_t* zm) { - int type; + int result; + int type=ZCAN; + unsigned attempts; - zmodem_send_zfin(zm); - do { - type = zmodem_recv_header(zm); - } while(type != ZFIN && type != TIMEOUT && is_connected(zm)); + for(attempts=0; attemptsmax_errors && is_connected(zm) && !is_cancelled(zm); attempts++) { + if(attempts&1) /* Alternate between ZABORT and ZFIN */ + result = zmodem_send_zabort(zm); + else + result = zmodem_send_zfin(zm); + if(result != 0) + return result; + if((type = zmodem_recv_header(zm)) == ZFIN) + break; + } /* * these Os are formally required; but they don't do a thing @@ -1351,34 +1463,61 @@ int zmodem_get_zfin(zmodem_t* zm) * (both programs already sent a ZFIN so why bother ?) */ - if(type != TIMEOUT) { + if(type == ZFIN) { zmodem_send_raw(zm,'O'); zmodem_send_raw(zm,'O'); } - return 0; + return type; +} + +BOOL zmodem_handle_zrpos(zmodem_t* zm, uint64_t* pos) +{ + if(zm->rxd_header_pos <= zm->current_file_size) { + if(*pos != zm->rxd_header_pos) { + *pos = zm->rxd_header_pos; + lprintf(zm,LOG_INFO,"Resuming transfer from offset: %"PRIu64, *pos); + } + return TRUE; + } + lprintf(zm,LOG_WARNING,"Invalid ZRPOS offset: %lu", zm->rxd_header_pos); + return FALSE; } +BOOL zmodem_handle_zack(zmodem_t* zm) +{ + if(zm->rxd_header_pos == zm->current_file_pos) + return TRUE; + lprintf(zm,LOG_WARNING,"ZACK for incorrect offset (%lu vs %lu)" + ,zm->rxd_header_pos, (ulong)zm->current_file_pos); + return FALSE; +} /* * send from the current position in the file * all the way to end of file or until something goes wrong. * (ZNAK or ZRPOS received) - * the name is only used to show progress + * returns ZRINIT on success. */ -int zmodem_send_from(zmodem_t* zm, FILE* fp, ulong pos, ulong* sent) +int zmodem_send_from(zmodem_t* zm, FILE* fp, uint64_t pos, uint64_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); + if(fseeko(fp,(off_t)pos,SEEK_SET)!=0) { + lprintf(zm,LOG_ERR,"ERROR %d seeking to file offset %"PRIu64 + ,errno, pos); + zmodem_send_pos_header(zm, ZFERR, (uint32_t)pos, /* Hex? */ TRUE); + return ZFERR; + } + zm->current_file_pos=pos; - zmodem_send_pos_header(zm, ZDATA, pos, /* Hex? */ FALSE); /* * send the data in the file @@ -1393,9 +1532,9 @@ int zmodem_send_from(zmodem_t* zm, FILE* n = fread(zm->tx_data_subpacket,sizeof(BYTE),zm->block_size,fp); if(zm->progress!=NULL) - zm->progress(zm->cbdata, ftell(fp)); - - type = ZCRCG; + zm->progress(zm->cbdata, ftello(fp)); + + type = ZCRCW; /** ZMODEM.DOC: ZCRCW data subpackets expect a response before the next frame is sent. @@ -1403,38 +1542,49 @@ int zmodem_send_from(zmodem_t* zm, FILE* CANOVIO bit, or sets a buffer size, the sender uses the ZCRCW to allow the receiver to write its buffer before sending more data. ***/ - if(!zm->can_overlap_io || zm->no_streaming - || (zm->recv_bufsize && buf_sent+n>=zm->recv_bufsize)) { - type=ZCRCW; - buf_sent=0; + /* Note: we always use ZCRCW for the first frame */ + if(subpkts_sent || n < zm->block_size) { + /* ZMODEM.DOC: + In the absence of fatal error, the sender eventually encounters end of + file. If the end of file is encountered within a frame, the frame is + closed with a ZCRCE data subpacket which does not elicit a response + except in case of error. + */ + if(n < zm->block_size) + type = ZCRCE; + else { + if(zm->can_overlap_io && !zm->no_streaming && (zm->recv_bufsize==0 || buf_sent+n < zm->recv_bufsize)) + type = ZCRCG; + else /* Send a ZCRCW frame */ + buf_sent = 0; + } } - if((ulong)ftell(fp) >= 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; + if(zm->current_file_pos > zm->current_file_size) + zm->current_file_size = zm->current_file_pos; + 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 == (ulong)ftell(fp)) + if(zmodem_handle_zack(zm)) break; - lprintf(zm,LOG_WARNING,"ZACK for incorrect offset (%lu vs %lu)" - ,zm->rxd_header_pos, ftell(fp)); } - } } @@ -1443,14 +1593,10 @@ int zmodem_send_from(zmodem_t* zm, FILE* buf_sent+=n; - if((ulong)ftell(fp) >= zm->current_file_size) { - lprintf(zm,LOG_DEBUG,"send_from: end of file (%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) ); - return ZACK; + if(n < zm->block_size) { + lprintf(zm,LOG_DEBUG,"send_from: end of file (or read error) reached at offset: %"PRId64, zm->current_file_pos); + zmodem_send_zeof(zm, (uint32_t)zm->current_file_pos); + return zmodem_recv_header(zm); /* If this is ZRINIT, Success */ } /* @@ -1458,17 +1604,24 @@ int zmodem_send_from(zmodem_t* zm, FILE* * check out that header */ - while(zmodem_data_waiting(zm, zm->consecutive_errors ? 1000:0) + while(zmodem_data_waiting(zm, zm->consecutive_errors ? 1: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); - 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 data: %s", chr(rx_type)); + if(rx_type >= 0) { + return rx_type; } } else lprintf(zm,LOG_DEBUG,"Received: %s",chr(c)); @@ -1483,9 +1636,6 @@ int zmodem_send_from(zmodem_t* zm, FILE* 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!"); @@ -1499,21 +1649,19 @@ int zmodem_send_from(zmodem_t* zm, FILE* } /* - * send a file; returns true when session is aborted. - * (using ZABORT frame) + * send a file; returns true when session is successful. (or file is skipped) */ -BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, time_t* start, ulong* sent) +BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, time_t* start, int64_t* sent) { - BOOL success=FALSE; - ulong pos=0; - ulong sent_bytes; - struct stat s; + uint64_t pos=0; + uint64_t sent_bytes; + struct stat s; unsigned char * p; - uchar zfile_frame[] = { ZFILE, 0, 0, 0, 0 }; - int type; - int i; - unsigned attempts; + uchar zfile_frame[] = { ZFILE, 0, 0, 0, 0 }; + int type; + int i; + unsigned attempts; if(zm->block_size == 0) zm->block_size = ZBLOCKLEN; @@ -1543,17 +1691,18 @@ BOOL zmodem_send_file(zmodem_t* zm, char if(request_init) { for(zm->errors=0; zm->errors<=zm->max_errors && !is_cancelled(zm) && is_connected(zm); zm->errors++) { - lprintf(zm,LOG_INFO,"Sending ZRQINIT (%u of %u)" - ,zm->errors+1,zm->max_errors+1); + if(zm->errors) + lprintf(zm,LOG_NOTICE,"Sending ZRQINIT (%u of %u)" + ,zm->errors+1,zm->max_errors+1); + else + lprintf(zm,LOG_INFO,"Sending ZRQINIT"); i = zmodem_get_zrinit(zm); - if(i == ZRINIT) { - zmodem_parse_zrinit(zm); + if(i == ZRINIT) break; - } - lprintf(zm,LOG_WARNING,"send_file: received header type %s" + lprintf(zm,LOG_WARNING,"send_file: received %s instead of ZRINIT" ,frame_desc(i)); } - if(zm->errors>=zm->max_errors || is_cancelled(zm)) + if(zm->errors>=zm->max_errors || is_cancelled(zm) || !is_connected(zm)) return(FALSE); } @@ -1619,29 +1768,35 @@ BOOL zmodem_send_file(zmodem_t* zm, char p += strlen(p) + 1; - sprintf(p,"%lu %lo %lo %d %u %lu %d" - ,zm->current_file_size + sprintf(p,"%"PRId64" %lo 0 0 %u %"PRId64" 0" + ,zm->current_file_size /* use for estimating only, could be zero! */ ,s.st_mtime - ,0 /* file mode */ - ,0 /* serial number */ ,zm->files_remaining ,zm->bytes_remaining - ,0 /* file type */ ); p += strlen(p) + 1; - do { + for(attempts=0;;attempts++) { + + if(attempts > zm->max_errors) + return(FALSE); + /* * send the header and the data */ - lprintf(zm,LOG_DEBUG,"Sending ZFILE header block: '%s'" + lprintf(zm,LOG_DEBUG,"Sending ZFILE frame: '%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((i=zmodem_send_bin_header(zm,zfile_frame))!=0) { + lprintf(zm,LOG_DEBUG,"zmodem_send_bin_header returned %d",i); + continue; + } + if((i=zmodem_send_data_subpkt(zm,ZCRCW,zm->tx_data_subpacket,p - zm->tx_data_subpacket))!=0) { + lprintf(zm,LOG_DEBUG,"zmodem_send_data_subpkt returned %d",i); + continue; + } /* * wait for anything but an ZACK packet */ @@ -1662,7 +1817,7 @@ BOOL zmodem_send_file(zmodem_t* zm, char if(type == ZSKIP) { zm->file_skipped=TRUE; lprintf(zm,LOG_WARNING,"File skipped by receiver"); - return(FALSE); + return(TRUE); } if(type == ZCRC) { @@ -1675,14 +1830,15 @@ BOOL zmodem_send_file(zmodem_t* zm, char type = zmodem_recv_header(zm); } - } while(type != ZRPOS); + if(type == ZRPOS) + break; + } + + if(!zmodem_handle_zrpos(zm, &pos)) + return(FALSE); + zm->transfer_start_pos = pos; zm->transfer_start_time = time(NULL); - zm->transfer_start_pos = 0; - if(zm->rxd_header_pos && zm->rxd_header_pos <= zm->current_file_size) { - pos = zm->transfer_start_pos = zm->rxd_header_pos; - lprintf(zm,LOG_INFO,"Starting transfer at offset: %lu (resume)", pos); - } if(start!=NULL) *start=zm->transfer_start_time; @@ -1690,6 +1846,8 @@ BOOL zmodem_send_file(zmodem_t* zm, char rewind(fp); zm->errors = 0; zm->consecutive_errors = 0; + + lprintf(zm,LOG_DEBUG,"Sending %s from offset %"PRIu64, fname, pos); do { /* * and start sending @@ -1701,17 +1859,30 @@ BOOL zmodem_send_file(zmodem_t* zm, char return(FALSE); if(type == ZFERR || type == ZABORT || is_cancelled(zm)) - return(FALSE); + break; + + if(type == ZSKIP) { + zm->file_skipped=TRUE; + lprintf(zm,LOG_WARNING,"File skipped by receiver at offset: %"PRIu64, pos + sent_bytes); + /* ZOC sends a ZRINIT after mid-file ZSKIP, so consume the ZRINIT here */ + zmodem_recv_header(zm); + return(TRUE); + } if(sent != NULL) *sent += sent_bytes; - pos += sent_bytes; + if(type == ZRINIT) + return(TRUE); /* Success */ - if(type == ZACK) /* success */ - break; + if(type==ZACK && zmodem_handle_zack(zm)) { + pos += sent_bytes; + continue; + } + + /* Error of some kind */ - lprintf(zm,LOG_ERR,"%s at offset: %lu", chr(type), pos); + lprintf(zm,LOG_ERR,"Received %s at offset: %"PRId64, chr(type), zm->current_file_pos); if(zm->block_size == zm->max_block_size && zm->max_block_size > ZBLOCKLEN) zm->max_block_size /= 2; @@ -1721,60 +1892,32 @@ BOOL zmodem_send_file(zmodem_t* zm, char zm->errors++; if(++zm->consecutive_errors > zm->max_errors) - return(FALSE); + break; /* failure */ - /* - * fetch pos from the ZRPOS header - */ - - if(type == ZRPOS) { - if(zm->rxd_header_pos <= zm->current_file_size) { - if(pos != zm->rxd_header_pos) { - pos = zm->rxd_header_pos; - lprintf(zm,LOG_INFO,"Resuming transfer from offset: %lu", pos); - } - } else - lprintf(zm,LOG_WARNING,"Invalid ZRPOS offset: %lu", zm->rxd_header_pos); + if(type==ZRPOS) { + if(!zmodem_handle_zrpos(zm, &pos)) + break; } + } while(TRUE); - } while(type == ZRPOS || type == ZNAK || type==TIMEOUT); - - - lprintf(zm,LOG_INFO,"Finishing transfer on receipt of header: %s", chr(type)); - if(sent!=NULL) - lprintf(zm,LOG_DEBUG,"Sent %lu total bytes", *sent); + lprintf(zm,LOG_WARNING,"Transfer failed on receipt of: %s", chr(type)); - if(type==ZACK) { - /* - * file sent. send end of file frame - * and wait for zrinit. if it doesnt come then try again - */ - - 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); - if(zmodem_recv_header(zm)==ZRINIT) { - success=TRUE; - break; - } - } - } - return(success); + return(FALSE); } -int zmodem_recv_files(zmodem_t* zm, const char* download_dir, ulong* bytes_received) +int zmodem_recv_files(zmodem_t* zm, const char* download_dir, int64_t* bytes_received) { char fpath[MAX_PATH+1]; FILE* fp; - long l; + int64_t l; BOOL skip; - ulong b; - ulong crc; - ulong rcrc; - ulong bytes; - ulong kbytes; - ulong start_bytes; + BOOL loop; + uint64_t b; + uint32_t crc; + uint32_t rcrc; + int64_t bytes; + int64_t kbytes; + int64_t start_bytes; unsigned files_received=0; time_t t; unsigned cps; @@ -1788,20 +1931,31 @@ int zmodem_recv_files(zmodem_t* zm, cons bytes=zm->current_file_size; kbytes=bytes/1024; if(kbytes<1) kbytes=0; - lprintf(zm,LOG_INFO,"Downloading %s (%lu KBytes) via Zmodem", zm->current_file_name, kbytes); + lprintf(zm,LOG_INFO,"Downloading %s (%"PRId64" KBytes) via Zmodem", zm->current_file_name, kbytes); do { /* try */ skip=TRUE; + loop=FALSE; sprintf(fpath,"%s/%s",download_dir,zm->current_file_name); lprintf(zm,LOG_DEBUG,"fpath=%s",fpath); if(fexist(fpath)) { l=flength(fpath); - lprintf(zm,LOG_WARNING,"%s already exists (%lu bytes)",fpath,l); - if(l>=(long)bytes) { - lprintf(zm,LOG_WARNING,"Local file size >= remote file size (%ld)" + lprintf(zm,LOG_WARNING,"%s already exists (%"PRId64" bytes)",fpath,l); + if(l>=(int32_t)bytes) { + lprintf(zm,LOG_WARNING,"Local file size >= remote file size (%"PRId64")" ,bytes); - break; + if(zm->duplicate_filename==NULL) + break; + else { + if(l > (int32_t)bytes) { + if(zm->duplicate_filename(zm->cbdata, zm)) { + loop=TRUE; + continue; + } + break; + } + } } if((fp=fopen(fpath,"rb"))==NULL) { lprintf(zm,LOG_ERR,"Error %d opening %s", errno, fpath); @@ -1809,18 +1963,34 @@ int zmodem_recv_files(zmodem_t* zm, cons } setvbuf(fp,NULL,_IOFBF,0x10000); - lprintf(zm,LOG_INFO,"Calculating CRC of: %s", fpath); - crc=fcrc32(fp,l); + lprintf(zm,LOG_NOTICE,"Requesting CRC of remote file: %s", zm->current_file_name); + if(!zmodem_request_crc(zm, (uint32_t)l)) { + fclose(fp); + lprintf(zm,LOG_ERR,"Failed to request CRC of remote file"); + break; + } + lprintf(zm,LOG_NOTICE,"Calculating CRC of: %s", fpath); + crc=fcrc32(fp,(uint32_t)l); /* Warning: 4GB limit! */ fclose(fp); lprintf(zm,LOG_INFO,"CRC of %s (%lu bytes): %08lX" - ,getfname(fpath), l, crc); - lprintf(zm,LOG_INFO,"Requesting CRC of remote file: %s", zm->current_file_name); - if(!zmodem_get_crc(zm,l,&rcrc)) { + ,getfname(fpath), (ulong)l, crc); + lprintf(zm,LOG_NOTICE,"Waiting for CRC of remote file: %s", zm->current_file_name); + if(!zmodem_recv_crc(zm,&rcrc)) { lprintf(zm,LOG_ERR,"Failed to get CRC of remote file"); break; } if(crc!=rcrc) { lprintf(zm,LOG_WARNING,"Remote file has different CRC value: %08lX", rcrc); + if(zm->duplicate_filename) { + if(zm->duplicate_filename(zm->cbdata, zm)) { + loop=TRUE; + continue; + } + } + break; + } + if(l == (int32_t)bytes) { + lprintf(zm,LOG_INFO,"CRC, length, and filename match."); break; } lprintf(zm,LOG_INFO,"Resuming download of %s",fpath); @@ -1833,12 +2003,8 @@ int zmodem_recv_files(zmodem_t* zm, cons start_bytes=filelength(fileno(fp)); skip=FALSE; - errors=zmodem_recv_file_data(zm,fp,flength(fpath)); + errors=zmodem_recv_file_data(zm,fp,start_bytes); - for(;errors<=zm->max_errors && !is_cancelled(zm); errors++) { - if(zmodem_recv_header_and_check(zm)) - break; - } fclose(fp); l=flength(fpath); if(errors && l==0) { /* aborted/failed download */ @@ -1848,16 +2014,16 @@ int zmodem_recv_files(zmodem_t* zm, cons lprintf(zm,LOG_INFO,"Deleted 0-byte file %s",fpath); } else { - if(l!=(long)bytes) { - lprintf(zm,LOG_WARNING,"Incomplete download (%ld bytes received, expected %lu)" + if(l!=bytes) { + lprintf(zm,LOG_WARNING,"Incomplete download (%"PRId64" bytes received, expected %"PRId64")" ,l,bytes); } else { if((t=time(NULL)-zm->transfer_start_time)<=0) t=1; b=l-start_bytes; - if((cps=b/t)==0) + if((cps=(unsigned)(b/t))==0) cps=1; - lprintf(zm,LOG_INFO,"Received %lu bytes successfully (%u CPS)",b,cps); + lprintf(zm,LOG_INFO,"Received %"PRIu64" bytes successfully (%u CPS)",b,cps); files_received++; if(bytes_received!=NULL) *bytes_received+=b; @@ -1866,7 +2032,7 @@ int zmodem_recv_files(zmodem_t* zm, cons setfdate(fpath,zm->current_file_time); } - } while(0); + } while(loop); /* finally */ if(skip) { @@ -1876,7 +2042,7 @@ int zmodem_recv_files(zmodem_t* zm, cons zm->current_file_num++; } if(zm->local_abort) - zmodem_abort_receive(zm); + zmodem_send_zabort(zm); /* wait for "over-and-out" */ timeout=zm->recv_timeout; @@ -1901,16 +2067,22 @@ int zmodem_recv_init(zmodem_t* zm) #endif for(errors=0; errors<=zm->max_errors && !is_cancelled(zm) && is_connected(zm); errors++) { - lprintf(zm,LOG_DEBUG,"Sending ZRINIT (%u of %u)" - ,errors+1, zm->max_errors+1); + if(errors) + lprintf(zm,LOG_NOTICE,"Sending ZRINIT (%u of %u)" + ,errors+1, zm->max_errors+1); + else + lprintf(zm,LOG_INFO,"Sending ZRINIT"); zmodem_send_zrinit(zm); type = zmodem_recv_header(zm); + if(zm->local_abort) + break; + if(type==TIMEOUT) continue; - lprintf(zm,LOG_DEBUG,"Received header: %s",chr(type)); + lprintf(zm,LOG_DEBUG,"recv_init: Received %s",chr(type)); if(type==ZFILE) { zmodem_parse_zfile_subpacket(zm); @@ -1922,7 +2094,7 @@ int zmodem_recv_init(zmodem_t* zm) return(type); } - lprintf(zm,LOG_WARNING,"Received frame: %s, expected ZFILE or ZFIN" + lprintf(zm,LOG_WARNING,"recv_init: Received %s instead of ZFILE or ZFIN" ,frame_desc(type)); lprintf(zm,LOG_DEBUG,"ZF0=%02X ZF1=%02X ZF2=%02X ZF3=%02X" ,zm->rxd_header[ZF0],zm->rxd_header[ZF1],zm->rxd_header[ZF2],zm->rxd_header[ZF3]); @@ -1934,8 +2106,9 @@ int zmodem_recv_init(zmodem_t* zm) void zmodem_parse_zfile_subpacket(zmodem_t* zm) { int i; - long mode=0; - long serial=-1; + int mode=0; + long serial=-1L; + ulong tmptime; SAFECOPY(zm->current_file_name,getfname(zm->rx_data_subpacket)); @@ -1944,16 +2117,17 @@ void zmodem_parse_zfile_subpacket(zmodem zm->files_remaining = 0; zm->bytes_remaining = 0; - i=sscanf(zm->rx_data_subpacket+strlen(zm->rx_data_subpacket)+1,"%lu %lo %lo %lo %lu %lu" + i=sscanf(zm->rx_data_subpacket+strlen(zm->rx_data_subpacket)+1,"%"PRId64" %lo %o %lo %u %"PRId64 ,&zm->current_file_size /* file size (decimal) */ - ,&zm->current_file_time /* file time (octal unix format) */ + ,&tmptime /* file time (octal unix format) */ ,&mode /* file mode */ ,&serial /* program serial number */ ,&zm->files_remaining /* remaining files to be sent */ ,&zm->bytes_remaining /* remaining bytes to be sent */ ); + zm->current_file_time=tmptime; - lprintf(zm,LOG_DEBUG,"Zmodem header (%u fields): %s" + lprintf(zm,LOG_DEBUG,"Zmodem file (ZFILE) data (%u fields): %s" ,i, zm->rx_data_subpacket+strlen(zm->rx_data_subpacket)+1); if(!zm->files_remaining) @@ -1972,75 +2146,132 @@ void zmodem_parse_zfile_subpacket(zmodem * the name is only used to show progress */ -unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, ulong offset) +unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, int64_t offset) { - int i=0; + int type=0; unsigned errors=0; + off_t pos; zm->transfer_start_pos=offset; zm->transfer_start_time=time(NULL); - fseek(fp,offset,SEEK_SET); - - while(errors<=zm->max_errors && is_connected(zm) - && (ulong)ftell(fp) < zm->current_file_size && !is_cancelled(zm)) { + if(fseeko(fp,(off_t)offset,SEEK_SET)!=0) { + lprintf(zm,LOG_ERR,"ERROR %d seeking to file offset %"PRId64 + ,errno, offset); + zmodem_send_pos_header(zm, ZFERR, (uint32_t)offset, /* Hex? */ TRUE); + return 1; /* errors */ + } + + /* zmodem.doc: + + The zmodem receiver uses the file length [from ZFILE data] as an estimate only. + It may be used to display an estimate of the transmission time, + and may be compared with the amount of free disk space. The + actual length of the received file is determined by the data + transfer. A file may grow after transmission commences, and + all the data will be sent. + */ + while(errors<=zm->max_errors && is_connected(zm) && !is_cancelled(zm)) { + + if((pos=ftello(fp)) > zm->current_file_size) + zm->current_file_size = pos; + + if(zm->max_file_size!=0 && pos >= zm->max_file_size) { + lprintf(zm,LOG_WARNING,"Specified maximum file size (%"PRId64" bytes) reached at offset %"PRId64 + ,zm->max_file_size, pos); + zmodem_send_pos_header(zm, ZFERR, (uint32_t)pos, /* Hex? */ TRUE); + break; + } - if(i!=ENDOFFRAME) - zmodem_send_pos_header(zm, ZRPOS, ftell(fp), /* Hex? */ TRUE); + if(type!=ENDOFFRAME) + zmodem_send_pos_header(zm, ZRPOS, (uint32_t)pos, /* Hex? */ TRUE); - if((i = zmodem_recv_file_frame(zm,fp)) == ZEOF) + type = zmodem_recv_file_frame(zm,fp); + if(type == ZEOF || type == ZFIN) break; - if(i!=ENDOFFRAME) { - if(i>0) - lprintf(zm,LOG_ERR,"%s at offset: %lu", chr(i), ftell(fp)); + if(type==ENDOFFRAME) + lprintf(zm,LOG_DEBUG,"Received complete frame at offset: %lu", (ulong)ftello(fp)); + else { + if(type>0 && !zm->local_abort) + lprintf(zm,LOG_ERR,"Received %s at offset: %lu", chr(type), (ulong)ftello(fp)); errors++; } } + + /* + * wait for the eof header + */ + for(;errors<=zm->max_errors && !is_cancelled(zm) && type!=ZEOF && type!=ZFIN; errors++) + type = zmodem_recv_header_and_check(zm); + return(errors); } int zmodem_recv_file_frame(zmodem_t* zm, FILE* fp) { - unsigned n; - int type; + unsigned n; + int type; + unsigned attempt; /* * wait for a ZDATA header with the right file offset * or a timeout or a ZFIN */ - do { - do { - type = zmodem_recv_header(zm); - if (type == TIMEOUT) { - return TIMEOUT; - } - if(is_cancelled(zm)) - return(ZCAN); - - } while(type != ZDATA); + for(attempt=0;;attempt++) { + if(attempt>=zm->max_errors) + return TIMEOUT; + type = zmodem_recv_header(zm); + switch(type) { + case ZEOF: + /* ZMODEM.DOC: + If the receiver has not received all the bytes of the file, + the receiver ignores the ZEOF because a new ZDATA is coming. + */ + if(zm->rxd_header_pos==(uint32_t)ftello(fp)) + return type; + lprintf(zm,LOG_WARNING,"Ignoring ZEOF as all bytes (%lu) have not been received" + ,zm->rxd_header_pos); + continue; + case ZFIN: + case TIMEOUT: + return type; + } + if(is_cancelled(zm) || !is_connected(zm)) + return ZCAN; - if(zm->rxd_header_pos==(ulong)ftell(fp)) + if(type==ZDATA) break; - lprintf(zm,LOG_WARNING,"Wrong ZDATA block (%lu vs %lu)", zm->rxd_header_pos, ftell(fp)); - } while(!is_cancelled(zm) && is_connected(zm)); - + lprintf(zm,LOG_WARNING,"Received %s instead of ZDATA frame", frame_desc(type)); + } + + if(zm->rxd_header_pos!=(uint32_t)ftello(fp)) { + lprintf(zm,LOG_WARNING,"Received wrong ZDATA frame (%lu vs %lu)" + ,zm->rxd_header_pos, (ulong)ftello(fp)); + return FALSE; + } + do { type = zmodem_recv_data(zm,zm->rx_data_subpacket,sizeof(zm->rx_data_subpacket),&n,TRUE); /* fprintf(stderr,"packet len %d type %d\n",n,type); */ if (type == ENDOFFRAME || type == FRAMEOK) { - fwrite(zm->rx_data_subpacket,1,n,fp); + if(fwrite(zm->rx_data_subpacket,1,n,fp)!=n) { + lprintf(zm,LOG_ERR,"ERROR %d writing %u bytes at file offset %"PRIu64 + ,errno, n,(uint64_t)ftello(fp)); + zmodem_send_pos_header(zm, ZFERR, (uint32_t)ftello(fp), /* Hex? */ TRUE); + return FALSE; + } } if(type==FRAMEOK) zm->block_size = n; if(zm->progress!=NULL) - zm->progress(zm->cbdata,ftell(fp)); + zm->progress(zm->cbdata, ftello(fp)); if(is_cancelled(zm)) return(ZCAN); @@ -2057,31 +2288,28 @@ const char* zmodem_source(void) char* zmodem_ver(char *buf) { - sscanf("$Revision: 1.1 $", "%*s %s", buf); + sscanf("$Revision: 1.1.1.2 $", "%*s %s", buf); return(buf); } void zmodem_init(zmodem_t* zm, void* cbdata ,int (*lputs)(void*, int level, const char* str) - ,void (*progress)(void* unused, ulong) - ,int (*send_byte)(void*, uchar ch, unsigned timeout) - ,int (*recv_byte)(void*, unsigned timeout) + ,void (*progress)(void* unused, int64_t) + ,int (*send_byte)(void*, uchar ch, unsigned timeout /* seconds */) + ,int (*recv_byte)(void*, unsigned timeout /* seconds */) ,BOOL (*is_connected)(void*) ,BOOL (*is_cancelled)(void*) - ,BOOL (*data_waiting)(void*, unsigned timeout)) + ,BOOL (*data_waiting)(void*, unsigned timeout /* seconds */) + ,void (*flush)(void*)) { memset(zm,0,sizeof(zmodem_t)); /* Use sane default values */ zm->init_timeout=10; /* seconds */ - zm->send_timeout=15; /* seconds */ - zm->recv_timeout=20; /* seconds */ - zm->crc_timeout=60; /* seconds */ -#if 0 - zm->byte_timeout=3; /* seconds */ - zm->ack_timeout=10; /* seconds */ -#endif + zm->send_timeout=10; /* seconds (reduced from 15) */ + zm->recv_timeout=10; /* seconds (reduced from 20) */ + zm->crc_timeout=120; /* seconds */ zm->block_size=ZBLOCKLEN; zm->max_block_size=ZBLOCKLEN; zm->max_errors=9; @@ -2094,4 +2322,5 @@ void zmodem_init(zmodem_t* zm, void* cbd zm->is_connected=is_connected; zm->is_cancelled=is_cancelled; zm->data_waiting=data_waiting; + zm->flush=flush; }