Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

xmodem.c 16.3 KB
Newer Older
1
/* Synchronet X/YMODEM Functions */
Rob Swindell's avatar
Rob Swindell committed
2
/* Synchronet X/YMODEM Functions */
3 4 5 6 7

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
8
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

23 24 25
/* Standard headers */
#include <stdio.h>
#include <sys/stat.h>	/* struct stat */
deuce's avatar
deuce committed
26
#include <stdarg.h>		/* va_list */
27 28

/* smblib */
rswindell's avatar
rswindell committed
29
#include "crc16.h"
30 31

/* xpdev */
32
#include "genwrap.h"	/* YIELD */
33
#include "dirwrap.h"	/* getfname */
34
#include "filewrap.h"	/* fileoff_t */
35 36 37

/* sexyz */
#include "sexyz.h"
38

39 40 41 42 43 44 45 46 47 48
#define getcom(t)	xm->recv_byte(xm->cbdata,t)
#define putcom(ch)	xm->send_byte(xm->cbdata,ch,xm->send_timeout)

static int lprintf(xmodem_t* xm, int level, const char *fmt, ...)
{
	va_list argptr;
	char	sbuf[1024];

	if(xm->lputs==NULL)
		return(-1);
49 50 51
	if(xm->log_level != NULL)
		if(level > *xm->log_level)
			return 0;
52 53 54 55 56 57 58

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
    return(xm->lputs(xm->cbdata,level,sbuf));
}
59

60 61 62 63 64 65 66
static BOOL is_connected(xmodem_t* xm)
{
	if(xm->is_connected!=NULL)
		return(xm->is_connected(xm->cbdata));
	return(TRUE);
}

67 68 69 70 71 72 73
static BOOL is_cancelled(xmodem_t* xm)
{
	if(xm->is_cancelled!=NULL)
		return(xm->cancelled=xm->is_cancelled(xm->cbdata));
	return(xm->cancelled);
}

74 75 76 77 78 79
static void xmodem_flush(xmodem_t* xm)
{
	if(xm->flush!=NULL)
		xm->flush(xm);
}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
static char *chr(uchar ch)
{
	static char str[25];

	switch(ch) {
		case SOH:	return("SOH");
		case STX:	return("STX");
		case ETX:	return("ETX");
		case EOT:	return("EOT");
		case ACK:	return("ACK");
		case NAK:	return("NAK");
		case CAN:	return("CAN");
	}
	if(ch>=' ' && ch<='~')
		sprintf(str,"'%c' (%02Xh)",ch,ch);
	else
		sprintf(str,"%u (%02Xh)",ch,ch);
	return(str); 
}


101
int xmodem_put_ack(xmodem_t* xm)
102
{
103 104
	int result;

105
	while(getcom(0)!=NOINP && is_connected(xm))
106
		;				/* wait for any trailing data */
107 108 109 110 111
	result = putcom(ACK);

	xmodem_flush(xm);

	return result;
112 113
}

114
int xmodem_put_nak(xmodem_t* xm, unsigned block_num)
115
{
116
	int i,dump_count=0;
117
	int	result;
118 119 120 121 122 123 124 125 126 127 128

	/* 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);
129 130

	if(block_num<=1) {
131
		if(*(xm->mode)&GMODE) {		/* G for X/Ymodem-G */
132
			lprintf(xm,LOG_INFO,"Block %u: Requesting mode: Streaming, 16-bit CRC", block_num);
133
			result = putcom('G');
134
		} else if(*(xm->mode)&CRC) {	/* C for CRC */
135
			lprintf(xm,LOG_INFO,"Block %u: Requesting mode: 16-bit CRC", block_num);
136
			result = putcom('C');
137
		} else {				/* NAK for checksum */
138
			lprintf(xm,LOG_INFO,"Block %u: Requesting mode: 8-bit Checksum", block_num);
139
			result = putcom(NAK);
140
		}
141 142 143 144 145 146
	} else
		result = putcom(NAK);

	xmodem_flush(xm);

	return result;
147 148
}

