[evolution/gnome-3-30] "Save Image..." fails on shown attached image in message preview



commit 671c98eb4d8ac1f67c1600171c5099432941b208
Author: Milan Crha <mcrha redhat com>
Date:   Mon Dec 10 16:21:39 2018 +0100

    "Save Image..." fails on shown attached image in message preview
    
    The image had been stored as "data:image/...;base64;<data>", which
    the context menu Save Image and Copy Image could not handle. This
    resulted in a lengthy error message, which could cause a crash.
    This commit fixes several things:
    a) replace "data:" URI-s with "mail://" URI-s, which means less resources
       are needed; it also fixes the Save/Copy Image actions as a side effect;
    b) fix memory leak for tiff images;
    c) let to Save/Copy Image handle base64-encoded "data:" URI-s (as
       the source HTML message can still contain them);
    d) shorten the URI in the error message when it's too long;
    e) pass also "filename" into the URI, thus the Save Image has prefilled it,
       the same as attachment's "Save as" action.
    
    Reported downstream at:
    https://bugzilla.redhat.com/show_bug.cgi?id=1657361

 src/e-util/e-web-view.c                | 59 ++++++++++++++++++++++++-
 src/em-format/e-mail-formatter-image.c | 78 +++++++++++++---------------------
 src/mail/e-mail-display.c              | 24 +++++++++++
 3 files changed, 110 insertions(+), 51 deletions(-)
---
diff --git a/src/e-util/e-web-view.c b/src/e-util/e-web-view.c
index 78b7ebc9af..6f88d30761 100644
--- a/src/e-util/e-web-view.c
+++ b/src/e-util/e-web-view.c
@@ -4247,6 +4247,7 @@ e_web_view_request (EWebView *web_view,
        AsyncContext *async_context;
        GSList *link;
        GTask *task;
+       gboolean is_processed = FALSE;
 
        g_return_if_fail (E_IS_WEB_VIEW (web_view));
        g_return_if_fail (uri != NULL);
@@ -4272,9 +4273,63 @@ e_web_view_request (EWebView *web_view,
        if (content_request) {
                async_context->content_request = g_object_ref (content_request);
                g_task_run_in_thread (task, web_view_request_process_thread);
-       } else {
+               is_processed = TRUE;
+
+       /* Handle base64-encoded "data:" URIs manually */
+       } else if (g_ascii_strncasecmp (uri, "data:", 5) == 0) {
+               /* data:[<mime type>][;charset=<charset>][;base64],<encoded data> */
+               const gchar *ptr, *from;
+               gboolean is_base64 = FALSE;
+
+               ptr = uri + 5;
+               from = ptr;
+               while (*ptr && *ptr != ',') {
+                       ptr++;
+
+                       if (*ptr == ',' || *ptr == ';') {
+                               if (g_ascii_strncasecmp (from, "base64", ptr - from) == 0)
+                                       is_base64 = TRUE;
+
+                               from = ptr + 1;
+                       }
+               }
+
+               if (is_base64 && *ptr == ',') {
+                       guchar *data;
+                       gsize len = 0;
+
+                       data = g_base64_decode (ptr + 1, &len);
+
+                       if (data && len > 0) {
+                               async_context->input_stream = g_memory_input_stream_new_from_data (data, len, 
g_free);
+                               g_task_return_boolean (task, TRUE);
+                               is_processed = TRUE;
+                       } else {
+                               g_free (data);
+                       }
+               }
+       }
+
+       if (!is_processed) {
+               GString *shorten_uri = NULL;
+               gint len;
+
+               len = g_utf8_strlen (uri, -1);
+
+               /* The "data:" URIs can be quite long */
+               if (len > 512) {
+                       const gchar *ptr = g_utf8_offset_to_pointer (uri, 512);
+
+                       shorten_uri = g_string_sized_new (ptr - uri + 16);
+                       g_string_append_len (shorten_uri, uri, ptr - uri);
+                       g_string_append (shorten_uri, "…");
+               }
+
                g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       _("Cannot get URI “%s”, do not know how to download it."), uri);
+                       _("Cannot get URI “%s”, do not know how to download it."), shorten_uri ? 
shorten_uri->str : uri);
+
+               if (shorten_uri)
+                       g_string_free (shorten_uri, TRUE);
        }
 
        g_object_unref (task);
diff --git a/src/em-format/e-mail-formatter-image.c b/src/em-format/e-mail-formatter-image.c
index 9f837d8a0a..d763c41829 100644
--- a/src/em-format/e-mail-formatter-image.c
+++ b/src/em-format/e-mail-formatter-image.c
@@ -65,12 +65,8 @@ emfe_image_format (EMailFormatterExtension *extension,
                    GOutputStream *stream,
                    GCancellable *cancellable)
 {
-       gchar *content;
        CamelMimePart *mime_part;
        CamelContentType *content_type;
-       CamelDataWrapper *dw;
-       GBytes *bytes;
-       GOutputStream *raw_content;
 
        if (g_cancellable_is_cancelled (cancellable))
                return FALSE;
@@ -82,24 +78,28 @@ emfe_image_format (EMailFormatterExtension *extension,
        /* Skip TIFF images, which cannot be shown inline */
        if (content_type && (
            camel_content_type_is (content_type, "image", "tiff") ||
-           camel_content_type_is (content_type, "image", "tif")))
+           camel_content_type_is (content_type, "image", "tif"))) {
+               g_clear_object (&mime_part);
                return FALSE;
+       }
 
