Files
evolution/em-format/e-mail-formatter.c
Milan Crha 57adde4be6 Revert "Teach EMailExtensionRegistry to find extensions."
This reverts commit bf30024dd7, because
it breaks EMailFormatter/Parser extensions, like the prefer-plain.
The thing is that the internal formatters/parsers (also extensions)
should be always added first, and only after then can be added extended
extensions, which are used before those internal. This constraint was not
satisfied with the reverted commit, the order of extension registration
was unpredictable, depended on GType.
2013-01-29 16:31:58 +01:00

1579 lines
40 KiB
C

/*
* 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>
#define d(x)
/* 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);
struct _EMailFormatterPrivate {
EMailImageLoadingPolicy image_loading_policy;
guint only_local_photos : 1;
guint show_sender_photo : 1;
guint show_real_date : 1;
guint animate_images : 1;
gchar *charset;
gchar *default_charset;
GQueue *header_list;
};
#define E_MAIL_FORMATTER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))\
static gpointer e_mail_formatter_parent_class = 0;
enum {
PROP_0,
PROP_BODY_COLOR,
PROP_CITATION_COLOR,
PROP_CONTENT_COLOR,
PROP_FRAME_COLOR,
PROP_HEADER_COLOR,
PROP_TEXT_COLOR,
PROP_IMAGE_LOADING_POLICY,
PROP_FORCE_IMAGE_LOADING,
PROP_MARK_CITATIONS,
PROP_ONLY_LOCAL_PHOTOS,
PROP_SHOW_SENDER_PHOTO,
PROP_SHOW_REAL_DATE,
PROP_ANIMATE_IMAGES,
PROP_CHARSET,
PROP_DEFAULT_CHARSET
};
enum {
NEED_REDRAW,
LAST_SIGNAL
};
static gint signals[LAST_SIGNAL];
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_BODY_COLOR:
e_mail_formatter_set_color (
E_MAIL_FORMATTER (object),
E_MAIL_FORMATTER_COLOR_BODY,
g_value_get_boxed (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_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_int (value));
return;
case PROP_MARK_CITATIONS:
e_mail_formatter_set_mark_citations (
E_MAIL_FORMATTER (object),
g_value_get_boolean (value));
return;
case PROP_ONLY_LOCAL_PHOTOS:
e_mail_formatter_set_only_local_photos (
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_SHOW_REAL_DATE:
e_mail_formatter_set_show_real_date (
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;
case PROP_ANIMATE_IMAGES:
e_mail_formatter_set_animate_images (
E_MAIL_FORMATTER (object),
g_value_get_boolean (value));
return;
case PROP_CHARSET:
e_mail_formatter_set_charset (
E_MAIL_FORMATTER (object),
g_value_get_string (value));
return;
case PROP_DEFAULT_CHARSET:
e_mail_formatter_set_default_charset (
E_MAIL_FORMATTER (object),
g_value_get_string (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_BODY_COLOR:
g_value_set_boxed (
value,
e_mail_formatter_get_color (
E_MAIL_FORMATTER (object),
E_MAIL_FORMATTER_COLOR_BODY));
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_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_int (
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_ONLY_LOCAL_PHOTOS:
g_value_set_boolean (
value,
e_mail_formatter_get_only_local_photos (
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_SHOW_REAL_DATE:
g_value_set_boolean (
value,
e_mail_formatter_get_show_real_date (
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;
case PROP_ANIMATE_IMAGES:
g_value_set_boolean (
value,
e_mail_formatter_get_animate_images (
E_MAIL_FORMATTER (object)));
return;
case PROP_CHARSET:
g_value_set_string (
value,
e_mail_formatter_get_charset (
E_MAIL_FORMATTER (object)));
return;
case PROP_DEFAULT_CHARSET:
g_value_set_string (
value,
e_mail_formatter_get_default_charset (
E_MAIL_FORMATTER (object)));
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 (object)->priv;
if (priv->charset) {
g_free (priv->charset);
priv->charset = NULL;
}
if (priv->default_charset) {
g_free (priv->default_charset);
priv->default_charset = NULL;
}
if (priv->header_list) {
e_mail_formatter_clear_headers (E_MAIL_FORMATTER (object));
g_queue_free (priv->header_list);
priv->header_list = NULL;
}
/* Chain up to parent's finalize() */
G_OBJECT_CLASS (e_mail_formatter_parent_class)->finalize (object);
}
static void
e_mail_formatter_constructed (GObject *object)
{
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;
gboolean ok;
if (g_cancellable_is_cancelled (cancellable))
break;
if (part->is_hidden && !part->is_error) {
if (g_str_has_suffix (part->id, ".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) {
if (!part->mime_type)
continue;
ok = e_mail_formatter_format_as (
formatter, context, part, stream,
part->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 && g_str_has_suffix (part->id, ".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 (g_str_has_suffix (part->id, ".headers") ||
g_str_has_suffix (part->id, "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 (g_str_has_suffix (part->id, ".rfc822")) {
do {
part = link->data;
if (g_str_has_suffix (part->id, ".rfc822.end"))
break;
link = g_list_next (link);
} while (link != NULL);
if (link == NULL)
break;
}
}
}
while (!g_queue_is_empty (&queue))
e_mail_part_unref (g_queue_pop_head (&queue));
camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
}
static void
mail_formatter_set_style (EMailFormatter *formatter,
GtkStyle *style,
GtkStateType state)
{
GdkColor *color;
EMailFormatterColorType type;
g_object_freeze_notify (G_OBJECT (formatter));
color = &style->bg[state];
type = E_MAIL_FORMATTER_COLOR_BODY;
e_mail_formatter_set_color (formatter, type, color);
color = &style->base[GTK_STATE_NORMAL];
type = E_MAIL_FORMATTER_COLOR_CONTENT;
e_mail_formatter_set_color (formatter, type, color);
color = &style->dark[state];
type = E_MAIL_FORMATTER_COLOR_FRAME;
e_mail_formatter_set_color (formatter, type, color);
color = &style->fg[state];
type = E_MAIL_FORMATTER_COLOR_HEADER;
e_mail_formatter_set_color (formatter, type, color);
color = &style->text[state];
type = E_MAIL_FORMATTER_COLOR_TEXT;
e_mail_formatter_set_color (formatter, type, color);
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_internal_extensions_load (
E_MAIL_EXTENSION_REGISTRY (class->extension_registry));
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;
GdkColor *color;
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->set_style = mail_formatter_set_style;
color = &class->colors[E_MAIL_FORMATTER_COLOR_BODY];
gdk_color_parse ("#eeeeee", color);
color = &class->colors[E_MAIL_FORMATTER_COLOR_CONTENT];
gdk_color_parse ("#ffffff", color);
color = &class->colors[E_MAIL_FORMATTER_COLOR_FRAME];
gdk_color_parse ("#3f3f3f", color);
color = &class->colors[E_MAIL_FORMATTER_COLOR_HEADER];
gdk_color_parse ("#eeeeee", color);
color = &class->colors[E_MAIL_FORMATTER_COLOR_TEXT];
gdk_color_parse ("#000000", color);
g_object_class_install_property (
object_class,
PROP_BODY_COLOR,
g_param_spec_boxed (
"body-color",
"Body Color",
NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_CITATION_COLOR,
g_param_spec_boxed (
"citation-color",
"Citation Color",
NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_CONTENT_COLOR,
g_param_spec_boxed (
"content-color",
"Content Color",
NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_FRAME_COLOR,
g_param_spec_boxed (
"frame-color",
"Frame Color",
NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_HEADER_COLOR,
g_param_spec_boxed (
"header-color",
"Header Color",
NULL,
GDK_TYPE_COLOR,
G_PARAM_READWRITE));
/* FIXME Make this a proper enum property. */
g_object_class_install_property (
object_class,
PROP_IMAGE_LOADING_POLICY,
g_param_spec_int (
"image-loading-policy",
"Image Loading Policy",
NULL,
E_MAIL_IMAGE_LOADING_POLICY_NEVER,
E_MAIL_IMAGE_LOADING_POLICY_ALWAYS,
E_MAIL_IMAGE_LOADING_POLICY_NEVER,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_MARK_CITATIONS,
g_param_spec_boolean (
"mark-citations",
"Mark Citations",
NULL,
TRUE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_ONLY_LOCAL_PHOTOS,
g_param_spec_boolean (
"only-local-photos",
"Only Local Photos",
NULL,
TRUE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
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_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_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_object_class_install_property (
object_class,
PROP_ANIMATE_IMAGES,
g_param_spec_boolean (
"animate-images",
"Animate images",
NULL,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_CHARSET,
g_param_spec_string (
"charset",
NULL,
NULL,
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_DEFAULT_CHARSET,
g_param_spec_string (
"default-charset",
NULL,
NULL,
NULL,
G_PARAM_READWRITE));
signals[NEED_REDRAW] = g_signal_new (
"need-redraw",
E_TYPE_MAIL_FORMATTER,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (EMailFormatterClass, need_redraw),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, NULL);
}
static void
e_mail_formatter_init (EMailFormatter *formatter)
{
formatter->priv = E_MAIL_FORMATTER_GET_PRIVATE (formatter);
formatter->priv->header_list = g_queue_new ();
e_mail_formatter_set_default_headers (formatter);
}
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 *parts,
CamelStream *stream,
EMailFormatterHeaderFlags flags,
EMailFormatterMode mode,
GCancellable *cancellable)
{
EMailFormatterContext *context;
EMailFormatterClass *formatter_class;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
g_return_if_fail (CAMEL_IS_STREAM (stream));
formatter_class = E_MAIL_FORMATTER_GET_CLASS (formatter);
g_return_if_fail (formatter_class->run != NULL);
context = mail_formatter_create_context (
formatter, parts, mode, flags);
formatter_class->run (
formatter, context, stream, cancellable);
mail_formatter_free_context (context);
}
static void
mail_format_async_prepare (GSimpleAsyncResult *result,
GObject *object,
GCancellable *cancellable)
{
EMailFormatterContext *context;
EMailFormatterClass *formatter_class;
CamelStream *stream;
context = g_object_get_data (G_OBJECT (result), "context");
stream = g_object_get_data (G_OBJECT (result), "stream");
formatter_class = E_MAIL_FORMATTER_GET_CLASS (object);
formatter_class->run (
E_MAIL_FORMATTER (object), context, stream, cancellable);
}
void
e_mail_formatter_format (EMailFormatter *formatter,
EMailPartList *parts,
CamelStream *stream,
EMailFormatterHeaderFlags flags,
EMailFormatterMode mode,
GAsyncReadyCallback callback,
GCancellable *cancellable,
gpointer user_data)
{
GSimpleAsyncResult *simple;
EMailFormatterContext *context;
EMailFormatterClass *formatter_class;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
g_return_if_fail (CAMEL_IS_STREAM (stream));
formatter_class = E_MAIL_FORMATTER_GET_CLASS (formatter);
g_return_if_fail (formatter_class->run != NULL);
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);
if (!parts) {
if (callback)
callback (G_OBJECT (formatter), G_ASYNC_RESULT (simple), user_data);
g_object_unref (simple);
return;
}
context = mail_formatter_create_context (
formatter, parts, mode, flags);
g_object_set_data (G_OBJECT (simple), "context", context);
g_object_set_data (G_OBJECT (simple), "stream", stream);
g_simple_async_result_run_in_thread (
simple, mail_format_async_prepare,
G_PRIORITY_DEFAULT, cancellable);
g_object_unref (simple);
}
CamelStream *
e_mail_formatter_format_finished (EMailFormatter *formatter,
GAsyncResult *result,
GError *error)
{
EMailFormatterContext *context;
g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
context = g_object_get_data (G_OBJECT (result), "context");
mail_formatter_free_context (context);
return g_object_get_data (G_OBJECT (result), "stream");
}
/**
* 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 *reg;
GQueue *formatters;
GList *iter;
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, FALSE);
g_return_val_if_fail (CAMEL_IS_STREAM (stream), FALSE);
if (!as_mime_type || !*as_mime_type)
as_mime_type = part->mime_type;
if (!as_mime_type || !*as_mime_type)
return FALSE;
reg = e_mail_formatter_get_extension_registry (formatter);
formatters = e_mail_extension_registry_get_for_mime_type (
reg, as_mime_type);
if (!formatters) {
formatters = e_mail_extension_registry_get_fallback (
reg, 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) {
for (iter = formatters->head; iter; iter = iter->next) {
EMailFormatterExtension *extension;
extension = iter->data;
if (!extension)
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;
CamelMimeFilterWindows *windows = NULL;
CamelStream *mem_stream = NULL;
CamelDataWrapper *dw;
if (g_cancellable_is_cancelled (cancellable))
return;
dw = CAMEL_DATA_WRAPPER (part->part);
if (formatter->priv->charset) {
charset = formatter->priv->charset;
} else if (dw->mime_type
&& (charset = camel_content_type_param (dw->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 = (CamelMimeFilterWindows *) camel_mime_filter_windows_new (charset);
camel_stream_filter_add (
CAMEL_STREAM_FILTER (filter_stream),
CAMEL_MIME_FILTER (windows));
camel_data_wrapper_decode_to_stream_sync (
dw, (CamelStream *) filter_stream, cancellable, NULL);
camel_stream_flush ((CamelStream *) filter_stream, cancellable, NULL);
g_object_unref (filter_stream);
charset = camel_mime_filter_windows_real_charset (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);
if ((filter = camel_mime_filter_charset_new (charset, "UTF-8"))) {
camel_stream_filter_add (
CAMEL_STREAM_FILTER (filter_stream),
CAMEL_MIME_FILTER (filter));
g_object_unref (filter);
}
camel_data_wrapper_decode_to_stream_sync (
camel_medium_get_content ((CamelMedium *) dw),
(CamelStream *) filter_stream, cancellable, NULL);
camel_stream_flush ((CamelStream *) 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, (CamelStream *) stream, cancellable, NULL);
camel_stream_flush ((CamelStream *) mem_stream, cancellable, NULL);
if (windows) {
g_object_unref (windows);
}
g_object_unref (mem_stream);
}
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 Component\" />\n"
"<title>Evolution Mail Display</title>\n"
"<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\" />\n"
"<style type=\"text/css\">\n"
" table th { color: #%06x; font-weight: bold; }\n"
"</style>\n"
"</head><body bgcolor=\"#%06x\" text=\"#%06x\">",
e_color_to_value ((GdkColor *)
e_mail_formatter_get_color (
formatter, E_MAIL_FORMATTER_COLOR_HEADER)),
e_color_to_value ((GdkColor *)
e_mail_formatter_get_color (
formatter, E_MAIL_FORMATTER_COLOR_BODY)),
e_color_to_value ((GdkColor *)
e_mail_formatter_get_color (
formatter, E_MAIL_FORMATTER_COLOR_TEXT)));
}
EMailExtensionRegistry *
e_mail_formatter_get_extension_registry (EMailFormatter *formatter)
{
EMailFormatterClass * formatter_class;
g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
formatter_class = E_MAIL_FORMATTER_GET_CLASS (formatter);
return E_MAIL_EXTENSION_REGISTRY (formatter_class->extension_registry);
}
guint32
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 GdkColor *
e_mail_formatter_get_color (EMailFormatter *formatter,
EMailFormatterColorType 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,
EMailFormatterColorType type,
const GdkColor *color)
{
GdkColor *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_color_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_set_style (EMailFormatter *formatter,
GtkStyle *style,
GtkStateType state)
{
EMailFormatterClass *formatter_class;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
g_return_if_fail (GTK_IS_STYLE (style));
formatter_class = E_MAIL_FORMATTER_GET_CLASS (formatter);
g_return_if_fail (formatter_class->set_style != NULL);
formatter_class->set_style (formatter, style, 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_only_local_photos (EMailFormatter *formatter)
{
g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE);
return formatter->priv->only_local_photos;
}
void
e_mail_formatter_set_only_local_photos (EMailFormatter *formatter,
gboolean only_local_photos)
{
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
if (formatter->priv->only_local_photos == only_local_photos)
return;
formatter->priv->only_local_photos = only_local_photos;
g_object_notify (G_OBJECT (formatter), "only-local-photos");
}
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;
}
void
e_mail_formatter_set_charset (EMailFormatter *formatter,
const gchar *charset)
{
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
if (g_strcmp0 (formatter->priv->charset, charset) == 0)
return;
g_free (formatter->priv->charset);
if (!charset) {
formatter->priv->charset = NULL;
} else {
formatter->priv->charset = g_strdup (charset);
}
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;
}
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);
if (g_strcmp0 (formatter->priv->default_charset, default_charset) == 0)
return;
g_free (formatter->priv->default_charset);
formatter->priv->default_charset = g_strdup (default_charset);
g_object_notify (G_OBJECT (formatter), "default-charset");
}
/* note: also copied in em-mailer-prefs.c */
static const struct {
const gchar *name;
guint32 flags;
} default_headers[] = {
{ N_("From"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Reply-To"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("To"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Cc"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Bcc"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Subject"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Date"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Newsgroups"), E_MAIL_FORMATTER_HEADER_FLAG_BOLD },
{ N_("Face"), 0 },
};
/**
* e_mail_formatter_get_headers:
* @formatter: an #EMailFormatter
*
* Returns list of currently set headers.
*
* Return Value: A #GQueue of headers which you should not modify or unref
*/
const GQueue *
e_mail_formatter_get_headers (EMailFormatter *formatter)
{
g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL);
return formatter->priv->header_list;
}
/**
* e_mail_formatter_clear_headers:
* @formatter: an #EMailFormatter
*
* Clear the list of headers to be displayed. This will force all headers to
* be shown.
**/
void
e_mail_formatter_clear_headers (EMailFormatter *formatter)
{
EMailFormatterHeader *header;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
while ((header = g_queue_pop_head (formatter->priv->header_list)) != NULL) {
e_mail_formatter_header_free (header);
}
}
/**
* e_mail_formatter_set_default_headers:
* @formatter: an #EMailFormatter
*
* Clear the list of headers and sets the default ones, e.g. "To", "From", "Cc"
* "Subject", etc...
*/
void
e_mail_formatter_set_default_headers (EMailFormatter *formatter)
{
gint ii;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
/* Set the default headers */
e_mail_formatter_clear_headers (formatter);
for (ii = 0; ii < G_N_ELEMENTS (default_headers); ii++) {
e_mail_formatter_add_header (
formatter, default_headers[ii].name, NULL,
default_headers[ii].flags);
}
}
/**
* e_mail_formatter_add_header:
* @formatter:
* @name: The name of the header, as it will appear during output.
* @value: Value of the header. Can be %NULL.
* @flags: a set of #EMailFormatterHeaderFlags to control display attributes.
*
* Add a specific header to show. If any headers are set, they will
* be displayed in the order set by this function. Certain known
* headers included in this list will be shown using special
* formatting routines.
**/
void
e_mail_formatter_add_header (EMailFormatter *formatter,
const gchar *name,
const gchar *value,
EMailFormatterHeaderFlags flags)
{
EMailFormatterHeader *h;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
g_return_if_fail (name && *name);
h = e_mail_formatter_header_new (name, value);
h->flags = flags;
g_queue_push_tail (formatter->priv->header_list, h);
g_signal_emit (formatter, signals[NEED_REDRAW], 0, NULL);
}
void
e_mail_formatter_add_header_struct (EMailFormatter *formatter,
const EMailFormatterHeader *header)
{
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
g_return_if_fail (header && header->name);
e_mail_formatter_add_header (formatter, header->name, header->value, header->flags);
}
void e_mail_formatter_remove_header (EMailFormatter *formatter,
const gchar *name,
const gchar *value)
{
GList *iter = NULL;
g_return_if_fail (E_IS_MAIL_FORMATTER (formatter));
g_return_if_fail (name && *name);
iter = g_queue_peek_head_link (formatter->priv->header_list);
while (iter) {
EMailFormatterHeader *header = iter->data;
if (!header->value || !*header->value) {
GList *next = iter->next;
if (g_strcmp0 (name, header->name) == 0)
g_queue_delete_link (formatter->priv->header_list, iter);
iter = next;
continue;
}
if (value && *value) {
if ((g_strcmp0 (name, header->name) == 0) &&
(g_strcmp0 (value, header->value) == 0))
break;
} else {
if (g_strcmp0 (name, header->name) == 0)
break;
}
iter = iter->next;
}
if (iter) {
e_mail_formatter_header_free (iter->data);
g_queue_delete_link (formatter->priv->header_list, iter);
}
}
void
e_mail_formatter_remove_header_struct (EMailFormatter *formatter,
const EMailFormatterHeader *header)
{
g_return_if_fail (header != NULL);
e_mail_formatter_remove_header (formatter, header->name, header->value);
}
EMailFormatterHeader *
e_mail_formatter_header_new (const gchar *name,
const gchar *value)
{
EMailFormatterHeader *header;
g_return_val_if_fail (name && *name, NULL);
header = g_new0 (EMailFormatterHeader, 1);
header->name = g_strdup (name);
if (value && *value)
header->value = g_strdup (value);
return header;
}
void
e_mail_formatter_header_free (EMailFormatterHeader *header)
{
g_return_if_fail (header);
if (header->name) {
g_free (header->name);
header->name = NULL;
}
if (header->value) {
g_free (header->value);
header->value = NULL;
}
g_free (header);
}