|
|
1.1 ! root 1: /* xmodem.c */ ! 2: ! 3: /* Synchronet X/YMODEM Functions */ ! 4: ! 5: /* $Id: xmodem.c,v 1.26 2006/02/24 09:50:50 rswindell Exp $ */ ! 6: ! 7: /**************************************************************************** ! 8: * @format.tab-size 4 (Plain Text/Source Code File Header) * ! 9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * ! 10: * * ! 11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * ! 12: * * ! 13: * This program is free software; you can redistribute it and/or * ! 14: * modify it under the terms of the GNU General Public License * ! 15: * as published by the Free Software Foundation; either version 2 * ! 16: * of the License, or (at your option) any later version. * ! 17: * See the GNU General Public License for more details: gpl.txt or * ! 18: * http://www.fsf.org/copyleft/gpl.html * ! 19: * * ! 20: * Anonymous FTP access to the most recent released source is available at * ! 21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * ! 22: * * ! 23: * Anonymous CVS access to the development source and modification history * ! 24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: * ! 25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login * ! 26: * (just hit return, no password is necessary) * ! 27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src * ! 28: * * ! 29: * For Synchronet coding style and modification guidelines, see * ! 30: * http://www.synchro.net/source.html * ! 31: * * ! 32: * You are encouraged to submit any modifications (preferably in Unix diff * ! 33: * format) via e-mail to [email protected] * ! 34: * * ! 35: * Note: If this box doesn't appear square, then you need to fix your tabs. * ! 36: ****************************************************************************/ ! 37: ! 38: /* Standard headers */ ! 39: #include <stdio.h> ! 40: #include <sys/stat.h> /* struct stat */ ! 41: #include <stdarg.h> /* va_list */ ! 42: ! 43: /* smblib */ ! 44: #include "crc16.h" ! 45: ! 46: /* xpdev */ ! 47: #include "genwrap.h" /* YIELD */ ! 48: #include "dirwrap.h" /* getfname */ ! 49: ! 50: /* sexyz */ ! 51: #include "sexyz.h" ! 52: ! 53: #define getcom(t) xm->recv_byte(xm->cbdata,t) ! 54: #define putcom(ch) xm->send_byte(xm->cbdata,ch,xm->send_timeout) ! 55: ! 56: static int lprintf(xmodem_t* xm, int level, const char *fmt, ...) ! 57: { ! 58: va_list argptr; ! 59: char sbuf[1024]; ! 60: ! 61: if(xm->lputs==NULL) ! 62: return(-1); ! 63: ! 64: va_start(argptr,fmt); ! 65: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); ! 66: sbuf[sizeof(sbuf)-1]=0; ! 67: va_end(argptr); ! 68: return(xm->lputs(xm->cbdata,level,sbuf)); ! 69: } ! 70: ! 71: static BOOL is_connected(xmodem_t* xm) ! 72: { ! 73: if(xm->is_connected!=NULL) ! 74: return(xm->is_connected(xm->cbdata)); ! 75: return(TRUE); ! 76: } ! 77: ! 78: static BOOL is_cancelled(xmodem_t* xm) ! 79: { ! 80: if(xm->is_cancelled!=NULL) ! 81: return(xm->cancelled=xm->is_cancelled(xm->cbdata)); ! 82: return(xm->cancelled); ! 83: } ! 84: ! 85: static char *chr(uchar ch) ! 86: { ! 87: static char str[25]; ! 88: ! 89: switch(ch) { ! 90: case SOH: return("SOH"); ! 91: case STX: return("STX"); ! 92: case ETX: return("ETX"); ! 93: case EOT: return("EOT"); ! 94: case ACK: return("ACK"); ! 95: case NAK: return("NAK"); ! 96: case CAN: return("CAN"); ! 97: } ! 98: if(ch>=' ' && ch<='~') ! 99: sprintf(str,"'%c' (%02Xh)",ch,ch); ! 100: else ! 101: sprintf(str,"%u (%02Xh)",ch,ch); ! 102: return(str); ! 103: } ! 104: ! 105: ! 106: int xmodem_put_ack(xmodem_t* xm) ! 107: { ! 108: while(getcom(0)!=NOINP && is_connected(xm)) ! 109: ; /* wait for any trailing data */ ! 110: return putcom(ACK); ! 111: } ! 112: ! 113: int xmodem_put_nak(xmodem_t* xm, unsigned block_num) ! 114: { ! 115: while(getcom(0)!=NOINP && is_connected(xm)) ! 116: ; /* wait for any trailing data */ ! 117: ! 118: if(block_num<=1) { ! 119: if(*(xm->mode)&GMODE) { /* G for Ymodem-G */ ! 120: lprintf(xm,LOG_INFO,"Requesting mode: Streaming, 16-bit CRC"); ! 121: return putcom('G'); ! 122: } else if(*(xm->mode)&CRC) { /* C for CRC */ ! 123: lprintf(xm,LOG_INFO,"Requesting mode: 16-bit CRC"); ! 124: return putcom('C'); ! 125: } else { /* NAK for checksum */ ! 126: lprintf(xm,LOG_INFO,"Requesting mode: 8-bit Checksum"); ! 127: return putcom(NAK); ! 128: } ! 129: } ! 130: return putcom(NAK); ! 131: } ! 132: ! 133: int xmodem_cancel(xmodem_t* xm) ! 134: { ! 135: int i; ! 136: int result; ! 137: ! 138: if(!is_cancelled(xm) && is_connected(xm)) { ! 139: for(i=0;i<8 && is_connected(xm);i++) ! 140: if((result=putcom(CAN))!=0) ! 141: return result; ! 142: for(i=0;i<10 && is_connected(xm);i++) ! 143: if((result=putcom('\b'))!=0) ! 144: return result; ! 145: xm->cancelled=TRUE; ! 146: } ! 147: ! 148: return 0; ! 149: } ! 150: ! 151: /****************************************************************************/ ! 152: /* Return 0 on success */ ! 153: /****************************************************************************/ ! 154: int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num) ! 155: { ! 156: uchar block_num; /* Block number received in header */ ! 157: uchar block_inv; ! 158: uchar chksum,calc_chksum; ! 159: int i,eot=0,can=0; ! 160: uint b,errors; ! 161: ushort crc,calc_crc; ! 162: ! 163: for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) { ! 164: ! 165: i=getcom(expected_block_num<=1 ? 5 : 10); ! 166: if(eot && i!=EOT && i!=NOINP) ! 167: eot=0; ! 168: if(can && i!=CAN) ! 169: can=0; ! 170: switch(i) { ! 171: case SOH: /* 128 byte blocks */ ! 172: xm->block_size=128; ! 173: break; ! 174: case STX: /* 1024 byte blocks */ ! 175: xm->block_size=1024; ! 176: break; ! 177: case EOT: ! 178: lprintf(xm,LOG_DEBUG,"EOT"); ! 179: if(/*((*xm->mode)&(YMODEM|GMODE))==YMODEM &&*/ !eot) { ! 180: lprintf(xm,LOG_INFO,"NAKing first EOT"); ! 181: eot=1; ! 182: xmodem_put_nak(xm,expected_block_num); /* chuck's double EOT trick */ ! 183: continue; ! 184: } ! 185: return(EOT); ! 186: case CAN: ! 187: if(!can) { /* must get two CANs in a row */ ! 188: can=1; ! 189: lprintf(xm,LOG_WARNING,"Received CAN Expected SOH, STX, or EOT"); ! 190: continue; ! 191: } ! 192: lprintf(xm,LOG_WARNING,"Cancelled remotely"); ! 193: return(CAN); ! 194: default: ! 195: lprintf(xm,LOG_WARNING,"Received %s Expected SOH, STX, or EOT",chr((uchar)i)); ! 196: case NOINP: /* Nothing came in */ ! 197: if(eot) ! 198: return(EOT); ! 199: return(NOINP); ! 200: } ! 201: if((i=getcom(xm->byte_timeout))==NOINP) ! 202: break; ! 203: block_num=i; ! 204: if((i=getcom(xm->byte_timeout))==NOINP) ! 205: break; ! 206: block_inv=i; ! 207: calc_crc=calc_chksum=0; ! 208: for(b=0;b<xm->block_size && is_connected(xm);b++) { ! 209: if((i=getcom(xm->byte_timeout))==NOINP) ! 210: break; ! 211: block[b]=i; ! 212: if((*xm->mode)&CRC) ! 213: calc_crc=ucrc16(block[b],calc_crc); ! 214: else ! 215: calc_chksum+=block[b]; ! 216: } ! 217: ! 218: if(b<xm->block_size) ! 219: break; ! 220: ! 221: if((*xm->mode)&CRC) { ! 222: crc=getcom(xm->byte_timeout)<<8; ! 223: crc|=getcom(xm->byte_timeout); ! 224: } ! 225: else ! 226: chksum=getcom(xm->byte_timeout); ! 227: ! 228: if(block_num!=(uchar)~block_inv) { ! 229: lprintf(xm,LOG_WARNING,"Block number bit error (0x%02X vs 0x%02x)" ! 230: ,block_num,(uchar)~block_inv); ! 231: break; ! 232: } ! 233: ! 234: if(block_num!=(uchar)(expected_block_num&0xff)) { ! 235: lprintf(xm,LOG_WARNING,"Block number error (%u received, expected %u)" ! 236: ,block_num,expected_block_num&0xff); ! 237: if(expected_block_num && block_num==(uchar)((expected_block_num-1)&0xff)) ! 238: continue; /* silently discard repeated packets (ymodem.doc 7.3.2) */ ! 239: break; ! 240: } ! 241: ! 242: if((*xm->mode)&CRC) { ! 243: if(crc!=calc_crc) { ! 244: lprintf(xm,LOG_WARNING,"Block %u: CRC ERROR", expected_block_num); ! 245: break; ! 246: } ! 247: } ! 248: else /* CHKSUM */ ! 249: { ! 250: if(chksum!=calc_chksum) { ! 251: lprintf(xm,LOG_WARNING,"Block %u: CHECKSUM ERROR", expected_block_num); ! 252: break; ! 253: } ! 254: } ! 255: return(0); /* Success */ ! 256: } ! 257: ! 258: return(-2); /* Failure */ ! 259: } ! 260: ! 261: /*****************/ ! 262: /* Sends a block */ ! 263: /*****************/ ! 264: int xmodem_put_block(xmodem_t* xm, uchar* block, unsigned block_size, unsigned block_num) ! 265: { ! 266: int result; ! 267: uchar ch,chksum; ! 268: uint i; ! 269: ushort crc; ! 270: ! 271: if(block_size==128) ! 272: result=putcom(SOH); ! 273: else /* 1024 */ ! 274: result=putcom(STX); ! 275: if(result!=0) ! 276: return(result); ! 277: ch=(uchar)(block_num&0xff); ! 278: if((result=putcom(ch))!=0) ! 279: return result; ! 280: if((result=putcom((uchar)~ch))!=0) ! 281: return result; ! 282: chksum=0; ! 283: crc=0; ! 284: for(i=0;i<block_size && is_connected(xm);i++) { ! 285: if((result=putcom(block[i]))!=0) ! 286: return result; ! 287: if((*xm->mode)&CRC) ! 288: crc=ucrc16(block[i],crc); ! 289: else ! 290: chksum+=block[i]; ! 291: } ! 292: ! 293: if((*xm->mode)&CRC) { ! 294: if((result= putcom((uchar)(crc >> 8)))!=0) ! 295: return result; ! 296: return putcom((uchar)(crc&0xff)); ! 297: } ! 298: return putcom(chksum); ! 299: } ! 300: ! 301: /************************************************************/ ! 302: /* Gets an acknowledgement - usually after sending a block */ ! 303: /* Returns 1 if ack received, 0 otherwise. */ ! 304: /************************************************************/ ! 305: BOOL xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num) ! 306: { ! 307: int i,can=0; ! 308: unsigned errors; ! 309: ! 310: for(errors=0;errors<tries && is_connected(xm);errors++) { ! 311: ! 312: if((*xm->mode)&GMODE) { /* Don't wait for ACK on Ymodem-G */ ! 313: SLEEP(xm->g_delay); ! 314: if(getcom(0)==CAN) { ! 315: lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num); ! 316: xmodem_cancel(xm); ! 317: return(FALSE); ! 318: } ! 319: return(TRUE); ! 320: } ! 321: ! 322: i=getcom(xm->ack_timeout); ! 323: if(can && i!=CAN) ! 324: can=0; ! 325: if(i==ACK) ! 326: return(TRUE); ! 327: if(i==CAN) { ! 328: if(can) { ! 329: lprintf(xm,LOG_WARNING,"Block %u: !Cancelled remotely", block_num); ! 330: xmodem_cancel(xm); ! 331: return(FALSE); ! 332: } ! 333: can=1; ! 334: } ! 335: if(i!=NOINP) { ! 336: lprintf(xm,LOG_WARNING,"Block %u: !Received %s Expected ACK" ! 337: ,block_num, chr((uchar)i)); ! 338: if(i!=CAN) ! 339: return(FALSE); ! 340: } ! 341: } ! 342: ! 343: return(FALSE); ! 344: } ! 345: ! 346: BOOL xmodem_get_mode(xmodem_t* xm) ! 347: { ! 348: int i; ! 349: unsigned errors; ! 350: unsigned can; ! 351: ! 352: lprintf(xm,LOG_INFO,"Waiting for transfer mode request..."); ! 353: ! 354: *(xm->mode)&=~(GMODE|CRC); ! 355: for(errors=can=0;errors<=xm->max_errors && is_connected(xm);errors++) { ! 356: i=getcom(xm->recv_timeout); ! 357: if(can && i!=CAN) ! 358: can=0; ! 359: switch(i) { ! 360: case NAK: /* checksum */ ! 361: lprintf(xm,LOG_INFO,"Receiver requested mode: 8-bit Checksum"); ! 362: return(TRUE); ! 363: case 'C': ! 364: lprintf(xm,LOG_INFO,"Receiver requested mode: 16-bit CRC"); ! 365: *(xm->mode)|=CRC; ! 366: return(TRUE); ! 367: case 'G': ! 368: lprintf(xm,LOG_INFO,"Receiver requested mode: Streaming, 16-bit CRC"); ! 369: *(xm->mode)|=(GMODE|CRC); ! 370: return(TRUE); ! 371: case CAN: ! 372: if(can) { ! 373: lprintf(xm,LOG_WARNING,"Cancelled remotely"); ! 374: return(FALSE); ! 375: } ! 376: can=1; ! 377: break; ! 378: case NOINP: ! 379: break; ! 380: default: ! 381: lprintf(xm,LOG_WARNING,"Received %s Expected NAK, C, or G" ! 382: ,chr((uchar)i)); ! 383: break; ! 384: } ! 385: } ! 386: ! 387: lprintf(xm,LOG_ERR,"Failed to get transfer mode request from receiver"); ! 388: return(FALSE); ! 389: } ! 390: ! 391: BOOL xmodem_put_eot(xmodem_t* xm) ! 392: { ! 393: int ch; ! 394: unsigned errors; ! 395: unsigned cans=0; ! 396: ! 397: for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) { ! 398: ! 399: lprintf(xm,LOG_INFO,"Sending End-of-Text (EOT) indicator (%d)",errors+1); ! 400: ! 401: while((ch=getcom(0))!=NOINP && is_connected(xm)) ! 402: lprintf(xm,LOG_INFO,"Throwing out received: %s",chr((uchar)ch)); ! 403: ! 404: putcom(EOT); ! 405: if((ch=getcom(xm->recv_timeout))==NOINP) ! 406: continue; ! 407: lprintf(xm,LOG_INFO,"Received %s",chr((uchar)ch)); ! 408: if(ch==ACK) ! 409: return(TRUE); ! 410: if(ch==CAN && ++cans>1) ! 411: break; ! 412: if(ch==NAK && errors==0 && (*(xm->mode)&(YMODEM|GMODE))==YMODEM) { ! 413: continue; /* chuck's double EOT trick so don't complain */ ! 414: } ! 415: lprintf(xm,LOG_WARNING,"Expected ACK"); ! 416: } ! 417: return(FALSE); ! 418: } ! 419: ! 420: BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start, ulong* sent) ! 421: { ! 422: BOOL success=FALSE; ! 423: ulong sent_bytes=0; ! 424: char block[1024]; ! 425: size_t block_len; ! 426: unsigned block_num; ! 427: size_t i; ! 428: size_t rd; ! 429: time_t startfile; ! 430: struct stat st; ! 431: ! 432: if(sent!=NULL) ! 433: *sent=0; ! 434: ! 435: if(start!=NULL) ! 436: *start=time(NULL); ! 437: ! 438: fstat(fileno(fp),&st); ! 439: ! 440: if(xm->total_files==0) ! 441: xm->total_files=1; ! 442: ! 443: if(xm->total_bytes==0) ! 444: xm->total_bytes=st.st_size; ! 445: ! 446: do { ! 447: /* try */ ! 448: if(*(xm->mode)&YMODEM) { ! 449: ! 450: if(!xmodem_get_mode(xm)) ! 451: break; ! 452: ! 453: memset(block,0,sizeof(block)); ! 454: SAFECOPY(block,getfname(fname)); ! 455: i=sprintf(block+strlen(block)+1,"%lu %lo 0 0 %d %ld" ! 456: ,(ulong)st.st_size ! 457: ,st.st_mtime ! 458: ,xm->total_files-xm->sent_files ! 459: ,xm->total_bytes-xm->sent_bytes); ! 460: ! 461: lprintf(xm,LOG_INFO,"Sending Ymodem header block: '%s'",block+strlen(block)+1); ! 462: ! 463: block_len=strlen(block)+1+i; ! 464: for(xm->errors=0;xm->errors<=xm->max_errors && !is_cancelled(xm) && is_connected(xm);xm->errors++) { ! 465: xmodem_put_block(xm, block, block_len <=128 ? 128:1024, 0 /* block_num */); ! 466: if(xmodem_get_ack(xm,1,0)) ! 467: break; ! 468: } ! 469: if(xm->errors>=xm->max_errors || is_cancelled(xm)) { ! 470: lprintf(xm,LOG_ERR,"Failed to send header block"); ! 471: break; ! 472: } ! 473: } ! 474: ! 475: if(!xmodem_get_mode(xm)) ! 476: break; ! 477: ! 478: startfile=time(NULL); /* reset time, don't count header block */ ! 479: if(start!=NULL) ! 480: *start=startfile; ! 481: ! 482: block_num=1; ! 483: xm->errors=0; ! 484: while(sent_bytes < (ulong)st.st_size && xm->errors<=xm->max_errors && !is_cancelled(xm) ! 485: && is_connected(xm)) { ! 486: fseek(fp,sent_bytes,SEEK_SET); ! 487: memset(block,CPMEOF,xm->block_size); ! 488: if((rd=fread(block,1,xm->block_size,fp))!=xm->block_size ! 489: && (long)(block_num*xm->block_size) < st.st_size) { ! 490: lprintf(xm,LOG_ERR,"READ ERROR %d instead of %d at offset %lu" ! 491: ,rd,xm->block_size,(block_num-1)*(long)xm->block_size); ! 492: xm->errors++; ! 493: continue; ! 494: } ! 495: if(xm->progress!=NULL) ! 496: xm->progress(xm->cbdata,block_num,ftell(fp),st.st_size,startfile); ! 497: xmodem_put_block(xm, block, xm->block_size, block_num); ! 498: if(!xmodem_get_ack(xm,5,block_num)) { ! 499: xm->errors++; ! 500: lprintf(xm,LOG_WARNING,"Error #%d at offset %ld" ! 501: ,xm->errors,ftell(fp)-xm->block_size); ! 502: } else { ! 503: block_num++; ! 504: sent_bytes+=rd; ! 505: } ! 506: } ! 507: if(sent_bytes >= (ulong)st.st_size && !is_cancelled(xm) && is_connected(xm)) { ! 508: ! 509: #if 0 /* !SINGLE_THREADED */ ! 510: lprintf(LOG_DEBUG,"Waiting for output buffer to empty... "); ! 511: if(WaitForEvent(outbuf_empty,5000)!=WAIT_OBJECT_0) ! 512: lprintf(xm,LOG_WARNING,"FAILURE"); ! 513: #endif ! 514: if(xmodem_put_eot(xm)) /* end-of-text, wait for ACK */ ! 515: success=TRUE; ! 516: } ! 517: } while(0); ! 518: /* finally */ ! 519: ! 520: if(!success) ! 521: xmodem_cancel(xm); ! 522: ! 523: if(sent!=NULL) ! 524: *sent=sent_bytes; ! 525: ! 526: return(success); ! 527: } ! 528: ! 529: ! 530: const char* xmodem_source(void) ! 531: { ! 532: return(__FILE__); ! 533: } ! 534: ! 535: char* xmodem_ver(char *buf) ! 536: { ! 537: sscanf("$Revision: 1.26 $", "%*s %s", buf); ! 538: ! 539: return(buf); ! 540: } ! 541: ! 542: void xmodem_init(xmodem_t* xm, void* cbdata, long* mode ! 543: ,int (*lputs)(void*, int level, const char* str) ! 544: ,void (*progress)(void* unused, unsigned block_num, ulong offset, ulong fsize, time_t t) ! 545: ,int (*send_byte)(void*, uchar ch, unsigned timeout) ! 546: ,int (*recv_byte)(void*, unsigned timeout) ! 547: ,BOOL (*is_connected)(void*) ! 548: ,BOOL (*is_cancelled)(void*)) ! 549: { ! 550: memset(xm,0,sizeof(xmodem_t)); ! 551: ! 552: /* Use sane default values */ ! 553: xm->send_timeout=10; /* seconds */ ! 554: xm->recv_timeout=10; /* seconds */ ! 555: xm->byte_timeout=3; /* seconds */ ! 556: xm->ack_timeout=10; /* seconds */ ! 557: ! 558: xm->block_size=1024; ! 559: xm->max_errors=9; ! 560: xm->g_delay=1; ! 561: ! 562: xm->cbdata=cbdata; ! 563: xm->mode=mode; ! 564: xm->lputs=lputs; ! 565: xm->progress=progress; ! 566: xm->send_byte=send_byte; ! 567: xm->recv_byte=recv_byte; ! 568: xm->is_connected=is_connected; ! 569: xm->is_cancelled=is_cancelled; ! 570: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.