diff options
Diffstat (limited to 'em-format/e-mail-formatter.c')
-rw-r--r-- | em-format/e-mail-formatter.c | 1435 |
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"); +} + |