149
int xmodem_cancel(xmodem_t* xm)
150 151
{
	int i;
152
	int result;
153

154
	if(!is_cancelled(xm) && is_connected(xm)) {
155
		xm->cancelled=TRUE;
156
		for(i=0;i<8 && is_connected(xm);i++)
157 158
			if((result=putcom(CAN))!=0)
				return result;
159
		for(i=0;i<10 && is_connected(xm);i++)
160 161
			if((result=putcom('\b'))!=0)
				return result;
162
	}
163

164 165
	xmodem_flush(xm);

166
	return SUCCESS;
167 168 169
}

/****************************************************************************/
170
/* Return 0 on success														*/
171
/****************************************************************************/
172
int xmodem_get_block(xmodem_t* xm, uchar* block, unsigned expected_block_num)
173
{
deuce's avatar
deuce committed
174 175 176 177 178 179
	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;
180

181
	lprintf(xm, LOG_DEBUG, "Requesting data block %u", expected_block_num);
182
	for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) {
183

184
		i=getcom(expected_block_num<=1 ? 3 : 10);
185
		if(eot && i!=EOT && i!=NOINP)
186 187 188 189
			eot=0;
		if(can && i!=CAN)
			can=0;
		switch(i) {
190
			case SOH: /* 128-byte blocks */
191
				xm->block_size=XMODEM_MIN_BLOCK_SIZE;
192
				break;
193 194 195 196
			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);
197
					return FAILURE;
198
				}
199
				xm->block_size=XMODEM_MAX_BLOCK_SIZE;
200 201
				break;
			case EOT:
202
				lprintf(xm,LOG_DEBUG,"Block %u: EOT received", expected_block_num);
203
				if(!((*xm->mode) & GMODE) && !eot) {
204 205 206
					lprintf(xm,LOG_INFO,"NAKing first EOT");
					eot=1;	
					xmodem_put_nak(xm,expected_block_num); /* chuck's double EOT trick */
207 208
					continue; 
				}
209
				return(EOT);
210 211 212
			case CAN:
				if(!can) {			/* must get two CANs in a row */
					can=1;
213 214
					lprintf(xm,LOG_WARNING,"Block %u: Received CAN  Expected SOH, STX, or EOT"
						,expected_block_num);
215 216
					continue; 
				}
217
				lprintf(xm,LOG_WARNING,"Block %u: Canceled remotely", expected_block_num);
218
				return(CAN);
219
			default:
220 221
				lprintf(xm,LOG_WARNING,"Block %u: Received %s  Expected SOH, STX, or EOT"
					,expected_block_num, chr((uchar)i));
222
				/* Fall-through */
223
			case NOINP: 	/* Nothing came in */
224 225 226
				if(eot)
					return(EOT);
				return(NOINP);
227
		}
228
		if((i=getcom(xm->byte_timeout))==NOINP)
229
			break; 
230
		block_num=i;
231
		if((i=getcom(xm->byte_timeout))==NOINP)
232 233
			break; 
		block_inv=i;
234
		calc_crc=calc_chksum=0;
235
		for(b=0;b<xm->block_size && is_connected(xm);b++) {
236
			if((i=getcom(xm->byte_timeout))==NOINP)
237 238
				break;
			block[b]=i;
239
			if((*xm->mode)&CRC)
240 241 242 243 244
				calc_crc=ucrc16(block[b],calc_crc);
			else
				calc_chksum+=block[b]; 
		}

245 246
		if(b<xm->block_size)
			break; 
247

248
		if((*xm->mode)&CRC) {
249 250
			crc=getcom(xm->byte_timeout)<<8;
			crc|=getcom(xm->byte_timeout); 
251 252
		}
		else
253
			chksum=getcom(xm->byte_timeout);
254

255
		if(block_num!=(uchar)~block_inv) {
256 257
			lprintf(xm,LOG_WARNING,"Block %u: Block number bit error (0x%02X vs 0x%02x)"
				,expected_block_num, block_num,(uchar)~block_inv);
258 259 260
			break; 
		}

