/* * 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 * */ #include "e-mail-formatter.h" #include "e-mail-formatter-extension.h" #include "e-mail-formatter-utils.h" #include "e-mail-part.h" #include #include #include #include #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, "", 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 ( "\n" "\n" "\n" "\n" "Evolution Mail Display\n" "\n" "\n" "" "", 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"); }