From 0f5293731024dab32023f58164a7e4540cb3d2fc Mon Sep 17 00:00:00 2001 From: peijiankang Date: Wed, 24 Apr 2024 11:36:22 +0800 Subject: [PATCH] CVE-2023-51698 (cherry picked from commit bc736ff602c0711375067c678dc784841604065a) --- CVE-2023-51698.patch | 1843 ++++++++++++++++++++++++++++++++++++++++++ atril.spec | 7 +- 2 files changed, 1849 insertions(+), 1 deletion(-) create mode 100644 CVE-2023-51698.patch diff --git a/CVE-2023-51698.patch b/CVE-2023-51698.patch new file mode 100644 index 0000000..7109351 --- /dev/null +++ b/CVE-2023-51698.patch @@ -0,0 +1,1843 @@ +From 71c3efaf1e42e6ba87f5385c5524ad403dd864d2 Mon Sep 17 00:00:00 2001 +From: peijiankang +Date: Wed, 24 Apr 2024 11:34:50 +0800 +Subject: [PATCH] CVE-2023-51698 + +--- + backend/comics/Makefile.am | 5 +- + backend/comics/comics-document.c | 1158 +++++++++++------------------- + backend/comics/comics-document.h | 9 +- + backend/comics/ev-archive.c | 323 +++++++++ + backend/comics/ev-archive.h | 56 ++ + configure.ac | 3 + + libdocument/ev-document.h | 1 + + 7 files changed, 806 insertions(+), 749 deletions(-) + create mode 100644 backend/comics/ev-archive.c + create mode 100644 backend/comics/ev-archive.h + +diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am +index 99ef5f4..519cb49 100644 +--- a/backend/comics/Makefile.am ++++ b/backend/comics/Makefile.am +@@ -12,12 +12,15 @@ backend_LTLIBRARIES = libcomicsdocument.la + + libcomicsdocument_la_SOURCES = \ + comics-document.c \ +- comics-document.h ++ comics-document.h \ ++ ev-archive.c \ ++ ev-archive.h + + libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) + libcomicsdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libatrildocument.la \ + $(BACKEND_LIBS) \ ++ $(COMICS_LIBS) \ + $(LIB_LIBS) + + backend_in_files = comicsdocument.atril-backend.in +diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c +index 4d4d293..1da1eee 100644 +--- a/backend/comics/comics-document.c ++++ b/backend/comics/comics-document.c +@@ -30,24 +30,17 @@ + #include + #include + +-#include +- + #include "comics-document.h" + #include "ev-document-misc.h" + #include "ev-document-thumbnails.h" + #include "ev-file-helpers.h" ++#include "ev-archive.h" ++#include ++#include + + #define EV_EOL "\n" + +-typedef enum +-{ +- RARLABS, +- GNAUNRAR, +- UNZIP, +- P7ZIP, +- TAR, +- UNARCHIVER +-} ComicBookDecompressType; ++#define BLOCK_SIZE 10240 + + typedef struct _ComicsDocumentClass ComicsDocumentClass; + +@@ -58,400 +51,269 @@ struct _ComicsDocumentClass + + struct _ComicsDocument + { +- EvDocument parent_instance; +- +- gchar *archive, *dir; +- GPtrArray *page_names; +- gchar *selected_command, *alternative_command; +- gchar *extract_command, *list_command, *decompress_tmp; +- gboolean regex_arg; +- gint offset; +- ComicBookDecompressType command_usage; ++ EvDocument parent_instance; ++ EvArchive *archive; ++ gchar *archive_path; ++ gchar *archive_uri; ++ GPtrArray *page_names; /* elem: char * */ ++ GHashTable *page_positions; /* key: char *, value: uint + 1 */ ++ + }; + +-#define OFFSET_7Z 53 +-#define OFFSET_ZIP 2 +-#define NO_OFFSET 0 +- +-/* For perfomance reasons of 7z* we've choosen to decompress on the temporary +- * directory instead of decompressing on the stdout */ +- +-/** +- * @extract: command line arguments to pass to extract a file from the archive +- * to stdout. +- * @list: command line arguments to list the archive contents +- * @decompress_tmp: command line arguments to pass to extract the archive +- * into a directory. +- * @regex_arg: whether the command can accept regex expressions +- * @offset: the position offset of the filename on each line in the output of +- * running the @list command +- */ +-typedef struct { +- char *extract; +- char *list; +- char *decompress_tmp; +- gboolean regex_arg; +- gint offset; +-} ComicBookDecompressCommand; ++static void ++comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); ++EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document, ++ { ++ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, ++ comics_document_document_thumbnails_iface_init); ++ } ); ++ ++#define FORMAT_UNKNOWN 0 ++#define FORMAT_SUPPORTED 1 ++#define FORMAT_UNSUPPORTED 2 ++ ++/* Returns a GHashTable of: ++ * : file extensions ++ * : degree of support in gdk-pixbuf */ ++static GHashTable * ++get_image_extensions(void) ++{ ++ GHashTable *extensions; ++ GSList *formats = gdk_pixbuf_get_formats (); ++ GSList *l; ++ guint i; ++ const char *known_image_formats[] = { ++ "png", ++ "jpg", ++ "jpeg", ++ "webp" ++ }; ++ ++ extensions = g_hash_table_new_full (g_str_hash, g_str_equal, ++ g_free, NULL); ++ for (l = formats; l != NULL; l = l->next) { ++ int i; ++ gchar **ext = gdk_pixbuf_format_get_extensions (l->data); + +-static const ComicBookDecompressCommand command_usage_def[] = { +- /* RARLABS unrar */ +- {"%s p -c- -ierr --", "%s vb -c- -- %s", NULL , FALSE, NO_OFFSET}, ++ for (i = 0; ext[i] != NULL; i++) { ++ g_hash_table_insert (extensions, ++ g_strdup (ext[i]), ++ GINT_TO_POINTER (FORMAT_SUPPORTED)); ++ } + +- /* GNA! unrar */ +- {NULL , "%s t %s" , "%s -xf %s %s" , FALSE, NO_OFFSET}, ++ g_strfreev (ext); ++ } ++ g_slist_free (formats); + +- /* unzip */ +- {"%s -p -C --" , "%s %s" , NULL , TRUE , OFFSET_ZIP}, ++ /* Add known image formats that aren't supported by gdk-pixbuf */ ++ for (i = 0; i < G_N_ELEMENTS (known_image_formats); i++) { ++ if (!g_hash_table_lookup (extensions, known_image_formats[i])) { ++ g_hash_table_insert (extensions, ++ g_strdup (known_image_formats[i]), ++ GINT_TO_POINTER (FORMAT_UNSUPPORTED)); ++ } ++ } + +- /* 7zip */ +- {NULL , "%s l -- %s" , "%s x -y %s -o%s", FALSE, OFFSET_7Z}, ++ return extensions; ++} + +- /* tar */ +- {"%s -xOf" , "%s -tf %s" , NULL , FALSE, NO_OFFSET}, ++static int ++has_supported_extension (const char *name, ++ GHashTable *supported_extensions) ++{ ++ gboolean ret = FALSE; ++ gchar *suffix; ++ suffix = g_strrstr (name, "."); ++ if (!suffix) ++ return ret; + +- /* UNARCHIVER */ +- {"unar -o -" , "%s %s" , NULL , FALSE, NO_OFFSET} +-}; ++ suffix = g_ascii_strdown (suffix + 1, -1); ++ ret = GPOINTER_TO_INT (g_hash_table_lookup (supported_extensions, suffix)); ++ g_free (suffix); + +-static void comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); ++ return ret; ++} + +-static GSList* get_supported_image_extensions (void); +-static void get_page_size_area_prepared_cb (GdkPixbufLoader *loader, +- gpointer data); +-static void render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, +- gint width, +- gint height, +- gpointer data); +-static char** extract_argv (EvDocument *document, +- gint page); ++#define APPLE_DOUBLE_PREFIX "._" ++static gboolean ++is_apple_double (const char *name) ++{ ++char *basename; ++ gboolean ret = FALSE; + ++ basename = g_path_get_basename (name); ++ if (basename == NULL) { ++ g_debug ("Filename '%s' doesn't have a basename?", name); ++ return ret; ++ } ++ ret = g_str_has_prefix (basename, APPLE_DOUBLE_PREFIX); ++ g_free (basename); + +-EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document, +- { +- EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, +- comics_document_document_thumbnails_iface_init); +- } ); +- +-/** +- * comics_regex_quote: +- * @unquoted_string: a literal string +- * +- * Quotes a string so unzip will not interpret the regex expressions of +- * @unquoted_string. Basically, this functions uses [] to disable regex +- * expressions. The return value must be freed with * g_free() +- * +- * Return value: quoted and disabled-regex string +- **/ +-static gchar * +-comics_regex_quote (const gchar *unquoted_string) ++ return ret; ++} ++ ++static gboolean ++archive_reopen_if_needed (ComicsDocument *comics_document, ++ const char *page_wanted, ++ GError **error) + { +- const gchar *p; +- GString *dest; +- +- dest = g_string_new ("'"); +- +- p = unquoted_string; +- +- while (*p) { +- switch (*p) { +- /* * matches a sequence of 0 or more characters */ +- case ('*'): +- /* ? matches exactly 1 charactere */ +- case ('?'): +- /* [...] matches any single character found inside +- * the brackets. Disabling the first bracket is enough. +- */ +- case ('['): +- g_string_append (dest, "["); +- g_string_append_c (dest, *p); +- g_string_append (dest, "]"); +- break; +- /* Because \ escapes regex expressions that we are +- * disabling for unzip, we need to disable \ too */ +- case ('\\'): +- g_string_append (dest, "[\\\\]"); +- break; +- /* Escape single quote inside the string */ +- case ('\''): +- g_string_append (dest, "'\\''"); +- break; +- default: +- g_string_append_c (dest, *p); +- break; ++ const char *current_page; ++ guint current_page_idx, page_wanted_idx; ++ ++ if (ev_archive_at_entry (comics_document->archive)) { ++ current_page = ev_archive_get_entry_pathname (comics_document->archive); ++ if (current_page) { ++ current_page_idx = GPOINTER_TO_UINT (g_hash_table_lookup (comics_document->page_positions, current_page)); ++ page_wanted_idx = GPOINTER_TO_UINT (g_hash_table_lookup (comics_document->page_positions, page_wanted)); ++ ++ if (current_page_idx != 0 && ++ page_wanted_idx != 0 && ++ page_wanted_idx > current_page_idx) ++ return TRUE; + } +- ++p; ++ ++ ev_archive_reset (comics_document->archive); + } +- g_string_append_c (dest, '\''); +- return g_string_free (dest, FALSE); ++return ev_archive_open_filename (comics_document->archive, comics_document->archive_path, error); + } + +- +-/* This function manages the command for decompressing a comic book */ +-static gboolean +-comics_decompress_temp_dir (const gchar *command_decompress_tmp, +- const gchar *command, +- GError **error) ++static GPtrArray * ++comics_document_list (ComicsDocument *comics_document, ++ GError **error) + { +- gboolean success; +- gchar *std_out, *basename; +- GError *err = NULL; +- gint retval; +- +- success = g_spawn_command_line_sync (command_decompress_tmp, &std_out, +- NULL, &retval, &err); +- basename = g_path_get_basename (command); +- if (!success) { +- g_set_error (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("Error launching the command “%s” in order to " +- "decompress the comic book: %s"), +- basename, +- err->message); +- g_error_free (err); +- } else if (WIFEXITED (retval)) { +- if (WEXITSTATUS (retval) == EXIT_SUCCESS) { +- g_free (std_out); +- g_free (basename); +- return TRUE; +- } else { +- g_set_error (error, ++GPtrArray *array = NULL; ++ gboolean has_encrypted_files, has_unsupported_images, has_archive_errors; ++ GHashTable *supported_extensions = NULL; ++ ++ if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, error)) { ++ if (*error != NULL) { ++ g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, (*error)->message); ++ g_clear_error (error); ++ } ++ ++ g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, +- _("The command “%s” failed at " +- "decompressing the comic book."), +- basename); +- g_free (std_out); ++ _("File is corrupted")); ++ goto out; ++ } ++ ++ supported_extensions = get_image_extensions (); ++ ++ has_encrypted_files = FALSE; ++ has_unsupported_images = FALSE; ++ has_archive_errors = FALSE; ++ array = g_ptr_array_sized_new (64); ++ ++ while (1) { ++ const char *name; ++ int supported; ++ ++ if (!ev_archive_read_next_header (comics_document->archive, error)) { ++ if (*error != NULL) { ++ g_debug ("Fatal error handling archive (%s): %s", G_STRFUNC, (*error)->message); ++ g_clear_error (error); ++ has_archive_errors = TRUE; ++ goto out; ++ } ++ break; + } +- } else { +- g_set_error (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("The command “%s” did not end normally."), +- basename); +- g_free (std_out); ++ ++ name = ev_archive_get_entry_pathname (comics_document->archive); ++ /* Ignore https://en.wikipedia.org/wiki/AppleSingle_and_AppleDouble_formats */ ++ if (is_apple_double (name)) { ++ g_debug ("Not adding AppleDouble file '%s' to the list of files in the comics", name); ++ continue; ++ } ++ ++ supported = has_supported_extension (name, supported_extensions); ++ if (supported == FORMAT_UNKNOWN) { ++ g_debug ("Not adding unsupported file '%s' to the list of files in the comics", name); ++ continue; ++ } else if (supported == FORMAT_UNSUPPORTED) { ++ g_debug ("Not adding unsupported image '%s' to the list of files in the comics", name); ++ has_unsupported_images = TRUE; ++ continue; ++ } ++ ++ if (ev_archive_get_entry_is_encrypted (comics_document->archive)) { ++ g_debug ("Not adding encrypted file '%s' to the list of files in the comics", name); ++ has_encrypted_files = TRUE; ++ continue; ++ } ++ ++ g_debug ("Adding '%s' to the list of files in the comics", name); ++ g_ptr_array_add (array, g_strdup (name)); + } +- g_free (basename); +- return FALSE; ++out: ++ if (array->len == 0) { ++ g_ptr_array_free (array, TRUE); ++ array = NULL; ++ ++ if (has_encrypted_files) { ++ g_set_error_literal (error, ++ EV_DOCUMENT_ERROR, ++ EV_DOCUMENT_ERROR_ENCRYPTED, ++ _("Archive is encrypted")); ++ } else if (has_unsupported_images) { ++ g_set_error_literal (error, ++ EV_DOCUMENT_ERROR, ++ EV_DOCUMENT_ERROR_UNSUPPORTED_CONTENT, ++ _("No supported images in archive")); ++ } else if (has_archive_errors) { ++ g_set_error_literal (error, ++ EV_DOCUMENT_ERROR, ++ EV_DOCUMENT_ERROR_INVALID, ++ _("File is corrupted")); ++ } else { ++ g_set_error_literal (error, ++ EV_DOCUMENT_ERROR, ++ EV_DOCUMENT_ERROR_INVALID, ++ _("No files in archive")); ++ } ++ } ++ ++ if (supported_extensions) ++ g_hash_table_destroy (supported_extensions); ++ ev_archive_reset (comics_document->archive); ++ return array; + } + +-/* This function shows how to use the choosen command for decompressing a +- * comic book file. It modifies fields of the ComicsDocument struct with +- * this information */ +-static gboolean +-comics_generate_command_lines (ComicsDocument *comics_document, +- GError **error) ++static GHashTable * ++save_positions (GPtrArray *page_names) + { +- gchar *quoted_file, *quoted_file_aux; +- gchar *quoted_command; +- ComicBookDecompressType type; +- +- type = comics_document->command_usage; +- comics_document->regex_arg = command_usage_def[type].regex_arg; +- quoted_command = g_shell_quote (comics_document->selected_command); +- if (comics_document->regex_arg) { +- quoted_file = comics_regex_quote (comics_document->archive); +- quoted_file_aux = g_shell_quote (comics_document->archive); +- comics_document->list_command = +- g_strdup_printf (command_usage_def[type].list, +- comics_document->alternative_command, +- quoted_file_aux); +- g_free (quoted_file_aux); +- } else { +- quoted_file = g_shell_quote (comics_document->archive); +- comics_document->list_command = +- g_strdup_printf (command_usage_def[type].list, +- quoted_command, quoted_file); +- } +- comics_document->extract_command = +- g_strdup_printf (command_usage_def[type].extract, +- quoted_command); +- comics_document->offset = command_usage_def[type].offset; +- if (command_usage_def[type].decompress_tmp) { +- comics_document->dir = ev_mkdtemp ("atril-comics-XXXXXX", error); +- if (comics_document->dir == NULL) +- return FALSE; +- +- /* unrar-free can't create directories, but ev_mkdtemp already created the dir */ +- +- comics_document->decompress_tmp = +- g_strdup_printf (command_usage_def[type].decompress_tmp, +- quoted_command, quoted_file, +- comics_document->dir); +- g_free (quoted_file); +- g_free (quoted_command); +- +- if (!comics_decompress_temp_dir (comics_document->decompress_tmp, +- comics_document->selected_command, error)) +- return FALSE; +- else +- return TRUE; +- } else { +- g_free (quoted_file); +- g_free (quoted_command); +- return TRUE; +- } ++ guint i; ++ GHashTable *ht; + ++ ht = g_hash_table_new (g_str_hash, g_str_equal); ++ for (i = 0; i < page_names->len; i++) ++ g_hash_table_insert (ht, page_names->pdata[i], GUINT_TO_POINTER(i + 1)); ++ return ht; + } + +-/* This function chooses an external command for decompressing a comic +- * book based on its mime tipe. */ +-static gboolean +-comics_check_decompress_command (gchar *mime_type, ++/*This function chooses the archive decompression support ++ * book based on its mime type. */ ++static gboolean ++comics_check_decompress_support (gchar *mime_type, + ComicsDocument *comics_document, + GError **error) + { +- gboolean success; +- gchar *std_out, *std_err; +- gint retval; +- GError *err = NULL; +- +- /* FIXME, use proper cbr/cbz mime types once they're +- * included in shared-mime-info */ +- + if (g_content_type_is_a (mime_type, "application/x-cbr") || + g_content_type_is_a (mime_type, "application/x-rar")) { +- /* The RARLAB provides a no-charge proprietary (freeware) +- * decompress-only client for Linux called unrar. Another +- * option is a GPLv2-licensed command-line tool developed by +- * the Gna! project. Confusingly enough, the free software RAR +- * decoder is also named unrar. For this reason we need to add +- * some lines for disambiguation. Sorry for the added the +- * complexity but it's life :) +- * Finally, some distributions, like Debian, rename this free +- * option as unrar-free. +- * */ +- comics_document->selected_command = +- g_find_program_in_path ("unrar"); +- if (comics_document->selected_command) { +- /* We only use std_err to avoid printing useless error +- * messages on the terminal */ +- success = +- g_spawn_command_line_sync ( +- comics_document->selected_command, +- &std_out, &std_err, +- &retval, &err); +- if (!success) { +- g_propagate_error (error, err); +- g_error_free (err); +- return FALSE; +- /* I don't check retval status because RARLAB unrar +- * doesn't have a way to return 0 without involving an +- * operation with a file*/ +- } else if (WIFEXITED (retval)) { +- if (g_strrstr (std_out,"freeware") != NULL) +- /* The RARLAB freeware client */ +- comics_document->command_usage = RARLABS; +- else +- /* The Gna! free software client */ +- comics_document->command_usage = GNAUNRAR; +- +- g_free (std_out); +- g_free (std_err); +- return TRUE; +- } +- } +- /* The Gna! free software client with Debian naming convention */ +- comics_document->selected_command = +- g_find_program_in_path ("unrar-free"); +- if (comics_document->selected_command) { +- comics_document->command_usage = GNAUNRAR; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("lsar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = UNARCHIVER; ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_RAR)) + return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; +- return TRUE; +- } +- + } else if (g_content_type_is_a (mime_type, "application/x-cbz") || + g_content_type_is_a (mime_type, "application/zip")) { +- /* InfoZIP's unzip program */ +- comics_document->selected_command = +- g_find_program_in_path ("unzip"); +- comics_document->alternative_command = +- g_find_program_in_path ("zipnote"); +- if (comics_document->selected_command && +- comics_document->alternative_command) { +- comics_document->command_usage = UNZIP; ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_ZIP)) + return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("lsar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = UNARCHIVER; +- return TRUE; +- } +- + } else if (g_content_type_is_a (mime_type, "application/x-cb7") || + g_content_type_is_a (mime_type, "application/x-7z-compressed")) { +- /* 7zr, 7za and 7z are the commands from the p7zip project able +- * to decompress .7z files */ +- comics_document->selected_command = +- g_find_program_in_path ("7zr"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("7za"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_7Z)) + return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("7z"); +- if (comics_document->selected_command) { +- comics_document->command_usage = P7ZIP; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("lsar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = UNARCHIVER; +- return TRUE; +- } + } else if (g_content_type_is_a (mime_type, "application/x-cbt") || + g_content_type_is_a (mime_type, "application/x-tar")) { +- /* tar utility (Tape ARchive) */ +- comics_document->selected_command = +- g_find_program_in_path ("tar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; +- return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("bsdtar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = TAR; ++ if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_TAR)) + return TRUE; +- } +- comics_document->selected_command = +- g_find_program_in_path ("lsar"); +- if (comics_document->selected_command) { +- comics_document->command_usage = UNARCHIVER; +- return TRUE; +- } + } else { + g_set_error (error, + EV_DOCUMENT_ERROR, +@@ -463,8 +325,9 @@ comics_check_decompress_command (gchar *mime_type, + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, +- _("Can't find an appropriate command to " +- "decompress this type of comic book")); ++ _("libarchive lacks support for this comic book’s " ++ "compression, please contact your distributor")); ++ + return FALSE; + } + +@@ -472,43 +335,15 @@ static int + sort_page_names (gconstpointer a, + gconstpointer b) + { +- const char *name_1, *name_2; +- gchar *key_1, *key_2; +- gboolean sort_last_1, sort_last_2; +- int compare; +- +- name_1 = * (const char **) a; +- name_2 = * (const char **) b; +- +- #define SORT_LAST_CHAR1 '.' +- #define SORT_LAST_CHAR2 '#' +- +- sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2; +- sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2; +- +- #undef SORT_LAST_CHAR1 +- #undef SORT_LAST_CHAR2 +- +- if (sort_last_1 && !sort_last_2) +- { +- compare = +1; +- } +- else if (!sort_last_1 && sort_last_2) +- { +- compare = -1; +- } +- else +- { +- key_1 = g_utf8_collate_key_for_filename (name_1, -1); +- key_2 = g_utf8_collate_key_for_filename (name_2, -1); +- +- compare = strcmp (key_1, key_2); +- +- g_free (key_1); +- g_free (key_2); +- } +- +- return compare; ++ gchar *temp1, *temp2; ++ gint ret; ++ temp1 = g_utf8_collate_key_for_filename (* (const char **) a, -1); ++ temp2 = g_utf8_collate_key_for_filename (* (const char **) b, -1); ++ ret = strcmp (temp1, temp2); ++ ++ g_free (temp1); ++ g_free (temp2); ++ return ret; + } + + static gboolean +@@ -517,50 +352,13 @@ comics_document_load (EvDocument *document, + GError **error) + { + ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- GSList *supported_extensions; +- gchar *std_out; + gchar *mime_type; +- gchar **cb_files, *cb_file; +- gboolean success; +- int i, retval; +- GError *err = NULL; +- +- comics_document->archive = g_filename_from_uri (uri, NULL, error); +- if (!comics_document->archive) +- return FALSE; +- +- mime_type = ev_file_get_mime_type (uri, FALSE, &err); +- if (!mime_type) { +- if (err) { +- g_propagate_error (error, err); +- } else { +- g_set_error_literal (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("Unknown MIME Type")); +- } ++ GFile *file; ++ file = g_file_new_for_uri (uri); ++ comics_document->archive_path = g_file_get_path (file); ++ g_object_unref (file); + +- return FALSE; +- } +- +- if (!comics_check_decompress_command (mime_type, comics_document, +- error)) { +- g_free (mime_type); +- return FALSE; +- } else if (!comics_generate_command_lines (comics_document, error)) { +- g_free (mime_type); +- return FALSE; +- } +- +- g_free (mime_type); +- +- /* Get list of files in archive */ +- success = g_spawn_command_line_sync (comics_document->list_command, +- &std_out, NULL, &retval, error); +- +- if (!success) { +- return FALSE; +- } else if (!WIFEXITED(retval) || WEXITSTATUS(retval) != EXIT_SUCCESS) { ++ if (!comics_document->archive_path) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, +@@ -568,58 +366,26 @@ comics_document_load (EvDocument *document, + return FALSE; + } + +- /* FIXME: is this safe against filenames containing \n in the archive ? */ +- cb_files = g_strsplit (std_out, EV_EOL, 0); ++ comics_document->archive_uri = g_strdup (uri); ++ mime_type = ev_file_get_mime_type (uri, FALSE, error); + +- g_free (std_out); ++ if (mime_type == NULL) ++ return FALSE; + +- if (!cb_files) { +- g_set_error_literal (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("No files in archive")); ++ if (!comics_check_decompress_support (mime_type, comics_document, error)) { ++ g_free (mime_type); + return FALSE; + } + +- comics_document->page_names = g_ptr_array_sized_new (64); +- +- supported_extensions = get_supported_image_extensions (); +- for (i = 0; cb_files[i] != NULL; i++) { +- if (comics_document->offset != NO_OFFSET) { +- if (g_utf8_strlen (cb_files[i],-1) > +- comics_document->offset) { +- cb_file = +- g_utf8_offset_to_pointer (cb_files[i], +- comics_document->offset); +- } else { +- continue; +- } +- } else { +- cb_file = cb_files[i]; +- } +- gchar *suffix = g_strrstr (cb_file, "."); +- if (!suffix) +- continue; +- suffix = g_ascii_strdown (suffix + 1, -1); +- if (g_slist_find_custom (supported_extensions, suffix, +- (GCompareFunc) strcmp) != NULL) { +- g_ptr_array_add (comics_document->page_names, +- g_strstrip (g_strdup (cb_file))); +- } +- g_free (suffix); +- } +- g_strfreev (cb_files); +- g_slist_foreach (supported_extensions, (GFunc) g_free, NULL); +- g_slist_free (supported_extensions); ++ g_free (mime_type); + +- if (comics_document->page_names->len == 0) { +- g_set_error (error, +- EV_DOCUMENT_ERROR, +- EV_DOCUMENT_ERROR_INVALID, +- _("No images found in archive %s"), +- uri); ++ /* Get list of files in archive */ ++ comics_document->page_names = comics_document_list (comics_document, error); ++ if (!comics_document->page_names) + return FALSE; +- } ++ ++ /* Keep an index */ ++ comics_document->page_positions = save_positions (comics_document->page_names); + + /* Now sort the pages */ + g_ptr_array_sort (comics_document->page_names, sort_page_names); +@@ -627,7 +393,6 @@ comics_document_load (EvDocument *document, + return TRUE; + } + +- + static gboolean + comics_document_save (EvDocument *document, + const char *uri, +@@ -635,7 +400,7 @@ comics_document_save (EvDocument *document, + { + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + +- return ev_xfer_uri_simple (comics_document->archive, uri, error); ++ return ev_xfer_uri_simple (comics_document->archive_uri, uri, error); + } + + static int +@@ -649,6 +414,23 @@ comics_document_get_n_pages (EvDocument *document) + return comics_document->page_names->len; + } + ++typedef struct { ++ gboolean got_info; ++ int height; ++ int width; ++} PixbufInfo; ++ ++static void ++get_page_size_prepared_cb (GdkPixbufLoader *loader, ++ int width, ++ int height, ++ PixbufInfo *info) ++{ ++ info->got_info = TRUE; ++ info->height = height; ++ info->width = width; ++} ++ + static void + comics_document_get_page_size (EvDocument *document, + EvPage *page, +@@ -656,74 +438,89 @@ comics_document_get_page_size (EvDocument *document, + double *height) + { + GdkPixbufLoader *loader; +- char **argv; +- guchar buf[1024]; +- gboolean success, got_size = FALSE; +- gint outpipe = -1; +- GPid child_pid; +- gssize bytes; +- GdkPixbuf *pixbuf; +- gchar *filename; ++ + ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- +- if (!comics_document->decompress_tmp) { +- argv = extract_argv (document, page->index); +- success = g_spawn_async_with_pipes (NULL, argv, NULL, +- G_SPAWN_SEARCH_PATH | +- G_SPAWN_STDERR_TO_DEV_NULL, +- NULL, NULL, +- &child_pid, +- NULL, &outpipe, NULL, NULL); +- g_strfreev (argv); +- g_return_if_fail (success == TRUE); +- +- loader = gdk_pixbuf_loader_new (); +- g_signal_connect (loader, "area-prepared", +- G_CALLBACK (get_page_size_area_prepared_cb), +- &got_size); +- +- while (outpipe >= 0) { +- bytes = read (outpipe, buf, 1024); +- +- if (bytes > 0) +- gdk_pixbuf_loader_write (loader, buf, bytes, NULL); +- if (bytes <= 0 || got_size) { +- close (outpipe); +- outpipe = -1; +- gdk_pixbuf_loader_close (loader, NULL); ++ ++ const char *page_path; ++ PixbufInfo info; ++ GError *error = NULL; ++ ++ page_path = g_ptr_array_index (comics_document->page_names, page->index); ++ ++ if (!archive_reopen_if_needed (comics_document, page_path, &error)) { ++ g_warning ("Fatal error opening archive: %s", error->message); ++ g_error_free (error); ++ return; ++ } ++ ++ loader = gdk_pixbuf_loader_new (); ++ info.got_info = FALSE; ++ g_signal_connect (loader, "size-prepared", ++ G_CALLBACK (get_page_size_prepared_cb), ++ &info); ++ ++ while (1) { ++ const char *name; ++ GError *error = NULL; ++ ++ if (!ev_archive_read_next_header (comics_document->archive, &error)) { ++ if (error != NULL) { ++ g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, error->message); ++ g_error_free (error); + } ++ break; + } +- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); +- if (pixbuf) { +- if (width) +- *width = gdk_pixbuf_get_width (pixbuf); +- if (height) +- *height = gdk_pixbuf_get_height (pixbuf); +- } +- g_spawn_close_pid (child_pid); +- g_object_unref (loader); +- } else { +- filename = g_build_filename (comics_document->dir, +- (char *) comics_document->page_names->pdata[page->index], +- NULL); +- pixbuf = gdk_pixbuf_new_from_file (filename, NULL); +- if (pixbuf) { +- if (width) +- *width = gdk_pixbuf_get_width (pixbuf); +- if (height) +- *height = gdk_pixbuf_get_height (pixbuf); +- g_object_unref (pixbuf); ++ ++ name = ev_archive_get_entry_pathname (comics_document->archive); ++ if (g_strcmp0 (name, page_path) == 0) { ++ char buf[BLOCK_SIZE]; ++ gssize read; ++ gint64 left; ++ ++ left = ev_archive_get_entry_size (comics_document->archive); ++ read = ev_archive_read_data (comics_document->archive, buf, ++ MIN(BLOCK_SIZE, left), &error); ++ while (read > 0 && !info.got_info) { ++ if (!gdk_pixbuf_loader_write (loader, (guchar *) buf, read, &error)) { ++ read = -1; ++ break; ++ } ++ left -= read; ++ read = ev_archive_read_data (comics_document->archive, buf, ++ MIN(BLOCK_SIZE, left), &error); ++ } ++ if (read < 0) { ++ g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); ++ g_error_free (error); ++ } ++ break; + } +- g_free (filename); ++ } ++ ++ gdk_pixbuf_loader_close (loader, NULL); ++ g_object_unref (loader); ++ ++ if (info.got_info) { ++ if (width) ++ *width = info.width; ++ if (height) ++ *height = info.height; + } + } + + static void +-get_page_size_area_prepared_cb (GdkPixbufLoader *loader, +- gpointer data) ++render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, ++ gint width, ++ gint height, ++ EvRenderContext *rc) + { +- gboolean *got_size = data; +- *got_size = TRUE; ++ // int scaled_width, scaled_height; ++ double scale = rc->scale; ++ int w = (width * scale + 0.5); ++ int h = (height * scale + 0.5); ++ ++ // ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height); ++ gdk_pixbuf_loader_set_size (loader, w, h); + } + + static GdkPixbuf * +@@ -731,69 +528,68 @@ comics_document_render_pixbuf (EvDocument *document, + EvRenderContext *rc) + { + GdkPixbufLoader *loader; +- GdkPixbuf *rotated_pixbuf, *tmp_pixbuf; +- char **argv; +- guchar buf[4096]; +- gboolean success; +- gint outpipe = -1; +- GPid child_pid; +- gssize bytes; +- gint width, height; +- gchar *filename; ++ GdkPixbuf *tmp_pixbuf; ++ GdkPixbuf *rotated_pixbuf = NULL; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- +- if (!comics_document->decompress_tmp) { +- argv = extract_argv (document, rc->page->index); +- success = g_spawn_async_with_pipes (NULL, argv, NULL, +- G_SPAWN_SEARCH_PATH | +- G_SPAWN_STDERR_TO_DEV_NULL, +- NULL, NULL, +- &child_pid, +- NULL, &outpipe, NULL, NULL); +- g_strfreev (argv); +- g_return_val_if_fail (success == TRUE, NULL); +- +- loader = gdk_pixbuf_loader_new (); +- g_signal_connect (loader, "size-prepared", +- G_CALLBACK (render_pixbuf_size_prepared_cb), +- &rc->scale); +- +- while (outpipe >= 0) { +- bytes = read (outpipe, buf, 4096); +- +- if (bytes > 0) { +- gdk_pixbuf_loader_write (loader, buf, bytes, +- NULL); +- } else if (bytes <= 0) { +- close (outpipe); +- gdk_pixbuf_loader_close (loader, NULL); +- outpipe = -1; ++ const char *page_path; ++ GError *error = NULL; ++ ++ page_path = g_ptr_array_index (comics_document->page_names, rc->page->index); ++ ++ if (!archive_reopen_if_needed (comics_document, page_path, &error)) { ++ g_warning ("Fatal error opening archive: %s", error->message); ++ g_error_free (error); ++ return NULL; ++ } ++ ++ loader = gdk_pixbuf_loader_new (); ++ g_signal_connect (loader, "size-prepared", ++ G_CALLBACK (render_pixbuf_size_prepared_cb), ++ rc); ++ ++ while (1) { ++ const char *name; ++ ++ if (!ev_archive_read_next_header (comics_document->archive, &error)) { ++ if (error != NULL) { ++ g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, error->message); ++ g_error_free (error); + } ++ break; + } +- tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); +- rotated_pixbuf = +- gdk_pixbuf_rotate_simple (tmp_pixbuf, +- 360 - rc->rotation); +- g_spawn_close_pid (child_pid); +- g_object_unref (loader); +- } else { +- filename = +- g_build_filename (comics_document->dir, +- (char *) comics_document->page_names->pdata[rc->page->index], +- NULL); +- +- gdk_pixbuf_get_file_info (filename, &width, &height); +- +- tmp_pixbuf = +- gdk_pixbuf_new_from_file_at_size ( +- filename, width * (rc->scale) + 0.5, +- height * (rc->scale) + 0.5, NULL); +- rotated_pixbuf = +- gdk_pixbuf_rotate_simple (tmp_pixbuf, +- 360 - rc->rotation); +- g_free (filename); +- g_object_unref (tmp_pixbuf); ++ ++ name = ev_archive_get_entry_pathname (comics_document->archive); ++ if (g_strcmp0 (name, page_path) == 0) { ++ size_t size = ev_archive_get_entry_size (comics_document->archive); ++ char *buf; ++ ssize_t read; ++ ++ buf = g_malloc (size); ++ read = ev_archive_read_data (comics_document->archive, buf, size, &error); ++ if (read <= 0) { ++ if (read < 0) { ++ g_warning ("Fatal error reading '%s' in archive: %s", name, error->message); ++ g_error_free (error); ++ } else { ++ g_warning ("Read an empty file from the archive"); ++ } ++ } else { ++ gdk_pixbuf_loader_write (loader, (guchar *) buf, size, NULL); ++ } ++ g_free (buf); ++ gdk_pixbuf_loader_close (loader, NULL); ++ break; ++ } ++ } ++ tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); ++ if (tmp_pixbuf) { ++ if ((rc->rotation % 360) == 0) ++ rotated_pixbuf = g_object_ref (tmp_pixbuf); ++ else ++ rotated_pixbuf = gdk_pixbuf_rotate_simple (tmp_pixbuf, ++ 360 - rc->rotation); + } ++ g_object_unref (loader); + return rotated_pixbuf; + } + +@@ -805,79 +601,26 @@ comics_document_render (EvDocument *document, + cairo_surface_t *surface; + + pixbuf = comics_document_render_pixbuf (document, rc); ++ if (!pixbuf) ++ return NULL; + surface = ev_document_misc_surface_from_pixbuf (pixbuf); +- g_object_unref (pixbuf); +- ++ g_clear_object (&pixbuf); + return surface; + } + +-static void +-render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, +- gint width, +- gint height, +- gpointer data) +-{ +- double *scale = data; +- int w = (width * (*scale) + 0.5); +- int h = (height * (*scale) + 0.5); +- +- gdk_pixbuf_loader_set_size (loader, w, h); +-} +- +-/** +- * comics_remove_dir: Removes a directory recursively. +- * Returns: +- * 0 if it was successfully deleted, +- * -1 if an error occurred +- */ +-static int +-comics_remove_dir (gchar *path_name) +-{ +- GDir *content_dir; +- const gchar *filename; +- gchar *filename_with_path; +- +- if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) { +- content_dir = g_dir_open (path_name, 0, NULL); +- filename = g_dir_read_name (content_dir); +- while (filename) { +- filename_with_path = +- g_build_filename (path_name, +- filename, NULL); +- comics_remove_dir (filename_with_path); +- g_free (filename_with_path); +- filename = g_dir_read_name (content_dir); +- } +- g_dir_close (content_dir); +- } +- /* Note from g_remove() documentation: on Windows, it is in general not +- * possible to remove a file that is open to some process, or mapped +- * into memory.*/ +- return (g_remove (path_name)); +-} +- + static void + comics_document_finalize (GObject *object) + { + ComicsDocument *comics_document = COMICS_DOCUMENT (object); +- +- if (comics_document->decompress_tmp) { +- if (comics_remove_dir (comics_document->dir) == -1) +- g_warning (_("There was an error deleting “%s”."), +- comics_document->dir); +- g_free (comics_document->dir); +- } +- ++ + if (comics_document->page_names) { + g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL); + g_ptr_array_free (comics_document->page_names, TRUE); + } +- +- g_free (comics_document->archive); +- g_free (comics_document->selected_command); +- g_free (comics_document->alternative_command); +- g_free (comics_document->extract_command); +- g_free (comics_document->list_command); ++ g_clear_pointer (&comics_document->page_positions, g_hash_table_destroy); ++ g_clear_object (&comics_document->archive); ++ g_free (comics_document->archive_path); ++ g_free (comics_document->archive_uri); + + G_OBJECT_CLASS (comics_document_parent_class)->finalize (object); + } +@@ -900,33 +643,7 @@ comics_document_class_init (ComicsDocumentClass *klass) + static void + comics_document_init (ComicsDocument *comics_document) + { +- comics_document->archive = NULL; +- comics_document->page_names = NULL; +- comics_document->extract_command = NULL; +-} +- +-/* Returns a list of file extensions supported by gdk-pixbuf */ +-static GSList* +-get_supported_image_extensions(void) +-{ +- GSList *extensions = NULL; +- GSList *formats = gdk_pixbuf_get_formats (); +- GSList *l; +- +- for (l = formats; l != NULL; l = l->next) { +- int i; +- gchar **ext = gdk_pixbuf_format_get_extensions (l->data); +- +- for (i = 0; ext[i] != NULL; i++) { +- extensions = g_slist_append (extensions, +- g_strdup (ext[i])); +- } +- +- g_strfreev (ext); +- } +- +- g_slist_free (formats); +- return extensions; ++ comics_document->archive = ev_archive_new (); + } + + static GdkPixbuf * +@@ -940,7 +657,7 @@ comics_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + + if (border) { + GdkPixbuf *tmp_pixbuf = thumbnail; +- ++ + thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf); + g_object_unref (tmp_pixbuf); + } +@@ -955,7 +672,7 @@ comics_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + gint *height) + { + gdouble page_width, page_height; +- ++ + comics_document_get_page_size (EV_DOCUMENT (document), rc->page, + &page_width, &page_height); + +@@ -974,48 +691,3 @@ comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *i + iface->get_thumbnail = comics_document_thumbnails_get_thumbnail; + iface->get_dimensions = comics_document_thumbnails_get_dimensions; + } +- +-static char** +-extract_argv (EvDocument *document, gint page) +-{ +- ComicsDocument *comics_document = COMICS_DOCUMENT (document); +- char **argv; +- char *command_line, *quoted_archive, *quoted_filename; +- GError *err = NULL; +- +- if (g_strrstr (comics_document->page_names->pdata[page], "--checkpoint-action=")) +- { +- g_warning ("File unsupported\n"); +- gtk_main_quit (); +- } +- +- if (page >= comics_document->page_names->len) +- return NULL; +- +- if (comics_document->regex_arg) { +- quoted_archive = g_shell_quote (comics_document->archive); +- quoted_filename = +- comics_regex_quote (comics_document->page_names->pdata[page]); +- } else { +- quoted_archive = g_shell_quote (comics_document->archive); +- quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]); +- } +- +- command_line = g_strdup_printf ("%s %s %s", +- comics_document->extract_command, +- quoted_archive, +- quoted_filename); +- g_free (quoted_archive); +- g_free (quoted_filename); +- +- g_shell_parse_argv (command_line, NULL, &argv, &err); +- g_free (command_line); +- +- if (err) { +- g_warning (_("Error %s"), err->message); +- g_error_free (err); +- return NULL; +- } +- +- return argv; +-} +diff --git a/backend/comics/comics-document.h b/backend/comics/comics-document.h +index 6cc55f0..4417a69 100644 +--- a/backend/comics/comics-document.h ++++ b/backend/comics/comics-document.h +@@ -16,9 +16,9 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +-#ifndef __COMICS_DOCUMENT_H__ +-#define __COMICS_DOCUMENT_H__ ++#pragma once + ++#include "ev-macros.h" + #include "ev-document.h" + + G_BEGIN_DECLS +@@ -30,9 +30,8 @@ G_BEGIN_DECLS + typedef struct _ComicsDocument ComicsDocument; + + GType comics_document_get_type (void) G_GNUC_CONST; ++GType register_atril_backend (GTypeModule *module); ++ + +-G_MODULE_EXPORT GType register_atril_backend (GTypeModule *module); +- + G_END_DECLS + +-#endif /* __COMICS_DOCUMENT_H__ */ +diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c +new file mode 100644 +index 0000000..568e162 +--- /dev/null ++++ b/backend/comics/ev-archive.c +@@ -0,0 +1,323 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ ++/* ++ * Copyright (C) 2017, Bastien Nocera ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "config.h" ++#include "ev-archive.h" ++ ++#include ++#include ++#include ++ ++#define BUFFER_SIZE (64 * 1024) ++ ++struct _EvArchive { ++ GObject parent_instance; ++ EvArchiveType type; ++ ++ /* libarchive */ ++ struct archive *libar; ++ struct archive_entry *libar_entry; ++}; ++ ++G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT); ++ ++static void ++ev_archive_finalize (GObject *object) ++{ ++ EvArchive *archive = EV_ARCHIVE (object); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_clear_pointer (&archive->libar, archive_free); ++ break; ++ default: ++ break; ++ } ++ ++ G_OBJECT_CLASS (ev_archive_parent_class)->finalize (object); ++} ++ ++static void ++ev_archive_class_init (EvArchiveClass *klass) ++{ ++ GObjectClass *object_class = (GObjectClass *) klass; ++ ++ object_class->finalize = ev_archive_finalize; ++} ++ ++EvArchive * ++ev_archive_new (void) ++{ ++ return g_object_new (EV_TYPE_ARCHIVE, NULL); ++} ++ ++static void ++libarchive_set_archive_type (EvArchive *archive, ++ EvArchiveType archive_type) ++{ ++ archive->type = archive_type; ++ archive->libar = archive_read_new (); ++ ++ if (archive_type == EV_ARCHIVE_TYPE_ZIP) ++ archive_read_support_format_zip (archive->libar); ++ else if (archive_type == EV_ARCHIVE_TYPE_7Z) ++ archive_read_support_format_7zip (archive->libar); ++ else if (archive_type == EV_ARCHIVE_TYPE_TAR) ++ archive_read_support_format_tar (archive->libar); ++ else if (archive_type == EV_ARCHIVE_TYPE_RAR) { ++ archive_read_support_format_rar (archive->libar); ++ archive_read_support_format_rar5 (archive->libar); ++ } else ++ g_assert_not_reached (); ++} ++ ++EvArchiveType ++ev_archive_get_archive_type (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE); ++ ++ return archive->type; ++} ++ ++gboolean ++ev_archive_set_archive_type (EvArchive *archive, ++ EvArchiveType archive_type) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type == EV_ARCHIVE_TYPE_NONE, FALSE); ++ ++ switch (archive_type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ libarchive_set_archive_type (archive, archive_type); ++ break; ++ default: ++ g_assert_not_reached (); ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++ev_archive_open_filename (EvArchive *archive, ++ const char *path, ++ GError **error) ++{ ++ int r; ++ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); ++ g_return_val_if_fail (path != NULL, FALSE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ r = archive_read_open_filename (archive->libar, path, BUFFER_SIZE); ++ if (r != ARCHIVE_OK) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Error opening archive: %s", archive_error_string (archive->libar)); ++ return FALSE; ++ } ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++libarchive_read_next_header (EvArchive *archive, ++ GError **error) ++{ ++ while (1) { ++ int r; ++ ++ r = archive_read_next_header (archive->libar, &archive->libar_entry); ++ if (r != ARCHIVE_OK) { ++ if (r != ARCHIVE_EOF) ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Error reading archive: %s", archive_error_string (archive->libar)); ++ return FALSE; ++ } ++ ++ if (archive_entry_filetype (archive->libar_entry) != AE_IFREG) { ++ g_debug ("Skipping '%s' as it's not a regular file", ++ archive_entry_pathname (archive->libar_entry)); ++ continue; ++ } ++ ++ g_debug ("At header for file '%s'", archive_entry_pathname (archive->libar_entry)); ++ ++ break; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++ev_archive_read_next_header (EvArchive *archive, ++ GError **error) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ return libarchive_read_next_header (archive, error); ++ } ++ ++ return FALSE; ++} ++ ++gboolean ++ev_archive_at_entry (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); ++ ++ return (archive->libar_entry != NULL); ++} ++ ++const char * ++ev_archive_get_entry_pathname (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, NULL); ++ return archive_entry_pathname (archive->libar_entry); ++ } ++ ++ return NULL; ++} ++ ++gint64 ++ev_archive_get_entry_size (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); ++ return archive_entry_size (archive->libar_entry); ++ } ++ ++ return -1; ++} ++ ++gboolean ++ev_archive_get_entry_is_encrypted (EvArchive *archive) ++{ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); ++ return archive_entry_is_encrypted (archive->libar_entry); ++ } ++ ++ return FALSE; ++} ++ ++gssize ++ev_archive_read_data (EvArchive *archive, ++ void *buf, ++ gsize count, ++ GError **error) ++{ ++ gssize r = -1; ++ ++ g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1); ++ g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_NONE: ++ g_assert_not_reached (); ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_return_val_if_fail (archive->libar_entry != NULL, -1); ++ r = archive_read_data (archive->libar, buf, count); ++ if (r < 0) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to decompress data: %s", archive_error_string (archive->libar)); ++ } ++ break; ++ } ++ ++ return r; ++} ++ ++void ++ev_archive_reset (EvArchive *archive) ++{ ++ g_return_if_fail (EV_IS_ARCHIVE (archive)); ++ g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE); ++ ++ switch (archive->type) { ++ case EV_ARCHIVE_TYPE_RAR: ++ case EV_ARCHIVE_TYPE_ZIP: ++ case EV_ARCHIVE_TYPE_7Z: ++ case EV_ARCHIVE_TYPE_TAR: ++ g_clear_pointer (&archive->libar, archive_free); ++ libarchive_set_archive_type (archive, archive->type); ++ archive->libar_entry = NULL; ++ break; ++ default: ++ g_assert_not_reached (); ++ } ++} ++ ++static void ++ev_archive_init (EvArchive *archive) ++{ ++} +diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h +new file mode 100644 +index 0000000..b4e1399 +--- /dev/null ++++ b/backend/comics/ev-archive.h +@@ -0,0 +1,56 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ ++/* ++ * Copyright (C) 2017, Bastien Nocera ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define EV_TYPE_ARCHIVE ev_archive_get_type () ++G_DECLARE_FINAL_TYPE (EvArchive, ev_archive, EV, ARCHIVE, GObject) ++ ++typedef enum { ++ EV_ARCHIVE_TYPE_NONE = 0, ++ EV_ARCHIVE_TYPE_RAR, ++ EV_ARCHIVE_TYPE_ZIP, ++ EV_ARCHIVE_TYPE_7Z, ++ EV_ARCHIVE_TYPE_TAR ++} EvArchiveType; ++ ++EvArchive *ev_archive_new (void); ++gboolean ev_archive_set_archive_type (EvArchive *archive, ++ EvArchiveType archive_type); ++EvArchiveType ev_archive_get_archive_type (EvArchive *archive); ++gboolean ev_archive_open_filename (EvArchive *archive, ++ const char *path, ++ GError **error); ++gboolean ev_archive_read_next_header (EvArchive *archive, ++ GError **error); ++gboolean ev_archive_at_entry (EvArchive *archive); ++const char *ev_archive_get_entry_pathname (EvArchive *archive); ++gint64 ev_archive_get_entry_size (EvArchive *archive); ++gboolean ev_archive_get_entry_is_encrypted (EvArchive *archive); ++gssize ev_archive_read_data (EvArchive *archive, ++ void *buf, ++ gsize count, ++ GError **error); ++void ev_archive_reset (EvArchive *archive); ++ ++G_END_DECLS +diff --git a/configure.ac b/configure.ac +index 90d2f09..5fced50 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -560,8 +560,11 @@ AC_ARG_ENABLE(comics, + [enable_comics=$enableval], + [enable_comics=yes]) + ++COMICS_DEPS="libarchive" + if test "x$enable_comics" = "xyes"; then + AC_DEFINE([ENABLE_COMICS], [1], [Enable support for comics.]) ++ PKG_CHECK_MODULES([COMICS], [$COMICS_DEPS]) ++ AC_SUBST(COMICS_LIBS) + fi + AM_CONDITIONAL(ENABLE_COMICS, test x$enable_comics = xyes) + +diff --git a/libdocument/ev-document.h b/libdocument/ev-document.h +index 6c492da..d57d002 100644 +--- a/libdocument/ev-document.h ++++ b/libdocument/ev-document.h +@@ -56,6 +56,7 @@ typedef struct _EvDocumentPrivate EvDocumentPrivate; + typedef enum + { + EV_DOCUMENT_ERROR_INVALID, ++ EV_DOCUMENT_ERROR_UNSUPPORTED_CONTENT, + EV_DOCUMENT_ERROR_ENCRYPTED + } EvDocumentError; + +-- +2.43.0 + diff --git a/atril.spec b/atril.spec index a5fd4c7..255effd 100644 --- a/atril.spec +++ b/atril.spec @@ -14,7 +14,7 @@ Name: atril Version: %{branch}.3 -Release: 3 +Release: 4 Summary: Document viewer License: GPLv2+ and LGPLv2+ and MIT URL: http://mate-desktop.org @@ -25,6 +25,8 @@ URL: http://mate-desktop.org # Source for snapshot-builds. %{!?rel_build:Source0: http://git.mate-desktop.org/%{name}/snapshot/%{name}-%{commit}.tar.xz#/%{git_tar}} Patch01: CVE-2023-52076.patch +#fix from https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2Fmate-desktop%2Fatril%2Fcommit%2Fce41df6467521ff9fd4f16514ae7d6ebb62eb1ed +Patch02: CVE-2023-51698.patch BuildRequires: gcc-c++ BuildRequires: gtk3-devel @@ -165,6 +167,9 @@ desktop-file-validate ${RPM_BUILD_ROOT}%{_datadir}/applications/atril.desktop %changelog +* Wed Apr 24 2024 peijiankang - 1.22.3-4 +- add CVE-2023-51698.patch + * Thu Feb 29 2024 peijiankang - 1.22.3-3 - add CVE-2023-52076.patch -- Gitee