Skip to content
Snippets Groups Projects
term.c 80.3 KiB
Newer Older
			conn_send(linebuf,strlen(linebuf),0);
		}
		/* Note, during ASCII uploads, do NOT send ANSI responses and don't
		 * allow speed changes. */
		while((inch=recv_byte(NULL, 0))>=0) {
			ch[0]=inch;
			cterm_write(cterm, ch, 1, NULL, 0, NULL);
static void transfer_complete(BOOL success)
{
	int timeout = success ? settings.xfer_success_keypress_timeout : settings.xfer_failure_keypress_timeout;

	conn_binary_mode_off();
	if(log_fp!=NULL)
		fflush(log_fp);
	/* TODO: Make this pretty (countdown timer) and don't delay a second between keyboard polls */
	lprintf(LOG_NOTICE,"Hit any key or wait %u seconds to continue...", timeout);
	while(timeout > 0) {
		if (kbhit()) {
			if(getch()==0 && getch()<<8 == CIO_KEY_QUIT)
				check_exit(FALSE);
			break;
		}
		timeout--;
		SLEEP(1000);
	}

	erase_transfer_window();
}

void zmodem_upload(struct bbslist *bbs, FILE *fp, char *path)
rswindell's avatar
rswindell committed
	int64_t		fsize;
	struct zmodem_cbdata cbdata;
	draw_transfer_window("ZMODEM Upload");
	cbdata.zm=&zm;
	cbdata.bbs=bbs;
	transfer_buf_len=0;
	zmodem_init(&zm
		,lputs, zmodem_progress
		,data_waiting
		,flush_send);
	zm.log_level=&log_level;

	zm.current_file_num = zm.total_files = 1;	/* ToDo: support multi-file/batch uploads */
	fsize=filelength(fileno(fp));

	lprintf(LOG_INFO,"Sending %s (%"PRId64" KB) via ZMODEM"
		,path,fsize/1024);

	if((success=zmodem_send_file(&zm, path, fp
		,/* ZRQINIT? */TRUE, /* start_time */NULL, /* sent_bytes */ NULL)) == TRUE)
		zmodem_get_zfin(&zm);

	fclose(fp);

BOOL zmodem_duplicate_callback(void *cbdata, void *zm_void)
{
	struct	text_info txtinfo;
	struct ciolib_screen *savscrn;
	BOOL	ret=FALSE;
	int		i;
	char 	*opts[4]={
					 "Overwrite"
					,"Choose New Name"
					,"Cancel Download"
					,NULL
				  };
	struct zmodem_cbdata *cb=(struct zmodem_cbdata *)cbdata;
	zmodem_t	*zm=(zmodem_t *)zm_void;
	char		fpath[MAX_PATH+1];
	BOOL		loop=TRUE;
	int			old_hold=hold_update;
    savscrn = savescreen();
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	setfont(0, FALSE, 3);
	setfont(0, FALSE, 4);
	window(1, 1, txtinfo.screenwidth, txtinfo.screenheight);
	while(loop) {
		loop=FALSE;
		i=0;
		uifc.helpbuf="Duplicate file... choose action\n";
		switch(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Duplicate File Name",opts)) {
			case -1:
				if (check_exit(FALSE)) {
					ret=FALSE;
					break;
				}
				loop=TRUE;
				break;
			case 0:	/* Overwrite */
				SAFEPRINTF2(fpath,"%s/%s",cb->bbs->dldir,zm->current_file_name);
				unlink(fpath);
				ret=TRUE;
				break;
			case 1:	/* Choose new name */
				uifc.changes=0;
				uifc.helpbuf="Duplicate Filename... enter new name";
				if(uifc.input(WIN_MID|WIN_SAV,0,0,"New Filename: ",zm->current_file_name,sizeof(zm->current_file_name)-1,K_EDIT)==-1) {
					loop=TRUE;
				}
				else {
					if(uifc.changes)
						ret=TRUE;
					else
						loop=TRUE;
				}
				break;
		}
	restorescreen(savscrn);
	freescreen(savscrn);
	gotoxy(txtinfo.curx, txtinfo.cury);
	hold_update=old_hold;
void zmodem_download(struct bbslist *bbs)
	uint64_t	bytes_received;
	if(safe_mode)
		return;
	draw_transfer_window("ZMODEM Download");
	transfer_buf_len=0;
		,data_waiting
		,flush_send);
	zm.log_level=&log_level;
	zm.duplicate_filename=zmodem_duplicate_callback;

	files_received=zmodem_recv_files(&zm,bbs->dldir,&bytes_received);

		lprintf(LOG_INFO,"Received %u files (%"PRId64" bytes) successfully", files_received, bytes_received);
	transfer_complete(files_received);
/* End of Zmodem Stuff */
/* X/Y-MODEM stuff */

uchar	block[1024];					/* Block buffer 					*/
ulong	block_num;						/* Block number 					*/

static BOOL xmodem_check_abort(void* vp)
{
	xmodem_t* xm = (xmodem_t*)vp;
	static time_t			last_check=0;
	time_t					now=time(NULL);
	if (xm == NULL)
		return FALSE;

	if(last_check != now) {
		last_check=now;
		while(kbhit()) {
			switch((key=getch())) {
				case ESC:
				case CTRL_C:
				case CTRL_X:
					xm->cancelled=TRUE;
					break;
				case 0:
					key |= (getch() << 8);
					if(key==CIO_KEY_MOUSE)
						getmouse(NULL);
					if (key==CIO_KEY_QUIT) {
						if (check_exit(FALSE))
							xm->cancelled=TRUE;
					}
		}
	}
	return(xm->cancelled);
}
deuce's avatar
deuce committed
/****************************************************************************/
/* Returns the number of blocks required to send len bytes					*/
/****************************************************************************/
uint64_t num_blocks(unsigned curr_block, uint64_t offset, uint64_t len, unsigned block_size)
deuce's avatar
deuce committed
{
	uint64_t blocks;
deuce's avatar
deuce committed

deuce's avatar
deuce committed
	blocks=len/block_size;
	if(len%block_size)
		blocks++;
	return(curr_block+blocks);
deuce's avatar
deuce committed
}

#if defined(__BORLANDC__)
	#pragma argsused
#endif
void xmodem_progress(void* cbdata, unsigned block_num, int64_t offset, int64_t fsize, time_t start)
	uint64_t	total_blocks;
deuce's avatar
deuce committed
	unsigned	cps;
deuce's avatar
deuce committed
	time_t		t;
	time_t		now;
	static time_t last_progress;
	int			old_hold=hold_update;
	xmodem_t*	xm=(xmodem_t*)cbdata;

	now=time(NULL);
	if(now-last_progress>0 || offset >= fsize) {
deuce's avatar
deuce committed
		hold_update = TRUE;
		window(((trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2)+2
				, ((trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2)+1
				, ((trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2) + TRANSFER_WIN_WIDTH - 2
				, ((trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2)+5);
		gotoxy(1,1);
		textattr(LIGHTCYAN | (BLUE<<4));
		t=now-start;
		if(t<=0)
			t=1;
		if((cps=(unsigned)(offset/t))==0)
deuce's avatar
deuce committed
			cps=1;		/* cps so far */
		l=(time_t)(fsize/cps);	/* total transfer est time */
deuce's avatar
deuce committed
		l-=t;			/* now, it's est time left */
		if(l<0) l=0;
		if((*(xm->mode))&SEND) {
			total_blocks=num_blocks(block_num,offset,fsize,xm->block_size);
deuce's avatar
deuce committed
			cprintf("Block (%lu%s): %u/%"PRId64"  Byte: %"PRId64
deuce's avatar
deuce committed
				,xm->block_size%1024L ? xm->block_size: xm->block_size/1024L
				,xm->block_size%1024L ? "" : "K"
				,block_num
				,total_blocks
				,offset);
			clreol();
			cputs("\r\n");
			cprintf("Time: %lu:%02lu/%lu:%02lu  %u cps"
				,(ulong)(t/60L)
				,(ulong)(t%60L)
				,(ulong)(l/60L)
				,(ulong)(l%60L)
deuce's avatar
deuce committed
				,cps
				);
			clreol();
			cputs("\r\n");
			cprintf("%*s%3d%%\r\n", TRANSFER_WIN_WIDTH/2-5, ""
deuce's avatar
deuce committed
				,fsize?(long)(((float)offset/(float)fsize)*100.0):100);
			l = fsize?(long)(((float)offset/(float)fsize)*60.0):60;
deuce's avatar
deuce committed
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					, 60-l, "");
		} else if((*(xm->mode))&YMODEM) {
			cprintf("Block (%lu%s): %lu  Byte: %"PRId64
deuce's avatar
deuce committed
				,xm->block_size%1024L ? xm->block_size: xm->block_size/1024L
				,xm->block_size%1024L ? "" : "K"
				,block_num
				,offset);
			clreol();
			cputs("\r\n");
			cprintf("Time: %lu:%02lu/%lu:%02lu  %u cps"
				,(ulong)(t/60L)
				,(ulong)(t%60L)
				,(ulong)(l/60L)
				,(ulong)(l%60L)
deuce's avatar
deuce committed
				,cps);
			clreol();
			cputs("\r\n");
			cprintf("%*s%3d%%\r\n", TRANSFER_WIN_WIDTH/2-5, ""
deuce's avatar
deuce committed
				,fsize?(long)(((float)offset/(float)fsize)*100.0):100);
			l = fsize?(long)(((float)offset/(float)fsize)*60.0):60;
deuce's avatar
deuce committed
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
					, 60-l, "");
		} else { /* XModem receive */
			cprintf("Block (%lu%s): %lu  Byte: %"PRId64
deuce's avatar
deuce committed
				,xm->block_size%1024L ? xm->block_size: xm->block_size/1024L
				,xm->block_size%1024L ? "" : "K"
				,block_num
				,offset);
			clreol();
			cputs("\r\n");
			cprintf("Time: %lu:%02lu  %u cps"
deuce's avatar
deuce committed
				,cps);
			clreol();
		}
		last_progress=now;
		hold_update = FALSE;
		gotoxy(wherex(), wherey());
		hold_update = old_hold;
	}
static int recv_g(void *cbdata, unsigned timeout)
{
	xmodem_t	*xm=(xmodem_t *)cbdata;
	xm->recv_byte=recv_byte;
	return('G');
}

static int recv_c(void *cbdata, unsigned timeout)
{
	xmodem_t	*xm=(xmodem_t *)cbdata;
	xm->recv_byte=recv_byte;
	return('C');
}

static int recv_nak(void *cbdata, unsigned timeout)
{
	xmodem_t	*xm=(xmodem_t *)cbdata;
deuce's avatar
deuce committed
	return(NAK);
}

void xmodem_upload(struct bbslist *bbs, FILE *fp, char *path, long mode, int lastch)
	xmodem_t	xm;

	conn_binary_mode_on();

	xmodem_init(&xm
		,/* cbdata */&xm
		,&mode
		,lputs
		,xmodem_progress
		,send_byte
		,recv_byte
		,is_connected
		,flush_send);
	xm.log_level=&log_level;
	if(!data_waiting(&xm, 0)) {
		switch(lastch) {
			case 'G':
				xm.recv_byte=recv_g;
				break;
			case 'C':
				xm.recv_byte=recv_c;
				break;
			case NAK:
				xm.recv_byte=recv_nak;
				break;
		}
	}
	if(mode & XMODEM_128B)
deuce's avatar
deuce committed
		xm.block_size=128;
	xm.total_files = 1;	/* ToDo: support multi-file/batch uploads */

	fsize=filelength(fileno(fp));

	if(mode&XMODEM) {
deuce's avatar
deuce committed
		if(mode&GMODE)
			draw_transfer_window("XMODEM-g Upload");
		else
			draw_transfer_window("XMODEM Upload");
		lprintf(LOG_INFO,"Sending %s (%"PRId64" KB) via XMODEM%s"
deuce's avatar
deuce committed
			,path,fsize/1024,(mode&GMODE)?"-g":"");
	}
	else if(mode&YMODEM) {
deuce's avatar
deuce committed
		if(mode&GMODE)
			draw_transfer_window("YMODEM-g Upload");
deuce's avatar
deuce committed
		else
			draw_transfer_window("YMODEM Upload");
		lprintf(LOG_INFO,"Sending %s (%"PRId64" KB) via YMODEM%s"
deuce's avatar
deuce committed
			,path,fsize/1024,(mode&GMODE)?"-g":"");
		fclose(fp);
		conn_binary_mode_off();
	if((success=xmodem_send_file(&xm, path, fp
		,/* start_time */NULL, /* sent_bytes */ NULL)) == TRUE) {
		if(mode&YMODEM) {

			if(xmodem_get_mode(&xm)) {

				lprintf(LOG_INFO,"Sending YMODEM termination block");

				memset(block,0,128);	/* send short block for terminator */
				xmodem_put_block(&xm, block, 128 /* block_size */, 0 /* block_num */);
				if(xmodem_get_ack(&xm,/* tries: */6, /* block_num: */0) != ACK) {
					lprintf(LOG_WARNING,"Failed to receive ACK after terminating block");
BOOL xmodem_duplicate(xmodem_t *xm, struct bbslist *bbs, char *path, size_t pathsize, char *fname)
{
	struct	text_info txtinfo;
	struct ciolib_screen *savscrn;
	BOOL	ret=FALSE;
	int		i;
	char 	*opts[4]={
					 "Overwrite"
					,"Choose New Name"
					,"Cancel Download"
					,NULL
				  };
	char	newfname[MAX_PATH+1];
	BOOL	loop=TRUE;
	int		old_hold=hold_update;

    gettextinfo(&txtinfo);
    savscrn = savescreen();
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	setfont(0, FALSE, 3);
	setfont(0, FALSE, 4);
	window(1, 1, txtinfo.screenwidth, txtinfo.screenheight);

	init_uifc(FALSE, FALSE);

	while(loop) {
		loop=FALSE;
		i=0;
		uifc.helpbuf="Duplicate file... choose action\n";
		switch(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Duplicate File Name",opts)) {
			case -1:
				if (check_exit(FALSE)) {
					ret=FALSE;
					break;
				}
				loop=TRUE;
				break;
			case 0:	/* Overwrite */
				unlink(path);
				ret=TRUE;
				break;
			case 1:	/* Choose new name */
				uifc.changes=0;
				uifc.helpbuf="Duplicate Filename... enter new name";
				SAFECOPY(newfname, getfname(fname));
				if(uifc.input(WIN_MID|WIN_SAV,0,0,"New Filename: ",newfname,sizeof(newfname)-1,K_EDIT)==-1) {
					loop=TRUE;
				}
				else {
					if(uifc.changes) {
						sprintf(path,"%s/%s",bbs->dldir,newfname);
						ret=TRUE;
					}
					else
						loop=TRUE;
				}
				break;
		}
	}

	uifcbail();
	restorescreen(savscrn);
	freescreen(savscrn);
	hold_update=old_hold;
deuce's avatar
deuce committed
void xmodem_download(struct bbslist *bbs, long mode, char *path)
{
	xmodem_t	xm;
	/* The better to -Wunused you with my dear! */
deuce's avatar
deuce committed
	char	str[MAX_PATH+1];
	char	fname[MAX_PATH+1];
deuce's avatar
deuce committed
	int		fnum=0;
	uint	errors;
deuce's avatar
deuce committed
	uint	total_files=0;
	uint	cps;
	uint	wr;
	BOOL	success=FALSE;
deuce's avatar
deuce committed
	long	fmode;
	long	serial_num=-1;
	ulong	tmpftime;
	int64_t	file_bytes=0,file_bytes_left=0;
	int64_t	total_bytes=0;
deuce's avatar
deuce committed
	FILE*	fp=NULL;

	if(safe_mode)
		return;

	if(mode&XMODEM)
deuce's avatar
deuce committed
		if(mode&GMODE)
			draw_transfer_window("XMODEM-g Download");
		else
			draw_transfer_window("XMODEM Download");
	else if(mode&YMODEM) {
		if(mode&GMODE)
			draw_transfer_window("YMODEM-g Download");
			draw_transfer_window("YMODEM Download");
deuce's avatar
deuce committed
	else
		return;

	conn_binary_mode_on();
	xmodem_init(&xm
		,/* cbdata */&xm
		,&mode
		,lputs
		,xmodem_progress
		,send_byte
		,recv_byte
		,is_connected
		,flush_send);
	xm.log_level=&log_level;
	while(is_connected(NULL)) {
deuce's avatar
deuce committed
		if(mode&XMODEM) {
				SAFECOPY(str,path);
			} else {
				SAFEPRINTF2(str,"%s/%s",bbs->dldir,path);
			}
deuce's avatar
deuce committed
			file_bytes=file_bytes_left=0x7fffffff;
		}
deuce's avatar
deuce committed
		else {
			lprintf(LOG_INFO,"Fetching YMODEM header block");
deuce's avatar
deuce committed
			for(errors=0;errors<=xm.max_errors && !xm.cancelled;errors++) {
				xmodem_put_nak(&xm, /* expected_block: */ 0);
deuce's avatar
deuce committed
				i=xmodem_get_block(&xm, block, /* expected_block: */ 0);
					if(!(mode&GMODE)) {
						send_byte(&xm,ACK,10);
						flush_send(&xm);
					}
deuce's avatar
deuce committed
					break;
				}
				if(i==NOINP && (mode&GMODE)) {			/* Timeout */
					mode &= ~GMODE;
					lprintf(LOG_WARNING,"Falling back to %s",
deuce's avatar
deuce committed
						(mode&CRC)?"CRC-16":"Checksum");
				}
				if(i==NOT_YMODEM) {
deuce's avatar
deuce committed
					lprintf(LOG_WARNING,"Falling back to XMODEM%s",(mode&GMODE)?"-g":"");
					mode &= ~(YMODEM);
deuce's avatar
deuce committed
					mode |= XMODEM|CRC;
					erase_transfer_window();
deuce's avatar
deuce committed
					if(uifc.input(WIN_MID|WIN_SAV,0,0,"XMODEM Filename",fname,sizeof(fname),0)==-1) {
						xmodem_cancel(&xm);
						goto end;
					}
deuce's avatar
deuce committed
					if(mode&GMODE)
						draw_transfer_window("XMODEM Download");
					else
						draw_transfer_window("XMODEM-g Download");
					lprintf(LOG_WARNING,"Falling back to XMODEM%s",(mode&GMODE)?"-g":"");
deuce's avatar
deuce committed
						SAFECOPY(str,fname);
					} else {
						SAFEPRINTF2(str,"%s/%s",bbs->dldir,fname);
					}
deuce's avatar
deuce committed
					file_bytes=file_bytes_left=0x7fffffff;
					break;
				if(errors+1>xm.max_errors/3 && mode&CRC && !(mode&GMODE)) {
					lprintf(LOG_NOTICE,"Falling back to 8-bit Checksum mode");
					mode&=~CRC;
				}
			if(errors>xm.max_errors || xm.cancelled) {
				xmodem_cancel(&xm);
deuce's avatar
deuce committed
				goto end;
deuce's avatar
deuce committed
			if(i!=NOT_YMODEM) {
				if(!block[0]) {
					lprintf(LOG_INFO,"Received YMODEM termination block");
deuce's avatar
deuce committed
				}
				file_bytes=total_bytes=0;
				total_files=0;
deuce's avatar
deuce committed
				i=sscanf(((char *)block)+strlen((char *)block)+1,"%"PRId64" %lo %lo %lo %d %"PRId64
deuce's avatar
deuce committed
					,&file_bytes			/* file size (decimal) */
					,&tmpftime 				/* file time (octal unix format) */
					,&fmode 				/* file mode (not used) */
					,&serial_num			/* program serial number */
					,&total_files			/* remaining files to be sent */
					,&total_bytes			/* remaining bytes to be sent */
					);
				ftime=tmpftime;
deuce's avatar
deuce committed
				lprintf(LOG_DEBUG,"YMODEM header (%u fields): %s", i, block+strlen((char *)block)+1);
				SAFECOPY(fname,((char *)block));
deuce's avatar
deuce committed

				if(!file_bytes)
					file_bytes=0x7fffffff;
				file_bytes_left=file_bytes;
				if(!total_files)
					total_files=1;
				if(total_bytes<file_bytes)
					total_bytes=file_bytes;

				lprintf(LOG_DEBUG,"Incoming filename: %.64s ",getfname(fname));
				SAFEPRINTF2(str,"%s/%s",bbs->dldir,getfname(fname));
				lprintf(LOG_INFO,"File size: %"PRId64" bytes", file_bytes);
deuce's avatar
deuce committed
				if(total_files>1)
					lprintf(LOG_INFO,"Remaining: %"PRId64" bytes in %u files", total_bytes, total_files);
deuce's avatar
deuce committed

		lprintf(LOG_DEBUG,"Receiving: %.64s ",str);

		fnum++;

		while(fexistcase(str) && !(mode&OVERWRITE)) {
deuce's avatar
deuce committed
			lprintf(LOG_WARNING,"%s already exists",str);
			if(!xmodem_duplicate(&xm, bbs, str, sizeof(str), getfname(fname))) {
				xmodem_cancel(&xm);
				goto end;
			}
deuce's avatar
deuce committed
		if((fp=fopen(str,"wb"))==NULL) {
			lprintf(LOG_ERR,"Error %d creating %s",errno,str);
			xmodem_cancel(&xm);
deuce's avatar
deuce committed
		if(mode&XMODEM)
			lprintf(LOG_INFO,"Receiving %s via %s %s"
deuce's avatar
deuce committed
				,str
				,mode&GMODE ? "XMODEM-g" : "XMODEM"
				,mode&CRC ? "CRC-16" : "Checksum");
deuce's avatar
deuce committed
		else
			lprintf(LOG_INFO,"Receiving %s (%"PRId64" KB) via %s %s"
deuce's avatar
deuce committed
				,str
				,file_bytes/1024
				,mode&GMODE ? "YMODEM-g" : "YMODEM"
deuce's avatar
deuce committed
				,mode&CRC ? "CRC-16" : "Checksum");

		startfile=time(NULL);
		success=FALSE;

		errors=0;
		block_num=1;
		if(i!=NOT_YMODEM)
			xmodem_put_nak(&xm, block_num);
deuce's avatar
deuce committed
		while(is_connected(NULL)) {
			xmodem_progress(&xm,block_num,ftello(fp),file_bytes,startfile);
			if(xm.is_cancelled(&xm)) {
				lprintf(LOG_WARNING,"Cancelled locally");
				xmodem_cancel(&xm);
			if(i==NOT_YMODEM)
			else
				i=xmodem_get_block(&xm, block, block_num);
deuce's avatar
deuce committed

deuce's avatar
deuce committed
				if(i==EOT)	{		/* end of transfer */
					success=TRUE;
					xmodem_put_ack(&xm);
					break;
				}
				if(i==CAN) {		/* Cancel */
					xm.cancelled=TRUE;
					break;
				}
				if(mode&GMODE) {
					lprintf(LOG_ERR,"Too many errors (%u)",++errors);
				if(++errors>xm.max_errors) {
deuce's avatar
deuce committed
					lprintf(LOG_ERR,"Too many errors (%u)",errors);
					xmodem_cancel(&xm);
					break;
				}
				if(i!=NOT_XMODEM
					&& block_num==1 && errors>(xm.max_errors/3) && mode&CRC && !(mode&GMODE)) {
					lprintf(LOG_NOTICE,"Falling back to 8-bit Checksum mode (error=%d)", i);
deuce's avatar
deuce committed
					mode&=~CRC;
deuce's avatar
deuce committed
				xmodem_put_nak(&xm, block_num);
				continue;
			}
deuce's avatar
deuce committed
			if(!(mode&GMODE)) {
				send_byte(&xm,ACK,10);
				flush_send(&xm);
			}
			if(file_bytes_left<=0L)  { /* No more bytes to receive */
				lprintf(LOG_WARNING,"Sender attempted to send more bytes than were specified in header");
deuce's avatar
deuce committed
			}
			wr=xm.block_size;
			if(wr>(uint)file_bytes_left)
				wr=(uint)file_bytes_left;
deuce's avatar
deuce committed
			if(fwrite(block,1,wr,fp)!=wr) {
				lprintf(LOG_ERR,"Error writing %u bytes to file at offset %"PRId64
					,wr,(int64_t)ftello(fp));
deuce's avatar
deuce committed
				xmodem_cancel(&xm);
deuce's avatar
deuce committed
			}
deuce's avatar
deuce committed
			block_num++;
		}
deuce's avatar
deuce committed
		/* Use correct file size */
		fflush(fp);

		lprintf(LOG_DEBUG,"file_bytes=%u", file_bytes);
		lprintf(LOG_DEBUG,"file_bytes_left=%u", file_bytes_left);
		lprintf(LOG_DEBUG,"filelength=%u", filelength(fileno(fp)));

deuce's avatar
deuce committed
		if(file_bytes < (ulong)filelength(fileno(fp))) {
			lprintf(LOG_INFO,"Truncating file to %lu bytes", (ulong)file_bytes);
			chsize(fileno(fp),(ulong)file_bytes);	/* 4GB limit! */
deuce's avatar
deuce committed
		} else
			file_bytes = filelength(fileno(fp));
		fclose(fp);
deuce's avatar
deuce committed
		fp = NULL;
deuce's avatar
deuce committed
		t=time(NULL)-startfile;
		if(!t) t=1;
		if(success)
			lprintf(LOG_INFO,"Successful - Time: %lu:%02lu  CPS: %lu"
				,(ulong)(t/60),(ulong)(t%60),(ulong)(file_bytes/t));
deuce's avatar
deuce committed
		else
			lprintf(LOG_ERR,"File Transfer %s", xm.cancelled ? "Cancelled":"Failure");
deuce's avatar
deuce committed

		if(!(mode&XMODEM) && ftime)
deuce's avatar
deuce committed

deuce's avatar
deuce committed
		if(!success && file_bytes==0) {	/* remove 0-byte files */
			if (remove(str) == -1)
				lprintf(LOG_ERR, "Unable to remove empty file %s\n", str);
		}
deuce's avatar
deuce committed
		if(mode&XMODEM)	/* maximum of one file */
			break;
		if((cps=(unsigned)(file_bytes/t))==0)
deuce's avatar
deuce committed
			cps=1;
		total_files--;
		total_bytes-=file_bytes;
		if(total_files>1 && total_bytes)
			lprintf(LOG_INFO,"Remaining - Time: %lu:%02lu  Files: %u  KBytes: %"PRId64
deuce's avatar
deuce committed
				,(total_bytes/cps)/60
				,(total_bytes/cps)%60
				,total_files
				,total_bytes/1024
				);
	}

end:
deuce's avatar
deuce committed
	if(fp)
		fclose(fp);
}

/* End of X/Y-MODEM stuff */

deuce's avatar
deuce committed
void music_control(struct bbslist *bbs)
{
	struct	text_info txtinfo;
	struct ciolib_screen *savscrn;
deuce's avatar
deuce committed

   	gettextinfo(&txtinfo);
   	savscrn = savescreen();
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	setfont(0, FALSE, 3);
	setfont(0, FALSE, 4);
deuce's avatar
deuce committed
	init_uifc(FALSE, FALSE);

	uifc.helpbuf=music_helpbuf;
	if(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"ANSI Music Setup",music_names)!=-1)
deuce's avatar
deuce committed
	uifcbail();
	restorescreen(savscrn);
	freescreen(savscrn);
deuce's avatar
deuce committed
void font_control(struct bbslist *bbs, struct cterminal *cterm)
	struct ciolib_screen *savscrn;
	if(safe_mode)
		return;
   	savscrn = savescreen();
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	setfont(0, FALSE, 3);
	setfont(0, FALSE, 4);
	switch(cio_api.mode) {
		case CIOLIB_MODE_CONIO:
		case CIOLIB_MODE_CONIO_FULLSCREEN:
deuce's avatar
deuce committed
		case CIOLIB_MODE_CURSES_ASCII:
		case CIOLIB_MODE_CURSES_IBM:
		case CIOLIB_MODE_ANSI:
			uifcmsg("Not supported in this video output mode."
				,"Font cannot be changed in the current video output mode");
deuce's avatar
deuce committed
			i=j=cterm->altfont[0];
			uifc.helpbuf="`Font Setup`\n\n"
						"Change the current font.  Font must support the current video mode:\n\n"
						"`8x8`  Used for screen modes with 35 or more lines and all C64/C128 modes\n"
						"`8x14` Used for screen modes with 28 and 34 lines\n"
						"`8x16` Used for screen modes with 30 lines or fewer than 28 lines.";
			k=uifc.list(WIN_MID|WIN_SAV|WIN_INS,0,0,0,&i,&j,"Font Setup",font_names);
			if(k!=-1) {
				if(k & MSK_INS) {
					struct file_pick fpick;
					j=filepick(&uifc, "Load Font From File", &fpick, ".", NULL, 0);
					if(j!=-1 && fpick.files>=1)
						loadfont(fpick.selected[0]);
					filepick_free(&fpick);
				}
deuce's avatar
deuce committed
					setfont(i,FALSE,1);
deuce's avatar
deuce committed
					cterm->altfont[0] = i;
	restorescreen(savscrn);
	freescreen(savscrn);
void capture_control(struct bbslist *bbs)
{
	struct ciolib_screen *savscrn;
	struct	text_info txtinfo;
	int i,j;

	if(safe_mode)
		return;
   	gettextinfo(&txtinfo);
   	savscrn = savescreen();
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	setfont(0, FALSE, 3);
	setfont(0, FALSE, 4);
	cap=(char *)alloca(cterm->height*cterm->width*2);
	gettext(cterm->x, cterm->y, cterm->x+cterm->width-1, cterm->y+cterm->height-1, cap);

	init_uifc(FALSE, FALSE);

		struct file_pick fpick;
						,"Binary"
						,"Binary with SAUCE"
		uifc.helpbuf="~ Capture Type ~\n\n"
			"`ASCII`              ASCII only (no ANSI escape sequences)\n"
			"`Raw`                Preserves ANSI sequences\n"
			"`Binary`             Saves current screen in IBM-CGA/BinaryText format\n"
			"`Binary with SAUCE`  Saves current screen in BinaryText format with SAUCE\n"
			"\n"
			"Raw is useful for stealing ANSI screens from other systems.\n"
			"Don't do that though.  :-)";
		if(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Capture Type",opts)!=-1) {
			j=filepick(&uifc, "Capture File", &fpick, bbs->dldir, i >= 2 ? "*.bin" : NULL
				, UIFC_FP_ALLOWENTRY|UIFC_FP_OVERPROMPT);
			if(j!=-1 && fpick.files>=1) {
				if(i >= 2) {
					FILE* fp = fopen(fpick.selected[0], "wb");
					if(fp == NULL) {
						char err[256];
						sprintf(err, "Error %u opening file '%s'", errno, fpick.selected[0]);
						uifc.msg(err);
					} else {
						char msg[256];
						uifc.pop("Writing to file");
						fwrite(cap, sizeof(uint8_t), cterm->width * cterm->height * 2, fp);
						if(i > 2) {
							time_t t = time(NULL);
							struct tm* tm;
							struct sauce sauce;

							memset(&sauce, 0, sizeof(sauce));
							memcpy(sauce.id, SAUCE_ID, sizeof(sauce.id));
							memcpy(sauce.ver, SAUCE_VERSION, sizeof(sauce.ver));
							memset(sauce.title, ' ', sizeof(sauce.title));
							memset(sauce.author, ' ', sizeof(sauce.author));
							memset(sauce.group, ' ', sizeof(sauce.group));
							if(bbs != NULL) {
								memcpy(sauce.title, bbs->name, MIN(strlen(bbs->name), sizeof(sauce.title)));
								memcpy(sauce.author, bbs->user, MIN(strlen(bbs->user), sizeof(sauce.author)));
							}
							if((tm=localtime(&t)) != NULL)	// The null-terminator overwrites the first byte of filesize
								sprintf(sauce.date, "%04u%02u%02u"
									,1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday);
							sauce.filesize = LE_INT32(ftell(fp));	// LE
							sauce.datatype = sauce_datatype_bin;
							sauce.filetype = cterm->width / 2;
							if(ciolib_getvideoflags() & (CIOLIB_VIDEO_BGBRIGHT|CIOLIB_VIDEO_NOBLINK))
								sauce.tflags |= sauce_ansiflag_nonblink;

							fputc(SAUCE_SEPARATOR, fp);
							/* No comment block (no comments) */
							fwrite(&sauce.id, sizeof(sauce.id), 1, fp);
							fwrite(&sauce.ver, sizeof(sauce.ver), 1, fp);
							fwrite(&sauce.title, sizeof(sauce.title), 1, fp);
							fwrite(&sauce.author, sizeof(sauce.author), 1, fp);
							fwrite(&sauce.group, sizeof(sauce.group), 1, fp);
							fwrite(&sauce.date, sizeof(sauce.date), 1, fp);
							fwrite(&sauce.filesize, sizeof(sauce.filesize), 1, fp);
							fwrite(&sauce.datatype, sizeof(sauce.datatype), 1, fp);