From fd1a2951ff982de850bc27d4120166fdbcf166fa Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Fri, 17 Nov 2023 20:12:33 -0600 Subject: [PATCH] Singe now checks the validity of games before unpacking them. --- src/main.c | 210 +++++++++++++++++++++++++++++++++++++++-------------- src/util.c | 4 + src/util.h | 1 + 3 files changed, 160 insertions(+), 55 deletions(-) diff --git a/src/main.c b/src/main.c index 7d9fc5c45..6636503db 100644 --- a/src/main.c +++ b/src/main.c @@ -853,17 +853,26 @@ void unpackData(char *name) { void unpackGames(void) { struct dirent *de; - DIR *dir = opendir("."); + DIR *dir = opendir("."); struct archive *a; struct archive *ext; struct archive_entry *entry; int flags; int r; - int count = 0; + int x; + int count = 0; bool ok; + bool hasGamesDat; const void *buff; size_t size; la_int64_t offset; + char *e; + char *gamesDat = NULL; + char *extension; + char *filename; + char *toplevel = NULL; + char *badFilenames[] = { "Framework.singe", 0 }; + char *badExtensions[] = { "exe", "sh", "bat", "cmd", "index", 0 }; if (dir == NULL) utilDie("Could not open the current directory."); @@ -872,70 +881,161 @@ void unpackGames(void) { if (utilStricmp(utilGetFileExtension(de->d_name), "game") == 0) { showHeader(); count++; - utilSay(">>> Unpacking Game: %s", de->d_name); + ok = true; + hasGamesDat = false; - // https://github.com/libarchive/libarchive/wiki/Examples#user-content-A_Complete_Extractor - ok = true; - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; + // Look through archive for things I've told people NOT to ship! + // https://github.com/libarchive/libarchive/wiki/Examples#user-content-List_contents_of_Archive_stored_in_File a = archive_read_new(); - archive_read_support_format_all(a); archive_read_support_filter_all(a); - ext = archive_write_disk_new(); - archive_write_disk_set_options(ext, flags); - archive_write_disk_set_standard_lookup(ext); - if ((r = archive_read_open_filename(a, de->d_name, 10240))) ok = false; - while (ok) { - r = archive_read_next_header(a, &entry); - if (r == ARCHIVE_EOF) break; - if (r < ARCHIVE_OK) utilSay("%s", archive_error_string(a)); - if (r < ARCHIVE_WARN) { - ok = false; - break; - } - r = archive_write_header(ext, entry); - if (r < ARCHIVE_OK) { - utilSay("%s", archive_error_string(ext)); - } else { - if (archive_entry_size(entry) > 0) { - for (;;) { - r = archive_read_data_block(a, &buff, &size, &offset); - if (r == ARCHIVE_EOF) { - r = ARCHIVE_OK; - break; - } - if (r < ARCHIVE_OK) break; - r = archive_write_data_block(ext, buff, size, offset); - if (r < ARCHIVE_OK) { - utilSay("%s", archive_error_string(ext)); - break; - } - } - if (r < ARCHIVE_OK) utilSay("%s", archive_error_string(ext)); - if (r < ARCHIVE_WARN) { + archive_read_support_format_all(a); + r = archive_read_open_filename(a, de->d_name, 10240); + if (r != ARCHIVE_OK) { + utilSay("!!! Cannot read game: %s", de->d_name); + ok = false; + } else { + while (archive_read_next_header(a, &entry) == ARCHIVE_OK && ok) { + e = (char *)archive_entry_pathname(entry); + filename = utilGetLastPathComponent(e); + extension = utilGetFileExtension(e); + + // Do we have a top level folder name yet? + if (toplevel == NULL) { + // No. Is this a folder? + if (strstr(e, "/") == NULL) { + // No. BAD! No files in the root! ok = false; - break; + utilSay("!!! Game has files in root: %s", de->d_name); + } else { + // Remember this folder. + toplevel = strdup(e); + gamesDat = utilCreateString("%sgames.dat", toplevel); + } + } else { + // Yes, we have a top level. Is this entry inside it? + if (!utilStartsWith(e, toplevel)) { + // No. BAD! Everything has to be in the top level. + ok = false; + if (strstr(e, "/") == NULL) { + utilSay("!!! Game has files in root: %s", de->d_name); + } else { + utilSay("!!! Game has multiple top level directories: %s", de->d_name); + } + utilSay("[%s] %s", toplevel, e); + } else { + // Is this a forbidden file? + x = 0; + while (badFilenames[x] != NULL && ok) { + if (utilStricmp(filename, badFilenames[x]) == 0) { + ok = false; + utilSay("!!! Game has %s: %s", badFilenames[x], de->d_name); + } + x++; + } + // Is this a forbidden extension? + x = 0; + while (badExtensions[x] != NULL && ok) { + if (utilStricmp(extension, badExtensions[x]) == 0) { + ok = false; + utilSay("!!! Game has %s file: %s", badExtensions[x], de->d_name); + } + x++; + } + // No extension, starts with 'singe' - could be unix binary. + if (ok && strlen(extension) == 0 && utilStartsWith(filename, "singe")) { + ok = false; + utilSay("!!! Game has singe file: %s", de->d_name); + } + // Is this games.dat? + if (ok && !hasGamesDat && utilStricmp(e, gamesDat) == 0) hasGamesDat = true; } } + //utilSay("%s [%s] %s", filename, extension, e); + archive_read_data_skip(a); } - r = archive_write_finish_entry(ext); - if (r < ARCHIVE_OK) utilSay("%s", archive_error_string(ext)); - if (r < ARCHIVE_WARN) { + r = archive_read_free(a); + if (toplevel != NULL) { + free(toplevel); + toplevel = NULL; + } + if (gamesDat != NULL) { + free(gamesDat); + gamesDat = NULL; + } + + // Did we get a games.dat? + if (ok && !hasGamesDat) { ok = false; - break; + utilSay("!!! Game has no games.dat: %s", de->d_name); } } - archive_read_close(a); - archive_read_free(a); - archive_write_close(ext); - archive_write_free(ext); - if (ok) unlink(de->d_name); - } - } - } + // Unpack it! + if (ok) { + utilSay(">>> Unpacking Game: %s", de->d_name); + // https://github.com/libarchive/libarchive/wiki/Examples#user-content-A_Complete_Extractor + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + a = archive_read_new(); + archive_read_support_format_all(a); + archive_read_support_filter_all(a); + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + if ((r = archive_read_open_filename(a, de->d_name, 10240))) ok = false; + while (ok) { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) break; + if (r < ARCHIVE_OK) utilSay("%s", archive_error_string(a)); + if (r < ARCHIVE_WARN) { + ok = false; + break; + } + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) { + utilSay("%s", archive_error_string(ext)); + } else { + if (archive_entry_size(entry) > 0) { + for (;;) { + r = archive_read_data_block(a, &buff, &size, &offset); + if (r == ARCHIVE_EOF) { + r = ARCHIVE_OK; + break; + } + if (r < ARCHIVE_OK) break; + r = archive_write_data_block(ext, buff, size, offset); + if (r < ARCHIVE_OK) { + utilSay("%s", archive_error_string(ext)); + break; + } + } + if (r < ARCHIVE_OK) utilSay("%s", archive_error_string(ext)); + if (r < ARCHIVE_WARN) { + ok = false; + break; + } + } + } + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) utilSay("%s", archive_error_string(ext)); + if (r < ARCHIVE_WARN) { + ok = false; + break; + } + } + archive_read_close(a); + archive_read_free(a); + archive_write_close(ext); + archive_write_free(ext); + + if (ok) unlink(de->d_name); + + } // if ok + } // if extension is game + } // if it's a file + } // while files if (count > 0) utilSay(""); diff --git a/src/util.c b/src/util.c index 2e6e8c544..0880f8b49 100644 --- a/src/util.c +++ b/src/util.c @@ -442,6 +442,10 @@ void utilSay(char *fmt, ...) { } +bool utilStartsWith(char *string, char *start) { + return strncmp(start, string, strlen(start)) == 0; +} + int utilStricmp(char *a, char *b) { for (;; a++, b++) { diff --git a/src/util.h b/src/util.h index 2afed6d9f..6621322b5 100644 --- a/src/util.h +++ b/src/util.h @@ -53,6 +53,7 @@ char *utilReadFile(char *filename, size_t *bytes); char *utilReadLine(char *haystack, size_t length, char **offset); void utilRedirectConsole(void); void utilSay(char *fmt, ...); +bool utilStartsWith(char *string, char *start); int utilStricmp(char *a, char *b); char *utilStrndup( const char *s1, size_t n); void utilTrace(char *fmt, ...);