261
		if((*xm->mode)&CRC) {
262
			if(crc!=calc_crc) {
263
				lprintf(xm,LOG_WARNING,"Block %u: CRC ERROR", block_num); 
264
				break;
265
			}
266 267
		}
		else	/* CHKSUM */
268
		{
269
			if(chksum!=calc_chksum) {
270
				lprintf(xm,LOG_WARNING,"Block %u: CHECKSUM ERROR", block_num); 
271
				break;
272
			}
273
		}
274 275 276 277

		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);
278 279
			if((*xm->mode)&XMODEM && expected_block_num==1 && block_num==0)
				return(NOT_XMODEM);
280 281 282 283 284 285 286
			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; 
		}

287
		return SUCCESS;	/* Success */
288 289
	}

290
	return FAILURE;		/* Failure */
291 292 293 294 295
}

/*****************/
/* Sends a block */
/*****************/
296
int xmodem_put_block(xmodem_t* xm, uchar* block, unsigned block_size, unsigned block_num)
297
{
deuce's avatar
deuce committed
298 299 300 301
	int			result;
	uchar		ch,chksum;
    uint		i;
	uint16_t	crc;
302

303
	if(block_size==XMODEM_MIN_BLOCK_SIZE)
304
		result=putcom(SOH);
305
	else			/* 1024 */
306 307 308
		result=putcom(STX);
	if(result!=0)
		return(result);
309
	ch=(uchar)(block_num&0xff);
310 311 312 313
	if((result=putcom(ch))!=0)
		return result;
	if((result=putcom((uchar)~ch))!=0)
		return result;
314 315
	chksum=0;
	crc=0;
316
	for(i=0;i<block_size && is_connected(xm);i++) {
317 318
		if((result=putcom(block[i]))!=0)
			return result;
319
		if((*xm->mode)&CRC)
320 321 322 323 324
			crc=ucrc16(block[i],crc);
		else
			chksum+=block[i]; 
	}

325
	if((*xm->mode)&CRC) {
326 327
		if((result=	putcom((uchar)(crc >> 8)))!=0)
			return result;
328 329 330 331 332 333 334
		result = putcom((uchar)(crc&0xff)); 
	} else
		result = putcom(chksum);

	xmodem_flush(xm);

	return result;
335 336 337 338
}

/************************************************************/
/* Gets an acknowledgement - usually after sending a block	*/
339
/* Returns ACK if ack received								*/
340
/************************************************************/
341
int xmodem_get_ack(xmodem_t* xm, unsigned tries, unsigned block_num)
342
{
343
	int i=NOINP,can=0;
rswindell's avatar
rswindell committed
344
	unsigned errors;
345

346
	for(errors=0;errors<tries && is_connected(xm);) {
347

348
		if((*xm->mode)&GMODE) {		/* Don't wait for ACK on X/Ymodem-G */
349
			SLEEP(xm->g_delay);
350
			if(getcom(0)==CAN) {
351
				lprintf(xm,LOG_WARNING,"Block %u: !Canceled remotely", block_num);
352
				xmodem_cancel(xm);
353
				return(CAN); 
354
			}
355
			return(ACK); 
356 357
		}

358
		i=getcom(xm->ack_timeout);
359 360 361
		if(can && i!=CAN)
			can=0;
		if(i==ACK)
362
			break;
363
		if(i==CAN) {
364
			if(can) {	/* 2 CANs in a row */
365
				lprintf(xm,LOG_WARNING,"Block %u: !Canceled remotely", block_num);
366
				xmodem_cancel(xm);
367
				return(CAN); 
368 369 370 371
			}
			can=1; 
		}
		if(i!=NOINP) {
372
			lprintf(xm,LOG_WARNING,"Block %u: !Received %s  Expected ACK"
373
				,block_num, chr((uchar)i));
374
			if(i!=CAN)
375 376 377 378
				return(i); 
		}
		if(i!=CAN)
			errors++;
379 380
	}

381
	return(i);
382
}
383

