From 67d6cf3c9dff82bef1246c33a9c5be12dfe55ae6 Mon Sep 17 00:00:00 2001
From: "Rob Swindell (in GitKraken)" <rob@synchro.net>
Date: Mon, 13 Mar 2023 14:52:42 -0700
Subject: [PATCH] Fix issue with DIZ extraction creating subdirs in temp

For archives with directories, the first call to extract_files_from_archive() from extract_diz() would create sub-directories in the target (temp) directory, but no files within them.

To correctly solve the original problem identified in commit 79a302f4, introduce/use a new 'recurse' argument to extract_files_from_archive() which means to recursively apply the file_list filter (if specified). Always pass 'with_path' argument as false to prevent sub-dir creation.

The JS Archive.extract() method now excepts an additional boolean argument (recurse) following the file list arguments, default is false.

Remove extra whitespace in Archive JSDOC method descriptions to be consistent with other object/class docs.
---
 src/sbbs3/filedat.c    | 13 +++++++++----
 src/sbbs3/filedat.h    |  2 +-
 src/sbbs3/js_archive.c | 24 ++++++++++++++----------
 src/sbbs3/pack_qwk.cpp |  1 +
 src/sbbs3/pack_rep.cpp |  1 +
 src/sbbs3/sbbsecho.c   |  2 +-
 src/sbbs3/un_qwk.cpp   |  1 +
 src/sbbs3/un_rep.cpp   |  1 +
 8 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/src/sbbs3/filedat.c b/src/sbbs3/filedat.c
index a86ad6cd71..cdba39e4a1 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 8c2a1b4d01..b99de1391c 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 fe3caf6a9a..9dfc7403bf 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 f028a25fd7..5a254cf692 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 8f70d9069e..56c163091c 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 d76c71e4b7..a365c7d02b 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 ac35974041..d717482153 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 f1721bd631..1d3571d0ed 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);
-- 
GitLab