diff --git a/src/sbbs3/filedat.c b/src/sbbs3/filedat.c
index a86ad6cd719b07fb561516863ca16be81d372843..cdba39e4a1d82dfc1d885423f248b13dfc516024 100644
--- a/src/sbbs3/filedat.c
+++ b/src/sbbs3/filedat.c
@@ -914,8 +914,10 @@ long create_archive(const char* archive, const char* format
 	return archived;
 }
 
+// 'with_path' means to extract with sub-dirs
+// 'recurse' means to apply the file_list filter (if non-NULL) recursively
 long extract_files_from_archive(const char* archive, const char* outdir, const char* allowed_filename_chars
-	,bool with_path, bool overwrite, long max_files, str_list_t file_list, char* error, size_t maxerrlen)
+	,bool with_path, bool overwrite, long max_files, str_list_t file_list, bool recurse, char* error, size_t maxerrlen)
 {
 	int result;
 	struct archive *ar;
@@ -971,9 +973,9 @@ long extract_files_from_archive(const char* archive, const char* outdir, const c
 		if(filetype != AE_IFREG)
 			continue;
 		char* filename = getfname(pathname);
-		if(!with_path)
-			pathname = filename;
 		if(file_list != NULL) {
+			if(recurse)
+				pathname = filename;
 			int i;
 			for (i = 0; file_list[i] != NULL; i++)
 				if(wildmatch(pathname, file_list[i], with_path, /* case-sensitive: */false))
@@ -987,6 +989,8 @@ long extract_files_from_archive(const char* archive, const char* outdir, const c
 			safe_snprintf(error, maxerrlen, "disallowed filename '%s'", pathname);
 			break;
 		}
+		if(!with_path)
+			pathname = filename;
 		SAFECOPY(fpath, outdir);
 		backslash(fpath);
 		SAFECAT(fpath, pathname);
@@ -1053,10 +1057,11 @@ bool extract_diz(scfg_t* cfg, file_t* f, str_list_t diz_fnames, char* path, size
 		if(extract_files_from_archive(archive
 			,/* outdir: */cfg->temp_dir
 			,/* allowed_filename_chars: */NULL /* any */
-			,/* with_path: */!nested
+			,/* with_path: */false
 			,/* overwrite: */false
 			,/* max_files: */strListCount(diz_fnames)
 			,/* file_list: */diz_fnames
+			,/* recurse: */nested
 			,/* error: */NULL, 0) >= 0) {
 			for(i = 0; diz_fnames[i] != NULL; i++) {
 				safe_snprintf(path, maxlen, "%s%s", cfg->temp_dir, diz_fnames[i]); // no slash
diff --git a/src/sbbs3/filedat.h b/src/sbbs3/filedat.h
index 8c2a1b4d01057048e25706f519f482292188b49c..b99de1391cf9f8bf9875743831e5f7a058db5d5b 100644
--- a/src/sbbs3/filedat.h
+++ b/src/sbbs3/filedat.h
@@ -74,7 +74,7 @@ DLLEXPORT long			create_archive(const char* archive, const char* format
 						               ,bool with_path, str_list_t file_list, char* error, size_t maxerrlen);
 DLLEXPORT char*			cmdstr(scfg_t*, user_t*, const char* instr, const char* fpath, const char* fspec, char* cmd, size_t);
 DLLEXPORT long			extract_files_from_archive(const char* archive, const char* outdir, const char* allowed_filename_chars
-						                           ,bool with_path, bool overwrite, long max_files, str_list_t file_list, char* error, size_t);
+						                           ,bool with_path, bool overwrite, long max_files, str_list_t file_list, bool recurse, char* error, size_t);
 DLLEXPORT int			archive_type(const char* archive, char* str, size_t size);
 extern const char*		supported_archive_formats[];
 DLLEXPORT bool			file_type_match(const char* filename, const char* type);
diff --git a/src/sbbs3/js_archive.c b/src/sbbs3/js_archive.c
index fe3caf6a9a27d64d6b8ff76468d90df10e48de48..9dfc7403bf0a684c127c9a08ef56a39df23b10de 100644
--- a/src/sbbs3/js_archive.c
+++ b/src/sbbs3/js_archive.c
@@ -110,6 +110,7 @@ js_extract(JSContext *cx, uintN argc, jsval *arglist)
 	str_list_t	file_list = NULL;
 	bool		with_path = false;
 	bool		overwrite = true;
+	bool		recurse = false;
 	int32		max_files = 0;
 	char		error[256] = "";
 	jsrefcount	rc;
@@ -149,17 +150,20 @@ js_extract(JSContext *cx, uintN argc, jsval *arglist)
 		}
 		argn++;
 	}
-	for(; argn < argc; argn++) {
-		if(JSVAL_IS_STRING(argv[argn])) {
-			char path[MAX_PATH + 1];
-			JSVALUE_TO_STRBUF(cx, argv[argn], path, sizeof(path), NULL);
-			strListPush(&file_list, path);
-		}
+	while(argc > argn && JSVAL_IS_STRING(argv[argn])) {
+		char path[MAX_PATH + 1];
+		JSVALUE_TO_STRBUF(cx, argv[argn], path, sizeof(path), NULL);
+		strListPush(&file_list, path);
+		argn++;
+	}
+	if(argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
+		recurse = JSVAL_TO_BOOLEAN(argv[argn]);
+		argn++;
 	}
 
 	rc = JS_SUSPENDREQUEST(cx);
 	long extracted = extract_files_from_archive(filename, outdir, allowed_filename_chars
-		,with_path, overwrite, (ulong)max_files, file_list, error, sizeof(error));
+		,with_path, overwrite, (ulong)max_files, file_list, recurse, error, sizeof(error));
 	strListFree(&file_list);
 	free(outdir);
 	JS_RESUMEREQUEST(cx, rc);
@@ -555,7 +559,7 @@ js_read(JSContext *cx, uintN argc, jsval *arglist)
 
 static jsSyncMethodSpec js_archive_functions[] = {
 	{ "create",		js_create,		1,	JSTYPE_NUMBER
-		,JSDOCSTR("[string format] [,boolean with_path = false] [,array file_list]")
+		,JSDOCSTR("[string format] [,boolean with_path=false] [,array file_list]")
 		,JSDOCSTR("Create an archive of the specified format (e.g. 'zip', '7z', 'tgz').<br>"
 			"Returns the number of files archived.<br>"
 			"Will throw exception upon error.")
@@ -567,14 +571,14 @@ static jsSyncMethodSpec js_archive_functions[] = {
 		,31900
 	},
 	{ "extract",	js_extract,		1,	JSTYPE_NUMBER
-		,JSDOCSTR("output_directory [,boolean with_path = false] [,boolean overwrite = true] [,number max_files = 0] [,string file/pattern [...]]")
+		,JSDOCSTR("output_directory [,boolean with_path=false] [,boolean overwrite=true] [,number max_files=0] [,string file/pattern [...]] [,boolean recurse=false]")
 		,JSDOCSTR("Extract files from an archive to specified output directory.<br>"
 			"Returns the number of files extracted.<br>"
 			"Will throw exception upon error.")
 		,31900
 	},
 	{ "list",		js_list,		1,	JSTYPE_ARRAY
-		,JSDOCSTR("[,boolean hash = false] [,string file/pattern]")
+		,JSDOCSTR("[,boolean hash=false] [,string file/pattern]")
 		,JSDOCSTR("Get list of archive contents as an array of objects.<br>"
 			"Archived object properties:<br>"
 			"<ol>"
diff --git a/src/sbbs3/pack_qwk.cpp b/src/sbbs3/pack_qwk.cpp
index f028a25fd75bb8d84401ffc46a6aa115da05a2a5..5a254cf692971d33c1632017cfe4547ff7c9385d 100644
--- a/src/sbbs3/pack_qwk.cpp
+++ b/src/sbbs3/pack_qwk.cpp
@@ -76,6 +76,7 @@ bool sbbs_t::pack_qwk(char *packet, uint *msgcnt, bool prepack)
 			,/* overwrite: */true
 			,/* max_files: */0 /* unlimited */
 			,/* file_list: */NULL /* all files */
+			,/* recurse: */false
 			,error, sizeof(error));
 		if(file_count > 0) {
 			lprintf(LOG_DEBUG, "libarchive extracted %u files from %s", file_count, str);
diff --git a/src/sbbs3/pack_rep.cpp b/src/sbbs3/pack_rep.cpp
index 8f70d9069ee860f7ef2edb7f2349a46191e9ae02..56c163091c2c2a20c7b7a85b16b8798b73c78f67 100644
--- a/src/sbbs3/pack_rep.cpp
+++ b/src/sbbs3/pack_rep.cpp
@@ -70,6 +70,7 @@ bool sbbs_t::pack_rep(uint hubnum)
 			,/* overwrite: */true
 			,/* max_files: */0 /* unlimited */
 			,/* file_list: */NULL /* all files */
+			,/* recurse: */false
 			,error, sizeof(error));
 		if(file_count > 0) {
 			lprintf(LOG_DEBUG, "libarchive extracted %lu files from %s", file_count, str);
diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c
index d76c71e4b745b1cc16948455356a3c6696cf31a0..a365c7d02b93ef9b48d3e6868b26aec6c2f6a358 100644
--- a/src/sbbs3/sbbsecho.c
+++ b/src/sbbs3/sbbsecho.c
@@ -2265,7 +2265,7 @@ int unpack(const char *infile, const char* outdir)
 
 	file_count = extract_files_from_archive(infile, outdir
 		,/* allowed_filename_chars: */SAFEST_FILENAME_CHARS, /* with_path */false, /* overwrite: */true
-		,/* max_files: */0, /* file_list = ALL */NULL, error, sizeof(error));
+		,/* max_files: */0, /* file_list = ALL */NULL, /* recurse: */false, error, sizeof(error));
 	if(file_count > 0) {
 		lprintf(LOG_DEBUG, "libarchive extracted %lu files from %s", file_count, infile);
 		return 0;
diff --git a/src/sbbs3/un_qwk.cpp b/src/sbbs3/un_qwk.cpp
index ac359740411736f87c71ff14d622bcc46876bc94..d717482153394b0ecd30c2e18c4a2830026b3639 100644
--- a/src/sbbs3/un_qwk.cpp
+++ b/src/sbbs3/un_qwk.cpp
@@ -80,6 +80,7 @@ bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
 		,/* overwrite: */true
 		,/* max_files: */0 /* unlimited */
 		,/* file_list: */NULL /* all files */
+		,/* recurse: */false
 		,error, sizeof(error));
 	if(file_count >= 0) {
 		lprintf(LOG_DEBUG, "libarchive extracted %ld files from %s", file_count, packet);
diff --git a/src/sbbs3/un_rep.cpp b/src/sbbs3/un_rep.cpp
index f1721bd6310d2a55cf0cfd011158ac513a952047..1d3571d0ed8c6adaa43f1d3720b9ed3991738427 100644
--- a/src/sbbs3/un_rep.cpp
+++ b/src/sbbs3/un_rep.cpp
@@ -76,6 +76,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 		,/* overwrite: */true
 		,/* max_files */1000
 		,/* file_list: */NULL /* all files */
+		,/* recurse: */false
 		,error, sizeof(error));
 	if(file_count > 0) {
 		lprintf(LOG_DEBUG, "libarchive extracted %lu files from %s", file_count, rep_fname);