rswindell's avatar
rswindell committed
384 385 386 387 388 389 390 391 392
BOOL xmodem_get_mode(xmodem_t* xm)
{
	int			i;
	unsigned	errors;
	unsigned	can;

	lprintf(xm,LOG_INFO,"Waiting for transfer mode request...");

	*(xm->mode)&=~(GMODE|CRC);
393
	for(errors=can=0;errors<=xm->max_errors && is_connected(xm);errors++) {
rswindell's avatar
rswindell committed
394 395 396 397 398
		i=getcom(xm->recv_timeout);
		if(can && i!=CAN)
			can=0;
		switch(i) {
			case NAK: 		/* checksum */
rswindell's avatar
rswindell committed
399
				lprintf(xm,LOG_INFO,"Receiver requested mode: 8-bit Checksum");
rswindell's avatar
rswindell committed
400 401
				return(TRUE); 
			case 'C':
rswindell's avatar
rswindell committed
402
				lprintf(xm,LOG_INFO,"Receiver requested mode: 16-bit CRC");
403 404
				if(!xm->crc_mode_supported)
					continue;
rswindell's avatar
rswindell committed
405 406 407
				*(xm->mode)|=CRC;
				return(TRUE); 
			case 'G':
rswindell's avatar
rswindell committed
408
				lprintf(xm,LOG_INFO,"Receiver requested mode: Streaming, 16-bit CRC");
409 410
				if(!xm->crc_mode_supported || !xm->g_mode_supported)
					continue;
rswindell's avatar
rswindell committed
411 412 413 414
				*(xm->mode)|=(GMODE|CRC);
				return(TRUE); 
			case CAN:
				if(can) {
415
					lprintf(xm,LOG_WARNING,"Canceled remotely");
rswindell's avatar
rswindell committed
416 417 418 419 420 421 422 423 424 425 426 427 428
					return(FALSE); 
				}
				can=1; 
				break;
			case NOINP:
				break;
			default:
				lprintf(xm,LOG_WARNING,"Received %s  Expected NAK, C, or G"
					,chr((uchar)i));
				break;
		} 
	}

429
	lprintf(xm,LOG_ERR,"Failed to get transfer mode request from receiver");
rswindell's avatar
rswindell committed
430 431 432 433 434 435 436
	return(FALSE);
}

BOOL xmodem_put_eot(xmodem_t* xm)
{
	int ch;
	unsigned errors;
437
	unsigned cans=0;
rswindell's avatar
rswindell committed
438

439
	for(errors=0;errors<=xm->max_errors && is_connected(xm);errors++) {
rswindell's avatar
rswindell committed
440

441
		lprintf(xm,LOG_INFO,"Sending End-of-Text (EOT) indicator (%d)",errors+1);
rswindell's avatar
rswindell committed
442

443
		while((ch=getcom(0))!=NOINP && is_connected(xm))
rswindell's avatar
rswindell committed
444 445 446
			lprintf(xm,LOG_INFO,"Throwing out received: %s",chr((uchar)ch));

		putcom(EOT);
447
		xmodem_flush(xm);
rswindell's avatar
rswindell committed
448 449
		if((ch=getcom(xm->recv_timeout))==NOINP)
			continue;
450
		lprintf(xm,LOG_INFO,"Received %s",chr((uchar)ch)); 
rswindell's avatar
rswindell committed
451 452
		if(ch==ACK)
			return(TRUE);
453 454
		if(ch==CAN && ++cans>1)
			break;
rswindell's avatar
rswindell committed
455 456 457 458 459 460 461 462
		if(ch==NAK && errors==0 && (*(xm->mode)&(YMODEM|GMODE))==YMODEM) {
			continue;  /* chuck's double EOT trick so don't complain */
		}
		lprintf(xm,LOG_WARNING,"Expected ACK");
	}
	return(FALSE);
}

