Commit 0f12f652 authored by Milan Crha's avatar Milan Crha

"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
parent 4f272411
......@@ -4243,6 +4243,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);
......@@ -4268,9 +4269,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);
......
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment