From 6d2c382788a4042d53f49a080acd11b499aa52f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Wed, 28 Mar 2012 18:38:11 +0200 Subject: WebKit port - port formatter and mail module --- mail/Makefile.am | 20 +- mail/e-mail-attachment-bar.c | 94 +- mail/e-mail-attachment-bar.h | 4 +- mail/e-mail-browser.c | 113 +- mail/e-mail-browser.h | 6 +- mail/e-mail-display.c | 1430 +++++++++++-- mail/e-mail-display.h | 50 +- mail/e-mail-notebook-view.c | 18 +- mail/e-mail-paned-view.c | 82 +- mail/e-mail-paned-view.h | 1 + mail/e-mail-printer.c | 859 ++++++++ mail/e-mail-printer.h | 85 + mail/e-mail-reader-utils.c | 151 +- mail/e-mail-reader-utils.h | 3 +- mail/e-mail-reader.c | 445 ++-- mail/e-mail-reader.h | 6 +- mail/e-mail-request.c | 771 +++++++ mail/e-mail-request.h | 36 + mail/em-account-editor.c | 6 +- mail/em-composer-utils.c | 31 +- mail/em-format-hook.c | 23 +- mail/em-format-hook.h | 4 +- mail/em-format-html-display.c | 1350 +++++-------- mail/em-format-html-display.h | 49 +- mail/em-format-html-print.c | 681 +++++-- mail/em-format-html-print.h | 19 +- mail/em-format-html.c | 4457 ++++++++++++++++++----------------------- mail/em-format-html.h | 191 +- mail/em-html-stream.c | 182 -- mail/em-html-stream.h | 77 - mail/em-utils.c | 74 +- mail/mail.error.xml | 5 + 32 files changed, 6953 insertions(+), 4370 deletions(-) create mode 100644 mail/e-mail-printer.c create mode 100644 mail/e-mail-printer.h create mode 100644 mail/e-mail-request.c create mode 100644 mail/e-mail-request.h delete mode 100644 mail/em-html-stream.c delete mode 100644 mail/em-html-stream.h (limited to 'mail') diff --git a/mail/Makefile.am b/mail/Makefile.am index 3a13284668..e6eccc83fe 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -22,7 +22,9 @@ libevolution_mail_la_CPPFLAGS = \ $(CERT_UI_CFLAGS) \ $(CANBERRA_CFLAGS) \ $(CLUTTER_CFLAGS) \ - $(GTKHTML_CFLAGS) \ + $(GTKHTML_CFLAGS) \ + $(JAVASCRIPTCORE_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ -DEVOLUTION_DATADIR=\""$(datadir)"\" \ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \ -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ @@ -56,11 +58,13 @@ mailinclude_HEADERS = \ e-mail-migrate.h \ e-mail-notebook-view.h \ e-mail-paned-view.h \ + e-mail-printer.h \ e-mail-reader-utils.h \ e-mail-reader.h \ - e-mail-ui-session.h \ + e-mail-request.h \ e-mail-sidebar.h \ e-mail-tag-editor.h \ + e-mail-ui-session.h \ e-mail-view.h \ em-account-editor.h \ em-composer-utils.h \ @@ -81,7 +85,6 @@ mailinclude_HEADERS = \ em-format-html-display.h \ em-format-html-print.h \ em-format-html.h \ - em-html-stream.h \ em-search-context.h \ em-subscription-editor.h \ em-sync-stream.h \ @@ -121,11 +124,13 @@ libevolution_mail_la_SOURCES = \ e-mail-migrate.c \ e-mail-notebook-view.c \ e-mail-paned-view.c \ + e-mail-printer.c \ e-mail-reader-utils.c \ e-mail-reader.c \ - e-mail-ui-session.c \ + e-mail-request.c \ e-mail-sidebar.c \ e-mail-tag-editor.c \ + e-mail-ui-session.c \ e-mail-view.c \ em-account-editor.c \ em-composer-utils.c \ @@ -146,7 +151,6 @@ libevolution_mail_la_SOURCES = \ em-format-html-display.c \ em-format-html-print.c \ em-format-html.c \ - em-html-stream.c \ em-search-context.c \ em-subscription-editor.c \ em-sync-stream.c \ @@ -194,7 +198,11 @@ libevolution_mail_la_LIBADD = \ $(CANBERRA_LIBS) \ $(CLUTTER_LIBS) \ $(GTKHTML_LIBS) \ - $(SMIME_LIBS) + $(JAVASCRIPTCORE_CFLAGS) \ + $(E_WIDGETS_LIBS) \ + $(SMIME_LIBS) \ + $(LIBSOUP_LIBS) \ + $(GNOME_PLATFORM_LIBS) libevolution_mail_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) diff --git a/mail/e-mail-attachment-bar.c b/mail/e-mail-attachment-bar.c index 21a298c56d..7572c664cb 100644 --- a/mail/e-mail-attachment-bar.c +++ b/mail/e-mail-attachment-bar.c @@ -60,7 +60,8 @@ enum { PROP_ACTIVE_VIEW, PROP_DRAGGING, PROP_EDITABLE, - PROP_EXPANDED + PROP_EXPANDED, + PROP_STORE }; /* Forward Declarations */ @@ -78,7 +79,6 @@ G_DEFINE_TYPE_WITH_CODE ( static void mail_attachment_bar_update_status (EMailAttachmentBar *bar) { - EAttachmentView *view; EAttachmentStore *store; GtkActivatable *activatable; GtkAction *action; @@ -88,8 +88,7 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar) gchar *display_size; gchar *markup; - view = E_ATTACHMENT_VIEW (bar); - store = e_attachment_view_get_store (view); + store = E_ATTACHMENT_STORE (bar->priv->model); label = GTK_LABEL (bar->priv->status_label); num_attachments = e_attachment_store_get_num_attachments (store); @@ -119,6 +118,31 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar) g_free (display_size); } +static void +mail_attachment_bar_set_store (EMailAttachmentBar *bar, + EAttachmentStore *store) +{ + g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); + + bar->priv->model = g_object_ref (store); + + gtk_icon_view_set_model (GTK_ICON_VIEW (bar->priv->icon_view), + bar->priv->model); + gtk_tree_view_set_model (GTK_TREE_VIEW (bar->priv->tree_view), + bar->priv->model); + + g_signal_connect_swapped ( + bar->priv->model, "notify::num-attachments", + G_CALLBACK (mail_attachment_bar_update_status), bar); + + g_signal_connect_swapped ( + bar->priv->model, "notify::total-size", + G_CALLBACK (mail_attachment_bar_update_status), bar); + + /* Initialize */ + mail_attachment_bar_update_status (bar); +} + static void mail_attachment_bar_set_property (GObject *object, guint property_id, @@ -127,7 +151,7 @@ mail_attachment_bar_set_property (GObject *object, { switch (property_id) { case PROP_ACTIVE_VIEW: - e_mail_attachment_bar_set_active_view ( + e_mail_attachment_bar_set_active_view ( E_MAIL_ATTACHMENT_BAR (object), g_value_get_int (value)); return; @@ -149,6 +173,11 @@ mail_attachment_bar_set_property (GObject *object, E_MAIL_ATTACHMENT_BAR (object), g_value_get_boolean (value)); return; + case PROP_STORE: + mail_attachment_bar_set_store ( + E_MAIL_ATTACHMENT_BAR (object), + g_value_get_object (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -188,6 +217,11 @@ mail_attachment_bar_get_property (GObject *object, e_mail_attachment_bar_get_expanded ( E_MAIL_ATTACHMENT_BAR (object))); return; + case PROP_STORE: + g_value_set_object ( + value, + e_mail_attachment_bar_get_store ( + E_MAIL_ATTACHMENT_BAR (object))); } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -201,8 +235,6 @@ mail_attachment_bar_dispose (GObject *object) priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object); if (priv->model != NULL) { - e_attachment_store_remove_all ( - E_ATTACHMENT_STORE (priv->model)); g_object_unref (priv->model); priv->model = NULL; } @@ -347,17 +379,6 @@ mail_attachment_bar_get_private (EAttachmentView *view) return e_attachment_view_get_private (view); } -static EAttachmentStore * -mail_attachment_bar_get_store (EAttachmentView *view) -{ - EMailAttachmentBar *bar; - - bar = E_MAIL_ATTACHMENT_BAR (view); - view = E_ATTACHMENT_VIEW (bar->priv->icon_view); - - return e_attachment_view_get_store (view); -} - static GtkTreePath * mail_attachment_bar_get_path_at_pos (EAttachmentView *view, gint x, @@ -488,6 +509,17 @@ e_mail_attachment_bar_class_init (EMailAttachmentBarClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property ( + object_class, + PROP_STORE, + g_param_spec_object ( + "store", + "Attachment Store", + NULL, + E_TYPE_ATTACHMENT_STORE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_override_property ( object_class, PROP_DRAGGING, "dragging"); @@ -499,7 +531,7 @@ static void e_mail_attachment_bar_interface_init (EAttachmentViewInterface *interface) { interface->get_private = mail_attachment_bar_get_private; - interface->get_store = mail_attachment_bar_get_store; + interface->get_store = e_mail_attachment_bar_get_store; interface->get_path_at_pos = mail_attachment_bar_get_path_at_pos; interface->get_selected_paths = mail_attachment_bar_get_selected_paths; interface->path_is_selected = mail_attachment_bar_path_is_selected; @@ -520,7 +552,6 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar) GtkAction *action; bar->priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (bar); - bar->priv->model = e_attachment_store_new (); gtk_box_set_spacing (GTK_BOX (bar), 6); @@ -644,23 +675,18 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar) bar->priv->status_label = g_object_ref (widget); gtk_widget_show (widget); - g_signal_connect_swapped ( - bar->priv->model, "notify::num-attachments", - G_CALLBACK (mail_attachment_bar_update_status), bar); - - g_signal_connect_swapped ( - bar->priv->model, "notify::total-size", - G_CALLBACK (mail_attachment_bar_update_status), bar); - g_object_unref (size_group); } GtkWidget * -e_mail_attachment_bar_new (void) +e_mail_attachment_bar_new (EAttachmentStore *store) { + g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL); + return g_object_new ( E_TYPE_MAIL_ATTACHMENT_BAR, - "editable", FALSE, NULL); + "editable", FALSE, + "store", store, NULL); } gint @@ -729,3 +755,11 @@ e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar, g_object_notify (G_OBJECT (bar), "expanded"); } + +EAttachmentStore * +e_mail_attachment_bar_get_store (EMailAttachmentBar *bar) +{ + g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), NULL); + + return E_ATTACHMENT_STORE (bar->priv->model); +} diff --git a/mail/e-mail-attachment-bar.h b/mail/e-mail-attachment-bar.h index 93c1b89261..b83d9733e0 100644 --- a/mail/e-mail-attachment-bar.h +++ b/mail/e-mail-attachment-bar.h @@ -60,7 +60,7 @@ struct _EMailAttachmentBarClass { }; GType e_mail_attachment_bar_get_type (void); -GtkWidget * e_mail_attachment_bar_new (void); +GtkWidget * e_mail_attachment_bar_new (EAttachmentStore *store); gint e_mail_attachment_bar_get_active_view (EMailAttachmentBar *bar); void e_mail_attachment_bar_set_active_view @@ -71,6 +71,8 @@ gboolean e_mail_attachment_bar_get_expanded void e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar, gboolean expanded); +EAttachmentStore * + e_mail_attachment_bar_get_store (EMailAttachmentBar *bar); G_END_DECLS diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c index 0dbb3d01e3..806980d602 100644 --- a/mail/e-mail-browser.c +++ b/mail/e-mail-browser.c @@ -55,7 +55,8 @@ struct _EMailBrowserPrivate { EMailBackend *backend; GtkUIManager *ui_manager; EFocusTracker *focus_tracker; - EMFormatHTMLDisplay *formatter; + + EMFormatWriteMode mode; GtkWidget *main_menu; GtkWidget *main_toolbar; @@ -74,7 +75,8 @@ enum { PROP_GROUP_BY_THREADS, PROP_SHOW_DELETED, PROP_REPLY_STYLE, - PROP_UI_MANAGER + PROP_UI_MANAGER, + PROP_DISPLAY_MODE, }; static gpointer parent_class; @@ -260,11 +262,10 @@ static void mail_browser_message_selected_cb (EMailBrowser *browser, const gchar *uid) { - EMFormatHTML *formatter; CamelMessageInfo *info; CamelFolder *folder; EMailReader *reader; - EWebView *web_view; + EMailDisplay *display; const gchar *title; guint32 state; @@ -276,8 +277,7 @@ mail_browser_message_selected_cb (EMailBrowser *browser, return; folder = e_mail_reader_get_folder (reader); - formatter = e_mail_reader_get_formatter (reader); - web_view = em_format_html_get_web_view (formatter); + display = e_mail_reader_get_mail_display (reader); info = camel_folder_get_message_info (folder, uid); @@ -289,7 +289,7 @@ mail_browser_message_selected_cb (EMailBrowser *browser, title = _("(No Subject)"); gtk_window_set_title (GTK_WINDOW (browser), title); - gtk_widget_grab_focus (GTK_WIDGET (web_view)); + gtk_widget_grab_focus (GTK_WIDGET (display)); camel_message_info_set_flags ( info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN); @@ -319,7 +319,6 @@ mail_browser_popup_event_cb (EMailBrowser *browser, GdkEventButton *event, const gchar *uri) { - EMFormatHTML *formatter; EMailReader *reader; EWebView *web_view; GtkMenu *menu; @@ -329,8 +328,7 @@ mail_browser_popup_event_cb (EMailBrowser *browser, return FALSE; reader = E_MAIL_READER (browser); - formatter = e_mail_reader_get_formatter (reader); - web_view = em_format_html_get_web_view (formatter); + web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader)); if (e_web_view_get_cursor_image (web_view) != NULL) return FALSE; @@ -415,6 +413,11 @@ mail_browser_set_property (GObject *object, E_MAIL_BROWSER (object), g_value_get_boolean (value)); return; + + case PROP_DISPLAY_MODE: + E_MAIL_BROWSER (object)->priv->mode = + g_value_get_int (value); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -468,6 +471,11 @@ mail_browser_get_property (GObject *object, value, e_mail_browser_get_ui_manager ( E_MAIL_BROWSER (object))); return; + + case PROP_DISPLAY_MODE: + g_value_set_int ( + value, E_MAIL_BROWSER (object)->priv->mode); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -495,11 +503,6 @@ mail_browser_dispose (GObject *object) priv->focus_tracker = NULL; } - if (priv->formatter != NULL) { - g_object_unref (priv->formatter); - priv->formatter = NULL; - } - if (priv->main_menu != NULL) { g_object_unref (priv->main_menu); priv->main_menu = NULL; @@ -534,14 +537,13 @@ static void mail_browser_constructed (GObject *object) { EMailBrowser *browser; - EMFormatHTML *formatter; EMailReader *reader; EMailBackend *backend; EMailSession *session; + EMailDisplay *display; EShellBackend *shell_backend; EShell *shell; EFocusTracker *focus_tracker; - ESearchBar *search_bar; GSettings *settings; GtkAccelGroup *accel_group; GtkActionGroup *action_group; @@ -549,7 +551,6 @@ mail_browser_constructed (GObject *object) GtkUIManager *ui_manager; GtkWidget *container; GtkWidget *widget; - EWebView *web_view; const gchar *domain; const gchar *id; guint merge_id; @@ -558,7 +559,6 @@ mail_browser_constructed (GObject *object) G_OBJECT_CLASS (parent_class)->constructed (object); browser = E_MAIL_BROWSER (object); - reader = E_MAIL_READER (object); backend = e_mail_reader_get_backend (reader); session = e_mail_backend_get_session (backend); @@ -575,9 +575,6 @@ mail_browser_constructed (GObject *object) gtk_application_add_window ( GTK_APPLICATION (shell), GTK_WINDOW (object)); - formatter = e_mail_reader_get_formatter (reader); - web_view = em_format_html_get_web_view (formatter); - /* The message list is a widget, but it is not shown in the browser. * Unfortunately, the widget is inseparable from its model, and the * model is all we need. */ @@ -592,15 +589,20 @@ mail_browser_constructed (GObject *object) browser->priv->message_list, "message-list-built", G_CALLBACK (mail_browser_message_list_built_cb), object); + display = g_object_new (E_TYPE_MAIL_DISPLAY, + "mode", E_MAIL_BROWSER (object)->priv->mode, NULL); + g_signal_connect_swapped ( - web_view, "popup-event", + display, "popup-event", G_CALLBACK (mail_browser_popup_event_cb), object); g_signal_connect_swapped ( - web_view, "status-message", + display, "status-message", G_CALLBACK (mail_browser_status_message_cb), object); - /* Add action groups before initializing the reader interface. */ + widget = e_preview_pane_new (E_WEB_VIEW (display)); + browser->priv->preview_pane = g_object_ref (widget); + gtk_widget_show (widget); action_group = gtk_action_group_new (ACTION_GROUP_STANDARD); gtk_action_group_set_translation_domain (action_group, domain); @@ -613,6 +615,7 @@ mail_browser_constructed (GObject *object) gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); /* For easy access. Takes ownership of the reference. */ + g_object_set_data_full ( object, ACTION_GROUP_STANDARD, action_group, (GDestroyNotify) g_object_unref); @@ -664,7 +667,6 @@ mail_browser_constructed (GObject *object) container = widget; - /* Create the status bar before connecting proxy widgets. */ widget = gtk_statusbar_new (); gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0); browser->priv->statusbar = g_object_ref (widget); @@ -682,21 +684,9 @@ mail_browser_constructed (GObject *object) gtk_style_context_add_class ( gtk_widget_get_style_context (widget), - GTK_STYLE_CLASS_PRIMARY_TOOLBAR); - - gtk_widget_show (GTK_WIDGET (web_view)); + GTK_STYLE_CLASS_PRIMARY_TOOLBAR); - widget = e_preview_pane_new (web_view); - gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); - browser->priv->preview_pane = g_object_ref (widget); - gtk_widget_show (widget); - - search_bar = e_preview_pane_get_search_bar (E_PREVIEW_PANE (widget)); - - g_signal_connect_swapped ( - search_bar, "changed", - G_CALLBACK (em_format_queue_redraw), - browser->priv->formatter); + gtk_container_add (GTK_CONTAINER (container), browser->priv->preview_pane); /* Bind GObject properties to GSettings keys. */ @@ -713,8 +703,6 @@ mail_browser_constructed (GObject *object) e_plugin_ui_register_manager (ui_manager, id, object); e_plugin_ui_enable_manager (ui_manager, id); - e_mail_reader_connect_headers (E_MAIL_READER (reader)); - e_extensible_load_extensions (E_EXTENSIBLE (object)); } @@ -772,14 +760,15 @@ mail_browser_get_hide_deleted (EMailReader *reader) return !e_mail_browser_get_show_deleted (browser); } -static EMFormatHTML * -mail_browser_get_formatter (EMailReader *reader) +static EMailDisplay * +mail_browser_get_mail_display (EMailReader *reader) { - EMailBrowser *browser; + EMailBrowserPrivate *priv; - browser = E_MAIL_BROWSER (reader); + priv = E_MAIL_BROWSER_GET_PRIVATE (E_MAIL_BROWSER (reader)); - return EM_FORMAT_HTML (browser->priv->formatter); + return E_MAIL_DISPLAY (e_preview_pane_get_web_view ( + E_PREVIEW_PANE (priv->preview_pane))); } static GtkWidget * @@ -916,6 +905,18 @@ e_mail_browser_class_init (EMailBrowserClass *class) "Show deleted messages", FALSE, G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_DISPLAY_MODE, + g_param_spec_int ( + "display-mode", + "Display Mode", + NULL, + 0, + G_MAXINT, + EM_FORMAT_WRITE_MODE_NORMAL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void @@ -923,7 +924,7 @@ e_mail_browser_reader_init (EMailReaderInterface *interface) { interface->get_action_group = mail_browser_get_action_group; interface->get_backend = mail_browser_get_backend; - interface->get_formatter = mail_browser_get_formatter; + interface->get_mail_display = mail_browser_get_mail_display; interface->get_hide_deleted = mail_browser_get_hide_deleted; interface->get_message_list = mail_browser_get_message_list; interface->get_popup_menu = mail_browser_get_popup_menu; @@ -936,7 +937,6 @@ static void e_mail_browser_init (EMailBrowser *browser) { browser->priv = E_MAIL_BROWSER_GET_PRIVATE (browser); - browser->priv->formatter = em_format_html_display_new (); gtk_window_set_title (GTK_WINDOW (browser), _("Evolution")); gtk_window_set_default_size (GTK_WINDOW (browser), 600, 400); @@ -948,13 +948,22 @@ e_mail_browser_init (EMailBrowser *browser) } GtkWidget * -e_mail_browser_new (EMailBackend *backend) +e_mail_browser_new (EMailBackend *backend, + CamelFolder *folder, + const gchar *msg_uid, + EMFormatWriteMode mode) { + GtkWidget *widget; + g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL); - return g_object_new ( + widget= g_object_new ( E_TYPE_MAIL_BROWSER, - "backend", backend, NULL); + "backend", backend, + "display-mode", mode, + NULL); + + return widget; } void diff --git a/mail/e-mail-browser.h b/mail/e-mail-browser.h index c09c85b1c8..88f8174a9d 100644 --- a/mail/e-mail-browser.h +++ b/mail/e-mail-browser.h @@ -24,6 +24,7 @@ #include #include +#include /* Standard GObject macros */ #define E_TYPE_MAIL_BROWSER \ @@ -60,7 +61,10 @@ struct _EMailBrowserClass { }; GType e_mail_browser_get_type (void); -GtkWidget * e_mail_browser_new (EMailBackend *backend); +GtkWidget * e_mail_browser_new (EMailBackend *backend, + CamelFolder *folder, + const gchar *message_uid, + EMFormatWriteMode mode); void e_mail_browser_close (EMailBrowser *browser); gboolean e_mail_browser_get_show_deleted (EMailBrowser *browser); void e_mail_browser_set_show_deleted (EMailBrowser *browser, diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c index 7461e595f3..07f45ad461 100644 --- a/mail/e-mail-display.c +++ b/mail/e-mail-display.c @@ -23,14 +23,33 @@ #include #endif +#define LIBSOUP_USE_UNSTABLE_REQUEST_API + #include "e-mail-display.h" #include +#include +#include "e-util/e-marshal.h" #include "e-util/e-util.h" #include "e-util/e-plugin-ui.h" #include "mail/em-composer-utils.h" #include "mail/em-utils.h" +#include "mail/e-mail-request.h" +#include "mail/em-format-html-display.h" +#include "mail/e-mail-attachment-bar.h" +#include "widgets/misc/e-attachment-button.h" + +#include + +#include +#include + +#include + +#define d(x) + +G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW) #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -38,13 +57,29 @@ struct _EMailDisplayPrivate { EMFormatHTML *formatter; + + EMFormatWriteMode mode; + gboolean headers_collapsable; + gboolean headers_collapsed; + + GtkActionGroup *mailto_actions; + GtkActionGroup *images_actions; + + gint force_image_load: 1; }; enum { PROP_0, - PROP_FORMATTER + PROP_FORMATTER, + PROP_MODE, + PROP_HEADERS_COLLAPSABLE, + PROP_HEADERS_COLLAPSED, }; +static gpointer parent_class; + +static CamelDataCache *emd_global_http_cache = 0; + static const gchar *ui = "" " " @@ -61,6 +96,15 @@ static const gchar *ui = " " ""; +static const gchar *image_ui = +"" +" " +" " +" " +" " +" " +""; + static GtkActionEntry mailto_entries[] = { { "add-to-address-book", @@ -88,7 +132,7 @@ static GtkActionEntry mailto_entries[] = { NULL, N_("Send _Reply To..."), NULL, - N_("Send a reply message to this address"), + N_("Send a reply message to this address"), NULL /* Handled by EMailReader */ }, /*** Menus ***/ @@ -101,7 +145,54 @@ static GtkActionEntry mailto_entries[] = { NULL } }; -G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW) +static GtkActionEntry image_entries[] = { + + { "image-save", + GTK_STOCK_SAVE, + N_("Save _Image..."), + NULL, + N_("Save the image to a file"), + NULL /* Handled by EMailReader */ }, + +}; + +static void +mail_display_webview_update_actions (EWebView *web_view, + gpointer user_data) +{ + 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 = camel_data_cache_get (emd_global_http_cache, "http", image_src, NULL); + + visible = image_stream != NULL; + + if (image_stream) + g_object_unref (image_stream); + } + + action = e_web_view_get_action (web_view, "image-save"); + if (action) + gtk_action_set_visible (action, visible); +} + +static void +formatter_image_loading_policy_changed_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + EMailDisplay *display = user_data; + + e_mail_display_load_images (display); +} static void mail_display_update_formatter_colors (EMailDisplay *display) @@ -115,6 +206,9 @@ mail_display_update_formatter_colors (EMailDisplay *display) state = gtk_widget_get_state (GTK_WIDGET (display)); formatter = display->priv->formatter; + if (!display->priv->formatter) + return; + style = gtk_widget_get_style (GTK_WIDGET (display)); if (style == NULL) return; @@ -156,6 +250,21 @@ mail_display_set_property (GObject *object, E_MAIL_DISPLAY (object), g_value_get_object (value)); return; + case PROP_MODE: + e_mail_display_set_mode ( + E_MAIL_DISPLAY (object), + g_value_get_int (value)); + return; + case PROP_HEADERS_COLLAPSABLE: + e_mail_display_set_headers_collapsable ( + E_MAIL_DISPLAY (object), + g_value_get_boolean (value)); + return; + case PROP_HEADERS_COLLAPSED: + e_mail_display_set_headers_collapsed ( + E_MAIL_DISPLAY (object), + g_value_get_boolean (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -173,6 +282,21 @@ mail_display_get_property (GObject *object, value, e_mail_display_get_formatter ( E_MAIL_DISPLAY (object))); return; + case PROP_MODE: + g_value_set_int ( + value, e_mail_display_get_mode ( + E_MAIL_DISPLAY (object))); + return; + case PROP_HEADERS_COLLAPSABLE: + g_value_set_boolean ( + value, e_mail_display_get_headers_collapsable ( + E_MAIL_DISPLAY (object))); + return; + case PROP_HEADERS_COLLAPSED: + g_value_set_boolean ( + value, e_mail_display_get_headers_collapsed ( + E_MAIL_DISPLAY (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -191,14 +315,14 @@ mail_display_dispose (GObject *object) } /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object); + G_OBJECT_CLASS (parent_class)->dispose (object); } static void mail_display_realize (GtkWidget *widget) { /* Chain up to parent's realize() method. */ - GTK_WIDGET_CLASS (e_mail_display_parent_class)->realize (widget); + GTK_WIDGET_CLASS (parent_class)->realize (widget); mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget)); } @@ -207,62 +331,28 @@ static void mail_display_style_set (GtkWidget *widget, GtkStyle *previous_style) { - EMailDisplayPrivate *priv; + EMailDisplay *display = E_MAIL_DISPLAY (widget); - priv = E_MAIL_DISPLAY_GET_PRIVATE (widget); + mail_display_update_formatter_colors (display); /* Chain up to parent's style_set() method. */ - GTK_WIDGET_CLASS (e_mail_display_parent_class)-> - style_set (widget, previous_style); - - mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget)); - em_format_queue_redraw (EM_FORMAT (priv->formatter)); -} - -static void -mail_display_load_string (EWebView *web_view, - const gchar *string) -{ - EMailDisplayPrivate *priv; - - priv = E_MAIL_DISPLAY_GET_PRIVATE (web_view); - g_return_if_fail (priv->formatter != NULL); - - if (em_format_busy (EM_FORMAT (priv->formatter))) - return; - - /* Chain up to parent's load_string() method. */ - E_WEB_VIEW_CLASS (e_mail_display_parent_class)-> - load_string (web_view, string); -} - -static void -mail_display_url_requested (GtkHTML *html, - const gchar *uri, - GtkHTMLStream *stream) -{ - /* XXX Sadly, we must block the default method - * until EMFormatHTML is made asynchronous. */ + GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style); } static gboolean mail_display_process_mailto (EWebView *web_view, - const gchar *mailto_uri) + const gchar *mailto_uri, + gpointer user_data) { - g_return_val_if_fail (web_view != NULL, FALSE); + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); g_return_val_if_fail (mailto_uri != NULL, FALSE); - g_return_val_if_fail (E_IS_MAIL_DISPLAY (web_view), FALSE); if (g_ascii_strncasecmp (mailto_uri, "mailto:", 7) == 0) { - EMailDisplayPrivate *priv; EMFormat *format; CamelFolder *folder = NULL; EShell *shell; - priv = E_MAIL_DISPLAY_GET_PRIVATE (web_view); - g_return_val_if_fail (priv->formatter != NULL, FALSE); - - format = EM_FORMAT (priv->formatter); + format = (EMFormat *) E_MAIL_DISPLAY (web_view)->priv->formatter; if (format != NULL && format->folder != NULL) folder = format->folder; @@ -277,79 +367,825 @@ mail_display_process_mailto (EWebView *web_view, return FALSE; } -static void -mail_display_link_clicked (GtkHTML *html, - const gchar *uri) +static gboolean +mail_display_link_clicked (WebKitWebView *web_view, + WebKitWebFrame *frame, + WebKitNetworkRequest *request, + WebKitWebNavigationAction *navigation_action, + WebKitWebPolicyDecision *policy_decision, + gpointer user_data) { - EMailDisplayPrivate *priv; + EMailDisplay *display; + const gchar *uri = webkit_network_request_get_uri (request); - priv = E_MAIL_DISPLAY_GET_PRIVATE (html); - g_return_if_fail (priv->formatter != NULL); - - if (g_str_has_prefix (uri, "##")) { - guint32 flags; - - flags = priv->formatter->header_wrap_flags; - - if (strcmp (uri, "##TO##") == 0) { - if (!(flags & EM_FORMAT_HTML_HEADER_TO)) - flags |= EM_FORMAT_HTML_HEADER_TO; - else - flags &= ~EM_FORMAT_HTML_HEADER_TO; - } else if (strcmp (uri, "##CC##") == 0) { - if (!(flags & EM_FORMAT_HTML_HEADER_CC)) - flags |= EM_FORMAT_HTML_HEADER_CC; - else - flags &= ~EM_FORMAT_HTML_HEADER_CC; - } else if (strcmp (uri, "##BCC##") == 0) { - if (!(flags & EM_FORMAT_HTML_HEADER_BCC)) - flags |= EM_FORMAT_HTML_HEADER_BCC; - else - flags &= ~EM_FORMAT_HTML_HEADER_BCC; - } else if (strcmp (uri, "##HEADERS##") == 0) { - EMFormatHTMLHeadersState state; - - state = em_format_html_get_headers_state ( - priv->formatter); - - if (state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED) - state = EM_FORMAT_HTML_HEADERS_STATE_EXPANDED; - else - state = EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED; - - em_format_html_set_headers_state ( - priv->formatter, state); - } + display = E_MAIL_DISPLAY (web_view); + if (display->priv->formatter == NULL) + return FALSE; - priv->formatter->header_wrap_flags = flags; - em_format_queue_redraw (EM_FORMAT (priv->formatter)); - - } else if (mail_display_process_mailto (E_WEB_VIEW (html), uri)) { + if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) { /* do nothing, function handled the "mailto:" uri already */ - } else if (*uri == '#') - gtk_html_jump_to_anchor (html, uri + 1); + webkit_web_policy_decision_ignore (policy_decision); + return TRUE; - else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) + } else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) { /* ignore */ ; + webkit_web_policy_decision_ignore (policy_decision); + return TRUE; - else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) + } else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) { /* ignore */ ; + webkit_web_policy_decision_ignore (policy_decision); + return TRUE; + + } + + /* Let webkit handle it */ + return FALSE; +} + +static void +webkit_request_load_from_file (WebKitNetworkRequest *request, + const gchar *path) +{ + gchar *data = NULL; + gsize length = 0; + gboolean status; + gchar *b64, *new_uri; + gchar *ct; + + status = g_file_get_contents (path, &data, &length, NULL); + if (!status) + return; + + b64 = g_base64_encode ((guchar *) data, length); + ct = g_content_type_guess (path, NULL, 0, NULL); + + new_uri = g_strdup_printf ("data:%s;base64,%s", ct, b64); + webkit_network_request_set_uri (request, new_uri); + + g_free (b64); + g_free (new_uri); + g_free (ct); + g_free (data); +} + +static void +mail_display_resource_requested (WebKitWebView *web_view, + WebKitWebFrame *frame, + WebKitWebResource *resource, + WebKitNetworkRequest *request, + WebKitNetworkResponse *response, + gpointer user_data) +{ + EMailDisplay *display = E_MAIL_DISPLAY (web_view); + EMFormat *formatter = EM_FORMAT (display->priv->formatter); + const gchar *uri = webkit_network_request_get_uri (request); + + if (!formatter) { + webkit_network_request_set_uri (request, "invalid://uri"); + return; + } + + /* Redirect cid:part_id to mail://mail_id/cid:part_id */ + if (g_str_has_prefix (uri, "cid:")) { + + /* Always write raw content of CID object */ + gchar *new_uri = em_format_build_mail_uri (formatter->folder, + formatter->message_uid, + "part_id", G_TYPE_STRING, uri, + "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, NULL); + + webkit_network_request_set_uri (request, new_uri); + + g_free (new_uri); + + /* WebKit won't allow to load a local file when displaing "remote" mail://, + protocol, so we need to handle this manually */ + } else if (g_str_has_prefix (uri, "file:")) { + gchar *path; + + path = g_filename_from_uri (uri, NULL, NULL); + if (!path) + return; + + webkit_request_load_from_file (request, path); + + g_free (path); + + /* Redirect http(s) request to evo-http(s) protocol. See EMailRequest for + * further details about this. */ + } else if (g_str_has_prefix (uri, "http:") || g_str_has_prefix (uri, "https")) { + + gchar *new_uri, *mail_uri, *enc; + SoupURI *soup_uri; + GHashTable *query; + gchar *uri_md5; + CamelStream *stream; + + /* Open Evolution's cache */ + uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); + stream = camel_data_cache_get ( + emd_global_http_cache, "http", uri_md5, NULL); + g_free (uri_md5); + + /* If the URI is not cached and we are not allowed to load it + * then redirect to invalid URI, so that webkit would display + * a native placeholder for it. */ + if (!stream && !display->priv->force_image_load && + !em_format_html_can_load_images (display->priv->formatter)) { + webkit_network_request_set_uri (request, "invalid://protocol"); + return; + } + + new_uri = g_strconcat ("evo-", uri, NULL); + mail_uri = em_format_build_mail_uri (formatter->folder, + formatter->message_uid, NULL, NULL); + + soup_uri = soup_uri_new (new_uri); + if (soup_uri->query) { + query = soup_form_decode (soup_uri->query); + } else { + query = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + } + enc = soup_uri_encode (mail_uri, NULL); + g_hash_table_insert (query, g_strdup ("__evo-mail"), enc); + + if (display->priv->force_image_load) { + g_hash_table_insert (query, + g_strdup ("__evo-load-images"), + g_strdup ("true")); + } + + g_free (mail_uri); + + soup_uri_set_query_from_form (soup_uri, query); + g_free (new_uri); + + new_uri = soup_uri_to_string (soup_uri, FALSE); + webkit_network_request_set_uri (request, new_uri); + + g_free (new_uri); + soup_uri_free (soup_uri); + g_hash_table_unref (query); + } +} + +static WebKitDOMElement * +find_element_by_id (WebKitDOMDocument *document, + const gchar *id) +{ + WebKitDOMNodeList *frames; + WebKitDOMElement *element; + gulong i, length; + + /* Try to look up the element in this DOM document */ + element = webkit_dom_document_get_element_by_id (document, id); + if (element) + return element; + + /* If the element is not here then recursively scan all frames */ + frames = webkit_dom_document_get_elements_by_tag_name(document, "iframe"); + length = webkit_dom_node_list_get_length (frames); + for (i = 0; i < length; i++) { + + WebKitDOMHTMLIFrameElement *iframe = + WEBKIT_DOM_HTML_IFRAME_ELEMENT ( + webkit_dom_node_list_item (frames, i)); + + WebKitDOMDocument *frame_doc = + webkit_dom_html_iframe_element_get_content_document (iframe); + + WebKitDOMElement *el = + find_element_by_id (frame_doc, id); + + if (el) + return el; + } + + return NULL; +} + +static void +mail_display_plugin_widget_resize (GObject *object, + gpointer dummy, + EMailDisplay *display) +{ + GtkWidget *widget; + WebKitDOMElement *parent_element; + gchar *dim; + gint height; + + widget = GTK_WIDGET (object); + gtk_widget_get_preferred_height (widget, &height, NULL); + parent_element = g_object_get_data (object, "parent_element"); + + if (!parent_element || !WEBKIT_DOM_IS_ELEMENT (parent_element)) { + d(printf("%s: %s does not have (valid) parent element!\n", + G_STRFUNC, (gchar *) g_object_get_data (object, "uri"))); + return; + } + + /* Int -> Str */ + dim = g_strdup_printf ("%d", height); + + /* Set height of the containment to match height of the + * GtkWidget it contains */ + webkit_dom_html_object_element_set_height ( + WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim); + g_free (dim); +} + +static void +mail_display_plugin_widget_realize_cb (GtkWidget *widget, + gpointer user_data) +{ + /* Initial resize of the element when the widget + * is displayed for the first time. */ + mail_display_plugin_widget_resize (G_OBJECT (widget), NULL, user_data); +} + +static void +plugin_widget_set_parent_element (GtkWidget *widget, + EMailDisplay *display) +{ + const gchar *uri; + WebKitDOMDocument *document; + WebKitDOMElement *element; + + uri = g_object_get_data (G_OBJECT (widget), "uri"); + if (!uri || !*uri) + return; + + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); + element = find_element_by_id (document, uri); + + if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) { + g_warning ("Failed to find parent for '%s' - no ID set?", uri); + return; + } + + /* Assign the WebKitDOMElement to "parent_element" data of the GtkWidget + * and the GtkWidget to "widget" data of the DOM Element */ + g_object_set_data (G_OBJECT (widget), "parent_element", element); + g_object_set_data (G_OBJECT (element), "widget", widget); +} + +static void +attachment_button_expanded (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + EAttachmentButton *button = E_ATTACHMENT_BUTTON (object); + WebKitDOMElement *attachment = user_data; + WebKitDOMCSSStyleDeclaration *css; + gboolean expanded; + + d(printf("Attachment button %s (%p) expansion state toggled!\n", + (gchar *) g_object_get_data (object, "uri"), object)); + + expanded = e_attachment_button_get_expanded (button) && + gtk_widget_get_visible (GTK_WIDGET (button)); + + if (!WEBKIT_DOM_IS_ELEMENT (attachment)) { + d(printf("%s: Parent element for button %s does not exist!\n", + G_STRFUNC, (gchar *) g_object_get_data (object, "uri"))); + return; + } + + /* Show or hide the DIV which contains the attachment (iframe, image...) */ + css = webkit_dom_element_get_style (attachment); + webkit_dom_css_style_declaration_set_property ( + css, "display", expanded ? "block" : "none", "", NULL); +} + +static void +constraint_widget_visibility (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GtkWidget *widget = GTK_WIDGET (object); + EAttachmentButton *button = user_data; + + gboolean can_show = e_attachment_button_get_expanded (button); + gboolean is_visible = gtk_widget_get_visible (widget); + + if (is_visible && !can_show) + gtk_widget_hide (widget); + else if (!is_visible && can_show) + gtk_widget_show (widget); + + /* Otherwise it's OK */ +} + +static void +bind_iframe_content_visibility (EAttachmentButton *button, + WebKitDOMElement *iframe) +{ + WebKitDOMDocument *document; + WebKitDOMNodeList *nodes; + gulong i, length; + + if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe)) + return; + + document = webkit_dom_html_iframe_element_get_content_document ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); + nodes = webkit_dom_document_get_elements_by_tag_name (document, "object"); + length = webkit_dom_node_list_get_length (nodes); - else { - /* Chain up to parent's link_clicked() method. */ - GTK_HTML_CLASS (e_mail_display_parent_class)-> - link_clicked (html, uri); + d(printf("Found %ld objects within iframe %s\n", length, + webkit_dom_html_iframe_element_get_name ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)))); + + /* Iterate through all s and bind visibility of their widget + * with expanded-state of related attachment button */ + for (i = 0; i < length; i++) { + + WebKitDOMNode *node = webkit_dom_node_list_item (nodes, i); + GtkWidget *widget; + + widget = g_object_get_data (G_OBJECT (node), "widget"); + if (!widget) + continue; + + d(printf("Binding visibility of widget %s (%p) with button %s (%p)\n", + (gchar *) g_object_get_data (G_OBJECT (widget), "uri"), widget, + (gchar *) g_object_get_data (G_OBJECT (button), "uri"), button)); + + g_object_bind_property ( + button, "expanded", + widget, "visible", + G_BINDING_SYNC_CREATE); + + /* Ensure that someone won't attempt to _show() the widget when + * it is supposed to be hidden and vice versa. */ + g_signal_connect (widget, "notify::visible", + G_CALLBACK (constraint_widget_visibility), button); } } +static void +bind_attachment_iframe_visibility (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + WebKitWebFrame *webframe; + const gchar *frame_name; + gchar *button_uri; + WebKitDOMDocument *document; + WebKitDOMElement *attachment; + WebKitDOMElement *button_element; + WebKitDOMNodeList *nodes; + gulong i, length; + GtkWidget *button; + + /* Whenever an " + "", + e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]), + e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]), + uri); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + g_free (uri); + } +} + +static void +efh_write_source (EMFormat *emf, + EMFormatPURI *puri, + CamelStream *stream, + EMFormatWriterInfo *info, + GCancellable *cancellable) { + EMFormatHTML *efh = (EMFormatHTML *) emf; + GString *buffer; CamelStream *filtered_stream; CamelMimeFilter *filter; - CamelDataWrapper *dw = (CamelDataWrapper *) part; + CamelDataWrapper *dw = (CamelDataWrapper *) puri->part; filtered_stream = camel_stream_filter_new (stream); @@ -805,1786 +1018,1153 @@ efh_format_source (EMFormat *emf, CAMEL_STREAM_FILTER (filtered_stream), filter); g_object_unref (filter); + buffer = g_string_new (""); + + g_string_append_printf ( + buffer, "
", + e_color_to_value ( + &efh->priv->colors[ + EM_FORMAT_HTML_COLOR_BODY]), + e_color_to_value ( + &efh->priv->colors[ + EM_FORMAT_HTML_COLOR_HEADER])); + camel_stream_write_string ( - stream, "
", cancellable, NULL); - em_format_format_text (emf, filtered_stream, dw, cancellable); + stream, buffer->str, cancellable, NULL); + camel_stream_write_string ( + stream, "", cancellable, NULL); + camel_data_wrapper_write_to_stream_sync (dw, filtered_stream, + cancellable, NULL); camel_stream_write_string ( - stream, "
", cancellable, NULL); + stream, "", cancellable, NULL); g_object_unref (filtered_stream); + g_string_free (buffer, TRUE); } static void -efh_format_attachment (EMFormat *emf, - CamelStream *stream, - CamelMimePart *part, - const gchar *mime_type, - const EMFormatHandler *handle, - GCancellable *cancellable) +efh_write_headers (EMFormat *emf, + EMFormatPURI *puri, + CamelStream *stream, + EMFormatWriterInfo *info, + GCancellable *cancellable) { - gchar *text, *html; + GString *buffer; + EMFormatHTML *efh = (EMFormatHTML *) emf; + gint bg_color; - /* we display all inlined attachments only */ + if (!puri->part) + return; - /* this could probably be cleaned up ... */ - camel_stream_write_string ( - stream, - "" - "
" - "" - "
" - "
\n", - cancellable, NULL); + buffer = g_string_new (""); - /* output some info about it */ - text = em_format_describe_part (part, mime_type); - html = camel_text_to_html ( - text, ((EMFormatHTML *) emf)->text_html_flags & - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_write_string (stream, html, cancellable, NULL); - g_free (html); - g_free (text); + if (info->mode & EM_FORMAT_WRITE_MODE_PRINTING) { + GdkColor white = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 }; + bg_color = e_color_to_value (&white); + } else { + bg_color = e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]); + } - camel_stream_write_string ( - stream, "
", cancellable, NULL); + g_string_append_printf ( + buffer, + "
" + "\n" + "
\n", + bg_color, + e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER])); + + if (info->headers_collapsable) { + g_string_append_printf (buffer, + "" + "", + EVOLUTION_IMAGESDIR, + (info->headers_collapsed) ? "plus.png" : "minus.png"); + + efh_format_short_headers (efh, buffer, (CamelMedium *) puri->part, + info->headers_collapsed, + cancellable); + } - if (handle && em_format_is_inline (emf, emf->part_id->str, part, handle)) - handle->handler (emf, stream, part, handle, cancellable, FALSE); -} + efh_format_full_headers (efh, buffer, (CamelMedium *) puri->part, + (info->mode == EM_FORMAT_WRITE_MODE_ALL_HEADERS), + !info->headers_collapsed, + cancellable); -static gboolean -efh_busy (EMFormat *emf) -{ - EMFormatHTMLPrivate *priv; + g_string_append (buffer, "
"); - priv = EM_FORMAT_HTML_GET_PRIVATE (emf); + camel_stream_write_string (stream, buffer->str, cancellable, NULL); - return (priv->format_id != -1); + g_string_free (buffer, true); } + static void -efh_base_init (EMFormatHTMLClass *class) +efh_write_error (EMFormat *emf, + EMFormatPURI *puri, + CamelStream *stream, + EMFormatWriterInfo *info, + GCancellable *cancellable) { - efh_builtin_init (class); + CamelStream *filtered_stream; + CamelMimeFilter *filter; + CamelDataWrapper *dw; + + dw = camel_medium_get_content ((CamelMedium *) puri->part); + + camel_stream_write_string (stream, "", cancellable, NULL); + + filtered_stream = camel_stream_filter_new (stream); + filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); + camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), filter); + g_object_unref (filter); + + camel_data_wrapper_decode_to_stream_sync (dw, filtered_stream, cancellable, NULL); + + g_object_unref (filtered_stream); + + camel_stream_write_string (stream, "
", cancellable, NULL); } static void -efh_class_init (EMFormatHTMLClass *class) +efh_write_message_rfc822 (EMFormat *emf, + EMFormatPURI *puri, + CamelStream *stream, + EMFormatWriterInfo *info, + GCancellable *cancellable) { - GObjectClass *object_class; - EMFormatClass *format_class; - const gchar *user_cache_dir; + if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - parent_class = g_type_class_peek_parent (class); - g_type_class_add_private (class, sizeof (EMFormatHTMLPrivate)); + GList *puris; + GList *iter; - object_class = G_OBJECT_CLASS (class); - object_class->set_property = efh_set_property; - object_class->get_property = efh_get_property; - object_class->finalize = efh_finalize; + /* Create a new fake list of PURIs which will contain only + * PURIs from this message. */ + iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); + if (!iter || !iter->next) + return; - format_class = EM_FORMAT_CLASS (class); - format_class->format_clone = efh_format_clone; - format_class->format_error = efh_format_error; - format_class->format_source = efh_format_source; - format_class->format_attachment = efh_format_attachment; - format_class->format_secure = efh_format_secure; - format_class->busy = efh_busy; + iter = iter->next; + puris = NULL; + while (iter) { - class->html_widget_type = E_TYPE_WEB_VIEW; + EMFormatPURI *p; + p = iter->data; - g_object_class_install_property ( - object_class, - PROP_BODY_COLOR, - g_param_spec_boxed ( - "body-color", - "Body Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); + if (g_str_has_suffix (p->uri, ".rfc822.end")) + break; - g_object_class_install_property ( - object_class, - PROP_CITATION_COLOR, - g_param_spec_boxed ( - "citation-color", - "Citation Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); + puris = g_list_append (puris, p); + iter = iter->next; - g_object_class_install_property ( - object_class, - PROP_CONTENT_COLOR, - g_param_spec_boxed ( - "content-color", - "Content Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); + }; - g_object_class_install_property ( - object_class, - PROP_FRAME_COLOR, - g_param_spec_boxed ( - "frame-color", - "Frame Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); + efh_write_message (emf, puris, stream, info, cancellable); - g_object_class_install_property ( - object_class, - PROP_HEADER_COLOR, - g_param_spec_boxed ( - "header-color", - "Header Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); + g_list_free (puris); - /* FIXME Make this a proper enum property. */ - g_object_class_install_property ( - object_class, - PROP_IMAGE_LOADING_POLICY, - g_param_spec_enum ( - "image-loading-policy", - "Image Loading Policy", - NULL, - E_TYPE_MAIL_IMAGE_LOADING_POLICY, - E_MAIL_IMAGE_LOADING_POLICY_ALWAYS, - G_PARAM_READWRITE)); + } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { - g_object_class_install_property ( - object_class, - PROP_MARK_CITATIONS, - g_param_spec_boolean ( - "mark-citations", - "Mark Citations", - NULL, - TRUE, - G_PARAM_READWRITE)); + GList *iter; + gboolean can_write = FALSE; - g_object_class_install_property ( - object_class, - PROP_ONLY_LOCAL_PHOTOS, - g_param_spec_boolean ( - "only-local-photos", - "Only Local Photos", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); + iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); + if (!iter || !iter->next) + return; - g_object_class_install_property ( - object_class, - PROP_SHOW_SENDER_PHOTO, - g_param_spec_boolean ( - "show-sender-photo", - "Show Sender Photo", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); + /* Skip everything before attachment bar, inclusive */\ + iter = iter->next; + while (iter) { - g_object_class_install_property ( - object_class, - PROP_SHOW_REAL_DATE, - g_param_spec_boolean ( - "show-real-date", - "Show real Date header value", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); + EMFormatPURI *p = iter->data; - g_object_class_install_property ( - object_class, - PROP_TEXT_COLOR, - g_param_spec_boxed ( - "text-color", - "Text Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); + /* EMFormatHTMLPrint has registered a special writer + * for headers, try to find it and use it. */ + if (g_str_has_suffix (p->uri, ".headers")) { - g_object_class_install_property ( - object_class, - PROP_WEB_VIEW, - g_param_spec_object ( - "web-view", - "Web View", - NULL, - E_TYPE_WEB_VIEW, - G_PARAM_READABLE)); + const EMFormatHandler *handler; - g_object_class_install_property ( - object_class, - PROP_HEADERS_STATE, - g_param_spec_int ( - "headers-state", - "Headers state", - NULL, - EM_FORMAT_HTML_HEADERS_STATE_EXPANDED, - EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED, - EM_FORMAT_HTML_HEADERS_STATE_EXPANDED, - G_PARAM_READWRITE)); + handler = em_format_find_handler ( + emf, "x-evolution/message/headers"); + if (handler && handler->write_func) + handler->write_func (emf, p, stream, info, cancellable); - g_object_class_install_property ( - object_class, - PROP_HEADERS_STATE, - g_param_spec_boolean ( - "headers-collapsable", - NULL, - NULL, - FALSE, - G_PARAM_READWRITE)); + iter = iter->next; + continue; + } - /* cache expiry - 2 hour access, 1 day max */ - user_cache_dir = e_get_user_cache_dir (); - emfh_http_cache = camel_data_cache_new (user_cache_dir, NULL); - if (emfh_http_cache) { - camel_data_cache_set_expire_age (emfh_http_cache, 24 *60 *60); - camel_data_cache_set_expire_access (emfh_http_cache, 2 *60 *60); - } -} + if (g_str_has_suffix (p->uri, ".rfc822.end")) + break; -static void -efh_init (EMFormatHTML *efh, - EMFormatHTMLClass *class) -{ - EWebView *web_view; - GdkColor *color; + if (g_str_has_suffix (p->uri, ".attachment-bar")) + can_write = TRUE; - efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh); + if (can_write && p->write_func) { + p->write_func ( + emf, p, stream, info, cancellable); + } - g_queue_init (&efh->pending_object_list); - g_queue_init (&efh->priv->pending_jobs); - efh->priv->lock = g_mutex_new (); - efh->priv->format_id = -1; - efh->priv->text_inline_parts = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) NULL, - (GDestroyNotify) efh_free_cache); - - web_view = g_object_new (class->html_widget_type, NULL); - efh->priv->web_view = g_object_ref_sink (web_view); - - gtk_html_set_blocking (GTK_HTML (web_view), FALSE); - gtk_html_set_caret_first_focus_anchor ( - GTK_HTML (web_view), EFM_MESSAGE_START_ANAME); - gtk_html_set_default_content_type ( - GTK_HTML (web_view), "text/html; charset=utf-8"); - e_web_view_set_editable (web_view, FALSE); - - g_signal_connect ( - web_view, "url-requested", - G_CALLBACK (efh_url_requested), efh); - g_signal_connect ( - web_view, "object-requested", - G_CALLBACK (efh_object_requested), efh); + iter = iter->next; + } - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]; - gdk_color_parse ("#eeeeee", color); + } else { + gchar *str; + gchar *uri; - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]; - gdk_color_parse ("#ffffff", color); + EMFormatHTML *efh = (EMFormatHTML *) emf; + EMFormatPURI *p; + GList *iter; - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]; - gdk_color_parse ("#3f3f3f", color); + iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); + if (!iter || !iter->next) + return; - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER]; - gdk_color_parse ("#eeeeee", color); + iter = iter->next; + p = iter->data; + + uri = em_format_build_mail_uri (emf->folder, emf->message_uid, + "part_id", G_TYPE_STRING, p->uri, + "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, + NULL); + + str = g_strdup_printf ( + "
" + "
\n" + "" + "
", + e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]), + e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]), + uri, puri->uri); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + g_free (uri); + } - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_TEXT]; - gdk_color_parse ("#000000", color); - - efh->text_html_flags = - CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - efh->show_icon = TRUE; - efh->state = EM_FORMAT_HTML_STATE_NONE; - - e_extensible_load_extensions (E_EXTENSIBLE (efh)); -} - -GType -em_format_html_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatHTMLClass), - (GBaseInitFunc) efh_base_init, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) efh_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormatHTML), - 0, /* n_preallocs */ - (GInstanceInitFunc) efh_init, - NULL /* value_table */ - }; - - static const GInterfaceInfo extensible_info = { - (GInterfaceInitFunc) NULL, - (GInterfaceFinalizeFunc) NULL, - NULL /* interface_data */ - }; - - type = g_type_register_static ( - em_format_get_type(), "EMFormatHTML", - &type_info, G_TYPE_FLAG_ABSTRACT); - - g_type_add_interface_static ( - type, E_TYPE_EXTENSIBLE, &extensible_info); - } - - return type; -} - -EWebView * -em_format_html_get_web_view (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), NULL); - - return efh->priv->web_view; -} - -void -em_format_html_load_images (EMFormatHTML *efh) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) - return; - - /* This will remain set while we're still - * rendering the same message, then it wont be. */ - efh->priv->load_images_now = TRUE; - em_format_queue_redraw (EM_FORMAT (efh)); -} - -void -em_format_html_get_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - GdkColor *color) -{ - GdkColor *format_color; - - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES); - g_return_if_fail (color != NULL); - - format_color = &efh->priv->colors[type]; - - color->red = format_color->red; - color->green = format_color->green; - color->blue = format_color->blue; -} - -void -em_format_html_set_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - const GdkColor *color) -{ - GdkColor *format_color; - const gchar *property_name; - - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES); - g_return_if_fail (color != NULL); - - format_color = &efh->priv->colors[type]; - - if (gdk_color_equal (color, format_color)) - return; - - format_color->red = color->red; - format_color->green = color->green; - format_color->blue = color->blue; - - switch (type) { - case EM_FORMAT_HTML_COLOR_BODY: - property_name = "body-color"; - break; - case EM_FORMAT_HTML_COLOR_CITATION: - property_name = "citation-color"; - break; - case EM_FORMAT_HTML_COLOR_CONTENT: - property_name = "content-color"; - break; - case EM_FORMAT_HTML_COLOR_FRAME: - property_name = "frame-color"; - break; - case EM_FORMAT_HTML_COLOR_HEADER: - property_name = "header-color"; - break; - case EM_FORMAT_HTML_COLOR_TEXT: - property_name = "text-color"; - break; - default: - g_return_if_reached (); - } - - g_object_notify (G_OBJECT (efh), property_name); -} - -EMailImageLoadingPolicy -em_format_html_get_image_loading_policy (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0); - - return efh->priv->image_loading_policy; -} - -void -em_format_html_set_image_loading_policy (EMFormatHTML *efh, - EMailImageLoadingPolicy policy) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (policy == efh->priv->image_loading_policy) - return; - - efh->priv->image_loading_policy = policy; - - g_object_notify (G_OBJECT (efh), "image-loading-policy"); -} - -gboolean -em_format_html_get_mark_citations (EMFormatHTML *efh) -{ - guint32 flags; - - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - flags = efh->text_html_flags; - - return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0); -} - -void -em_format_html_set_mark_citations (EMFormatHTML *efh, - gboolean mark_citations) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (mark_citations) - efh->text_html_flags |= - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - else - efh->text_html_flags &= - ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - - g_object_notify (G_OBJECT (efh), "mark-citations"); -} - -gboolean -em_format_html_get_only_local_photos (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->only_local_photos; } -void -em_format_html_set_only_local_photos (EMFormatHTML *efh, - gboolean only_local_photos) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->only_local_photos = only_local_photos; - - g_object_notify (G_OBJECT (efh), "only-local-photos"); -} +/*****************************************************************************/ -gboolean -em_format_html_get_show_sender_photo (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->show_sender_photo; -} - -void -em_format_html_set_show_sender_photo (EMFormatHTML *efh, - gboolean show_sender_photo) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->show_sender_photo = show_sender_photo; - - g_object_notify (G_OBJECT (efh), "show-sender-photo"); -} +/* Notes: + * + * image/tiff is omitted because it's a multi-page image format, but + * gdk-pixbuf unconditionally renders the first page only, and doesn't + * even indicate through meta-data whether multiple pages are present + * (see bug 335959). Therefore, make no attempt to render TIFF images + * inline and defer to an application that can handle multi-page TIFF + * files properly like Evince or Gimp. Once the referenced bug is + * fixed we can reevaluate this policy. + */ +static EMFormatHandler type_builtin_table[] = { + { (gchar *) "image/gif", efh_parse_image, efh_write_image, }, + { (gchar *) "image/jpeg", efh_parse_image, efh_write_image, }, + { (gchar *) "image/png", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-png", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-bmp", efh_parse_image, efh_write_image, }, + { (gchar *) "image/bmp", efh_parse_image, efh_write_image, }, + { (gchar *) "image/svg", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-cmu-raster", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-ico", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-portable-anymap", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-portable-bitmap", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-portable-graymap", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-portable-pixmap", efh_parse_image, efh_write_image, }, + { (gchar *) "image/x-xpixmap", efh_parse_image, efh_write_image, }, + { (gchar *) "text/enriched", efh_parse_text_enriched, efh_write_text_enriched, }, + { (gchar *) "text/plain", efh_parse_text_plain, efh_write_text_plain, }, + { (gchar *) "text/html", efh_parse_text_html, efh_write_text_html, }, + { (gchar *) "text/richtext", efh_parse_text_enriched, efh_write_text_enriched, }, + { (gchar *) "text/*", efh_parse_text_plain, efh_write_text_plain, }, + { (gchar *) "message/rfc822", efh_parse_message_rfc822, efh_write_message_rfc822, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, + { (gchar *) "message/news", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, + { (gchar *) "message/delivery-status", efh_parse_message_deliverystatus, efh_write_text_plain, }, + { (gchar *) "message/external-body", efh_parse_message_external, efh_write_text_plain, }, + { (gchar *) "message/*", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE }, -gboolean -em_format_html_get_show_real_date (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); + /* This is where one adds those busted, non-registered types, + * that some idiot mailer writers out there decide to pull out + * of their proverbials at random. */ + { (gchar *) "image/jpg", efh_parse_image, efh_write_image, }, + { (gchar *) "image/pjpeg", efh_parse_image, efh_write_image, }, - return efh->priv->show_real_date; -} + /* special internal types */ + { (gchar *) "x-evolution/message/rfc822", 0, efh_write_text_plain, }, + { (gchar *) "x-evolution/message/headers", 0, efh_write_headers, }, + { (gchar *) "x-evolution/message/source", 0, efh_write_source, }, + { (gchar *) "x-evolution/message/attachment", 0, efh_write_attachment, }, + { (gchar *) "x-evolution/message/error", 0, efh_write_error, }, +}; -void -em_format_html_set_show_real_date (EMFormatHTML *efh, - gboolean show_real_date) +static void +efh_builtin_init (EMFormatHTMLClass *efhc) { - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); + EMFormatClass *emfc; + gint ii; - efh->priv->show_real_date = show_real_date; + emfc = (EMFormatClass *) efhc; - g_object_notify (G_OBJECT (efh), "show-real-date"); + for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++) + em_format_class_add_handler ( + emfc, &type_builtin_table[ii]); } -EMFormatHTMLHeadersState -em_format_html_get_headers_state (EMFormatHTML *efh) +static void +efh_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), EM_FORMAT_HTML_HEADERS_STATE_EXPANDED); - - return efh->priv->headers_state; -} + switch (property_id) { + case PROP_BODY_COLOR: + em_format_html_set_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_BODY, + g_value_get_boxed (value)); + return; -void -em_format_html_set_headers_state (EMFormatHTML *efh, - EMFormatHTMLHeadersState state) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); + case PROP_CITATION_COLOR: + em_format_html_set_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_CITATION, + g_value_get_boxed (value)); + return; - efh->priv->headers_state = state; + case PROP_CONTENT_COLOR: + em_format_html_set_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_CONTENT, + g_value_get_boxed (value)); + return; - g_object_notify (G_OBJECT (efh), "headers-state"); -} + case PROP_FRAME_COLOR: + em_format_html_set_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_FRAME, + g_value_get_boxed (value)); + return; -gboolean -em_format_html_get_headers_collapsable (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); + case PROP_HEADER_COLOR: + em_format_html_set_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_HEADER, + g_value_get_boxed (value)); + return; - return efh->priv->headers_collapsable; -} + case PROP_IMAGE_LOADING_POLICY: + em_format_html_set_image_loading_policy ( + EM_FORMAT_HTML (object), + g_value_get_enum (value)); + return; -void -em_format_html_set_headers_collapsable (EMFormatHTML *efh, - gboolean collapsable) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); + case PROP_MARK_CITATIONS: + em_format_html_set_mark_citations ( + EM_FORMAT_HTML (object), + g_value_get_boolean (value)); + return; - efh->priv->headers_collapsable = collapsable; + case PROP_ONLY_LOCAL_PHOTOS: + em_format_html_set_only_local_photos ( + EM_FORMAT_HTML (object), + g_value_get_boolean (value)); + return; - g_object_notify (G_OBJECT (efh), "headers-collapsable"); -} + case PROP_SHOW_SENDER_PHOTO: + em_format_html_set_show_sender_photo ( + EM_FORMAT_HTML (object), + g_value_get_boolean (value)); + return; -CamelMimePart * -em_format_html_file_part (EMFormatHTML *efh, - const gchar *mime_type, - const gchar *filename, - GCancellable *cancellable) -{ - CamelMimePart *part; - CamelStream *stream; - CamelDataWrapper *dw; - gchar *basename; + case PROP_SHOW_REAL_DATE: + em_format_html_set_show_real_date ( + EM_FORMAT_HTML (object), + g_value_get_boolean (value)); + return; - stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL); - if (stream == NULL) - return NULL; + case PROP_TEXT_COLOR: + em_format_html_set_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_TEXT, + g_value_get_boxed (value)); + return; - dw = camel_data_wrapper_new (); - camel_data_wrapper_construct_from_stream_sync ( - dw, stream, cancellable, NULL); - g_object_unref (stream); - if (mime_type) - camel_data_wrapper_set_mime_type (dw, mime_type); - part = camel_mime_part_new (); - camel_medium_set_content ((CamelMedium *) part, dw); - g_object_unref (dw); - basename = g_path_get_basename (filename); - camel_mime_part_set_filename (part, basename); - g_free (basename); + case PROP_ANIMATE_IMAGES: + em_format_html_set_animate_images ( + EM_FORMAT_HTML (object), + g_value_get_boolean (value)); + return; + } - return part; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } -/* all this api is a pain in the bum ... */ - -EMFormatHTMLPObject * -em_format_html_add_pobject (EMFormatHTML *efh, - gsize size, - const gchar *classid, - CamelMimePart *part, - EMFormatHTMLPObjectFunc func) +static void +efh_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - EMFormatHTMLPObject *pobj; + GdkColor color; - if (size < sizeof (EMFormatHTMLPObject)) { - g_warning ("size is less than the size of EMFormatHTMLPObject\n"); - size = sizeof (EMFormatHTMLPObject); - } + switch (property_id) { + case PROP_BODY_COLOR: + em_format_html_get_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_BODY, + &color); + g_value_set_boxed (value, &color); + return; - pobj = g_malloc0 (size); - if (classid) - pobj->classid = g_strdup (classid); - else - pobj->classid = g_strdup_printf("e-object:///%s", ((EMFormat *)efh)->part_id->str); + case PROP_CITATION_COLOR: + em_format_html_get_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_CITATION, + &color); + g_value_set_boxed (value, &color); + return; - pobj->format = efh; - pobj->func = func; - pobj->part = part; + case PROP_CONTENT_COLOR: + em_format_html_get_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_CONTENT, + &color); + g_value_set_boxed (value, &color); + return; - g_queue_push_tail (&efh->pending_object_list, pobj); + case PROP_FRAME_COLOR: + em_format_html_get_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_FRAME, + &color); + g_value_set_boxed (value, &color); + return; - return pobj; -} + case PROP_HEADER_COLOR: + em_format_html_get_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_HEADER, + &color); + g_value_set_boxed (value, &color); + return; -EMFormatHTMLPObject * -em_format_html_find_pobject (EMFormatHTML *emf, - const gchar *classid) -{ - GList *link; + case PROP_IMAGE_LOADING_POLICY: + g_value_set_enum ( + value, + em_format_html_get_image_loading_policy ( + EM_FORMAT_HTML (object))); + return; - g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL); - g_return_val_if_fail (classid != NULL, NULL); + case PROP_MARK_CITATIONS: + g_value_set_boolean ( + value, em_format_html_get_mark_citations ( + EM_FORMAT_HTML (object))); + return; - link = g_queue_peek_head_link (&emf->pending_object_list); + case PROP_ONLY_LOCAL_PHOTOS: + g_value_set_boolean ( + value, em_format_html_get_only_local_photos ( + EM_FORMAT_HTML (object))); + return; - while (link != NULL) { - EMFormatHTMLPObject *pw = link->data; + case PROP_SHOW_SENDER_PHOTO: + g_value_set_boolean ( + value, em_format_html_get_show_sender_photo ( + EM_FORMAT_HTML (object))); + return; - if (!strcmp (pw->classid, classid)) - return pw; + case PROP_SHOW_REAL_DATE: + g_value_set_boolean ( + value, em_format_html_get_show_real_date ( + EM_FORMAT_HTML (object))); + return; - link = g_list_next (link); + case PROP_TEXT_COLOR: + em_format_html_get_color ( + EM_FORMAT_HTML (object), + EM_FORMAT_HTML_COLOR_TEXT, + &color); + g_value_set_boxed (value, &color); + return; + case PROP_ANIMATE_IMAGES: + g_value_set_boolean ( + value, em_format_html_get_animate_images ( + EM_FORMAT_HTML (object))); + return; } - return NULL; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } -EMFormatHTMLPObject * -em_format_html_find_pobject_func (EMFormatHTML *emf, - CamelMimePart *part, - EMFormatHTMLPObjectFunc func) +static void +efh_finalize (GObject *object) { - GList *link; - - g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL); - - link = g_queue_peek_head_link (&emf->pending_object_list); - - while (link != NULL) { - EMFormatHTMLPObject *pw = link->data; - - if (pw->func == func && pw->part == part) - return pw; - - link = g_list_next (link); - } - - return NULL; + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); } -void -em_format_html_remove_pobject (EMFormatHTML *emf, - EMFormatHTMLPObject *pobject) +static void +efh_write_attachment (EMFormat *emf, + EMFormatPURI *puri, + CamelStream *stream, + EMFormatWriterInfo *info, + GCancellable *cancellable) { - g_return_if_fail (EM_IS_FORMAT_HTML (emf)); - g_return_if_fail (pobject != NULL); + gchar *text, *html; + CamelContentType *ct; + gchar *mime_type; + const EMFormatHandler *handler; - g_queue_remove (&emf->pending_object_list, pobject); + /* we display all inlined attachments only */ - if (pobject->free != NULL) - pobject->free (pobject); + /* this could probably be cleaned up ... */ + camel_stream_write_string ( + stream, + "" + "
" + "" + "
" + "
\n", + cancellable, NULL); - g_free (pobject->classid); - g_free (pobject); -} + ct = camel_mime_part_get_content_type (puri->part); + mime_type = camel_content_type_simple (ct); -void -em_format_html_clear_pobject (EMFormatHTML *emf) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (emf)); + /* output some info about it */ + text = em_format_describe_part (puri->part, mime_type); + html = camel_text_to_html ( + text, ((EMFormatHTML *) emf)->text_html_flags & + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); + camel_stream_write_string (stream, html, cancellable, NULL); + g_free (html); + g_free (text); - while (!g_queue_is_empty (&emf->pending_object_list)) { - EMFormatHTMLPObject *pobj; + camel_stream_write_string ( + stream, "
", cancellable, NULL); - pobj = g_queue_pop_head (&emf->pending_object_list); - em_format_html_remove_pobject (emf, pobj); + handler = em_format_find_handler (emf, mime_type); + if (handler && handler->write_func && handler->write_func != efh_write_attachment) { + if (em_format_is_inline (emf, puri->uri, puri->part, handler)) + handler->write_func (emf, puri, stream, info, cancellable); } -} - -struct _EMFormatHTMLJob * -em_format_html_job_new (EMFormatHTML *emfh, - void (*callback) (struct _EMFormatHTMLJob *job, - GCancellable *cancellable), - gpointer data) -{ - struct _EMFormatHTMLJob *job = g_malloc0 (sizeof (*job)); - - job->format = emfh; - job->puri_level = ((EMFormat *) emfh)->pending_uri_level; - job->callback = callback; - job->u.data = data; - if (((EMFormat *) emfh)->base) - job->base = camel_url_copy (((EMFormat *) emfh)->base); - return job; + g_free (mime_type); } -void -em_format_html_job_queue (EMFormatHTML *emfh, - struct _EMFormatHTMLJob *job) +static void +efh_preparse (EMFormat *emf) { - g_mutex_lock (emfh->priv->lock); - g_queue_push_tail (&emfh->priv->pending_jobs, job); - g_mutex_unlock (emfh->priv->lock); -} + CamelInternetAddress *addr; -/* ********************************************************************** */ + EMFormatHTML *efh = EM_FORMAT_HTML (emf); -static void -emfh_getpuri (struct _EMFormatHTMLJob *job, - GCancellable *cancellable) -{ - d(printf(" running getpuri task\n")); - if (!g_cancellable_is_cancelled (cancellable)) - job->u.puri->func ( - EM_FORMAT (job->format), job->stream, - job->u.puri, cancellable); + if (!emf->message) { + efh->priv->can_load_images = FALSE; + return; + } + + addr = camel_mime_message_get_from (emf->message); + efh->priv->can_load_images = em_utils_in_addressbook (addr, FALSE); } static void -emfh_configure_stream_for_proxy (CamelHttpStream *stream, - const gchar *uri) +efh_write_message (EMFormat *emf, + GList *puris, + CamelStream *stream, + EMFormatWriterInfo *info, + GCancellable *cancellable) { - EProxy *proxy; - SoupURI *proxy_uri; - gchar *basic; - gchar *basic64; - const gchar *user = ""; - const gchar *password = ""; - - proxy = em_utils_get_proxy (); + GList *iter; + EMFormatHTML *efh; + gchar *header; - if (!e_proxy_require_proxy_for_uri (proxy, uri)) - return; + efh = (EMFormatHTML *) emf; - proxy_uri = e_proxy_peek_uri_for (proxy, uri); + header = g_strdup_printf ( + "\n\n" + "\n\n" + "Evolution Mail Display\n" + "\n" + "\n" + "", + e_color_to_value (&efh->priv->colors[ + EM_FORMAT_HTML_COLOR_BODY])); - if (proxy_uri == NULL) - return; + camel_stream_write_string (stream, header, cancellable, NULL); + g_free (header); - if (proxy_uri->user != NULL) - user = proxy_uri->user; + if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) { - if (proxy_uri->password != NULL) - password = proxy_uri->password; + efh_write_source (emf, emf->mail_part_list->data, + stream, info, cancellable); - if (*user == '\0' && *password == '\0') + camel_stream_write_string (stream, "", cancellable, NULL); return; + } - basic = g_strdup_printf ("%s:%s", user, password); - basic64 = g_base64_encode ((guchar *) basic, strlen (basic)); - camel_http_stream_set_proxy_authpass (stream, basic64); - g_free (basic64); - g_free (basic); -} + for (iter = puris; iter; iter = iter->next) { -static void -emfh_gethttp (struct _EMFormatHTMLJob *job, - GCancellable *cancellable) -{ - CamelStream *cistream = NULL, *costream = NULL, *instream = NULL; - CamelURL *url; - CamelHttpStream *tmp_stream; - gssize n, total = 0, pc_complete = 0, nread = 0; - gchar buffer[1500]; - const gchar *length; - - if (g_cancellable_is_cancelled (cancellable) - || (url = camel_url_new (job->u.uri, NULL)) == NULL) - goto badurl; - - d(printf(" running load uri task: %s\n", job->u.uri)); - - if (emfh_http_cache) - instream = cistream = camel_data_cache_get (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL); - - if (instream == NULL) { - EMailImageLoadingPolicy policy; - - policy = em_format_html_get_image_loading_policy (job->format); - - if (!(job->format->priv->load_images_now - || policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS - || (policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES - && em_utils_in_addressbook ((CamelInternetAddress *) camel_mime_message_get_from (job->format->parent.message), FALSE)))) { - /* TODO: Ideally we would put the http requests into - * another queue and only send them out if the user - * selects 'load images', when they do. The problem - * is how to maintain this state with multiple - * renderings, and how to adjust the thread - * dispatch/setup routine to handle it */ - camel_url_free (url); - goto done; - } + EMFormatPURI *puri = iter->data; - instream = camel_http_stream_new (CAMEL_HTTP_METHOD_GET, ((EMFormat *) job->format)->session, url); - camel_http_stream_set_user_agent((CamelHttpStream *) instream, "CamelHttpStream/1.0 Evolution/" VERSION); - emfh_configure_stream_for_proxy ((CamelHttpStream *) instream, job->u.uri); - - camel_operation_push_message ( - cancellable, _("Retrieving '%s'"), job->u.uri); - tmp_stream = (CamelHttpStream *) instream; - if (camel_stream_read (CAMEL_STREAM (instream), NULL, 0, cancellable, NULL) == 0) { - CamelContentType *content_type; - - content_type = camel_http_stream_get_content_type (tmp_stream); - length = camel_header_raw_find(&tmp_stream->headers, "Content-Length", NULL); - d(printf(" Content-Length: %s\n", length)); - if (length != NULL) - total = atoi (length); - camel_content_type_unref (content_type); - } - } else - camel_operation_push_message ( - cancellable, _("Retrieving '%s'"), job->u.uri); + if (!puri) + continue; + + /* If current PURI has suffix .rfc822 then iterate through all + * subsequent PURIs until PURI with suffix .rfc822.end is found. + * These skipped PURIs contain entire RFC message which will + * be written in \n
\n", cid, cid); - camel_stream_write_string (stream, content, cancellable, NULL); - g_free (content); - g_free (cid); + g_object_notify (G_OBJECT (efh), property_name); } -/* This is a lot of code for something useless ... */ -static void -efh_message_external (EMFormat *emf, - CamelStream *stream, - CamelMimePart *part, - const EMFormatHandler *info, - GCancellable *cancellable, - gboolean is_fallback) +EMailImageLoadingPolicy +em_format_html_get_image_loading_policy (EMFormatHTML *efh) { - CamelContentType *type; - const gchar *access_type; - gchar *url = NULL, *desc = NULL; - gchar *content; - - if (!part) { - camel_stream_write_string ( - stream, _("Unknown external-body part."), - cancellable, NULL); - return; - } - - /* needs to be cleaner */ - type = camel_mime_part_get_content_type (part); - access_type = camel_content_type_param (type, "access-type"); - if (!access_type) { - camel_stream_write_string ( - stream, _("Malformed external-body part."), - cancellable, NULL); - return; - } - - if (!g_ascii_strcasecmp(access_type, "ftp") || - !g_ascii_strcasecmp(access_type, "anon-ftp")) { - const gchar *name, *site, *dir, *mode; - gchar *path; - gchar ftype[16]; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - dir = camel_content_type_param (type, "directory"); - mode = camel_content_type_param (type, "mode"); - if (name == NULL || site == NULL) - goto fail; + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0); - /* Generate the path. */ - if (dir) - path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name); - else - path = g_strdup_printf("/%s", *name=='/'?name+1:name); + return efh->priv->image_loading_policy; +} - if (mode && *mode) - sprintf(ftype, ";type=%c", *mode); - else - ftype[0] = 0; +void +em_format_html_set_image_loading_policy (EMFormatHTML *efh, + EMailImageLoadingPolicy policy) +{ + g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype); - g_free (path); - desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url); - } else if (!g_ascii_strcasecmp (access_type, "local-file")) { - const gchar *name, *site; + if (policy == efh->priv->image_loading_policy) + return; - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - if (name == NULL) - goto fail; + efh->priv->image_loading_policy = policy; - url = g_filename_to_uri (name, NULL, NULL); - if (site) - desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site); - else - desc = g_strdup_printf(_("Pointer to local file (%s)"), name); - } else if (!g_ascii_strcasecmp (access_type, "URL")) { - const gchar *urlparam; - gchar *s, *d; + g_object_notify (G_OBJECT (efh), "image-loading-policy"); +} - /* RFC 2017 */ +gboolean +em_format_html_get_mark_citations (EMFormatHTML *efh) +{ + guint32 flags; - urlparam = camel_content_type_param (type, "url"); - if (urlparam == NULL) - goto fail; + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - /* For obscure MIMEy reasons, the URL may be split into words */ - url = g_strdup (urlparam); - s = d = url; - while (*s) { - /* FIXME: use camel_isspace */ - if (!isspace ((guchar) * s)) - *d++ = *s; - s++; - } - *d = 0; - desc = g_strdup_printf (_("Pointer to remote data (%s)"), url); - } else - goto fail; + flags = efh->text_html_flags; - content = g_strdup_printf ("%s", url, desc); - camel_stream_write_string (stream, content, cancellable, NULL); - g_free (content); + return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0); +} - g_free (url); - g_free (desc); +void +em_format_html_set_mark_citations (EMFormatHTML *efh, + gboolean mark_citations) +{ + g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - return; + if (mark_citations) + efh->text_html_flags |= + CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; + else + efh->text_html_flags &= + ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; -fail: - content = g_strdup_printf ( - _("Pointer to unknown external data (\"%s\" type)"), - access_type); - camel_stream_write_string (stream, content, cancellable, NULL); - g_free (content); + g_object_notify (G_OBJECT (efh), "mark-citations"); } -static void -efh_message_deliverystatus (EMFormat *emf, - CamelStream *stream, - CamelMimePart *part, - const EMFormatHandler *info, - GCancellable *cancellable, - gboolean is_fallback) +gboolean +em_format_html_get_only_local_photos (EMFormatHTML *efh) { - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - CamelStream *filtered_stream; - CamelMimeFilter *html_filter; - guint32 rgb = 0x737373; - gchar *content; + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - /* Yuck, this is copied from efh_text_plain */ - content = g_strdup_printf ( - "
\n", - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_CONTENT]), - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - camel_stream_write_string (stream, content, cancellable, NULL); - g_free (content); + return efh->priv->only_local_photos; +} - filtered_stream = camel_stream_filter_new (stream); - html_filter = camel_mime_filter_tohtml_new (efh->text_html_flags, rgb); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), html_filter); - g_object_unref (html_filter); +void +em_format_html_set_only_local_photos (EMFormatHTML *efh, + gboolean only_local_photos) +{ + g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - camel_stream_write_string (stream, "\n" EFH_MESSAGE_START, cancellable, NULL); - em_format_format_text ( - emf, filtered_stream, - (CamelDataWrapper *) part, cancellable); - camel_stream_flush (filtered_stream, cancellable, NULL); - camel_stream_write_string (stream, "\n", cancellable, NULL); + efh->priv->only_local_photos = only_local_photos; - camel_stream_write_string (stream, "
", cancellable, NULL); + g_object_notify (G_OBJECT (efh), "only-local-photos"); } -static void -emfh_write_related (EMFormat *emf, - CamelStream *stream, - EMFormatPURI *puri, - GCancellable *cancellable) +gboolean +em_format_html_get_show_sender_photo (EMFormatHTML *efh) { - em_format_format_content (emf, stream, puri->part, cancellable); + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - camel_stream_close (stream, cancellable, NULL); + return efh->priv->show_sender_photo; } -static void -emfh_multipart_related_check (struct _EMFormatHTMLJob *job, - GCancellable *cancellable) +void +em_format_html_set_show_sender_photo (EMFormatHTML *efh, + gboolean show_sender_photo) { - EMFormat *format; - GList *link; - gchar *oldpartid; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - format = EM_FORMAT (job->format); - - d(printf(" running multipart/related check task\n")); - oldpartid = g_strdup (format->part_id->str); - - link = g_queue_peek_head_link (job->puri_level->data); + g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - if (!link) { - g_string_printf (format->part_id, "%s", oldpartid); - g_free (oldpartid); - return; - } + efh->priv->show_sender_photo = show_sender_photo; - while (link != NULL) { - EMFormatPURI *puri = link->data; - - if (puri->use_count == 0) { - d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count)); - if (puri->func == emfh_write_related) { - g_string_printf (format->part_id, "%s", puri->part_id); - /* FIXME Not passing a GCancellable here. */ - em_format_part ( - format, CAMEL_STREAM (job->stream), - puri->part, NULL); - } - /* else it was probably added by a previous format this loop */ - } + g_object_notify (G_OBJECT (efh), "show-sender-photo"); +} - link = g_list_next (link); - } +gboolean +em_format_html_get_show_real_date (EMFormatHTML *efh) +{ + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - g_string_printf (format->part_id, "%s", oldpartid); - g_free (oldpartid); + return efh->priv->show_real_date; } -/* RFC 2387 */ -static void -efh_multipart_related (EMFormat *emf, - CamelStream *stream, - CamelMimePart *part, - const EMFormatHandler *info, - GCancellable *cancellable, - gboolean is_fallback) +void +em_format_html_set_show_real_date (EMFormatHTML *efh, + gboolean show_real_date) { - CamelMultipart *mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - CamelMimePart *body_part, *display_part = NULL; - CamelContentType *content_type; - const gchar *start; - gint i, nparts, partidlen, displayid = 0; - struct _EMFormatHTMLJob *job; - - if (!CAMEL_IS_MULTIPART (mp)) { - em_format_format_source (emf, stream, part, cancellable); - return; - } + g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - nparts = camel_multipart_get_number (mp); - content_type = camel_mime_part_get_content_type (part); - start = camel_content_type_param (content_type, "start"); - if (start && strlen (start) > 2) { - gint len; - const gchar *cid; + efh->priv->show_real_date = show_real_date; - /* strip <>'s */ - len = strlen (start) - 2; - start++; + g_object_notify (G_OBJECT (efh), "show-real-date"); +} - for (i = 0; i < nparts; i++) { - body_part = camel_multipart_get_part (mp, i); - cid = camel_mime_part_get_content_id (body_part); +gboolean +em_format_html_get_animate_images (EMFormatHTML *efh) +{ + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - if (cid && !strncmp (cid, start, len) && strlen (cid) == len) { - display_part = body_part; - displayid = i; - break; - } - } - } else { - display_part = camel_multipart_get_part (mp, 0); - } + return efh->priv->animate_images; +} - if (display_part == NULL) { - em_format_part_as ( - emf, stream, part, - "multipart/mixed", cancellable); - return; - } +void +em_format_html_set_animate_images (EMFormatHTML *efh, + gboolean animate_images) +{ + g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - em_format_push_level (emf); + efh->priv->animate_images = animate_images; - partidlen = emf->part_id->len; + g_object_notify (G_OBJECT (efh), "animate-images"); +} - /* queue up the parts for possible inclusion */ - for (i = 0; i < nparts; i++) { - body_part = camel_multipart_get_part (mp, i); - if (body_part != display_part) { - g_string_append_printf(emf->part_id, "related.%d", i); - em_format_add_puri (emf, sizeof (EMFormatPURI), NULL, body_part, emfh_write_related); - g_string_truncate (emf->part_id, partidlen); - d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid)); - } - } +CamelMimePart * +em_format_html_file_part (EMFormatHTML *efh, + const gchar *mime_type, + const gchar *filename, + GCancellable *cancellable) +{ + CamelMimePart *part; + CamelStream *stream; + CamelDataWrapper *dw; + gchar *basename; - g_string_append_printf(emf->part_id, "related.%d", displayid); - em_format_part (emf, stream, display_part, cancellable); - g_string_truncate (emf->part_id, partidlen); - camel_stream_flush (stream, cancellable, NULL); + stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL); + if (stream == NULL) + return NULL; - /* queue a job to check for un-referenced parts to add as attachments */ - job = em_format_html_job_new ( - EM_FORMAT_HTML (emf), emfh_multipart_related_check, NULL); - job->stream = stream; - g_object_ref (stream); - em_format_html_job_queue ((EMFormatHTML *) emf, job); + dw = camel_data_wrapper_new (); + camel_data_wrapper_construct_from_stream_sync ( + dw, stream, cancellable, NULL); + g_object_unref (stream); + if (mime_type) + camel_data_wrapper_set_mime_type (dw, mime_type); + part = camel_mime_part_new (); + camel_medium_set_content ((CamelMedium *) part, dw); + g_object_unref (dw); + basename = g_path_get_basename (filename); + camel_mime_part_set_filename (part, basename); + g_free (basename); - em_format_pull_level (emf); + return part; } -static void -efh_write_image (EMFormat *emf, - CamelStream *stream, - EMFormatPURI *puri, - GCancellable *cancellable) +void +em_format_html_format_cert_infos (GQueue *cert_infos, + GString *output_buffer) { - CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part); + GQueue valid = G_QUEUE_INIT; + GList *head, *link; + + g_return_if_fail (cert_infos != NULL); + g_return_if_fail (output_buffer != NULL); - d(printf("writing image '%s'\n", puri->cid)); - camel_data_wrapper_decode_to_stream_sync ( - dw, stream, cancellable, NULL); - camel_stream_close (stream, cancellable, NULL); -} + head = g_queue_peek_head_link (cert_infos); -static void -efh_image (EMFormat *emf, - CamelStream *stream, - CamelMimePart *part, - const EMFormatHandler *info, - GCancellable *cancellable, - gboolean is_fallback) -{ - EMFormatPURI *puri; - gchar *content; + /* Make sure we have a valid CamelCipherCertInfo before + * appending anything to the output buffer, so we don't + * end up with "()". */ + for (link = head; link != NULL; link = g_list_next (link)) { + CamelCipherCertInfo *cinfo = link->data; - puri = em_format_add_puri ( - emf, sizeof (EMFormatPURI), NULL, part, efh_write_image); + if ((cinfo->name != NULL && *cinfo->name != '\0') || + (cinfo->email != NULL && *cinfo->email != '\0')) { + g_queue_push_tail (&valid, cinfo); + } + } - content = g_strdup_printf ( - "", puri->cid); - camel_stream_write_string (stream, content, cancellable, NULL); - g_free (content); -} + if (g_queue_is_empty (&valid)) + return; -/* Notes: - * - * image/tiff is omitted because it's a multi-page image format, but - * gdk-pixbuf unconditionally renders the first page only, and doesn't - * even indicate through meta-data whether multiple pages are present - * (see bug 335959). Therefore, make no attempt to render TIFF images - * inline and defer to an application that can handle multi-page TIFF - * files properly like Evince or Gimp. Once the referenced bug is - * fixed we can reevaluate this policy. - */ -static EMFormatHandler type_builtin_table[] = { - { (gchar *) "image/gif", efh_image }, - { (gchar *) "image/jpeg", efh_image }, - { (gchar *) "image/png", efh_image }, - { (gchar *) "image/x-png", efh_image }, - { (gchar *) "image/x-bmp", efh_image }, - { (gchar *) "image/bmp", efh_image }, - { (gchar *) "image/svg", efh_image }, - { (gchar *) "image/x-cmu-raster", efh_image }, - { (gchar *) "image/x-ico", efh_image }, - { (gchar *) "image/x-portable-anymap", efh_image }, - { (gchar *) "image/x-portable-bitmap", efh_image }, - { (gchar *) "image/x-portable-graymap", efh_image }, - { (gchar *) "image/x-portable-pixmap", efh_image }, - { (gchar *) "image/x-xpixmap", efh_image }, - { (gchar *) "text/enriched", efh_text_enriched }, - { (gchar *) "text/plain", efh_text_plain }, - { (gchar *) "text/html", efh_text_html }, - { (gchar *) "text/richtext", efh_text_enriched }, - { (gchar *) "text/*", efh_text_plain }, - { (gchar *) "message/external-body", efh_message_external }, - { (gchar *) "message/delivery-status", efh_message_deliverystatus }, - { (gchar *) "multipart/related", efh_multipart_related }, + g_string_append (output_buffer, " ("); - /* This is where one adds those busted, non-registered types, - * that some idiot mailer writers out there decide to pull out - * of their proverbials at random. */ + while (!g_queue_is_empty (&valid)) { + CamelCipherCertInfo *cinfo; - { (gchar *) "image/jpg", efh_image }, - { (gchar *) "image/pjpeg", efh_image }, + cinfo = g_queue_pop_head (&valid); - /* special internal types */ + if (cinfo->name != NULL && *cinfo->name != '\0') { + g_string_append (output_buffer, cinfo->name); - { (gchar *) "x-evolution/message/rfc822", efh_format_message } -}; + if (cinfo->email != NULL && *cinfo->email != '\0') { + g_string_append (output_buffer, " <"); + g_string_append (output_buffer, cinfo->email); + g_string_append (output_buffer, ">"); + } -static void -efh_builtin_init (EMFormatHTMLClass *efhc) -{ - EMFormatClass *efc; - gint ii; + } else if (cinfo->email != NULL && *cinfo->email != '\0') { + g_string_append (output_buffer, cinfo->email); + } - efc = (EMFormatClass *) efhc; + if (!g_queue_is_empty (&valid)) + g_string_append (output_buffer, ", "); + } - for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++) - em_format_class_add_handler ( - efc, &type_builtin_table[ii]); + g_string_append_c (output_buffer, ')'); } -/* ********************************************************************** */ - static void efh_format_text_header (EMFormatHTML *emfh, GString *buffer, @@ -2608,37 +2188,34 @@ efh_format_text_header (EMFormatHTML *emfh, html = value; is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; - if (emfh->simple_headers) { - fmt = "%s: %s
"; + + if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) { + if (flags & EM_FORMAT_HEADER_BOLD) { + fmt = "%s: %s"; + } else { + fmt = "%s: %s"; + } + } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) { + if (is_rtl) + fmt = "%2$s%1$s "; + else + fmt = "%s %s"; } else { - if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) { - if (flags & EM_FORMAT_HEADER_BOLD) { - fmt = "%s: %s"; - } else { - fmt = "%s: %s"; - } - } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) { + if (flags & EM_FORMAT_HEADER_BOLD) { if (is_rtl) - fmt = "%2$s%1$s "; + fmt = "%2$s%1$s: "; else - fmt = "%s %s"; + fmt = "%s: %s"; } else { - - if (flags & EM_FORMAT_HEADER_BOLD) { - if (is_rtl) - fmt = "%2$s%1$s: "; - else - fmt = "%s: %s"; - } else { - if (is_rtl) - fmt = "%2$s%1$s: "; - else - fmt = "%s: %s"; - } + if (is_rtl) + fmt = "%2$s%1$s: "; + else + fmt = "%s: %s"; } } - g_string_append_printf (buffer, fmt, label, html); + g_string_append_printf (buffer, fmt, + (flags & EM_FORMAT_HTML_HEADER_HIDDEN ? "none" : "table-row"), label, html); g_free (mhtml); } @@ -2653,22 +2230,15 @@ static gchar * efh_format_address (EMFormatHTML *efh, GString *out, struct _camel_header_address *a, - gchar *field) + gchar *field, + gboolean no_links) { guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; gchar *name, *mailto, *addr; gint i = 0; - gboolean wrap = FALSE; gchar *str = NULL; gint limit = mail_config_get_address_count (); - if (field ) { - if ((!strcmp (field, _("To")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_TO)) - || (!strcmp (field, _("Cc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_CC)) - || (!strcmp (field, _("Bcc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_BCC))) - wrap = TRUE; - } - while (a) { if (a->name) name = camel_text_to_html (a->name, flags, 0); @@ -2700,7 +2270,10 @@ efh_format_address (EMFormatHTML *efh, mailto = camel_url_encode (a->v.addr, "?=&()"); } addr = camel_text_to_html (a->v.addr, flags, 0); - g_string_append_printf (out, "%s", mailto, addr); + if (no_links) + g_string_append_printf (out, "%s", addr); + else + g_string_append_printf (out, "%s", mailto, addr); g_free (mailto); g_free (addr); @@ -2709,7 +2282,7 @@ efh_format_address (EMFormatHTML *efh, break; case CAMEL_HEADER_ADDRESS_GROUP: g_string_append_printf (out, "%s: ", name); - efh_format_address (efh, out, a->v.members, field); + efh_format_address (efh, out, a->v.members, field, no_links); g_string_append_printf (out, ";"); break; default: @@ -2725,48 +2298,51 @@ efh_format_address (EMFormatHTML *efh, g_string_append (out, ", "); /* Let us add a '...' if we have more addresses */ - if (limit > 0 && wrap && a && (i > (limit - 1))) { - gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); - - if (!strcmp (field, _("To"))) { - g_string_append (out, "..."); - str = g_strdup_printf (" ", evolution_imagesdir); - } - else if (!strcmp (field, _("Cc"))) { - g_string_append (out, "..."); - str = g_strdup_printf (" ", evolution_imagesdir); - } - else if (!strcmp (field, _("Bcc"))) { - g_string_append (out, "..."); - str = g_strdup_printf (" ", evolution_imagesdir); + if (limit > 0 && (i == limit - 1)) { + const gchar *id = NULL; + + if (strcmp (field, _("To")) == 0) { + id = "to"; + } else if (strcmp (field, _("Cc")) == 0) { + id = "cc"; + } else if (strcmp (field, _("Bcc")) == 0) { + id = "bcc"; } - g_free (evolution_imagesdir); - - if (str) - return str; + if (id) { + g_string_append_printf (out, + "", id); + str = g_strdup_printf ( + "", + EVOLUTION_IMAGESDIR, id); + } } - } - if (limit > 0 && i > (limit)) { - gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + if (str) { + const gchar *id = NULL; - if (!strcmp (field, _("To"))) { - str = g_strdup_printf (" ", evolution_imagesdir); - } - else if (!strcmp (field, _("Cc"))) { - str = g_strdup_printf (" ", evolution_imagesdir); - } - else if (!strcmp (field, _("Bcc"))) { - str = g_strdup_printf (" ", evolution_imagesdir); + if (strcmp (field, _("To")) == 0) { + id = "to"; + } else if (strcmp (field, _("Cc")) == 0) { + id = "cc"; + } else if (strcmp (field, _("Bcc")) == 0) { + id = "bcc"; } - g_free (evolution_imagesdir); + if (id) { + g_string_append_printf (out, + "" + "...", + id); + } } return str; - } static void @@ -2793,15 +2369,15 @@ canon_header_name (gchar *name) } } -static void -efh_format_header (EMFormat *emf, - GString *buffer, - CamelMedium *part, - struct _camel_header_raw *header, - guint32 flags, - const gchar *charset) +void +em_format_html_format_header (EMFormat *emf, + GString *buffer, + CamelMedium *part, + struct _camel_header_raw *header, + guint32 flags, + const gchar *charset) { - EMFormatHTML *efh = (EMFormatHTML *) emf; + EMFormatHTML *efh = EM_FORMAT_HTML (emf); gchar *name, *buf, *value = NULL; const gchar *label, *txt; gboolean addrspec = FALSE; @@ -2825,9 +2401,11 @@ efh_format_header (EMFormat *emf, struct _camel_header_address *addrs; GString *html; gchar *img; + const gchar *charset = em_format_get_charset (emf) ? + em_format_get_charset (emf) : em_format_get_default_charset (emf); buf = camel_header_unfold (header->value); - if (!(addrs = camel_header_address_decode (buf, emf->charset ? emf->charset : emf->default_charset))) { + if (!(addrs = camel_header_address_decode (buf, charset))) { g_free (buf); return; } @@ -2835,7 +2413,8 @@ efh_format_header (EMFormat *emf, g_free (buf); html = g_string_new(""); - img = efh_format_address (efh, html, addrs, (gchar *) label); + img = efh_format_address (efh, html, addrs, (gchar *) label, + (flags & EM_FORMAT_HTML_HEADER_NOLINKS)); if (img) { str_field = g_strdup_printf ("%s%s:", img, label); @@ -2921,7 +2500,11 @@ efh_format_header (EMFormat *emf, html = g_string_new(""); scan = ng; while (scan) { - g_string_append_printf(html, "%s", scan->newsgroup, scan->newsgroup); + if (flags & EM_FORMAT_HTML_HEADER_NOLINKS) + g_string_append_printf (html, "%s", scan->newsgroup); + else + g_string_append_printf(html, "%s", + scan->newsgroup, scan->newsgroup); scan = scan->next; if (scan) g_string_append_printf(html, ", "); @@ -2949,100 +2532,129 @@ efh_format_header (EMFormat *emf, } static void -efh_format_headers (EMFormatHTML *efh, - GString *buffer, - CamelMedium *part, - GCancellable *cancellable) +efh_format_short_headers (EMFormatHTML *efh, + GString *buffer, + CamelMedium *part, + gboolean visible, + GCancellable *cancellable) { - EMFormat *emf = (EMFormat *) efh; + EMFormat *emf = EM_FORMAT (efh); const gchar *charset; CamelContentType *ct; - struct _camel_header_raw *header; - gboolean have_icon = FALSE; - const gchar *photo_name = NULL; - CamelInternetAddress *cia = NULL; - gboolean face_decoded = FALSE, contact_has_photo = FALSE; - guchar *face_header_value = NULL; - gsize face_header_len = 0; - gchar *header_sender = NULL, *header_from = NULL, *name; - gboolean mail_from_delegate = FALSE; const gchar *hdr_charset; gchar *evolution_imagesdir; + gchar *subject = NULL; + struct _camel_header_address *addrs = NULL; + struct _camel_header_raw *header; + GString *from; + gboolean is_rtl; - if (!part) + if (cancellable && g_cancellable_is_cancelled (cancellable)) return; ct = camel_mime_part_get_content_type ((CamelMimePart *) part); charset = camel_content_type_param (ct, "charset"); charset = camel_iconv_charset_name (charset); + hdr_charset = em_format_get_charset (emf) ? + em_format_get_charset (emf) : em_format_get_default_charset (emf); - if (!efh->simple_headers) - g_string_append_printf ( - buffer, "\n" - "", - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_HEADER])); - - hdr_charset = emf->charset ? emf->charset : emf->default_charset; evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + from = g_string_new (""); - /* If the header is collapsed, display just subject and sender in one row and leave */ - if (efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED && efh->priv->headers_collapsable) { - gchar *subject = NULL; - struct _camel_header_address *addrs = NULL; - GString *from = g_string_new (""); + g_string_append_printf (buffer, + "
", + visible ? "block" : "none"); - header = ((CamelMimePart *) part)->headers; - while (header) { - if (!g_ascii_strcasecmp (header->name, "From")) { - GString *tmp; - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) { - header = header->next; - continue; - } - tmp = g_string_new (""); - efh_format_address (efh, tmp, addrs, header->name); - - if (tmp->len) - g_string_printf (from, _("From: %s"), tmp->str); - g_string_free (tmp, TRUE); - } else if (!g_ascii_strcasecmp (header->name, "Subject")) { - gchar *buf = NULL; - buf = camel_header_unfold (header->value); - g_free (subject); - subject = camel_header_decode_string (buf, hdr_charset); - g_free (buf); + header = ((CamelMimePart *) part)->headers; + while (header) { + if (!g_ascii_strcasecmp (header->name, "From")) { + GString *tmp; + if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) { + header = header->next; + continue; } - header = header->next; + tmp = g_string_new (""); + efh_format_address (efh, tmp, addrs, header->name, FALSE); + + if (tmp->len) + g_string_printf (from, _("From: %s"), tmp->str); + g_string_free (tmp, TRUE); + + } else if (!g_ascii_strcasecmp (header->name, "Subject")) { + gchar *buf = NULL; + subject = camel_header_unfold (header->value); + buf = camel_header_decode_string (subject, hdr_charset); + g_free (subject); + subject = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); + g_free (buf); } + header = header->next; + } + is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; + if (is_rtl) { g_string_append_printf ( buffer, - "" - "" - "" - "", - evolution_imagesdir, - subject ? subject : _("(no subject)"), - from->len ? "(" : "", - from->str, - from->len ? ")" : ""); - - g_free (subject); - if (addrs) - camel_header_address_list_clear (&addrs); - g_string_free (from, TRUE); + "", + from->len ? "(" : "", from->str, from->len ? ")" : "", + subject ? subject : _("(no subject)")); + } else { + g_string_append_printf ( + buffer, + "", + subject ? subject : _("(no subject)"), + from->len ? "(" : "", from->str, from->len ? ")" : ""); + } + + g_string_append (buffer, "
" - "" - "" - "%s %s%s%s
%s%s%s %s
%s %s%s%s
"); + + g_free (subject); + if (addrs) + camel_header_address_list_clear (&addrs); - g_string_append (buffer, ""); + g_string_free (from, TRUE); + g_free (evolution_imagesdir); +} - g_free (evolution_imagesdir); +static void +efh_format_full_headers (EMFormatHTML *efh, + GString *buffer, + CamelMedium *part, + gboolean all_headers, + gboolean visible, + GCancellable *cancellable) +{ + EMFormat *emf = EM_FORMAT (efh); + const gchar *charset; + CamelContentType *ct; + struct _camel_header_raw *header; + gboolean have_icon = FALSE; + const gchar *photo_name = NULL; + CamelInternetAddress *cia = NULL; + gboolean face_decoded = FALSE, contact_has_photo = FALSE; + guchar *face_header_value = NULL; + gsize face_header_len = 0; + gchar *header_sender = NULL, *header_from = NULL, *name; + gboolean mail_from_delegate = FALSE; + const gchar *hdr_charset; + gchar *evolution_imagesdir; + if (cancellable && g_cancellable_is_cancelled (cancellable)) return; - } + + ct = camel_mime_part_get_content_type ((CamelMimePart *) part); + charset = camel_content_type_param (ct, "charset"); + charset = camel_iconv_charset_name (charset); + hdr_charset = em_format_get_charset (emf) ? + em_format_get_charset (emf) : em_format_get_default_charset (emf); + + evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + + g_string_append_printf (buffer, + "", + visible ? "block" : "none"); header = ((CamelMimePart *) part)->headers; while (header) { @@ -3054,7 +2666,7 @@ efh_format_headers (EMFormatHTML *efh, break; html = g_string_new(""); - name = efh_format_address (efh, html, addrs, header->name); + name = efh_format_address (efh, html, addrs, header->name, FALSE); header_sender = html->str; camel_header_address_list_clear (&addrs); @@ -3069,7 +2681,7 @@ efh_format_headers (EMFormatHTML *efh, break; html = g_string_new(""); - name = efh_format_address (efh, html, addrs, header->name); + name = efh_format_address (efh, html, addrs, header->name, FALSE); header_from = html->str; camel_header_address_list_clear (&addrs); @@ -3113,50 +2725,15 @@ efh_format_headers (EMFormatHTML *efh, g_free (header_sender); g_free (header_from); - if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) { - if (efh->priv->headers_collapsable) - g_string_append_printf ( - buffer, - "" - "" - "
" - "" - "" - "\n", - evolution_imagesdir); - else - g_string_append ( - buffer, - "
" - "\n"); - - } else { - if (efh->priv->headers_collapsable) - g_string_append_printf ( - buffer, - "" - "" - "", + classid); - handle = em_format_find_handler(emf, "x-evolution/message/post-header"); - if (handle) - handle->handler ( - emf, stream, part, handle, cancellable, FALSE); + puri = em_format_puri_new ( + emf, sizeof (EMFormatPURI), part, classid); + puri->write_func = efh_write_image; + em_format_add_puri (emf, puri); - camel_stream_write_string ( - stream, EM_FORMAT_HTML_VPAD, cancellable, NULL); - em_format_part (emf, stream, part, cancellable); + g_object_unref (part); + g_free (face_header_value); + } - if (emf->message != (CamelMimeMessage *) part) - camel_stream_write_string ( - stream, "\n", cancellable, NULL); + if (have_icon && efh->show_icon) { + GtkIconInfo *icon_info; + const gchar *classid; + CamelMimePart *iconpart = NULL; + EMFormatPURI *puri; - camel_cipher_validity_free (emf->valid); + classid = "icon:///em-format-html/header/icon"; + g_string_append_printf ( + buffer, + "", + classid); + icon_info = gtk_icon_theme_lookup_icon ( + gtk_icon_theme_get_default (), + "evolution", 16, GTK_ICON_LOOKUP_NO_SVG); + if (icon_info != NULL) { + iconpart = em_format_html_file_part ( + (EMFormatHTML *) emf, "image/png", + gtk_icon_info_get_filename (icon_info), + cancellable); + gtk_icon_info_free (icon_info); + } + if (iconpart) { + puri = em_format_puri_new ( + emf, sizeof (EMFormatPURI), iconpart, classid); + puri->write_func = efh_write_image; + em_format_add_puri (emf, puri); + g_object_unref (iconpart); + } + } - emf->valid = save; - emf->valid_parent = save_parent; + g_string_append (buffer, "
" - "" - "" - "\n", - evolution_imagesdir); - else - g_string_append ( - buffer, - ""); - if (!contact_has_photo && face_decoded) { - gchar *classid; - CamelMimePart *part; - - part = camel_mime_part_new (); - camel_mime_part_set_content ( - (CamelMimePart *) part, - (const gchar *) face_header_value, - face_header_len, "image/png"); - classid = g_strdup_printf ( - "icon:///em-format-html/face/photo/header"); - g_string_append_printf ( - buffer, - "", - classid); - em_format_add_puri ( - emf, sizeof (EMFormatPURI), - classid, part, efh_write_image); - g_object_unref (part); - } + if (photo_name) { + const gchar *classid; + CamelMimePart *photopart; + gboolean only_local_photo; - if (have_icon && efh->show_icon) { - GtkIconInfo *icon_info; - gchar *classid; - CamelMimePart *iconpart = NULL; + cia = camel_internet_address_new (); + camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name); + only_local_photo = em_format_html_get_only_local_photos (efh); + photopart = em_utils_contact_photo (cia, only_local_photo); - classid = g_strdup_printf ( - "icon:///em-format-html/%s/icon/header", - emf->part_id->str); + if (photopart) { + EMFormatPURI *puri; + contact_has_photo = TRUE; + classid = "icon:///em-format-html/headers/photo"; g_string_append_printf ( buffer, "", + "", classid); - - icon_info = gtk_icon_theme_lookup_icon ( - gtk_icon_theme_get_default (), - "evolution", 16, GTK_ICON_LOOKUP_NO_SVG); - if (icon_info != NULL) { - iconpart = em_format_html_file_part ( - (EMFormatHTML *) emf, "image/png", - gtk_icon_info_get_filename (icon_info), - cancellable); - gtk_icon_info_free (icon_info); - } - - if (iconpart) { - em_format_add_puri ( - emf, sizeof (EMFormatPURI), - classid, iconpart, efh_write_image); - g_object_unref (iconpart); - } - g_free (classid); + puri = em_format_puri_new ( + emf, sizeof (EMFormatPURI), photopart, classid); + puri->write_func = efh_write_image; + em_format_add_puri (emf, puri); + g_object_unref (photopart); } - - g_string_append (buffer, "
" - "\n"); - } + g_string_append (buffer, ""); - - if (photo_name) { - gchar *classid; - CamelMimePart *photopart; - gboolean only_local_photo; - - cia = camel_internet_address_new (); - camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name); - only_local_photo = em_format_html_get_only_local_photos (efh); - photopart = em_utils_contact_photo (cia, only_local_photo); - - if (photopart) { - contact_has_photo = TRUE; - classid = g_strdup_printf ( - "icon:///em-format-html/%s/photo/header", - emf->part_id->str); - g_string_append_printf ( - buffer, - "", - classid); - em_format_add_puri (emf, sizeof (EMFormatPURI), classid, - photopart, efh_write_image); - g_object_unref (photopart); - - g_free (classid); - } - g_object_unref (cia); - } + g_string_append (buffer, "
\n"); g_free (evolution_imagesdir); /* dump selected headers */ - if (emf->mode == EM_FORMAT_MODE_ALLHEADERS) { + if (all_headers) { header = ((CamelMimePart *) part)->headers; while (header) { - efh_format_header ( + em_format_html_format_header ( emf, buffer, part, header, EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset); header = header->next; @@ -3205,7 +2782,7 @@ efh_format_headers (EMFormatHTML *efh, xmailer.value = use_header->value; mailer_shown = TRUE; - efh_format_header ( + em_format_html_format_header ( emf, buffer, part, &xmailer, h->flags, charset); if (strstr(use_header->value, "Evolution")) @@ -3226,7 +2803,7 @@ efh_format_headers (EMFormatHTML *efh, face_decoded = TRUE; /* Showing an encoded "Face" header makes little sense */ } else if (!g_ascii_strcasecmp (header->name, h->name) && !face) { - efh_format_header ( + em_format_html_format_header ( emf, buffer, part, header, h->flags, charset); } @@ -3238,214 +2815,162 @@ efh_format_headers (EMFormatHTML *efh, } } - if (!efh->simple_headers) { - g_string_append (buffer, "
" - "
" - "" - "
\n\n"); + g_object_unref (cia); } -} - -static void -efh_format_message (EMFormat *emf, - CamelStream *stream, - CamelMimePart *part, - const EMFormatHandler *info, - GCancellable *cancellable, - gboolean is_fallback) -{ - const EMFormatHandler *handle; - GString *buffer; - - /* TODO: make this validity stuff a method */ - EMFormatHTML *efh = (EMFormatHTML *) emf; - CamelCipherValidity *save = emf->valid, *save_parent = emf->valid_parent; - - emf->valid = NULL; - emf->valid_parent = NULL; - - buffer = g_string_sized_new (1024); - - if (emf->message != (CamelMimeMessage *) part) - g_string_append (buffer, "
\n"); - if (!efh->hide_headers) - efh_format_headers ( - efh, buffer, CAMEL_MEDIUM (part), cancellable); - - camel_stream_write ( - stream, buffer->str, buffer->len, cancellable, NULL); - - g_string_free (buffer, TRUE); + if (!contact_has_photo && face_decoded) { + const gchar *classid; + CamelMimePart *part; + EMFormatPURI *puri; + + part = camel_mime_part_new (); + camel_mime_part_set_content ( + (CamelMimePart *) part, + (const gchar *) face_header_value, + face_header_len, "image/png"); + classid = "icon:///em-format-html/headers/face/photo"; + g_string_append_printf ( + buffer, + "
" + "" + "
"); } -void -em_format_html_format_cert_infos (GQueue *cert_infos, - GString *output_buffer) +gboolean +em_format_html_can_load_images (EMFormatHTML *efh) { - GQueue valid = G_QUEUE_INIT; - GList *head, *link; + g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - g_return_if_fail (cert_infos != NULL); - g_return_if_fail (output_buffer != NULL); + return ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) || + ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES) && + efh->priv->can_load_images)); +} - head = g_queue_peek_head_link (cert_infos); +void +em_format_html_animation_extract_frame (const GByteArray *anim, + gchar **frame, + gsize *len) +{ + GdkPixbufLoader *loader; + GdkPixbufAnimation *animation; + GdkPixbuf *frame_buf; + + /* GIF89a (GIF image signature) */ + const gchar GIF_HEADER[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; + const gint GIF_HEADER_LEN = sizeof (GIF_HEADER); + + /* NETSCAPE2.0 (extension describing animated GIF, starts on 0x310) */ + const gchar GIF_APPEXT[] = { 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, + 0x50, 0x45, 0x32, 0x2E, 0x30 }; + const gint GIF_APPEXT_LEN = sizeof (GIF_APPEXT); + + /* Check if the image is an animated GIF. We don't care about any + * other animated formats (APNG or MNG) as WebKit does not support them + * and displays only the first frame. */ + if ((anim->len < 0x331) + || (memcmp (anim->data, GIF_HEADER, GIF_HEADER_LEN) != 0) + || (memcmp (&anim->data[0x310], GIF_APPEXT, GIF_APPEXT_LEN) != 0)) { + + *frame = g_memdup (anim->data, anim->len); + *len = anim->len; + return; + } - /* Make sure we have a valid CamelCipherCertInfo before - * appending anything to the output buffer, so we don't - * end up with "()". */ - for (link = head; link != NULL; link = g_list_next (link)) { - CamelCipherCertInfo *cinfo = link->data; + loader = gdk_pixbuf_loader_new (); + gdk_pixbuf_loader_write (loader, (guchar *) anim->data, anim->len, NULL); + gdk_pixbuf_loader_close (loader, NULL); + animation = gdk_pixbuf_loader_get_animation (loader); + if (!animation) { - if ((cinfo->name != NULL && *cinfo->name != '\0') || - (cinfo->email != NULL && *cinfo->email != '\0')) - g_queue_push_tail (&valid, cinfo); + *frame = g_memdup (anim->data, anim->len); + *len = anim->len; + g_object_unref (loader); + return; } - if (g_queue_is_empty (&valid)) + /* Extract first frame */ + frame_buf = gdk_pixbuf_animation_get_static_image (animation); + if (!frame_buf) { + *frame = g_memdup (anim->data, anim->len); + *len = anim->len; + g_object_unref (loader); + g_object_unref (animation); return; - - g_string_append (output_buffer, " ("); - - while (!g_queue_is_empty (&valid)) { - CamelCipherCertInfo *cinfo; - - cinfo = g_queue_pop_head (&valid); - - if (cinfo->name != NULL && *cinfo->name != '\0') { - g_string_append (output_buffer, cinfo->name); - - if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, " <"); - g_string_append (output_buffer, cinfo->email); - g_string_append (output_buffer, ">"); - } - - } else if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, cinfo->email); - } - - if (!g_queue_is_empty (&valid)) - g_string_append (output_buffer, ", "); } - g_string_append_c (output_buffer, ')'); -} - -/* 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; + /* Unforunatelly, GdkPixbuf cannot save to GIF, but WebKit does not + * have any trouble displaying PNG image despite the part having + * image/gif mime-type */ + gdk_pixbuf_save_to_buffer (frame_buf, frame, len, "png", NULL, NULL); - return camel_data_cache_get ( - emfh_http_cache, EMFH_HTTP_CACHE_PATH, image_uri, NULL); + g_object_unref (loader); } - diff --git a/mail/em-format-html.h b/mail/em-format-html.h index bc6a171255..9749c37f31 100644 --- a/mail/em-format-html.h +++ b/mail/em-format-html.h @@ -30,7 +30,6 @@ #include #include -#include #include /* Standard GObject macros */ @@ -57,6 +56,7 @@ G_BEGIN_DECLS typedef struct _EMFormatHTML EMFormatHTML; typedef struct _EMFormatHTMLClass EMFormatHTMLClass; typedef struct _EMFormatHTMLPrivate EMFormatHTMLPrivate; +typedef struct _EMFormatWidgetPURI EMFormatWidgetPURI; enum _em_format_html_header_flags { EM_FORMAT_HTML_HEADER_TO = 1 << 0, @@ -64,16 +64,6 @@ enum _em_format_html_header_flags { EM_FORMAT_HTML_HEADER_BCC = 1 << 2 }; -typedef enum { - EM_FORMAT_HTML_STATE_NONE = 0, - EM_FORMAT_HTML_STATE_RENDERING -} EMFormatHTMLState; - -typedef enum { - EM_FORMAT_HTML_HEADERS_STATE_EXPANDED = 0, /* Default value */ - EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED -} EMFormatHTMLHeadersState; - typedef enum { EM_FORMAT_HTML_COLOR_BODY, /* header area background */ EM_FORMAT_HTML_COLOR_CITATION, /* citation font color */ @@ -84,94 +74,13 @@ typedef enum { EM_FORMAT_HTML_NUM_COLOR_TYPES } EMFormatHTMLColorType; -/* A HTMLJob will be executed in another thread, in sequence. - * It's job is to write to its stream, close it if successful, - * then exit. */ - -typedef struct _EMFormatHTMLJob EMFormatHTMLJob; - -typedef void (*EMFormatHTMLJobCallback) (EMFormatHTMLJob *job, - GCancellable *cancellable); - -/** - * struct _EMFormatHTMLJob - A formatting job. - * - * @format: Set by allocation function. - * @stream: Free for use by caller. - * @puri_level: Set by allocation function. - * @base: Set by allocation function, used to save state. - * @callback: This callback will always be invoked, only once, even if the user - * cancelled the display. So the callback should free any extra data - * it allocated every time it is called. - * @u: Union data, free for caller to use. - * - * This object is used to queue a long-running-task which cannot be - * processed in the primary thread. When its turn comes, the job will - * be de-queued and the @callback invoked to perform its processing, - * restoring various state to match the original state. This is used - * for image loading and other internal tasks. - * - * This object is struct-subclassable. Only em_format_html_job_new() - * may be used to allocate these. - **/ -struct _EMFormatHTMLJob { - EMFormatHTML *format; - CamelStream *stream; - - /* We need to track the state of the visibility tree at - * the point this uri was generated */ - GNode *puri_level; - CamelURL *base; - - EMFormatHTMLJobCallback callback; - union { - gchar *uri; - CamelMedium *msg; - EMFormatPURI *puri; - GNode *puri_level; - gpointer data; - } u; -}; - -/* Pending object (classid: url) */ -typedef struct _EMFormatHTMLPObject EMFormatHTMLPObject; - -typedef gboolean - (*EMFormatHTMLPObjectFunc) (EMFormatHTML *md, - GtkHTMLEmbedded *eb, - EMFormatHTMLPObject *pobject); - -/** - * struct _EMFormatHTMLPObject - Pending object. - * - * @free: Invoked when the object is no longer needed. - * @format: The parent formatter. - * @classid: The assigned class id as passed to add_pobject(). - * @func: Callback function. - * @part: The part as passed to add_pobject(). - * - * This structure is used to track OBJECT tags which have been - * inserted into the HTML stream. When GtkHTML requests them the - * @func will be invoked to create the embedded widget. - * - * This object is struct-subclassable. Only - * em_format_html_add_pobject() may be used to allocate these. - **/ -struct _EMFormatHTMLPObject { - void (*free)(EMFormatHTMLPObject *); - EMFormatHTML *format; - - gchar *classid; - - EMFormatHTMLPObjectFunc func; - CamelMimePart *part; -}; - #define EM_FORMAT_HTML_HEADER_NOCOLUMNS (EM_FORMAT_HEADER_LAST) /* header already in html format */ #define EM_FORMAT_HTML_HEADER_HTML (EM_FORMAT_HEADER_LAST<<1) #define EM_FORMAT_HTML_HEADER_NODEC (EM_FORMAT_HEADER_LAST<<2) +#define EM_FORMAT_HTML_HEADER_NOLINKS (EM_FORMAT_HEADER_LAST<<3) +#define EM_FORMAT_HTML_HEADER_HIDDEN (EM_FORMAT_HEADER_LAST<<4) #define EM_FORMAT_HTML_HEADER_LAST (EM_FORMAT_HEADER_LAST<<8) @@ -197,14 +106,13 @@ struct _EMFormatHTMLPObject { * @load_http:2: * @load_http_now:1: * @mark_citations:1: - * @simple_headers:1: * @hide_headers:1: * @show_icon:1: * * Most of these fields are private or read-only. * * The base HTML formatter object. This object drives HTML generation - * into a GtkHTML parser. It also handles text to HTML conversion, + * into a WebKit parser. It also handles text to HTML conversion, * multipart/related objects and inline images. **/ struct _EMFormatHTML { @@ -216,12 +124,9 @@ struct _EMFormatHTML { GSList *headers; guint32 text_html_flags; /* default flags for text to html conversion */ - guint simple_headers:1; /* simple header format, no box/table */ guint hide_headers:1; /* no headers at all */ guint show_icon:1; /* show an icon when the sender used Evo */ guint32 header_wrap_flags; - - EMFormatHTMLState state; /* actual state of the object */ }; struct _EMFormatHTMLClass { @@ -231,8 +136,6 @@ struct _EMFormatHTMLClass { }; GType em_format_html_get_type (void); -EWebView * em_format_html_get_web_view (EMFormatHTML *efh); -void em_format_html_load_images (EMFormatHTML *efh); void em_format_html_get_color (EMFormatHTML *efh, EMFormatHTMLColorType type, GdkColor *color); @@ -260,65 +163,61 @@ gboolean em_format_html_get_show_sender_photo void em_format_html_set_show_sender_photo (EMFormatHTML *efh, gboolean show_sender_photo); - -/* retrieves a pseudo-part icon wrapper for a file */ -CamelMimePart * em_format_html_file_part (EMFormatHTML *efh, - const gchar *mime_type, - const gchar *filename, - GCancellable *cancellable); - -/* for implementers */ -EMFormatHTMLPObject * - em_format_html_add_pobject (EMFormatHTML *efh, - gsize size, - const gchar *classid, - CamelMimePart *part, - EMFormatHTMLPObjectFunc func); -EMFormatHTMLPObject * - em_format_html_find_pobject (EMFormatHTML *efh, - const gchar *classid); -EMFormatHTMLPObject * - em_format_html_find_pobject_func - (EMFormatHTML *efh, - CamelMimePart *part, - EMFormatHTMLPObjectFunc func); -void em_format_html_remove_pobject (EMFormatHTML *efh, - EMFormatHTMLPObject *pobject); -void em_format_html_clear_pobject (EMFormatHTML *efh); -EMFormatHTMLJob * - em_format_html_job_new (EMFormatHTML *efh, - EMFormatHTMLJobCallback callback, - gpointer data); -void em_format_html_job_queue (EMFormatHTML *efh, - EMFormatHTMLJob *job); +gboolean em_format_html_get_animate_images + (EMFormatHTML *efh); +void em_format_html_set_animate_images + (EMFormatHTML *efh, + gboolean animate_images); void em_format_html_clone_sync (CamelFolder *folder, const gchar *message_uid, CamelMimeMessage *message, EMFormatHTML *efh, EMFormat *source); - gboolean em_format_html_get_show_real_date (EMFormatHTML *efh); void em_format_html_set_show_real_date (EMFormatHTML *efh, gboolean show_real_date); -EMFormatHTMLHeadersState - em_format_html_get_headers_state - (EMFormatHTML *efh); -void em_format_html_set_headers_state - (EMFormatHTML *efh, - EMFormatHTMLHeadersState state); -gboolean em_format_html_get_headers_collapsable - (EMFormatHTML *efh); -void em_format_html_set_headers_collapsable - (EMFormatHTML *efh, - gboolean collapsable); + +/* retrieves a pseudo-part icon wrapper for a file */ +CamelMimePart * em_format_html_file_part (EMFormatHTML *efh, + const gchar *mime_type, + const gchar *filename, + GCancellable *cancellable); + void em_format_html_format_cert_infos (GQueue *cert_infos, GString *output_buffer); -CamelStream * em_format_html_get_cached_image (EMFormatHTML *efh, - const gchar *image_uri); +void em_format_html_format_message (EMFormatHTML *efh, + CamelStream *stream, + GCancellable *cancellable); + +void em_format_html_format_message_part + (EMFormatHTML *efh, + const gchar *part_id, + CamelStream *stream, + GCancellable *cancellable); + +void em_format_html_format_headers (EMFormatHTML *efh, + CamelStream *stream, + CamelMedium *part, + gboolean all_headers, + GCancellable *cancellable); +void em_format_html_format_header (EMFormat *emf, + GString *buffer, + CamelMedium *part, + struct _camel_header_raw *header, + guint32 flags, + const gchar *charset); + +gboolean em_format_html_can_load_images (EMFormatHTML *efh); + +void em_format_html_animation_extract_frame + (const GByteArray *anim, + gchar **frame, + gsize *len); + G_END_DECLS #endif /* EM_FORMAT_HTML_H */ diff --git a/mail/em-html-stream.c b/mail/em-html-stream.c deleted file mode 100644 index a633946541..0000000000 --- a/mail/em-html-stream.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see - * - * - * Authors: - * Jeffrey Stedfast - * Michael Zucchi - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include "em-html-stream.h" - -#define d(x) - -G_DEFINE_TYPE (EMHTMLStream, em_html_stream, EM_TYPE_SYNC_STREAM) - -static void -html_stream_cleanup (EMHTMLStream *emhs) -{ - if (emhs->sync.cancel && emhs->html_stream) - gtk_html_stream_close ( - emhs->html_stream, GTK_HTML_STREAM_ERROR); - - emhs->html_stream = NULL; - emhs->sync.cancel = TRUE; - g_signal_handler_disconnect (emhs->html, emhs->destroy_id); - g_object_unref (emhs->html); - emhs->html = NULL; -} - -static void -html_stream_gtkhtml_destroy (GtkHTML *html, - EMHTMLStream *emhs) -{ - emhs->sync.cancel = TRUE; - html_stream_cleanup (emhs); -} - -static void -html_stream_dispose (GObject *object) -{ - EMHTMLStream *emhs = EM_HTML_STREAM (object); - - if (emhs->html_stream) { - /* set 'in finalize' flag */ - camel_stream_close (CAMEL_STREAM (emhs), NULL, NULL); - } -} - -static gssize -html_stream_sync_write (CamelStream *stream, - const gchar *buffer, - gsize n, - GError **error) -{ - EMHTMLStream *emhs = EM_HTML_STREAM (stream); - - if (emhs->html == NULL) { - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("No HTML stream available")); - return -1; - } - - if (emhs->html_stream == NULL) - emhs->html_stream = gtk_html_begin_full ( - emhs->html, NULL, NULL, emhs->flags); - - gtk_html_stream_write (emhs->html_stream, buffer, n); - - return (gssize) n; -} - -static gint -html_stream_sync_flush (CamelStream *stream, - GError **error) -{ - EMHTMLStream *emhs = (EMHTMLStream *) stream; - - if (emhs->html_stream == NULL) { - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("No HTML stream available")); - return -1; - } - - gtk_html_flush (emhs->html); - - return 0; -} - -static gint -html_stream_sync_close (CamelStream *stream, - GError **error) -{ - EMHTMLStream *emhs = (EMHTMLStream *) stream; - - if (emhs->html_stream == NULL) { - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("No HTML stream available")); - return -1; - } - - gtk_html_stream_close (emhs->html_stream, GTK_HTML_STREAM_OK); - html_stream_cleanup (emhs); - - return 0; -} - -static void -em_html_stream_class_init (EMHTMLStreamClass *class) -{ - GObjectClass *object_class; - EMSyncStreamClass *sync_stream_class; - - object_class = G_OBJECT_CLASS (class); - object_class->dispose = html_stream_dispose; - - sync_stream_class = EM_SYNC_STREAM_CLASS (class); - sync_stream_class->sync_write = html_stream_sync_write; - sync_stream_class->sync_flush = html_stream_sync_flush; - sync_stream_class->sync_close = html_stream_sync_close; -} - -static void -em_html_stream_init (EMHTMLStream *emhs) -{ -} - -/* TODO: Could pass NULL for html_stream, and do a gtk_html_begin - * on first data -> less flashing */ -CamelStream * -em_html_stream_new (GtkHTML *html, - GtkHTMLStream *html_stream) -{ - EMHTMLStream *new; - - g_return_val_if_fail (GTK_IS_HTML (html), NULL); - - new = g_object_new (EM_TYPE_HTML_STREAM, NULL); - new->html_stream = html_stream; - new->html = g_object_ref (html); - new->flags = 0; - new->destroy_id = g_signal_connect ( - html, "destroy", - G_CALLBACK (html_stream_gtkhtml_destroy), new); - - em_sync_stream_set_buffer_size (&new->sync, 8192); - - return CAMEL_STREAM (new); -} - -void -em_html_stream_set_flags (EMHTMLStream *emhs, - GtkHTMLBeginFlags flags) -{ - g_return_if_fail (EM_IS_HTML_STREAM (emhs)); - - emhs->flags = flags; -} diff --git a/mail/em-html-stream.h b/mail/em-html-stream.h deleted file mode 100644 index 24d32f76d8..0000000000 --- a/mail/em-html-stream.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see - * - * - * Authors: - * Jeffrey Stedfast - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef EM_HTML_STREAM_H -#define EM_HTML_STREAM_H - -#include -#include -#include - -/* Standard GObject macros */ -#define EM_TYPE_HTML_STREAM \ - (em_html_stream_get_type ()) -#define EM_HTML_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_HTML_STREAM, EMHTMLStream)) -#define EM_HTML_STREAM_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_HTML_STREAM, EMHTMLStreamClass)) -#define EM_IS_HTML_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_HTML_STREAM)) -#define EM_IS_HTML_STREAM_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_HTML_STREAM)) -#define EM_HTML_STREAM_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_HTML_STREAM, EMHTMLStreamClass)) - -G_BEGIN_DECLS - -typedef struct _EMHTMLStream EMHTMLStream; -typedef struct _EMHTMLStreamClass EMHTMLStreamClass; - -struct _EMHTMLStream { - EMSyncStream sync; - - guint destroy_id; - GtkHTML *html; - GtkHTMLStream *html_stream; - GtkHTMLBeginFlags flags; -}; - -struct _EMHTMLStreamClass { - EMSyncStreamClass parent_class; - -}; - -GType em_html_stream_get_type (void); -CamelStream * em_html_stream_new (GtkHTML *html, - GtkHTMLStream *html_stream); -void em_html_stream_set_flags (EMHTMLStream *emhs, - GtkHTMLBeginFlags flags); - -G_END_DECLS - -#endif /* EM_HTML_STREAM_H */ diff --git a/mail/em-utils.c b/mail/em-utils.c index 4d74b80a13..a9b57125a2 100644 --- a/mail/em-utils.c +++ b/mail/em-utils.c @@ -72,9 +72,11 @@ #include "e-mail-tag-editor.h" #include "em-composer-utils.h" -#include "em-format-quote.h" +#include "em-format-html-display.h" #include "em-format-html-print.h" #include "em-utils.h" +#include "e-mail-printer.h" +#include "em-format/em-format-quote.h" /* XXX This is a dirty hack on a dirty hack. We really need * to rework or get rid of the functions that use this. */ @@ -380,7 +382,7 @@ em_utils_flag_for_followup (EMailReader *reader, EMailBackend *backend; EShellSettings *shell_settings; EShellBackend *shell_backend; - EMFormatHTML *formatter; + EMailDisplay *display; GtkWidget *editor; GtkWindow *window; CamelTag *tags; @@ -470,8 +472,8 @@ em_utils_flag_for_followup (EMailReader *reader, camel_folder_thaw (folder); camel_tag_list_free (&tags); - formatter = e_mail_reader_get_formatter (reader); - em_format_queue_redraw (EM_FORMAT (formatter)); + display = e_mail_reader_get_mail_display (reader); + e_mail_display_reload (display); exit: /* XXX We shouldn't be freeing this. */ @@ -616,26 +618,44 @@ em_utils_write_messages_to_stream (CamelFolder *folder, return res; } +static void +do_print_msg_to_file (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + + EMFormatHTML *efh = EM_FORMAT_HTML (source); + gchar *filename = user_data; + + EMailPrinter *printer; + + printer = e_mail_printer_new (efh); + e_mail_printer_set_export_filename (printer, filename); + g_signal_connect_swapped (printer, "done", + G_CALLBACK (g_object_unref), printer); + + e_mail_printer_print (printer, TRUE, NULL); + + g_object_unref (efh); +} + static gboolean em_utils_print_messages_to_file (CamelFolder *folder, const gchar *uid, const gchar *filename) { - EMFormatHTMLPrint *efhp; + EMFormatHTMLDisplay *efhd; CamelMimeMessage *message; message = camel_folder_get_message_sync (folder, uid, NULL, NULL); if (message == NULL) return FALSE; - efhp = em_format_html_print_new (NULL, GTK_PRINT_OPERATION_ACTION_EXPORT); - efhp->export_filename = g_strdup (filename); - efhp->async = FALSE; + efhd = em_format_html_display_new (); + ((EMFormat *) efhd)->message_uid = g_strdup (uid); - em_format_html_print_message (efhp, message, folder, uid); - - g_object_unref (efhp); - g_object_unref (message); + em_format_parse_async ((EMFormat *) efhd, message, folder, NULL, + (GAsyncReadyCallback) do_print_msg_to_file, g_strdup (filename)); return TRUE; } @@ -1173,7 +1193,7 @@ em_utils_message_to_html (CamelMimeMessage *message, camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (mem), buf); emfq = em_format_quote_new (credits, mem, flags); - ((EMFormat *) emfq)->composer = TRUE; + em_format_set_composer ((EMFormat *) emfq, TRUE); if (!source) { GSettings *settings; @@ -1189,10 +1209,30 @@ em_utils_message_to_html (CamelMimeMessage *message, } /* FIXME Not passing a GCancellable here. */ - em_format_format_clone ( - EM_FORMAT (emfq), NULL, NULL, message, source, NULL); - if (validity_found) - *validity_found = ((EMFormat *)emfq)->validity_found; + em_format_parse (EM_FORMAT (emfq), message, NULL, NULL); + + if (validity_found) { + GList *iter; + EMFormat *emf = (EMFormat *) emfq; + + if (validity_found) + *validity_found = 0; + + /* Return all found validities */ + for (iter = emf->mail_part_list; iter; iter = iter->next) { + + EMFormatPURI *puri = iter->data; + if (!puri) + continue; + + if (*validity_found && puri->validity_type) + *validity_found |= puri->validity_type; + } + + } + + em_format_quote_write (emfq, mem, NULL); + g_object_unref (emfq); if (append && *append) diff --git a/mail/mail.error.xml b/mail/mail.error.xml index 2a516d9bfa..b5a714ff18 100644 --- a/mail/mail.error.xml +++ b/mail/mail.error.xml @@ -520,5 +520,10 @@ An mbox account will be created to preserve the old mbox folders. You can delete <_secondary xml:space="preserve">The attachment named {0} is a hidden file and may contain sensitive data. Please review it before sending. + + <_primary>Printing failed. + <_secondary>The printer replied "{0}". + + -- cgit v1.2.3