463
BOOL xmodem_send_file(xmodem_t* xm, const char* fname, FILE* fp, time_t* start, uint64_t* sent)
464 465
{
	BOOL		success=FALSE;
466
	int64_t		sent_bytes=0;
467
	char		block[XMODEM_MAX_BLOCK_SIZE];
468 469 470 471 472 473
	size_t		block_len;
	unsigned	block_num;
	size_t		i;
	size_t		rd;
	time_t		startfile;
	struct		stat st;
474
	BOOL		sent_header=FALSE;
475 476 477 478 479 480 481

	if(sent!=NULL)	
		*sent=0;

	if(start!=NULL)		
		*start=time(NULL);

482 483 484 485
	if(fstat(fileno(fp),&st) != 0) {
		lprintf(xm,LOG_ERR,"Failed to fstat file");
		return FALSE;
	}
486 487 488 489 490 491 492

	if(xm->total_files==0)
		xm->total_files=1;

	if(xm->total_bytes==0)
		xm->total_bytes=st.st_size;

493 494 495
	do {
	/* try */
		if(*(xm->mode)&YMODEM) {
496

497 498
			if(!xmodem_get_mode(xm))
				break;
499

500 501
			memset(block,0,sizeof(block));
			SAFECOPY(block,getfname(fname));
deuce's avatar
deuce committed
502
			i=sprintf(block+strlen(block)+1,"%"PRIu64" %"PRIoMAX" 0 0 %lu %"PRId64
503
				,(uint64_t)st.st_size
deuce's avatar
deuce committed
504
				,(uintmax_t)st.st_mtime
505 506 507
				,xm->total_files-xm->sent_files
				,xm->total_bytes-xm->sent_bytes);
			
508
			lprintf(xm,LOG_INFO,"Sending YMODEM header block: '%s'",block+strlen(block)+1);
509 510
			
			block_len=strlen(block)+1+i;
511
			for(xm->errors=0;xm->errors<=xm->max_errors && !is_cancelled(xm) && is_connected(xm);xm->errors++) {
rswindell's avatar
rswindell committed
512 513 514
				int ch;
				while((ch=getcom(0))!=NOINP && is_connected(xm))
					lprintf(xm,LOG_INFO,"Throwing out received: %s",chr((uchar)ch));
515
				xmodem_put_block(xm, (uchar*)block, block_len <=XMODEM_MIN_BLOCK_SIZE ? XMODEM_MIN_BLOCK_SIZE:XMODEM_MAX_BLOCK_SIZE, 0  /* block_num */);
516
				if((i=xmodem_get_ack(xm,/* tries: */1, /* block_num: */0)) == ACK) {
517
					sent_header=TRUE;
518
					break; 
519
				}
520 521 522 523 524 525 526
				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;
				}
527
			}
528
			if(xm->errors>xm->max_errors || is_cancelled(xm)) {
529 530 531
				lprintf(xm,LOG_ERR,"Failed to send header block");
				break;
			}
532
		}
533 534 535 536 537 538 539 540 541

		if(!xmodem_get_mode(xm))
			break;

		startfile=time(NULL);	/* reset time, don't count header block */
		if(start!=NULL)
			*start=startfile;

		block_num=1;
542
		xm->errors=0;
543
		while(sent_bytes < st.st_size && xm->errors<=xm->max_errors && !is_cancelled(xm)
544
			&& is_connected(xm)) {
545
			fseeko(fp,(off_t)sent_bytes,SEEK_SET);
546
			memset(block,CPMEOF,xm->block_size);
547
			if(!sent_header) {
548
				if(xm->block_size>XMODEM_MIN_BLOCK_SIZE) {
549 550
					if((sent_bytes+xm->block_size) > st.st_size) {
						if((sent_bytes+xm->block_size-XMODEM_MIN_BLOCK_SIZE) >= st.st_size) {
551
							lprintf(xm,LOG_INFO,"Falling back to 128-byte blocks for end of file");
552
							xm->block_size=XMODEM_MIN_BLOCK_SIZE;
553
						}
554 555 556
					}
				}
			}
557
			if((rd=fread(block,1,xm->block_size,fp))!=xm->block_size 
558
				&& (sent_bytes + rd) != st.st_size) {
559 560
				lprintf(xm,LOG_ERR,"ERROR %d reading %u bytes at file offset %"PRId64
					,errno,xm->block_size,(int64_t)ftello(fp));
561
				xm->errors++;
562 563 564
				continue;
			}
			if(xm->progress!=NULL)
565
				xm->progress(xm->cbdata,block_num,ftello(fp),st.st_size,startfile);
566
			xmodem_put_block(xm, (uchar*)block, xm->block_size, block_num);
567
			if(xmodem_get_ack(xm, /* tries: */5,block_num) != ACK) {
568
				xm->errors++;
569 570
				lprintf(xm,LOG_WARNING,"Block %u: Error #%d at offset %"PRId64
					,block_num, xm->errors,(int64_t)(ftello(fp)-xm->block_size));
571
				if(xm->errors==3 && block_num==1 && xm->block_size>XMODEM_MIN_BLOCK_SIZE) {
572
					lprintf(xm,LOG_NOTICE,"Block %u: Falling back to 128-byte blocks", block_num);
573
					xm->block_size=XMODEM_MIN_BLOCK_SIZE;
574
				}
575 576 577 578
			} else {
				block_num++; 
				sent_bytes+=rd;
			}
579
		}
580
		if(sent_bytes >= st.st_size && !is_cancelled(xm) && is_connected(xm)) {
581 582 583 584 585 586 587 588

	#if 0 /* !SINGLE_THREADED */
			lprintf(LOG_DEBUG,"Waiting for output buffer to empty... ");
			if(WaitForEvent(outbuf_empty,5000)!=WAIT_OBJECT_0)
				lprintf(xm,LOG_WARNING,"FAILURE");
	#endif
			if(xmodem_put_eot(xm))	/* end-of-text, wait for ACK */
				success=TRUE;
589
		}
590 591 592 593 594
	} while(0);
	/* finally */

	if(!success)
		xmodem_cancel(xm);