-       dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
-       g_return_val_if_fail (dw, FALSE);
+       if (context->mode == E_MAIL_FORMATTER_MODE_RAW) {
+               CamelDataWrapper *dw;
+               GBytes *bytes;
+               GOutputStream *raw_content;
 
-       raw_content = g_memory_output_stream_new_resizable ();
-       camel_data_wrapper_decode_to_output_stream_sync (
-               dw, raw_content, cancellable, NULL);
-       g_output_stream_close (raw_content, NULL, NULL);
+               dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+               g_return_val_if_fail (dw, FALSE);
 
-       bytes = g_memory_output_stream_steal_as_bytes (
-               G_MEMORY_OUTPUT_STREAM (raw_content));
+               raw_content = g_memory_output_stream_new_resizable ();
+               camel_data_wrapper_decode_to_output_stream_sync (
+                       dw, raw_content, cancellable, NULL);
+               g_output_stream_close (raw_content, NULL, NULL);
 
-       if (context->mode == E_MAIL_FORMATTER_MODE_RAW) {
+               bytes = g_memory_output_stream_steal_as_bytes (
+                       G_MEMORY_OUTPUT_STREAM (raw_content));
 
                if (!e_mail_formatter_get_animate_images (formatter)) {
-
                        gchar *buff;
                        gsize len;
 
@@ -121,52 +121,32 @@ emfe_image_format (EMailFormatterExtension *extension,
                                stream, data, size, NULL, cancellable, NULL);
                }
 
+               g_bytes_unref (bytes);
+               g_object_unref (raw_content);
        } else {
-               gchar *buffer;
-               const gchar *mime_type;
-
-               if (!e_mail_formatter_get_animate_images (formatter)) {
-
-                       gchar *buff;
-                       gsize len;
-
-                       e_mail_part_animation_extract_frame (
-                               bytes, &buff, &len);
+               gchar *buffer, *uri;
+               const gchar *filename;
 
-                       content = g_base64_encode ((guchar *) buff, len);
-                       g_free (buff);
+               filename = camel_mime_part_get_filename (mime_part);
 
-               } else {
-                       gconstpointer data;
-                       gsize size;
+               uri = e_mail_part_build_uri (
+                       e_mail_part_list_get_folder (context->part_list),
+                       e_mail_part_list_get_message_uid (context->part_list),
+                       "part_id", G_TYPE_STRING, e_mail_part_get_id (part),
+                       "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW,
+                       "filename", G_TYPE_STRING, filename ? filename : "",
+                       NULL);
 
-                       data = g_bytes_get_data (bytes, &size);
-                       content = g_base64_encode (data, size);
-               }
-
-               mime_type = e_mail_part_get_mime_type (part);
-               if (mime_type == NULL)
-                       mime_type = "image/*";
-
-               /* The image is already base64-encrypted so we can directly
-                * paste it to the output */
-               buffer = g_strdup_printf (
-                       "<img src=\"data:%s;base64,%s\" "
-                       "     style=\"max-width: 100%%;\" />",
-                       mime_type, content);
+               buffer = g_strdup_printf ("<img src=\"%s\" style=\"max-width:100%%;\" />", uri);
 
                g_output_stream_write_all (
                        stream, buffer, strlen (buffer),
                        NULL, cancellable, NULL);
 
                g_free (buffer);
-               g_free (content);
+               g_free (uri);
        }
 
-       g_bytes_unref (bytes);
-
-       g_object_unref (raw_content);
-
        g_object_unref (mime_part);
 
        return TRUE;
diff --git a/src/mail/e-mail-display.c b/src/mail/e-mail-display.c
index 458e9731d0..4004d85194 100644
--- a/src/mail/e-mail-display.c
+++ b/src/mail/e-mail-display.c
@@ -1837,6 +1837,7 @@ mail_display_suggest_filename (EWebView *web_view,
 {
        EMailDisplay *display;
        CamelMimePart *mime_part;
+       SoupURI *suri;
 
        /* Note, this assumes the URI comes
         * from the currently loaded message. */
@@ -1847,6 +1848,29 @@ mail_display_suggest_filename (EWebView *web_view,
        if (mime_part)
                return g_strdup (camel_mime_part_get_filename (mime_part));
 
+       suri = soup_uri_new (uri);
+       if (suri) {
+               gchar *filename = NULL;
+
+               if (suri->query) {
+                       GHashTable *uri_query;
+
+                       uri_query = soup_form_decode (suri->query);
+                       if (uri_query && g_hash_table_contains (uri_query, "filename"))
+                               filename = g_strdup (g_hash_table_lookup (uri_query, "filename"));
+
+                       if (uri_query)
+                               g_hash_table_destroy (uri_query);
+               }
+
+               soup_uri_free (suri);
+
+               if (filename && *filename)
+                       return filename;
+
+               g_free (filename);
+       }
+
        /* Chain up to parent's suggest_filename() method. */
        return E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
                suggest_filename (web_view, uri);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]