From 33be6d5da315a0abfd3fb4f74dccc9cc4a249f5e Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 9 Aug 2011 10:32:36 +0200 Subject: Bug #655669 - Can't save inline pictures embedded in HTML Mails --- mail/em-format-html-display.c | 162 ++++++++++++++++++++++++++++++++++++++++++ mail/em-format-html.c | 13 ++++ mail/em-format-html.h | 2 + widgets/misc/e-web-view.c | 53 +++++++++++++- widgets/misc/e-web-view.h | 3 + 5 files changed, 232 insertions(+), 1 deletion(-) diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c index d5e0353bf8..8a4077998d 100644 --- a/mail/em-format-html-display.c +++ b/mail/em-format-html-display.c @@ -50,6 +50,9 @@ #include #include +#include +#include + #if defined (HAVE_NSS) && defined (ENABLE_SMIME) #include "certificate-viewer.h" #include "e-cert-db.h" @@ -59,6 +62,7 @@ #include "e-mail-attachment-bar.h" #include "em-format-html-display.h" #include "em-utils.h" +#include "widgets/misc/e-attachment.h" #include "widgets/misc/e-attachment-button.h" #include "widgets/misc/e-attachment-view.h" @@ -655,6 +659,145 @@ efhd_format_secure (EMFormat *emf, } } +static void +attachment_load_finish (EAttachment *attachment, + GAsyncResult *result, + GFile *file) +{ + EShell *shell; + GtkWindow *parent; + + e_attachment_load_finish (attachment, result, NULL); + + shell = e_shell_get_default (); + parent = e_shell_get_active_window (shell); + + e_attachment_save_async ( + attachment, file, (GAsyncReadyCallback) + e_attachment_save_handle_error, parent); + + g_object_unref (file); +} + +static void +action_image_save_cb (GtkAction *actions, EMFormatHTMLDisplay *efhd) +{ + EWebView *web_view; + EMFormat *emf; + const gchar *image_src; + CamelMimePart *part; + EAttachment *attachment; + GFile *file; + + web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhd)); + g_return_if_fail (web_view != NULL); + + image_src = e_web_view_get_cursor_image_src (web_view); + if (!image_src) + return; + + emf = EM_FORMAT (efhd); + g_return_if_fail (emf != NULL); + g_return_if_fail (emf->message != NULL); + + if (g_str_has_prefix (image_src, "cid:")) { + part = camel_mime_message_get_part_by_content_id (emf->message, image_src + 4); + g_return_if_fail (part != NULL); + + g_object_ref (part); + } else { + CamelStream *image_stream; + CamelDataWrapper *dw; + const gchar *filename; + + image_stream = em_format_html_get_cached_image (EM_FORMAT_HTML (efhd), image_src); + if (!image_stream) + return; + + filename = strrchr (image_src, '/'); + if (filename && strchr (filename, '?')) + filename = NULL; + else if (filename) + filename = filename + 1; + + part = camel_mime_part_new (); + if (filename) + camel_mime_part_set_filename (part, filename); + + dw = camel_data_wrapper_new (); + camel_data_wrapper_set_mime_type (dw, "application/octet-stream"); + camel_data_wrapper_construct_from_stream_sync (dw, image_stream, NULL, NULL); + camel_medium_set_content (CAMEL_MEDIUM (part), dw); + g_object_unref (dw); + + camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64); + + g_object_unref (image_stream); + } + + file = e_shell_run_save_dialog ( + e_shell_get_default (), + _("Save Image"), camel_mime_part_get_filename (part), + NULL, NULL, NULL); + if (file == NULL) { + g_object_unref (part); + return; + } + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, part); + + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + attachment_load_finish, file); + + g_object_unref (part); +} + +static void +efhd_web_view_update_actions_cb (EWebView *web_view, EMFormatHTMLDisplay *efhd) +{ + const gchar *image_src; + gboolean visible; + GtkAction *action; + + g_return_if_fail (web_view != NULL); + + image_src = e_web_view_get_cursor_image_src (web_view); + visible = image_src && g_str_has_prefix (image_src, "cid:"); + if (!visible && image_src) { + CamelStream *image_stream; + + image_stream = em_format_html_get_cached_image (EM_FORMAT_HTML (efhd), image_src); + visible = image_stream != NULL; + + if (image_stream) + g_object_unref (image_stream); + } + + action = e_web_view_get_action (web_view, "efhd-image-save"); + if (action) + gtk_action_set_visible (action, visible); +} + +static GtkActionEntry image_entries[] = { + { "efhd-image-save", + GTK_STOCK_SAVE, + N_("Save _Image..."), + NULL, + N_("Save the image to a file"), + G_CALLBACK (action_image_save_cb) } +}; + +static const gchar *image_ui = + "" + " " + " " + " " + " " + " " + ""; + static void efhd_finalize (GObject *object) { @@ -701,6 +844,9 @@ static void efhd_init (EMFormatHTMLDisplay *efhd) { EWebView *web_view; + GtkActionGroup *image_actions; + GtkUIManager *ui_manager; + GError *error = NULL; web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhd)); @@ -716,6 +862,22 @@ efhd_init (EMFormatHTMLDisplay *efhd) EM_FORMAT_HTML (efhd)->text_html_flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; + + image_actions = e_web_view_get_action_group (web_view, "image"); + g_return_if_fail (image_actions != NULL); + + gtk_action_group_add_actions ( + image_actions, image_entries, + G_N_ELEMENTS (image_entries), efhd); + + ui_manager = e_web_view_get_ui_manager (web_view); + gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, &error); + + if (error) + g_debug ("%s: Failed to add image_ui: %s", G_STRFUNC, error->message); + g_clear_error (&error); + + g_signal_connect (web_view, "update-actions", G_CALLBACK (efhd_web_view_update_actions_cb), efhd); } GType diff --git a/mail/em-format-html.c b/mail/em-format-html.c index d79fa450bc..745c275d8b 100644 --- a/mail/em-format-html.c +++ b/mail/em-format-html.c @@ -3328,3 +3328,16 @@ em_format_html_format_cert_infos (CamelCipherCertInfo *first_cinfo) return g_string_free (res, FALSE); } + +/* unref returned pointer with g_object_unref(), if not NULL */ +CamelStream * +em_format_html_get_cached_image (EMFormatHTML *efh, const gchar *image_uri) +{ + g_return_val_if_fail (efh != NULL, NULL); + g_return_val_if_fail (image_uri != NULL, NULL); + + if (!emfh_http_cache) + return NULL; + + return camel_data_cache_get (emfh_http_cache, EMFH_HTTP_CACHE_PATH, image_uri, NULL); +} diff --git a/mail/em-format-html.h b/mail/em-format-html.h index 68b5b438eb..7704cb640a 100644 --- a/mail/em-format-html.h +++ b/mail/em-format-html.h @@ -311,6 +311,8 @@ void em_format_html_set_headers_collapsable gchar * em_format_html_format_cert_infos (CamelCipherCertInfo *first_cinfo); +CamelStream * em_format_html_get_cached_image (EMFormatHTML *efh, + const gchar *image_uri); G_END_DECLS #endif /* EM_FORMAT_HTML_H */ diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c index 0c027431e7..f0eb3a64ba 100644 --- a/widgets/misc/e-web-view.c +++ b/widgets/misc/e-web-view.c @@ -47,6 +47,7 @@ struct _EWebViewPrivate { GtkUIManager *ui_manager; gchar *selected_uri; GdkPixbufAnimation *cursor_image; + gchar *cursor_image_src; GtkAction *open_proxy; GtkAction *print_proxy; @@ -85,7 +86,8 @@ enum { PROP_PRINT_PROXY, PROP_SAVE_AS_PROXY, PROP_SELECTED_URI, - PROP_CURSOR_IMAGE + PROP_CURSOR_IMAGE, + PROP_CURSOR_IMAGE_SRC }; enum { @@ -478,6 +480,7 @@ web_view_button_press_event_cb (EWebView *web_view, if (event) { GdkPixbufAnimation *anim; + gchar *image_src; if (frame == NULL) frame = GTK_HTML (web_view); @@ -486,6 +489,10 @@ web_view_button_press_event_cb (EWebView *web_view, e_web_view_set_cursor_image (web_view, anim); if (anim != NULL) g_object_unref (anim); + + image_src = gtk_html_get_image_src_at (frame, event->x, event->y); + e_web_view_set_cursor_image_src (web_view, image_src); + g_free (image_src); } if (event != NULL && event->button != 3) @@ -635,6 +642,11 @@ web_view_set_property (GObject *object, E_WEB_VIEW (object), g_value_get_object (value)); return; + case PROP_CURSOR_IMAGE_SRC: + e_web_view_set_cursor_image_src ( + E_WEB_VIEW (object), + g_value_get_string (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -736,6 +748,11 @@ web_view_get_property (GObject *object, value, e_web_view_get_cursor_image ( E_WEB_VIEW (object))); return; + case PROP_CURSOR_IMAGE_SRC: + g_value_set_string ( + value, e_web_view_get_cursor_image_src ( + E_WEB_VIEW (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -783,6 +800,11 @@ web_view_dispose (GObject *object) priv->cursor_image = NULL; } + if (priv->cursor_image_src != NULL) { + g_free (priv->cursor_image_src); + priv->cursor_image_src = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -1500,6 +1522,16 @@ e_web_view_class_init (EWebViewClass *class) GDK_TYPE_PIXBUF_ANIMATION, G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_CURSOR_IMAGE_SRC, + g_param_spec_string ( + "cursor-image-src", + "Image source uri at the mouse cursor", + NULL, + NULL, + G_PARAM_READWRITE)); + signals[COPY_CLIPBOARD] = g_signal_new ( "copy-clipboard", G_TYPE_FROM_CLASS (class), @@ -2004,6 +2036,25 @@ e_web_view_set_cursor_image (EWebView *web_view, g_object_notify (G_OBJECT (web_view), "cursor-image"); } +const gchar * +e_web_view_get_cursor_image_src (EWebView *web_view) +{ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + return web_view->priv->cursor_image_src; +} + +void +e_web_view_set_cursor_image_src (EWebView *web_view, const gchar *src_uri) +{ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + g_free (web_view->priv->cursor_image_src); + web_view->priv->cursor_image_src = g_strdup (src_uri); + + g_object_notify (G_OBJECT (web_view), "cursor-image-src"); +} + GtkAction * e_web_view_get_open_proxy (EWebView *web_view) { diff --git a/widgets/misc/e-web-view.h b/widgets/misc/e-web-view.h index b5f368f622..1ca9915d5e 100644 --- a/widgets/misc/e-web-view.h +++ b/widgets/misc/e-web-view.h @@ -128,6 +128,9 @@ GdkPixbufAnimation * e_web_view_get_cursor_image (EWebView *web_view); void e_web_view_set_cursor_image (EWebView *web_view, GdkPixbufAnimation *animation); +const gchar * e_web_view_get_cursor_image_src (EWebView *web_view); +void e_web_view_set_cursor_image_src (EWebView *web_view, + const gchar *src_uri); GtkAction * e_web_view_get_open_proxy (EWebView *web_view); void e_web_view_set_open_proxy (EWebView *web_view, GtkAction *open_proxy); -- cgit v1.2.3