595 596 597 598 599 600 601 602

	if(sent!=NULL)
		*sent=sent_bytes;

	return(success);
}


603 604 605 606 607
const char* xmodem_source(void)
{
	return(__FILE__);
}

608 609
char* xmodem_ver(char *buf)
{
Rob Swindell's avatar
Rob Swindell committed
610
	return strcpy(buf, "2.0");
611 612
}

613
void xmodem_init(xmodem_t* xm, void* cbdata, long* mode
rswindell's avatar
rswindell committed
614
				,int	(*lputs)(void*, int level, const char* str)
615
				,void	(*progress)(void* unused, unsigned block_num, int64_t offset, int64_t fsize, time_t t)
rswindell's avatar
rswindell committed
616
				,int	(*send_byte)(void*, uchar ch, unsigned timeout)
617
				,int	(*recv_byte)(void*, unsigned timeout)
618
				,BOOL	(*is_connected)(void*)
deuce's avatar
deuce committed
619 620
				,BOOL	(*is_cancelled)(void*)
				,void	(*flush)(void*))
621 622 623 624
{
	memset(xm,0,sizeof(xmodem_t));

	/* Use sane default values */
rswindell's avatar
rswindell committed
625 626 627 628 629
	xm->send_timeout=10;		/* seconds */
	xm->recv_timeout=10;		/* seconds */
	xm->byte_timeout=3;			/* seconds */
	xm->ack_timeout=10;			/* seconds */

630
	xm->block_size=XMODEM_MAX_BLOCK_SIZE;
631
	xm->max_block_size=XMODEM_MAX_BLOCK_SIZE;
632
	xm->max_errors=9;
633
	xm->g_delay=1;
634 635 636

	xm->cbdata=cbdata;
	xm->mode=mode;
637 638
	xm->g_mode_supported=TRUE;
	xm->crc_mode_supported=TRUE;
639
	xm->lputs=lputs;
rswindell's avatar
rswindell committed
640
	xm->progress=progress;
641 642
	xm->send_byte=send_byte;
	xm->recv_byte=recv_byte;
643
	xm->is_connected=is_connected;
644
	xm->is_cancelled=is_cancelled;
deuce's avatar
deuce committed
645
	xm->flush=flush;
646
}