|
|
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.