diff --git a/src/sbbs3/sexyz.c b/src/sbbs3/sexyz.c
index 5f59c1275b23461ed56e99501cbce357fb1e222b..1b9ef471b1eb84925a72f540a0ada657e835ec63 100644
--- a/src/sbbs3/sexyz.c
+++ b/src/sbbs3/sexyz.c
@@ -696,8 +696,7 @@ void xmodem_progress(void* unused, unsigned block_num, ulong offset, ulong fsize
  * show the progress of the transfer like this:
  * zmtx: sending file "garbage" 4096 bytes ( 20%)
  */
-void zmodem_progress(void* unused, ulong start_pos, ulong current_pos
-					 ,ulong fsize, time_t start)
+void zmodem_progress(void* cbdata, ulong start_pos, ulong current_pos)
 {
 	char		orig[128];
 	unsigned	cps;
@@ -705,17 +704,18 @@ void zmodem_progress(void* unused, ulong start_pos, ulong current_pos
 	long		t;
 	time_t		now;
 	static time_t last_progress;
+	zmodem_t*	zm = (zmodem_t*)cbdata;
 
 	now=time(NULL);
-	if(now-last_progress>=progress_interval || current_pos >= fsize || newline) {
-		t=now-start;
+	if(now-last_progress>=progress_interval || current_pos >= zm->current_file_size || newline) {
+		t=now-zm->transfer_start;
 		if(t<=0)
 			t=1;
 		if(start_pos>current_pos)
 			start_pos=0;
 		if((cps=(current_pos-start_pos)/t)==0)
 			cps=1;		/* cps so far */
-		l=fsize/cps;	/* total transfer est time */
+		l=zm->current_file_size/cps;	/* total transfer est time */
 		l-=t;			/* now, it's est time left */
 		if(l<0) l=0;
 		if(start_pos)
@@ -726,13 +726,13 @@ void zmodem_progress(void* unused, ulong start_pos, ulong current_pos
 			"Time: %lu:%02lu/%lu:%02lu  CPS: %u  %lu%% "
 			,orig
 			,current_pos/1024
-			,fsize/1024
+			,zm->current_file_size/1024
 			,t/60L
 			,t%60L
 			,l/60L
 			,l%60L
 			,cps
-			,(long)(((float)current_pos/(float)fsize)*100.0)
+			,(long)(((float)current_pos/(float)zm->current_file_size)*100.0)
 			);
 		newline=FALSE;
 		last_progress=now;
@@ -783,8 +783,8 @@ static int send_files(char** fname, uint fnames)
 		lprintf(LOG_INFO,"Sending %u files (%lu KB total)"
 			,xm.total_files,xm.total_bytes/1024);
 
-	zm.n_files_remaining = xm.total_files;
-	zm.n_bytes_remaining = xm.total_bytes;
+	zm.files_remaining = xm.total_files;
+	zm.bytes_remaining = xm.total_bytes;
 
 	/***********************************************/
 	/* Send every file matching names or filespecs */
@@ -976,20 +976,20 @@ static int receive_files(char** fname_list, int fnames)
 
 			} else {	/* Zmodem */
 				lprintf(LOG_INFO,"Waiting for Zmodem sender...");
-				i=zmodem_recv_init(&zm
-							,fname,sizeof(fname)
-							,&file_bytes
-							,&ftime
-							,&fmode
-							,&serial_num
-							,&total_files
-							,&total_bytes);
+
+				i=zmodem_recv_init(&zm);
+
 				if(zm.cancelled)
 					return(1);
 				if(i<0)
 					return(-1);
 				switch(i) {
 					case ZFILE:
+						SAFECOPY(fname,zm.current_file_name);
+						file_bytes = zm.current_file_size;
+						ftime = zm.current_file_time;
+						total_files = zm.files_remaining;
+						total_bytes = zm.bytes_remaining;
 						break;
 					case ZFIN:
 					case ZCOMPL:
@@ -1084,7 +1084,7 @@ static int receive_files(char** fname_list, int fnames)
 		success=FALSE;
 		if(mode&ZMODEM) {
 
-			errors=zmodem_recv_file_data(&zm,fp,0,file_bytes,startfile);
+			errors=zmodem_recv_file_data(&zm,fp,0);
 
 			/*
  			 * wait for the eof header
diff --git a/src/sbbs3/zmodem.c b/src/sbbs3/zmodem.c
index 7d8771cafec97a7b1c1c13e94aa97c425509d5b0..3ecb99830d7ef4bdc5dafbd061fcba44444b8184 100644
--- a/src/sbbs3/zmodem.c
+++ b/src/sbbs3/zmodem.c
@@ -28,7 +28,8 @@
 #include <sys/stat.h>	/* struct stat */
 
 #include "genwrap.h"
-#include "dirwrap.h"
+#include "dirwrap.h"	/* getfname() */
+#include "filewrap.h"	/* filelength() */
 
 #include "zmodem.h"
 #include "crc16.h"
@@ -1326,7 +1327,7 @@ int zmodem_get_zfin(zmodem_t* zm)
  * the name is only used to show progress
  */
 
-int zmodem_send_from(zmodem_t* zm, FILE* fp, ulong pos, ulong fsize, ulong* sent)
+int zmodem_send_from(zmodem_t* zm, FILE* fp, ulong pos, ulong* sent)
 {
 	int n;
 	uchar type;
@@ -1360,7 +1361,7 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, ulong pos, ulong fsize, ulong* sent
 		}
 #endif
 		if(zm->progress!=NULL)
-			zm->progress(zm->cbdata, pos, ftell(fp), fsize, zm->transfer_start);
+			zm->progress(zm->cbdata, pos, ftell(fp));
 
 		type = ZCRCG;
 
@@ -1376,7 +1377,7 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, ulong pos, ulong fsize, ulong* sent
 			buf_sent=0;
 		}
 
-		if((ulong)ftell(fp) >= fsize || n==0)	// can't use feof() here!
+		if((ulong)ftell(fp) >= zm->current_file_size || n==0)	// can't use feof() here!
 			type = ZCRCE;
 
 		zmodem_send_data(zm, type, zm->tx_data_subpacket, n);
@@ -1404,8 +1405,8 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, ulong pos, ulong fsize, ulong* sent
 				} 
 			}
 
-			if((ulong)ftell(fp) >= fsize) {
-				lprintf(zm,LOG_DEBUG,"zmodem_send_from: end of file (%ld)", fsize );
+			if((ulong)ftell(fp) >= zm->current_file_size) {
+				lprintf(zm,LOG_DEBUG,"zmodem_send_from: end of file (%ld)", zm->current_file_size );
 				return ZACK;
 			}
 			if(n==0) {
@@ -1561,8 +1562,8 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti
 		,s.st_mtime
 		,0						/* file mode */
 		,0						/* serial number */
-		,zm->n_files_remaining
-		,zm->n_bytes_remaining
+		,zm->files_remaining
+		,zm->bytes_remaining
 		,0						/* file type */
 		);
 
@@ -1637,7 +1638,7 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti
 		 * and start sending
 		 */
 
-		type = zmodem_send_from(zm, fp, pos, s.st_size, &sent_bytes);
+		type = zmodem_send_from(zm, fp, pos, &sent_bytes);
 
 		if(!is_connected(zm))
 			return(FALSE);
@@ -1679,14 +1680,127 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti
 	return(success);
 }
 
-int zmodem_recv_init(zmodem_t* zm
-						   ,char* fname, size_t maxlen
-						   ,ulong* p_size
-						   ,time_t* p_time
-						   ,long* p_mode
-						   ,long* p_serial
-						   ,ulong* p_total_files
-						   ,ulong* p_total_bytes)
+int zmodem_recv_files(zmodem_t* zm, const char* download_dir, ulong* bytes_received)
+{
+	char		fpath[MAX_PATH+1];
+	FILE*		fp;
+	long		l;
+	BOOL		skip;
+	ulong		b;
+	ulong		crc;
+	ulong		rcrc;
+	ulong		bytes;
+	ulong		kbytes;
+	ulong		start_bytes;
+	unsigned	files_received=0;
+	time_t		t;
+	unsigned	cps;
+	unsigned	timeout;
+	unsigned	errors=0;
+
+	if(bytes_received!=NULL)
+		*bytes_received=0;
+	zm->current_file_num=1;
+	while(zmodem_recv_init(zm)==ZFILE) {
+		bytes=zm->current_file_size;
+		kbytes=bytes/1024;
+		if(kbytes<1) kbytes=0;
+		lprintf(zm,LOG_INFO,"Downloading %s (%lu KBytes) via Zmodem", zm->current_file_name, kbytes);
+
+		do {	/* try */
+			skip=TRUE;
+
+			sprintf(fpath,"%s/%s",download_dir,zm->current_file_name);
+			lprintf(zm,LOG_DEBUG,"fpath=%s",fpath);
+			if(fexist(fpath)) {
+				lprintf(zm,LOG_WARNING,"%s already exists",fpath);
+				l=flength(fpath);
+				if(l>=(long)bytes) {
+					lprintf(zm,LOG_WARNING,"Local file size (%lu bytes) >= remote file size (%ld)"
+						,l, bytes);
+					break;
+				}
+				if((fp=fopen(fpath,"rb"))==NULL) {
+					lprintf(zm,LOG_ERR,"Error %d opening %s",errno,fpath);
+					break;
+				}
+				crc=fcrc32(fp,l);
+				fclose(fp);
+				if(!zmodem_get_crc(zm,l,&rcrc)) {
+					lprintf(zm,LOG_ERR,"Failed to get CRC of remote file: %s", fpath);
+					break;
+				}
+				if(crc!=rcrc) {
+					lprintf(zm,LOG_WARNING,"Remote file has different CRC value");
+					lprintf(zm,LOG_DEBUG,"Remote CRC: %08lx vs Local CRC: %08lx)", rcrc, crc);
+					break;
+				}
+				lprintf(zm,LOG_INFO,"Resuming download of %s",fpath);
+			}
+
+			if((fp=fopen(fpath,"ab"))==NULL) {
+				lprintf(zm,LOG_ERR,"Error %d opening/creating/appending %s",errno,fpath);
+				break;
+			}
+			start_bytes=filelength(fileno(fp));
+
+			skip=FALSE;
+			errors=zmodem_recv_file_data(zm,fp,flength(fpath));
+
+			for(;errors<=zm->max_errors && !zm->cancelled; errors++) {
+				if(zmodem_recv_header_and_check(zm))
+					break;
+			}
+			fclose(fp);
+			l=flength(fpath);
+			if(errors && l==0)	{	/* aborted/failed download */
+				if(remove(fpath))	/* don't save 0-byte file */
+					lprintf(zm,LOG_ERR,"Error %d removing %s",errno,fpath);
+				else
+					lprintf(zm,LOG_INFO,"Deleted 0-byte file %s",fpath);
+			}
+			else {
+				if(l!=(long)bytes) {
+					lprintf(zm,LOG_WARNING,"Incomplete download (%ld bytes received, expected %lu)"
+						,l,bytes);
+				} else {
+					if((t=time(NULL)-zm->transfer_start)<=0)
+						t=1;
+					b=l-start_bytes;
+					if((cps=b/t)==0)
+						cps=1;
+					lprintf(zm,LOG_INFO,"Received %lu bytes successfully (%u CPS)",b,cps);
+					files_received++;
+					if(bytes_received!=NULL)
+						*bytes_received+=b;
+				}
+				if(zm->current_file_time)
+					setfdate(fpath,zm->current_file_time);
+			}
+
+		} while(0);
+		/* finally */
+
+		if(skip) {
+			lprintf(zm,LOG_WARNING,"Skipping file");
+			zmodem_send_zskip(zm);
+		}
+		zm->current_file_num++;
+	}
+	if(zm->local_abort)
+		zmodem_abort_receive(zm);
+
+	/* wait for "over-and-out" */
+	timeout=zm->recv_timeout;
+	zm->recv_timeout=2;
+	if(zmodem_rx(zm)=='O')
+		zmodem_rx(zm);
+	zm->recv_timeout=timeout;
+
+	return(files_received);
+}
+
+int zmodem_recv_init(zmodem_t* zm)
 {
 	int			type=CAN;
 	unsigned	errors;
@@ -1709,15 +1823,7 @@ int zmodem_recv_init(zmodem_t* zm
 		lprintf(zm,LOG_DEBUG,"Received header: %s",chr((uchar)type));
 
 		if(type==ZFILE) {
-			if(!zmodem_recv_file_info(zm
-				,fname,maxlen
-				,p_size
-				,p_time
-				,p_mode
-				,p_serial
-				,p_total_files
-				,p_total_bytes))
-				continue;
+			zmodem_parse_zfile_subpacket(zm);
 			return(type);
 		}
 
@@ -1735,51 +1841,40 @@ int zmodem_recv_init(zmodem_t* zm
 	return(type);
 }
 
-BOOL zmodem_recv_file_info(zmodem_t* zm
-						   ,char* fname, size_t maxlen
-						   ,ulong* p_size
-						   ,time_t* p_time
-						   ,long* p_mode
-						   ,long* p_serial
-						   ,ulong* p_total_files
-						   ,ulong* p_total_bytes)
+void zmodem_parse_zfile_subpacket(zmodem_t* zm)
 {
 	int			i;
-	ulong		size=0;
-	ulong		time=0;
-	ulong		total_files=0;
-	ulong		total_bytes=0;
 	long		mode=0;
 	long		serial=-1;
 
-	if(fname!=NULL)
-		safe_snprintf(fname,maxlen,"%s",zm->rx_data_subpacket);
+	SAFECOPY(zm->current_file_name,getfname(zm->rx_data_subpacket));
+
+	zm->current_file_size = 0;
+	zm->current_file_time = 0;
+	zm->files_remaining = 0;
+	zm->bytes_remaining = 0;
 
 	i=sscanf(zm->rx_data_subpacket+strlen(zm->rx_data_subpacket)+1,"%lu %lo %lo %lo %lu %lu"
-		,&size					/* file size (decimal) */
-		,&time 					/* file time (octal unix format) */
-		,&mode 					/* file mode */
+		,&zm->current_file_size	/* file size (decimal) */
+		,&zm->current_file_time /* file time (octal unix format) */
+		,&mode					/* file mode */
 		,&serial				/* program serial number */
-		,&total_files			/* remaining files to be sent */
-		,&total_bytes			/* remaining bytes to be sent */
+		,&zm->files_remaining	/* remaining files to be sent */
+		,&zm->bytes_remaining	/* remaining bytes to be sent */
 		);
 
 	lprintf(zm,LOG_DEBUG,"Zmodem header (%u fields): %s"
 		,i, zm->rx_data_subpacket+strlen(zm->rx_data_subpacket)+1);
 
-	if(!total_files)
-		total_files=1;
-	if(!total_bytes)
-		total_bytes=size;
+	if(!zm->files_remaining)
+		zm->files_remaining = 1;
+	if(!zm->bytes_remaining)
+		zm->bytes_remaining = zm->current_file_size;
 
-	if(p_size)			*p_size=size;
-	if(p_time)			*p_time=time;
-	if(p_mode)			*p_mode=mode;
-	if(p_serial)		*p_serial=serial;
-	if(p_total_files)	*p_total_files=total_files;
-	if(p_total_bytes)	*p_total_bytes=total_bytes;
-
-	return(TRUE);
+	if(!zm->total_files)
+		zm->total_files = zm->files_remaining;
+	if(!zm->total_bytes)
+		zm->total_bytes = zm->bytes_remaining;
 }
 
 /*
@@ -1787,24 +1882,23 @@ BOOL zmodem_recv_file_info(zmodem_t* zm
  * the name is only used to show progress
  */
 
-unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, ulong offset, ulong fsize, time_t start)
+unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, ulong offset)
 {
 	int			i=0;
 	unsigned	errors=0;
 
-	if(start==0)
-		start=time(NULL);
+	zm->transfer_start=time(NULL);
 
 	fseek(fp,offset,SEEK_SET);
 	offset=ftell(fp);
 
 	while(errors<=zm->max_errors && is_connected(zm)
-		&& (ulong)ftell(fp) < fsize && !zm->cancelled) {
+		&& (ulong)ftell(fp) < zm->current_file_size && !zm->cancelled) {
 
 		if(i!=ENDOFFRAME)
 			zmodem_send_pos_header(zm, ZRPOS, ftell(fp), /* Hex? */ TRUE);
 
-		if((i = zmodem_recv_file_frame(zm,fp,offset,fsize,start)) == ZEOF)
+		if((i = zmodem_recv_file_frame(zm,fp,offset)) == ZEOF)
 			break;
 		if(i!=ENDOFFRAME) {
 			if(i>0)
@@ -1816,7 +1910,7 @@ unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, ulong offset, ulong fsize
 }
 
 
-int zmodem_recv_file_frame(zmodem_t* zm, FILE* fp, ulong offset, ulong fsize, time_t start)
+int zmodem_recv_file_frame(zmodem_t* zm, FILE* fp, ulong offset)
 {
 	unsigned n;
 	int type;
@@ -1853,7 +1947,7 @@ int zmodem_recv_file_frame(zmodem_t* zm, FILE* fp, ulong offset, ulong fsize, ti
 		}
 
 		if(zm->progress!=NULL)
-			zm->progress(zm->cbdata,offset,ftell(fp),fsize,start);
+			zm->progress(zm->cbdata,offset,ftell(fp));
 
 		if(zm->cancelled)
 			return(ZCAN);
@@ -1878,7 +1972,7 @@ char* zmodem_ver(char *buf)
 
 void zmodem_init(zmodem_t* zm, void* cbdata
 				,int	(*lputs)(void*, int level, const char* str)
-				,void	(*progress)(void* unused, ulong, ulong, ulong, time_t)
+				,void	(*progress)(void* unused, ulong, ulong)
 				,int	(*send_byte)(void*, uchar ch, unsigned timeout)
 				,int	(*recv_byte)(void*, unsigned timeout)
 				,BOOL	(*is_connected)(void*)
diff --git a/src/sbbs3/zmodem.h b/src/sbbs3/zmodem.h
index 53b520d9c271b40b785d88a36ae897617227ec40..1866d07cc7c71f3070adaceec518c682e99beda3 100644
--- a/src/sbbs3/zmodem.h
+++ b/src/sbbs3/zmodem.h
@@ -223,14 +223,18 @@ typedef struct {
 
 	#define MAX_SUBPACKETSIZE 1024
 
-	int n_files_remaining;
-	int n_bytes_remaining;
 	BYTE tx_data_subpacket[MAX_SUBPACKETSIZE];
 	BYTE rx_data_subpacket[8192];							/* zzap = 8192 */
 
-	ulong current_file_size;
-	time_t transfer_start;
-	time_t last_status;
+	char		current_file_name[MAX_PATH+1];
+	ulong		current_file_size;
+	time_t		current_file_time;
+	unsigned	current_file_num;
+	unsigned	total_files;
+	ulong		total_bytes;
+	unsigned	files_remaining;
+	unsigned	bytes_remaining;
+	time_t		transfer_start;
 
 	int receive_32bit_data;
 	int use_crc16;
@@ -262,7 +266,7 @@ typedef struct {
 	int			(*lputs)(void*, int level, const char* str);
 	int			(*send_byte)(void*, BYTE ch, unsigned timeout);
 	int			(*recv_byte)(void*, unsigned timeout);
-	void		(*progress)(void*, ulong start_pos, ulong current_pos, ulong fsize, time_t start);
+	void		(*progress)(void*, ulong start_pos, ulong current_pos);
 	BOOL		(*is_connected)(void*);
 	BOOL		(*data_waiting)(void*);
 
@@ -270,7 +274,7 @@ typedef struct {
 
 void		zmodem_init(zmodem_t*, void* cbdata
 						,int	(*lputs)(void*, int level, const char* str)
-						,void	(*progress)(void*, ulong, ulong, ulong, time_t)
+						,void	(*progress)(void*, ulong, ulong)
 						,int	(*send_byte)(void*, BYTE ch, unsigned timeout)
 						,int	(*recv_byte)(void*, unsigned timeout)
 						,BOOL	(*is_connected)(void*)
@@ -290,26 +294,13 @@ int			zmodem_get_zrinit(zmodem_t*);
 int			zmodem_get_zfin(zmodem_t* zm);
 BOOL		zmodem_get_crc(zmodem_t*, long length, ulong* crc);
 void		zmodem_parse_zrinit(zmodem_t*);
+void		zmodem_parse_zfile_subpacket(zmodem_t* zm);
 int			zmodem_send_zfin(zmodem_t*);
 BOOL		zmodem_send_file(zmodem_t*, char* name, FILE* fp, BOOL request_init, time_t* start, ulong* bytes_sent);
-int			zmodem_recv_init(zmodem_t* zm
-									,char* fname, size_t maxlen
-									,ulong* size
-									,time_t* time
-									,long* mode
-									,long* serial_num
-									,ulong* total_files
-									,ulong* total_bytes);
-BOOL		zmodem_recv_file_info(zmodem_t* zm
-									,char* fname, size_t maxlen
-									,ulong* size
-									,time_t* time
-									,long* mode
-									,long* serial_num
-									,ulong* total_files
-									,ulong* total_bytes);
-unsigned	zmodem_recv_file_data(zmodem_t*, FILE*, ulong offset, ulong fsize, time_t start);
-int			zmodem_recv_file_frame(zmodem_t* zm, FILE* fp, ulong offset, ulong fsize, time_t start);
+int			zmodem_recv_files(zmodem_t* zm, const char* download_dir, ulong* bytes_received);
+int			zmodem_recv_init(zmodem_t* zm);
+unsigned	zmodem_recv_file_data(zmodem_t*, FILE*, ulong offset);
+int			zmodem_recv_file_frame(zmodem_t* zm, FILE* fp, ulong offset);
 int			zmodem_recv_header_and_check(zmodem_t* zm);
 #endif