[evolution] Bug #655669 - Can't save inline pictures embedded in HTML Mails



commit 33be6d5da315a0abfd3fb4f74dccc9cc4a249f5e
Author: Milan Crha <mcrha redhat com>
Date:   Tue Aug 9 10:32:36 2011 +0200

    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 deletions(-)
---
diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c
index d5e0353..8a40779 100644
--- a/mail/em-format-html-display.c
+++ b/mail/em-format-html-display.c
@@ -50,6 +50,9 @@
 #include <e-util/e-dialog-utils.h>
 #include <e-util/e-icon-factory.h>
 
+#include <shell/e-shell.h>
+#include <shell/e-shell-utils.h>
+
 #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"
 
@@ -656,6 +660,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 =
+	"<ui>"
+	"  <popup name='context'>"
+	"    <placeholder name='custom-actions-2'>"
+	"      <menuitem action='efhd-image-save'/>"
+	"    </placeholder>"
+	"  </popup>"
+	"</ui>";
+
+static void
 efhd_finalize (GObject *object)
 {
 	EMFormatHTMLDisplay *efhd;
@@ -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 d79fa45..745c275 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 68b5b43..7704cb6 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 0c02743..f0eb3a6 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 b5f368f..1ca9915 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);



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