aboutsummaryrefslogtreecommitdiffstats
path: root/em-format/e-mail-formatter.c
diff options
context:
space:
mode:
Diffstat (limited to 'em-format/e-mail-formatter.c')
-rw-r--r--em-format/e-mail-formatter.c1435
1 files changed, 1435 insertions, 0 deletions
diff --git a/em-format/e-mail-formatter.c b/em-format/e-mail-formatter.c
new file mode 100644
index 0000000000..f3f09670d4
--- /dev/null
+++ b/em-format/e-mail-formatter.c
@@ -0,0 +1,1435 @@
+/*
+ * e-mail-formatter.c
+ *
+ * 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-formatter.h"
+
+#include "e-mail-formatter-extension.h"
+#include "e-mail-formatter-utils.h"
+#include "e-mail-part.h"
+
+#include <e-util/e-util.h>
+#include <libebackend/libebackend.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+
+#include "libemail-engine/e-mail-enumtypes.h"
+
+#define d(x)
+
+#define E_MAIL_FORMATTER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))\
+
+#define STYLESHEET_URI \
+ "evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css"
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _EMailFormatterPrivate {
+ EMailImageLoadingPolicy image_loading_policy;
+
+ gboolean show_sender_photo;
+ gboolean show_real_date;
+ gboolean animate_images;
+
+ GMutex property_lock;
+
+ gchar *charset;
+ gchar *default_charset;
+};
+
+struct _AsyncContext {
+ CamelStream *stream;
+ EMailPartList *part_list;
+ EMailFormatterHeaderFlags flags;
+ EMailFormatterMode mode;
+};
+
+/* internal formatter extensions */
+GType e_mail_formatter_attachment_get_type (void);
+GType e_mail_formatter_attachment_bar_get_type (void);
+GType e_mail_formatter_error_get_type (void);
+GType e_mail_formatter_headers_get_type (void);
+GType e_mail_formatter_image_get_type (void);
+GType e_mail_formatter_message_rfc822_get_type (void);
+GType e_mail_formatter_secure_button_get_type (void);
+GType e_mail_formatter_source_get_type (void);
+GType e_mail_formatter_text_enriched_get_type (void);
+GType e_mail_formatter_text_html_get_type (void);
+GType e_mail_formatter_text_plain_get_type (void);
+
+void e_mail_formatter_internal_extensions_load (EMailExtensionRegistry *ereg);
+
+static gpointer e_mail_formatter_parent_class = 0;
+
+enum {
+ PROP_0,
+ PROP_ANIMATE_IMAGES,
+ PROP_BODY_COLOR,
+ PROP_CHARSET,
+ PROP_CITATION_COLOR,
+ PROP_CONTENT_COLOR,
+ PROP_DEFAULT_CHARSET,
+ PROP_FRAME_COLOR,
+ PROP_HEADER_COLOR,
+ PROP_IMAGE_LOADING_POLICY,
+ PROP_MARK_CITATIONS,
+ PROP_SHOW_REAL_DATE,
+ PROP_SHOW_SENDER_PHOTO,
+ PROP_TEXT_COLOR
+};
+
+enum {
+ NEED_REDRAW,
+ LAST_SIGNAL
+};
+
+static gint signals[LAST_SIGNAL];
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+ g_clear_object (&async_context->part_list);
+ g_clear_object (&async_context->stream);
+
+ g_slice_free (AsyncContext, async_context);
+}
+
+static EMailFormatterContext *
+mail_formatter_create_context (EMailFormatter *formatter,
+ EMailPartList *part_list,
+ EMailFormatterMode mode,
+ EMailFormatterHeaderFlags flags)
+{
+ EMailFormatterClass *class;
+ EMailFormatterContext *context;
+
+ class = E_MAIL_FORMATTER_GET_CLASS (formatter);
+
+ g_warn_if_fail (class->context_size >= sizeof (EMailFormatterContext));
+
+ context = g_malloc0 (class->context_size);
+ context->part_list = g_object_ref (part_list);
+ context->mode = mode;
+ context->flags = flags;
+
+ return context;
+}
+
+static void
+mail_formatter_free_context (EMailFormatterContext *context)
+{
+ if (context->part_list != NULL)
+ g_object_unref (context->part_list);
+
+ g_free (context);
+}
+
+static void
+e_mail_formatter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ANIMATE_IMAGES:
+ e_mail_formatter_set_animate_images (
+ E_MAIL_FORMATTER (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_BODY_COLOR:
+ e_mail_formatter_set_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_BODY,
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_CHARSET:
+ e_mail_formatter_set_charset (
+ E_MAIL_FORMATTER (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_CITATION_COLOR:
+ e_mail_formatter_set_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_CITATION,
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_CONTENT_COLOR:
+ e_mail_formatter_set_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_CONTENT,
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_DEFAULT_CHARSET:
+ e_mail_formatter_set_default_charset (
+ E_MAIL_FORMATTER (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_FRAME_COLOR:
+ e_mail_formatter_set_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_FRAME,
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_HEADER_COLOR:
+ e_mail_formatter_set_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_HEADER,
+ g_value_get_boxed (value));
+ return;
+
+ case PROP_IMAGE_LOADING_POLICY:
+ e_mail_formatter_set_image_loading_policy (
+ E_MAIL_FORMATTER (object),
+ g_value_get_enum (value));
+ return;
+
+ case PROP_MARK_CITATIONS:
+ e_mail_formatter_set_mark_citations (
+ E_MAIL_FORMATTER (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SHOW_REAL_DATE:
+ e_mail_formatter_set_show_real_date (
+ E_MAIL_FORMATTER (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SHOW_SENDER_PHOTO:
+ e_mail_formatter_set_show_sender_photo (
+ E_MAIL_FORMATTER (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_TEXT_COLOR:
+ e_mail_formatter_set_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_TEXT,
+ g_value_get_boxed (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_mail_formatter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ANIMATE_IMAGES:
+ g_value_set_boolean (
+ value,
+ e_mail_formatter_get_animate_images (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_BODY_COLOR:
+ g_value_set_boxed (
+ value,
+ e_mail_formatter_get_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_BODY));
+ return;
+
+ case PROP_CHARSET:
+ g_value_take_string (
+ value,
+ e_mail_formatter_dup_charset (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_CITATION_COLOR:
+ g_value_set_boxed (
+ value,
+ e_mail_formatter_get_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_CITATION));
+ return;
+
+ case PROP_CONTENT_COLOR:
+ g_value_set_boxed (
+ value,
+ e_mail_formatter_get_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_CONTENT));
+ return;
+
+ case PROP_DEFAULT_CHARSET:
+ g_value_take_string (
+ value,
+ e_mail_formatter_dup_default_charset (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_FRAME_COLOR:
+ g_value_set_boxed (
+ value,
+ e_mail_formatter_get_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_FRAME));
+ return;
+
+ case PROP_HEADER_COLOR:
+ g_value_set_boxed (
+ value,
+ e_mail_formatter_get_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_HEADER));
+ return;
+
+ case PROP_IMAGE_LOADING_POLICY:
+ g_value_set_enum (
+ value,
+ e_mail_formatter_get_image_loading_policy (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_MARK_CITATIONS:
+ g_value_set_boolean (
+ value,
+ e_mail_formatter_get_mark_citations (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_SHOW_REAL_DATE:
+ g_value_set_boolean (
+ value,
+ e_mail_formatter_get_show_real_date (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_SHOW_SENDER_PHOTO:
+ g_value_set_boolean (
+ value,
+ e_mail_formatter_get_show_sender_photo (
+ E_MAIL_FORMATTER (object)));
+ return;
+
+ case PROP_TEXT_COLOR:
+ g_value_set_boxed (
+ value,
+ e_mail_formatter_get_color (
+ E_MAIL_FORMATTER (object),
+ E_MAIL_FORMATTER_COLOR_TEXT));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_mail_formatter_finalize (GObject *object)
+{
+ EMailFormatterPrivate *priv;
+
+ priv = E_MAIL_FORMATTER_GET_PRIVATE (object);
+
+ g_free (priv->charset);
+ g_free (priv->default_charset);
+
+ g_mutex_clear (&priv->property_lock);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_mail_formatter_parent_class)->finalize (object);
+}
+
+static void
+e_mail_formatter_constructed (GObject *object)
+{
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_mail_formatter_parent_class)->constructed (object);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static void
+mail_formatter_run (EMailFormatter *formatter,
+ EMailFormatterContext *context,
+ CamelStream *stream,
+ GCancellable *cancellable)
+{
+ GQueue queue = G_QUEUE_INIT;
+ GList *head, *link;
+ gchar *hdr;
+
+ hdr = e_mail_formatter_get_html_header (formatter);
+ camel_stream_write_string (stream, hdr, cancellable, NULL);
+ g_free (hdr);
+
+ e_mail_part_list_queue_parts (context->part_list, NULL, &queue);
+
+ head = g_queue_peek_head_link (&queue);
+
+ for (link = head; link != NULL; link = g_list_next (link)) {
+ EMailPart *part = link->data;
+ const gchar *part_id;
+ gboolean ok;
+
+ part_id = e_mail_part_get_id (part);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ break;
+
+ if (part->is_hidden && !part->is_error) {
+ if (e_mail_part_id_has_suffix (part, ".rfc822")) {
+ link = e_mail_formatter_find_rfc822_end_iter (link);
+ }
+
+ if (link == NULL)
+ break;
+
+ continue;
+ }
+
+ /* Force formatting as source if needed */
+ if (context->mode != E_MAIL_FORMATTER_MODE_SOURCE) {
+ const gchar *mime_type;
+
+ mime_type = e_mail_part_get_mime_type (part);
+ if (mime_type == NULL)
+ continue;
+
+ ok = e_mail_formatter_format_as (
+ formatter, context, part, stream,
+ mime_type, cancellable);
+
+ /* If the written part was message/rfc822 then
+ * jump to the end of the message, because content
+ * of the whole message has been formatted by
+ * message_rfc822 formatter */
+ if (ok && e_mail_part_id_has_suffix (part, ".rfc822")) {
+ link = e_mail_formatter_find_rfc822_end_iter (link);
+
+ if (link == NULL)
+ break;
+
+ continue;
+ }
+
+ } else {
+ ok = FALSE;
+ }
+
+ if (!ok) {
+ /* We don't want to source these */
+ if (e_mail_part_id_has_suffix (part, ".headers") ||
+ e_mail_part_id_has_suffix (part, "attachment-bar"))
+ continue;
+
+ e_mail_formatter_format_as (
+ formatter, context, part, stream,
+ "application/vnd.evolution.source", cancellable);
+
+ /* .message is the entire message. There's nothing more
+ * to be written. */
+ if (g_strcmp0 (part_id, ".message") == 0)
+ break;
+
+ /* If we just wrote source of a rfc822 message, then jump
+ * behind the message (otherwise source of all parts
+ * would be rendered twice) */
+ if (e_mail_part_id_has_suffix (part, ".rfc822")) {
+
+ do {
+ part = link->data;
+ if (e_mail_part_id_has_suffix (part, ".rfc822.end"))
+ break;
+
+ link = g_list_next (link);
+ } while (link != NULL);
+
+ if (link == NULL)
+ break;
+ }
+ }
+ }
+
+ while (!g_queue_is_empty (&queue))
+ g_object_unref (g_queue_pop_head (&queue));
+
+ camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
+}
+
+static void
+mail_formatter_update_style (EMailFormatter *formatter,
+ GtkStateFlags state)
+{
+ GtkStyleContext *style_context;
+ GtkWidgetPath *widget_path;
+ GdkRGBA rgba;
+
+ g_object_freeze_notify (G_OBJECT (formatter));
+
+ /* derive colors from top-level window */
+ style_context = gtk_style_context_new ();
+ widget_path = gtk_widget_path_new ();
+ gtk_widget_path_append_type (widget_path, GTK_TYPE_WINDOW);
+ gtk_style_context_set_path (style_context, widget_path);
+ gtk_style_context_invalidate (style_context);
+
+ gtk_style_context_save (style_context);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_TOOLBAR);
+
+ gtk_style_context_get_background_color (style_context, state, &rgba);
+ e_mail_formatter_set_color (
+ formatter, E_MAIL_FORMATTER_COLOR_BODY, &rgba);
+
+ rgba.red *= 0.8;
+ rgba.green *= 0.8;
+ rgba.blue *= 0.8;
+ e_mail_formatter_set_color (
+ formatter, E_MAIL_FORMATTER_COLOR_FRAME, &rgba);
+
+ gtk_style_context_restore (style_context);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ENTRY);
+
+ gtk_style_context_get_color (style_context, state, &rgba);
+ e_mail_formatter_set_color (
+ formatter, E_MAIL_FORMATTER_COLOR_HEADER, &rgba);
+
+ gtk_style_context_get_background_color (
+ style_context, state | GTK_STATE_FLAG_FOCUSED, &rgba);
+ e_mail_formatter_set_color (
+ formatter, E_MAIL_FORMATTER_COLOR_CONTENT, &rgba);
+
+ gtk_style_context_get_color (
+ style_context, state | GTK_STATE_FLAG_FOCUSED, &rgba);
+ e_mail_formatter_set_color (
+ formatter, E_MAIL_FORMATTER_COLOR_TEXT, &rgba);
+
+ gtk_widget_path_free (widget_path);
+ g_object_unref (style_context);
+
+ g_object_thaw_notify (G_OBJECT (formatter));
+}
+
+static void
+e_mail_formatter_base_init (EMailFormatterClass *class)
+{
+ /* Register internal extensions. */
+ g_type_ensure (e_mail_formatter_attachment_get_type ());
+ g_type_ensure (e_mail_formatter_attachment_bar_get_type ());
+ g_type_ensure (e_mail_formatter_error_get_type ());
+ g_type_ensure (e_mail_formatter_headers_get_type ());
+ g_type_ensure (e_mail_formatter_image_get_type ());
+ g_type_ensure (e_mail_formatter_message_rfc822_get_type ());
+ g_type_ensure (e_mail_formatter_secure_button_get_type ());
+ g_type_ensure (e_mail_formatter_source_get_type ());
+ g_type_ensure (e_mail_formatter_text_enriched_get_type ());
+ g_type_ensure (e_mail_formatter_text_html_get_type ());
+ g_type_ensure (e_mail_formatter_text_plain_get_type ());
+
+ class->extension_registry = g_object_new (
+ E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, NULL);
+
+ e_mail_formatter_extension_registry_load (
+ class->extension_registry,
+ E_TYPE_MAIL_FORMATTER_EXTENSION);
+
+ e_extensible_load_extensions (
+ E_EXTENSIBLE (class->extension_registry));
+
+ class->text_html_flags =
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES |
+ CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+}
+
+static void
+e_mail_formatter_base_finalize (EMailFormatterClass *class)
+{
+ g_object_unref (class->extension_registry);
+}
+
+static void
+e_mail_formatter_class_init (EMailFormatterClass *class)
+{
+ GObjectClass *object_class;
+ GdkRGBA *rgba;
+
+ e_mail_formatter_parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EMailFormatterPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = e_mail_formatter_set_property;
+ object_class->get_property = e_mail_formatter_get_property;
+ object_class->finalize = e_mail_formatter_finalize;
+ object_class->constructed = e_mail_formatter_constructed;
+
+ class->context_size = sizeof (EMailFormatterContext);
+ class->run = mail_formatter_run;
+ class->update_style = mail_formatter_update_style;
+
+ rgba = &class->colors[E_MAIL_FORMATTER_COLOR_BODY];
+ gdk_rgba_parse (rgba, "#eeeeee");
+
+ rgba = &class->colors[E_MAIL_FORMATTER_COLOR_CONTENT];
+ gdk_rgba_parse (rgba, "#ffffff");
+
+ rgba = &class->colors[E_MAIL_FORMATTER_COLOR_FRAME];
+ gdk_rgba_parse (rgba, "#3f3f3f");
+
+ rgba = &class->colors[E_MAIL_FORMATTER_COLOR_HEADER];
+ gdk_rgba_parse (rgba, "#eeeeee");
+
+ rgba = &class->colors[E_MAIL_FORMATTER_COLOR_TEXT];
+ gdk_rgba_parse (rgba, "#000000");
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ANIMATE_IMAGES,
+ g_param_spec_boolean (
+ "animate-images",
+ "Animate images",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_BODY_COLOR,
+ g_param_spec_boxed (
+ "body-color",
+ "Body Color",
+ NULL,
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CHARSET,
+ g_param_spec_string (
+ "charset",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CITATION_COLOR,
+ g_param_spec_boxed (
+ "citation-color",
+ "Citation Color",
+ NULL,
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CONTENT_COLOR,
+ g_param_spec_boxed (
+ "content-color",
+ "Content Color",
+ NULL,
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DEFAULT_CHARSET,
+ g_param_spec_string (
+ "default-charset",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_FRAME_COLOR,
+ g_param_spec_boxed (
+ "frame-color",
+ "Frame Color",
+ NULL,
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HEADER_COLOR,
+ g_param_spec_boxed (
+ "header-color",
+ "Header Color",
+ NULL,
+ GDK_TYPE_RGBA,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ 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_NEVER,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MARK_CITATIONS,
+ g_param_spec_boolean (
+ "mark-citations",
+ "Mark Citations",
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ 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 |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SHOW_SENDER_PHOTO,
+ g_param_spec_boolean (
+ "show-sender-photo",
+ "Show Sender Photo",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ 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 |
+ G_PARAM_STATIC_STRINGS));
+
+ signals[NEED_REDRAW] = g_signal_new (
+ "need-redraw",
+ E_TYPE_MAIL_FORMATTER,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EMailFormatterClass, need_redraw),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+e_mail_formatter_init (EMailFormatter *formatter)
+{
+ formatter->priv = E_MAIL_FORMATTER_GET_PRIVATE (formatter);
+
+ g_mutex_init (&formatter->priv->property_lock);
+}
+
+static void
+e_mail_formatter_extensible_interface_init (EExtensibleInterface *interface)
+{
+
+}
+
+EMailFormatter *
+e_mail_formatter_new (void)
+{
+ return g_object_new (E_TYPE_MAIL_FORMATTER, NULL);
+}
+
+GType
+e_mail_formatter_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ const GTypeInfo type_info = {
+ sizeof (EMailFormatterClass),
+ (GBaseInitFunc) e_mail_formatter_base_init,
+ (GBaseFinalizeFunc) e_mail_formatter_base_finalize,
+ (GClassInitFunc) e_mail_formatter_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMailFormatter),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_mail_formatter_init,
+ NULL /* value_table */
+ };
+
+ const GInterfaceInfo e_extensible_interface_info = {
+ (GInterfaceInitFunc) e_mail_formatter_extensible_interface_init
+ };
+
+ type = g_type_register_static (
+ G_TYPE_OBJECT,
+ "EMailFormatter", &type_info, 0);
+
+ g_type_add_interface_static (
+ type, E_TYPE_EXTENSIBLE, &e_extensible_interface_info);
+ }
+
+ return type;
+}
+
+void
+e_mail_formatter_format_sync (EMailFormatter *formatter,
+ EMailPartList *part_list,
+ CamelStream *stream,
+ EMailFormatterHeaderFlags flags,
+ EMailFormatterMode mode,
+ GCancellable *cancellable)
+{
+ EMailFormatterContext *context;
+ EMailFormatterClass *class;
+
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+ /* EMailPartList can be NULL. */
+ g_return_if_fail (CAMEL_IS_STREAM (stream));
+
+ class = E_MAIL_FORMATTER_GET_CLASS (formatter);
+ g_return_if_fail (class->run != NULL);
+
+ context = mail_formatter_create_context (
+ formatter, part_list, mode, flags);
+
+ class->run (formatter, context, stream, cancellable);
+
+ mail_formatter_free_context (context);
+}
+
+static void
+mail_formatter_format_thread (GSimpleAsyncResult *simple,
+ GObject *source_object,
+ GCancellable *cancellable)
+{
+ AsyncContext *async_context;
+
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_formatter_format_sync (
+ E_MAIL_FORMATTER (source_object),
+ async_context->part_list,
+ async_context->stream,
+ async_context->flags,
+ async_context->mode,
+ cancellable);
+}
+
+void
+e_mail_formatter_format (EMailFormatter *formatter,
+ EMailPartList *part_list,
+ CamelStream *stream,
+ EMailFormatterHeaderFlags flags,
+ EMailFormatterMode mode,
+ GAsyncReadyCallback callback,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+ EMailFormatterClass *class;
+
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+ /* EMailPartList can be NULL. */
+ g_return_if_fail (CAMEL_IS_STREAM (stream));
+
+ class = E_MAIL_FORMATTER_GET_CLASS (formatter);
+ g_return_if_fail (class->run != NULL);
+
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->stream = g_object_ref (stream);
+ async_context->flags = flags;
+ async_context->mode = mode;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (formatter), callback,
+ user_data, e_mail_formatter_format);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, async_context, (GDestroyNotify) async_context_free);
+
+ if (part_list != NULL) {
+ async_context->part_list = g_object_ref (part_list);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_formatter_format_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+ } else {
+ g_simple_async_result_complete_in_idle (simple);
+ }
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_formatter_format_finish (EMailFormatter *formatter,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (formatter),
+ e_mail_formatter_format), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_mail_formatter_format_as:
+ * @formatter: an #EMailFormatter
+ * @context: an #EMailFormatterContext
+ * @part: an #EMailPart
+ * @stream: a #CamelStream
+ * @as_mime_type: (allow-none) mime-type to use for formatting, or %NULL
+ * @cancellable: (allow-none) an optional #GCancellable
+ *
+ * Formats given @part using a @formatter extension for given mime type. When
+ * the mime type is %NULL, the function will try to lookup the best formatter
+ * for given @part by it's default mime type.
+ *
+ * Return Value: %TRUE on success, %FALSE when no suitable formatter is found or
+ * when it fails to format the part.
+ */
+gboolean
+e_mail_formatter_format_as (EMailFormatter *formatter,
+ EMailFormatterContext *context,
+ EMailPart *part,
+ CamelStream *stream,
+ const gchar *as_mime_type,
+ GCancellable *cancellable)
+{
+ EMailExtensionRegistry *extension_registry;
+ GQueue *formatters;
+ gboolean ok;
+ d (
+ gint _call_i;
+ static gint _call = 0;
+ G_LOCK_DEFINE_STATIC (_call);
+ G_LOCK (_call);
+ _call++;
+ _call_i = _call;
+ G_UNLOCK (_call)
+ );
+
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (CAMEL_IS_STREAM (stream), FALSE);
+
+ if (as_mime_type == NULL || *as_mime_type == '\0')
+ as_mime_type = e_mail_part_get_mime_type (part);
+
+ if (as_mime_type == NULL || *as_mime_type == '\0')
+ return FALSE;
+
+ extension_registry =
+ e_mail_formatter_get_extension_registry (formatter);
+ formatters = e_mail_extension_registry_get_for_mime_type (
+ extension_registry, as_mime_type);
+ if (formatters == NULL)
+ formatters = e_mail_extension_registry_get_fallback (
+ extension_registry, as_mime_type);
+
+ ok = FALSE;
+
+ d (
+ printf ("(%d) Formatting for part %s of type %s (found %d formatters)\n",
+ _call_i, part->id, as_mime_type,
+ formatters ? g_queue_get_length (formatters) : 0));
+
+ if (formatters != NULL) {
+ GList *head, *link;
+
+ head = g_queue_peek_head_link (formatters);
+
+ for (link = head; link != NULL; link = g_list_next (link)) {
+ EMailFormatterExtension *extension;
+
+ extension = link->data;
+ if (extension == NULL)
+ continue;
+
+ ok = e_mail_formatter_extension_format (
+ extension, formatter, context,
+ part, stream, cancellable);
+
+ d (
+ printf (
+ "\t(%d) trying %s...%s\n", _call_i,
+ G_OBJECT_TYPE_NAME (extension),
+ ok ? "OK" : "failed"));
+
+ if (ok)
+ break;
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * em_format_format_text:
+ * @part: an #EMailPart to decode
+ * @formatter: an #EMailFormatter
+ * @stream: Where to write the converted text
+ * @cancellable: optional #GCancellable object, or %NULL
+ *
+ * Decode/output a part's content to @stream.
+ **/
+void
+e_mail_formatter_format_text (EMailFormatter *formatter,
+ EMailPart *part,
+ CamelStream *stream,
+ GCancellable *cancellable)
+{
+ CamelStream *filter_stream;
+ CamelMimeFilter *filter;
+ const gchar *charset = NULL;
+ CamelMimeFilter *windows = NULL;
+ CamelStream *mem_stream = NULL;
+ CamelMimePart *mime_part;
+ CamelContentType *mime_type;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ mime_part = e_mail_part_ref_mime_part (part);
+ mime_type = CAMEL_DATA_WRAPPER (mime_part)->mime_type;
+
+ if (formatter->priv->charset != NULL) {
+ charset = formatter->priv->charset;
+ } else if (mime_type != NULL
+ && (charset = camel_content_type_param (mime_type, "charset"))
+ && g_ascii_strncasecmp (charset, "iso-8859-", 9) == 0) {
+ CamelStream *null;
+
+ /* Since a few Windows mailers like to claim they sent
+ * out iso-8859-# encoded text when they really sent
+ * out windows-cp125#, do some simple sanity checking
+ * before we move on... */
+
+ null = camel_stream_null_new ();
+ filter_stream = camel_stream_filter_new (null);
+ g_object_unref (null);
+
+ windows = camel_mime_filter_windows_new (charset);
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filter_stream), windows);
+
+ camel_data_wrapper_decode_to_stream_sync (
+ CAMEL_DATA_WRAPPER (mime_part),
+ filter_stream, cancellable, NULL);
+ camel_stream_flush (filter_stream, cancellable, NULL);
+ g_object_unref (filter_stream);
+
+ charset = camel_mime_filter_windows_real_charset (
+ CAMEL_MIME_FILTER_WINDOWS (windows));
+ } else if (charset == NULL) {
+ charset = formatter->priv->default_charset;
+ }
+
+ mem_stream = (CamelStream *) camel_stream_mem_new ();
+ filter_stream = camel_stream_filter_new (mem_stream);
+
+ filter = camel_mime_filter_charset_new (charset, "UTF-8");
+ if (filter != NULL) {
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filter_stream), filter);
+ g_object_unref (filter);
+ }
+
+ camel_data_wrapper_decode_to_stream_sync (
+ camel_medium_get_content (CAMEL_MEDIUM (mime_part)),
+ filter_stream, cancellable, NULL);
+ camel_stream_flush (filter_stream, cancellable, NULL);
+ g_object_unref (filter_stream);
+
+ g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
+
+ camel_stream_write_to_stream (
+ mem_stream, stream, cancellable, NULL);
+ camel_stream_flush (mem_stream, cancellable, NULL);
+
+ if (windows != NULL)
+ g_object_unref (windows);
+
+ g_object_unref (mem_stream);
+
+ g_object_unref (mime_part);
+}
+
+gchar *
+e_mail_formatter_get_html_header (EMailFormatter *formatter)
+{
+ return g_strdup_printf (
+ "<!DOCTYPE HTML>\n"
+ "<html>\n"
+ "<head>\n"
+ "<meta name=\"generator\" content=\"Evolution Mail\"/>\n"
+ "<title>Evolution Mail Display</title>\n"
+ "<link type=\"text/css\" rel=\"stylesheet\" "
+ " href=\"" STYLESHEET_URI "\"/>\n"
+ "<style type=\"text/css\">\n"
+ " table th { color: #%06x; font-weight: bold; }\n"
+ "</style>\n"
+ "</head>"
+ "<body bgcolor=\"#%06x\" text=\"#%06x\">",
+ e_rgba_to_value (
+ e_mail_formatter_get_color (
+ formatter, E_MAIL_FORMATTER_COLOR_HEADER)),
+ e_rgba_to_value (
+ e_mail_formatter_get_color (
+ formatter, E_MAIL_FORMATTER_COLOR_BODY)),
+ e_rgba_to_value (
+ e_mail_formatter_get_color (
+ formatter, E_MAIL_FORMATTER_COLOR_TEXT)));
+}
+
+EMailExtensionRegistry *
+e_mail_formatter_get_extension_registry (EMailFormatter *formatter)
+{
+ EMailFormatterClass * class;
+
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
+
+ class = E_MAIL_FORMATTER_GET_CLASS (formatter);
+ return E_MAIL_EXTENSION_REGISTRY (class->extension_registry);
+}
+
+CamelMimeFilterToHTMLFlags
+e_mail_formatter_get_text_format_flags (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0);
+
+ return E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags;
+}
+
+const GdkRGBA *
+e_mail_formatter_get_color (EMailFormatter *formatter,
+ EMailFormatterColor type)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
+ g_return_val_if_fail (type < E_MAIL_FORMATTER_NUM_COLOR_TYPES, NULL);
+
+ return &E_MAIL_FORMATTER_GET_CLASS (formatter)->colors[type];
+}
+
+void
+e_mail_formatter_set_color (EMailFormatter *formatter,
+ EMailFormatterColor type,
+ const GdkRGBA *color)
+{
+ GdkRGBA *format_color;
+ const gchar *property_name;
+
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+ g_return_if_fail (type < E_MAIL_FORMATTER_NUM_COLOR_TYPES);
+ g_return_if_fail (color != NULL);
+
+ format_color = &E_MAIL_FORMATTER_GET_CLASS (formatter)->colors[type];
+
+ if (gdk_rgba_equal (color, format_color))
+ return;
+
+ format_color->red = color->red;
+ format_color->green = color->green;
+ format_color->blue = color->blue;
+
+ switch (type) {
+ case E_MAIL_FORMATTER_COLOR_BODY:
+ property_name = "body-color";
+ break;
+ case E_MAIL_FORMATTER_COLOR_CITATION:
+ property_name = "citation-color";
+ break;
+ case E_MAIL_FORMATTER_COLOR_CONTENT:
+ property_name = "content-color";
+ break;
+ case E_MAIL_FORMATTER_COLOR_FRAME:
+ property_name = "frame-color";
+ break;
+ case E_MAIL_FORMATTER_COLOR_HEADER:
+ property_name = "header-color";
+ break;
+ case E_MAIL_FORMATTER_COLOR_TEXT:
+ property_name = "text-color";
+ break;
+ default:
+ g_return_if_reached ();
+ }
+
+ g_object_notify (G_OBJECT (formatter), property_name);
+}
+
+void
+e_mail_formatter_update_style (EMailFormatter *formatter,
+ GtkStateFlags state)
+{
+ EMailFormatterClass *class;
+
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ class = E_MAIL_FORMATTER_GET_CLASS (formatter);
+ g_return_if_fail (class->update_style != NULL);
+
+ class->update_style (formatter, state);
+}
+
+EMailImageLoadingPolicy
+e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0);
+
+ return formatter->priv->image_loading_policy;
+}
+
+void
+e_mail_formatter_set_image_loading_policy (EMailFormatter *formatter,
+ EMailImageLoadingPolicy policy)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ if (policy == formatter->priv->image_loading_policy)
+ return;
+
+ formatter->priv->image_loading_policy = policy;
+
+ g_object_notify (G_OBJECT (formatter), "image-loading-policy");
+}
+
+gboolean
+e_mail_formatter_get_mark_citations (EMailFormatter *formatter)
+{
+ guint32 flags;
+
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
+
+ flags = E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags;
+
+ return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0);
+}
+
+void
+e_mail_formatter_set_mark_citations (EMailFormatter *formatter,
+ gboolean mark_citations)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ if (mark_citations)
+ E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags |=
+ CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+ else
+ E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags &=
+ ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+
+ g_object_notify (G_OBJECT (formatter), "mark-citations");
+}
+
+gboolean
+e_mail_formatter_get_show_sender_photo (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
+
+ return formatter->priv->show_sender_photo;
+}
+
+void
+e_mail_formatter_set_show_sender_photo (EMailFormatter *formatter,
+ gboolean show_sender_photo)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ if (formatter->priv->show_sender_photo == show_sender_photo)
+ return;
+
+ formatter->priv->show_sender_photo = show_sender_photo;
+
+ g_object_notify (G_OBJECT (formatter), "show-sender-photo");
+}
+
+gboolean
+e_mail_formatter_get_show_real_date (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
+
+ return formatter->priv->show_real_date;
+}
+
+void
+e_mail_formatter_set_show_real_date (EMailFormatter *formatter,
+ gboolean show_real_date)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ if (formatter->priv->show_real_date == show_real_date)
+ return;
+
+ formatter->priv->show_real_date = show_real_date;
+
+ g_object_notify (G_OBJECT (formatter), "show-real-date");
+}
+
+gboolean
+e_mail_formatter_get_animate_images (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
+
+ return formatter->priv->animate_images;
+}
+
+void
+e_mail_formatter_set_animate_images (EMailFormatter *formatter,
+ gboolean animate_images)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ if (formatter->priv->animate_images == animate_images)
+ return;
+
+ formatter->priv->animate_images = animate_images;
+
+ g_object_notify (G_OBJECT (formatter), "animate-images");
+}
+
+const gchar *
+e_mail_formatter_get_charset (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
+
+ return formatter->priv->charset;
+}
+
+gchar *
+e_mail_formatter_dup_charset (EMailFormatter *formatter)
+{
+ const gchar *protected;
+ gchar *duplicate;
+
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
+
+ g_mutex_lock (&formatter->priv->property_lock);
+
+ protected = e_mail_formatter_get_charset (formatter);
+ duplicate = g_strdup (protected);
+
+ g_mutex_unlock (&formatter->priv->property_lock);
+
+ return duplicate;
+}
+
+void
+e_mail_formatter_set_charset (EMailFormatter *formatter,
+ const gchar *charset)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+
+ g_mutex_lock (&formatter->priv->property_lock);
+
+ if (g_strcmp0 (formatter->priv->charset, charset) == 0) {
+ g_mutex_unlock (&formatter->priv->property_lock);
+ return;
+ }
+
+ g_free (formatter->priv->charset);
+ formatter->priv->charset = g_strdup (charset);
+
+ g_mutex_unlock (&formatter->priv->property_lock);
+
+ g_object_notify (G_OBJECT (formatter), "charset");
+}
+
+const gchar *
+e_mail_formatter_get_default_charset (EMailFormatter *formatter)
+{
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
+
+ return formatter->priv->default_charset;
+}
+
+gchar *
+e_mail_formatter_dup_default_charset (EMailFormatter *formatter)
+{
+ const gchar *protected;
+ gchar *duplicate;
+
+ g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
+
+ g_mutex_lock (&formatter->priv->property_lock);
+
+ protected = e_mail_formatter_get_default_charset (formatter);
+ duplicate = g_strdup (protected);
+
+ g_mutex_unlock (&formatter->priv->property_lock);
+
+ return duplicate;
+}
+
+void
+e_mail_formatter_set_default_charset (EMailFormatter *formatter,
+ const gchar *default_charset)
+{
+ g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
+ g_return_if_fail (default_charset && *default_charset);
+
+ g_mutex_lock (&formatter->priv->property_lock);
+
+ if (g_strcmp0 (formatter->priv->default_charset, default_charset) == 0) {
+ g_mutex_unlock (&formatter->priv->property_lock);
+ return;
+ }
+
+ g_free (formatter->priv->default_charset);
+ formatter->priv->default_charset = g_strdup (default_charset);
+
+ g_mutex_unlock (&formatter->priv->property_lock);
+
+ g_object_notify (G_OBJECT (formatter), "default-charset");
+}
+