--- sbbs/src/sbbs3/xmodem.c 2018/04/24 16:41:23 1.1 +++ sbbs/src/sbbs3/xmodem.c 2018/04/24 16:43:04 1.1.1.2 @@ -2,13 +2,13 @@ /* Synchronet X/YMODEM Functions */ -/* $Id: xmodem.c,v 1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: xmodem.c,v 1.1.1.2 2018/04/24 16:43:04 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2010 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -46,6 +46,7 @@ /* xpdev */ #include "genwrap.h" /* YIELD */ #include "dirwrap.h" /* getfname */ +#include "filewrap.h" /* fileoff_t */ /* sexyz */ #include "sexyz.h" @@ -60,6 +61,9 @@ static int lprintf(xmodem_t* xm, int lev if(xm->lputs==NULL) return(-1); + if(xm->log_level != NULL) + if(level > *xm->log_level) + return 0; va_start(argptr,fmt); vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); @@ -82,6 +86,12 @@ static BOOL is_cancelled(xmodem_t* xm) return(xm->cancelled); } +static void xmodem_flush(xmodem_t* xm) +{ + if(xm->flush!=NULL) + xm->flush(xm); +} + static char *chr(uchar ch) { static char str[25]; @@ -105,29 +115,50 @@ static char *chr(uchar ch) int xmodem_put_ack(xmodem_t* xm) { + int result; + while(getcom(0)!=NOINP && is_connected(xm)) ; /* wait for any trailing data */ - return putcom(ACK); + result = putcom(ACK); + + xmodem_flush(xm); + + return result; } int xmodem_put_nak(xmodem_t* xm, unsigned block_num) { - while(getcom(0)!=NOINP && is_connected(xm)) - ; /* wait for any trailing data */ + int i,dump_count=0; + int result; + + /* wait for any trailing data */ + while((i=getcom(0))!=NOINP && is_connected(xm)) { + dump_count++; + lprintf(xm,LOG_DEBUG,"Block %u: Dumping byte: %02Xh" + ,block_num, (BYTE)i); + SLEEP(1); + } + if(dump_count) + lprintf(xm,LOG_INFO,"Block %u: Dumped %u bytes" + ,block_num, dump_count); if(block_num<=1) { - if(*(xm->mode)&GMODE) { /* G for Ymodem-G */ - lprintf(xm,LOG_INFO,"Requesting mode: Streaming, 16-bit CRC"); - return putcom('G'); + if(*(xm->mode)&GMODE) { /* G for X/Ymodem-G */ + lprintf(xm,LOG_INFO,"Block %u: Requesting mode: Streaming, 16-bit CRC", block_num); + result = putcom('G'); } else if(*(xm->mode)&CRC) { /* C for CRC */ - lprintf(xm,LOG_INFO,"Requesting mode: 16-bit CRC"); - return putcom('C'); + lprintf(xm,LOG_INFO,"Block %u: Requesting mode: 16-bit CRC", block_num); + result = putcom('C'); } else { /* NAK for checksum */ - lprintf(xm,LOG_INFO,"Requesting mode: 8-bit Checksum"); - return putcom(NAK); + lprintf(xm,LOG_INFO,"Block %u: Requesting mode: 8-bit Checksum", block_num); + result = putcom(NAK); } - } - return putcom(NAK); + } else + result = putcom(NAK); + + xmodem_flush(xm); + + return result; } int xmodem_cancel(xmodem_t* xm) @@ -145,7 +176,9 @@ int xmodem_cancel(xmodem_t* xm) xm->cancelled=TRUE; } - return 0; + xmodem_flush(xm); + + return SUCCESS; } /****************************************************************************/ @@ -153,29 +186,34 @@ int xmodem_cancel(xmodem_t* xm) /****************************************************************************/ int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num) { - uchar block_num; /* Block number received in header */ - uchar block_inv; - uchar chksum,calc_chksum; - int i,eot=0,can=0; - uint b,errors; - ushort crc,calc_crc; + uchar block_num; /* Block number received in header */ + uchar block_inv; + uchar chksum,calc_chksum; + int i,eot=0,can=0; + uint b,errors; + uint16_t crc,calc_crc; for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) { - i=getcom(expected_block_num<=1 ? 5 : 10); + i=getcom(expected_block_num<=1 ? 3 : 10); if(eot && i!=EOT && i!=NOINP) eot=0; if(can && i!=CAN) can=0; switch(i) { - case SOH: /* 128 byte blocks */ - xm->block_size=128; + case SOH: /* 128-byte blocks */ + xm->block_size=XMODEM_MIN_BLOCK_SIZE; break; - case STX: /* 1024 byte blocks */ - xm->block_size=1024; + case STX: /* 1024-byte blocks */ + if(xm->max_block_size < XMODEM_MAX_BLOCK_SIZE) { + lprintf(xm,LOG_WARNING,"Block %u: 1024-byte blocks not supported" + ,expected_block_num); + return FAILURE; + } + xm->block_size=XMODEM_MAX_BLOCK_SIZE; break; case EOT: - lprintf(xm,LOG_DEBUG,"EOT"); + lprintf(xm,LOG_DEBUG,"Block %u: EOT received", expected_block_num); if(/*((*xm->mode)&(YMODEM|GMODE))==YMODEM &&*/ !eot) { lprintf(xm,LOG_INFO,"NAKing first EOT"); eot=1; @@ -186,13 +224,15 @@ int xmodem_get_block(xmodem_t* xm, uchar case CAN: if(!can) { /* must get two CANs in a row */ can=1; - lprintf(xm,LOG_WARNING,"Received CAN Expected SOH, STX, or EOT"); + lprintf(xm,LOG_WARNING,"Block %u: Received CAN Expected SOH, STX, or EOT" + ,expected_block_num); continue; } - lprintf(xm,LOG_WARNING,"Cancelled remotely"); + lprintf(xm,LOG_WARNING,"Block %u: Cancelled remotely", expected_block_num); return(CAN); default: - lprintf(xm,LOG_WARNING,"Received %s Expected SOH, STX, or EOT",chr((uchar)i)); + lprintf(xm,LOG_WARNING,"Block %u: Received %s Expected SOH, STX, or EOT" + ,expected_block_num, chr((uchar)i)); case NOINP: /* Nothing came in */ if(eot) return(EOT); @@ -226,36 +266,41 @@ int xmodem_get_block(xmodem_t* xm, uchar chksum=getcom(xm->byte_timeout); if(block_num!=(uchar)~block_inv) { - lprintf(xm,LOG_WARNING,"Block number bit error (0x%02X vs 0x%02x)" - ,block_num,(uchar)~block_inv); - break; - } - - if(block_num!=(uchar)(expected_block_num&0xff)) { - lprintf(xm,LOG_WARNING,"Block number error (%u received, expected %u)" - ,block_num,expected_block_num&0xff); - if(expected_block_num && block_num==(uchar)((expected_block_num-1)&0xff)) - continue; /* silently discard repeated packets (ymodem.doc 7.3.2) */ + lprintf(xm,LOG_WARNING,"Block %u: Block number bit error (0x%02X vs 0x%02x)" + ,expected_block_num, block_num,(uchar)~block_inv); break; } if((*xm->mode)&CRC) { if(crc!=calc_crc) { - lprintf(xm,LOG_WARNING,"Block %u: CRC ERROR", expected_block_num); + lprintf(xm,LOG_WARNING,"Block %u: CRC ERROR", block_num); break; } } else /* CHKSUM */ - { + { if(chksum!=calc_chksum) { - lprintf(xm,LOG_WARNING,"Block %u: CHECKSUM ERROR", expected_block_num); + lprintf(xm,LOG_WARNING,"Block %u: CHECKSUM ERROR", block_num); break; } } - return(0); /* Success */ + + if(block_num!=(uchar)(expected_block_num&0xff)) { + lprintf(xm,LOG_WARNING,"Block number error (%u received, expected %u)" + ,block_num,expected_block_num&0xff); + if((*xm->mode)&XMODEM && expected_block_num==1 && block_num==0) + return(NOT_XMODEM); + if(expected_block_num==0 && block_num==1) + return(NOT_YMODEM); + if(expected_block_num && block_num==(uchar)((expected_block_num-1)&0xff)) + continue; /* silently discard repeated packets (ymodem.doc 7.3.2) */ + break; + } + + return SUCCESS; /* Success */ } - return(-2); /* Failure */ + return FAILURE; /* Failure */ } /*****************/ @@ -263,12 +308,12 @@ int xmodem_get_block(xmodem_t* xm, uchar /*****************/ int xmodem_put_block(xmodem_t* xm, uchar* block, unsigned block_size, unsigned block_num) { - int result; - uchar ch,chksum; - uint i; - ushort crc; + int result; + uchar ch,chksum; + uint i; + uint16_t crc; - if(block_size==128) + if(block_size==XMODEM_MIN_BLOCK_SIZE) result=putcom(SOH); else /* 1024 */ result=putcom(STX); @@ -293,42 +338,46 @@ int xmodem_put_block(xmodem_t* xm, uchar if((*xm->mode)&CRC) { if((result= putcom((uchar)(crc >> 8)))!=0) return result; - return putcom((uchar)(crc&0xff)); - } - return putcom(chksum); + result = putcom((uchar)(crc&0xff)); + } else + result = putcom(chksum); + + xmodem_flush(xm); + + return result; } /************************************************************/ /* Gets an acknowledgement - usually after sending a block */ -/* Returns 1 if ack received, 0 otherwise. */ +/* Returns ACK if ack received */ /************************************************************/ -BOOL xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num) +int xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num) { - int i,can=0; + int i=NOINP,can=0; unsigned errors; - for(errors=0;errorsmode)&GMODE) { /* Don't wait for ACK on Ymodem-G */ + if((*xm->mode)&GMODE) { /* Don't wait for ACK on X/Ymodem-G */ SLEEP(xm->g_delay); if(getcom(0)==CAN) { lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num); xmodem_cancel(xm); - return(FALSE); + return(CAN); } - return(TRUE); + return(ACK); } i=getcom(xm->ack_timeout); if(can && i!=CAN) can=0; if(i==ACK) - return(TRUE); + break; if(i==CAN) { - if(can) { + if(can) { /* 2 CANs in a row */ lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num); xmodem_cancel(xm); - return(FALSE); + return(CAN); } can=1; } @@ -336,11 +385,13 @@ BOOL xmodem_get_ack(xmodem_t* xm, unsign lprintf(xm,LOG_WARNING,"Block %u: !Received %s Expected ACK" ,block_num, chr((uchar)i)); if(i!=CAN) - return(FALSE); - } + return(i); + } + if(i!=CAN) + errors++; } - return(FALSE); + return(i); } BOOL xmodem_get_mode(xmodem_t* xm) @@ -362,10 +413,14 @@ BOOL xmodem_get_mode(xmodem_t* xm) return(TRUE); case 'C': lprintf(xm,LOG_INFO,"Receiver requested mode: 16-bit CRC"); + if(!xm->crc_mode_supported) + continue; *(xm->mode)|=CRC; return(TRUE); case 'G': lprintf(xm,LOG_INFO,"Receiver requested mode: Streaming, 16-bit CRC"); + if(!xm->crc_mode_supported || !xm->g_mode_supported) + continue; *(xm->mode)|=(GMODE|CRC); return(TRUE); case CAN: @@ -402,6 +457,7 @@ BOOL xmodem_put_eot(xmodem_t* xm) lprintf(xm,LOG_INFO,"Throwing out received: %s",chr((uchar)ch)); putcom(EOT); + xmodem_flush(xm); if((ch=getcom(xm->recv_timeout))==NOINP) continue; lprintf(xm,LOG_INFO,"Received %s",chr((uchar)ch)); @@ -417,17 +473,18 @@ BOOL xmodem_put_eot(xmodem_t* xm) return(FALSE); } -BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start, ulong* sent) +BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start, int64_t* sent) { BOOL success=FALSE; - ulong sent_bytes=0; - char block[1024]; + int64_t sent_bytes=0; + char block[XMODEM_MAX_BLOCK_SIZE]; size_t block_len; unsigned block_num; size_t i; size_t rd; time_t startfile; struct stat st; + BOOL sent_header=FALSE; if(sent!=NULL) *sent=0; @@ -452,21 +509,30 @@ BOOL xmodem_send_file(xmodem_t* xm, cons memset(block,0,sizeof(block)); SAFECOPY(block,getfname(fname)); - i=sprintf(block+strlen(block)+1,"%lu %lo 0 0 %d %ld" - ,(ulong)st.st_size + i=sprintf(block+strlen(block)+1,"%"PRIu64" %lo 0 0 %d %u" + ,(uint64_t)st.st_size ,st.st_mtime ,xm->total_files-xm->sent_files ,xm->total_bytes-xm->sent_bytes); - lprintf(xm,LOG_INFO,"Sending Ymodem header block: '%s'",block+strlen(block)+1); + lprintf(xm,LOG_INFO,"Sending YMODEM header block: '%s'",block+strlen(block)+1); block_len=strlen(block)+1+i; for(xm->errors=0;xm->errors<=xm->max_errors && !is_cancelled(xm) && is_connected(xm);xm->errors++) { - xmodem_put_block(xm, block, block_len <=128 ? 128:1024, 0 /* block_num */); - if(xmodem_get_ack(xm,1,0)) + xmodem_put_block(xm, block, block_len <=XMODEM_MIN_BLOCK_SIZE ? XMODEM_MIN_BLOCK_SIZE:XMODEM_MAX_BLOCK_SIZE, 0 /* block_num */); + if((i=xmodem_get_ack(xm,/* tries: */1, /* block_num: */0)) == ACK) { + sent_header=TRUE; break; + } + if((i==NAK || i=='C' || i=='G') + && xm->fallback_to_xmodem && xm->errors+1 == xm->fallback_to_xmodem) { + lprintf(xm,LOG_NOTICE,"Falling back to XMODEM mode after %u attempts" + ,xm->fallback_to_xmodem); + *(xm->mode)&=~YMODEM; + break; + } } - if(xm->errors>=xm->max_errors || is_cancelled(xm)) { + if(xm->errors>xm->max_errors || is_cancelled(xm)) { lprintf(xm,LOG_ERR,"Failed to send header block"); break; } @@ -481,30 +547,44 @@ BOOL xmodem_send_file(xmodem_t* xm, cons block_num=1; xm->errors=0; - while(sent_bytes < (ulong)st.st_size && xm->errors<=xm->max_errors && !is_cancelled(xm) + while(sent_bytes < st.st_size && xm->errors<=xm->max_errors && !is_cancelled(xm) && is_connected(xm)) { - fseek(fp,sent_bytes,SEEK_SET); + fseeko(fp,(off_t)sent_bytes,SEEK_SET); memset(block,CPMEOF,xm->block_size); + if(!sent_header) { + if(xm->block_size>XMODEM_MIN_BLOCK_SIZE) { + if((sent_bytes+xm->block_size) > st.st_size) { + if((sent_bytes+xm->block_size-XMODEM_MIN_BLOCK_SIZE) >= st.st_size) { + lprintf(xm,LOG_INFO,"Falling back to 128-byte blocks for end of file"); + xm->block_size=XMODEM_MIN_BLOCK_SIZE; + } + } + } + } if((rd=fread(block,1,xm->block_size,fp))!=xm->block_size - && (long)(block_num*xm->block_size) < st.st_size) { - lprintf(xm,LOG_ERR,"READ ERROR %d instead of %d at offset %lu" - ,rd,xm->block_size,(block_num-1)*(long)xm->block_size); + && (sent_bytes + rd) != st.st_size) { + lprintf(xm,LOG_ERR,"ERROR %d reading %u bytes at file offset %"PRId64 + ,errno,xm->block_size,(int64_t)ftello(fp)); xm->errors++; continue; } if(xm->progress!=NULL) - xm->progress(xm->cbdata,block_num,ftell(fp),st.st_size,startfile); + xm->progress(xm->cbdata,block_num,ftello(fp),st.st_size,startfile); xmodem_put_block(xm, block, xm->block_size, block_num); - if(!xmodem_get_ack(xm,5,block_num)) { + if(xmodem_get_ack(xm, /* tries: */5,block_num) != ACK) { xm->errors++; - lprintf(xm,LOG_WARNING,"Error #%d at offset %ld" - ,xm->errors,ftell(fp)-xm->block_size); + lprintf(xm,LOG_WARNING,"Block %u: Error #%d at offset %"PRId64 + ,block_num, xm->errors,(int64_t)(ftello(fp)-xm->block_size)); + if(xm->errors==3 && block_num==1 && xm->block_size>XMODEM_MIN_BLOCK_SIZE) { + lprintf(xm,LOG_NOTICE,"Block %u: Falling back to 128-byte blocks", block_num); + xm->block_size=XMODEM_MIN_BLOCK_SIZE; + } } else { block_num++; sent_bytes+=rd; } } - if(sent_bytes >= (ulong)st.st_size && !is_cancelled(xm) && is_connected(xm)) { + if(sent_bytes >= st.st_size && !is_cancelled(xm) && is_connected(xm)) { #if 0 /* !SINGLE_THREADED */ lprintf(LOG_DEBUG,"Waiting for output buffer to empty... "); @@ -534,18 +614,19 @@ const char* xmodem_source(void) char* xmodem_ver(char *buf) { - sscanf("$Revision: 1.1 $", "%*s %s", buf); + sscanf("$Revision: 1.1.1.2 $", "%*s %s", buf); return(buf); } void xmodem_init(xmodem_t* xm, void* cbdata, long* mode ,int (*lputs)(void*, int level, const char* str) - ,void (*progress)(void* unused, unsigned block_num, ulong offset, ulong fsize, time_t t) + ,void (*progress)(void* unused, unsigned block_num, int64_t offset, int64_t fsize, time_t t) ,int (*send_byte)(void*, uchar ch, unsigned timeout) ,int (*recv_byte)(void*, unsigned timeout) ,BOOL (*is_connected)(void*) - ,BOOL (*is_cancelled)(void*)) + ,BOOL (*is_cancelled)(void*) + ,void (*flush)(void*)) { memset(xm,0,sizeof(xmodem_t)); @@ -555,16 +636,20 @@ void xmodem_init(xmodem_t* xm, void* cbd xm->byte_timeout=3; /* seconds */ xm->ack_timeout=10; /* seconds */ - xm->block_size=1024; + xm->block_size=XMODEM_MAX_BLOCK_SIZE; + xm->max_block_size=XMODEM_MAX_BLOCK_SIZE; xm->max_errors=9; xm->g_delay=1; xm->cbdata=cbdata; xm->mode=mode; + xm->g_mode_supported=TRUE; + xm->crc_mode_supported=TRUE; xm->lputs=lputs; xm->progress=progress; xm->send_byte=send_byte; xm->recv_byte=recv_byte; xm->is_connected=is_connected; xm->is_cancelled=is_cancelled; + xm->flush=flush; }