diff options
author | Dan Vrátil <dvratil@redhat.com> | 2012-06-06 21:27:19 +0800 |
---|---|---|
committer | Dan Vrátil <dvratil@redhat.com> | 2012-06-06 21:27:19 +0800 |
commit | 5b8340563c271fb684a88c6e5bb6dd3bfb629058 (patch) | |
tree | c1c7d606fb4ce9fd2fe459a9226bfb9125423991 | |
parent | 26a4f24188fd89dbabaff192bec9c54af8fe5a80 (diff) | |
download | gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.gz gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.bz2 gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.lz gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.xz gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.zst gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.zip |
Mail formatter rewrite
All mail-parsing and formatting code has been moved to em-format.
Parsing is handeled by EMailParser class, formatting by EMailFormatter.
Both classes have registry which hold extensions - simple classes
that do actual parsing and formatting. Each supported mime-type
has it's own parser and formatter extension class.
119 files changed, 16050 insertions, 10539 deletions
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index ae5048208e..90ce610eb9 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -40,11 +40,15 @@ #include <libevolution-utils/e-alert-dialog.h> #include <e-util/e-dialog-utils.h> #include <e-util/e-util-private.h> -#include <em-format/em-format.h> -#include <em-format/em-format-quote.h> #include "e-composer-private.h" +#include <em-format/e-mail-part.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-formatter-quote.h> + +#include <shell/e-shell.h> + typedef struct _AsyncContext AsyncContext; struct _AsyncContext { @@ -179,49 +183,50 @@ static gchar * emcu_part_to_html (CamelSession *session, CamelMimePart *part, gssize *len, - EMFormat *source, GCancellable *cancellable) { - EMFormatQuote *emfq; CamelStreamMem *mem; GByteArray *buf; gchar *text; - EMFormatParserInfo p_info = { 0 }; - EMFormatWriterInfo w_info = { 0 }; + EMailParser *parser; + EMailFormatter *formatter; + EMailPartList *part_list; GString *part_id; + EShell *shell; + GtkWindow *window; + + shell = e_shell_get_default (); + window = e_shell_get_active_window (shell); buf = g_byte_array_new (); mem = (CamelStreamMem *) camel_stream_mem_new (); camel_stream_mem_set_byte_array (mem, buf); - emfq = em_format_quote_new ( - session, NULL, (CamelStream *) mem, - EM_FORMAT_QUOTE_KEEP_SIG); - em_format_set_composer ((EMFormat *) emfq, TRUE); - if (source) { - /* Copy over things we can, other things are internal. - * XXX Perhaps need different api than 'clone'. */ - if (em_format_get_default_charset (source)) - em_format_set_default_charset ( - (EMFormat *) emfq, em_format_get_default_charset (source)); - if (em_format_get_charset (source)) - em_format_set_charset ( - (EMFormat *) emfq, em_format_get_charset (source)); - } + part_list = e_mail_part_list_new (); part_id = g_string_sized_new (0); - em_format_parse_part (EM_FORMAT (emfq), part, part_id, &p_info, cancellable); - em_format_write (EM_FORMAT (emfq), CAMEL_STREAM (mem), &w_info, cancellable); + parser = e_mail_parser_new (session); + part_list->list = e_mail_parser_parse_part (parser, part, part_id, cancellable); g_string_free (part_id, TRUE); + g_object_unref (parser); + + formatter = e_mail_formatter_quote_new (NULL, E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG); + e_mail_formatter_set_style (formatter, + gtk_widget_get_style (GTK_WIDGET (window)), + gtk_widget_get_state (GTK_WIDGET (window))); - g_object_unref (emfq); + e_mail_formatter_format_sync ( + formatter, part_list, (CamelStream *) mem, + 0, E_MAIL_FORMATTER_MODE_PRINTING, cancellable); + g_object_unref (formatter); + g_object_unref (part_list); camel_stream_write((CamelStream *) mem, "", 1, cancellable, NULL); g_object_unref (mem); text = (gchar *) buf->data; if (len) - *len = buf->len-1; + *len = buf->len - 1; g_byte_array_free (buf, FALSE); return text; @@ -2711,7 +2716,7 @@ handle_multipart_signed (EMsgComposer *composer, gssize length; html = emcu_part_to_html ( - session, mime_part, &length, NULL, cancellable); + session, mime_part, &length, cancellable); e_msg_composer_set_pending_body (composer, html, length); } else { e_msg_composer_attach (composer, mime_part); @@ -2799,7 +2804,7 @@ handle_multipart_encrypted (EMsgComposer *composer, gssize length; html = emcu_part_to_html ( - session, mime_part, &length, NULL, cancellable); + session, mime_part, &length, cancellable); e_msg_composer_set_pending_body (composer, html, length); } else { e_msg_composer_attach (composer, mime_part); @@ -2880,7 +2885,7 @@ handle_multipart_alternative (EMsgComposer *composer, gssize length; html = emcu_part_to_html ( - session, text_part, &length, NULL, cancellable); + session, text_part, &length, cancellable); e_msg_composer_set_pending_body (composer, html, length); } } @@ -2948,7 +2953,7 @@ handle_multipart (EMsgComposer *composer, /* Since the first part is not multipart/alternative, * this must be the body. */ html = emcu_part_to_html ( - session, mime_part, &length, NULL, cancellable); + session, mime_part, &length, cancellable); e_msg_composer_set_pending_body (composer, html, length); } else if (camel_mime_part_get_content_id (mime_part) || camel_mime_part_get_content_location (mime_part)) { @@ -3325,7 +3330,7 @@ e_msg_composer_new_with_message (EShell *shell, html = emcu_part_to_html ( session, CAMEL_MIME_PART (message), - &length, NULL, cancellable); + &length, cancellable); e_msg_composer_set_pending_body (composer, html, length); } diff --git a/data/webview.css b/data/webview.css index 5b0e7348c3..09f94043fb 100644 --- a/data/webview.css +++ b/data/webview.css @@ -58,13 +58,13 @@ img.navigable { } .part-container { - width: 100%; height: 100%; background: #FFF; margin-top: 2px; margin-bottom: 3px; border-width: 1px; border-style: solid; + right: 0; } .part-container-inner-margin { diff --git a/em-format/Makefile.am b/em-format/Makefile.am index 392a195044..6b42ec1262 100644 --- a/em-format/Makefile.am +++ b/em-format/Makefile.am @@ -3,33 +3,118 @@ emformatincludedir = $(privincludedir)/em-format privsolib_LTLIBRARIES = libemformat.la emformatinclude_HEADERS = \ - em-format.h \ - em-format-quote.h \ - em-inline-filter.h \ - em-stripsig-filter.h + e-mail-extension-registry.h \ + e-mail-extension.h \ + e-mail-formatter-extension.h \ + e-mail-formatter.h \ + e-mail-formatter-print.h \ + e-mail-formatter-quote.h \ + e-mail-formatter-utils.h \ + e-mail-inline-filter.h \ + e-mail-parser-extension.h \ + e-mail-parser.h \ + e-mail-part.h \ + e-mail-part-attachment.h \ + e-mail-part-attachment-bar.h \ + e-mail-part-list.h \ + e-mail-part-utils.h \ + e-mail-stripsig-filter.h libemformat_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ -I$(top_srcdir) \ + -I$(top_srcdir)/em-format \ + -I$(top_srcdir)/smime/lib \ + -I$(top_srcdir)/smime/gui \ -I$(top_srcdir)/widgets \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(LIBSOUP_CFLAGS) + $(LIBSOUP_CFLAGS) \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" + +if ENABLE_SMIME +SMIME_EXTENSIONS = e-mail-parser-application-smime.c +endif libemformat_la_SOURCES = \ $(emformatinclude_HEADERS) \ - em-format.c \ - em-format-quote.c \ - em-inline-filter.c \ - em-stripsig-filter.c + $(emformatextensions_SOURCES) \ + e-mail-extension-registry.c \ + e-mail-extension.c \ + e-mail-inline-filter.c \ + e-mail-format-extensions.c \ + e-mail-formatter.c \ + e-mail-formatter-print.c \ + e-mail-formatter-quote.c \ + e-mail-formatter-utils.c \ + e-mail-formatter-attachment.c \ + e-mail-formatter-attachment-bar.c \ + e-mail-formatter-error.c \ + e-mail-formatter-extension.c \ + e-mail-formatter-headers.c \ + e-mail-formatter-image.c \ + e-mail-formatter-message-rfc822.c \ + e-mail-formatter-secure-button.c \ + e-mail-formatter-source.c \ + e-mail-formatter-text-enriched.c \ + e-mail-formatter-text-html.c \ + e-mail-formatter-text-plain.c \ + e-mail-formatter-print-headers.c \ + e-mail-formatter-quote-attachment.c \ + e-mail-formatter-quote-headers.c \ + e-mail-formatter-quote-message-rfc822.c \ + e-mail-formatter-quote-text-enriched.c \ + e-mail-formatter-quote-text-html.c \ + e-mail-formatter-quote-text-plain.c \ + e-mail-parser-extension.c \ + e-mail-parser.c \ + e-mail-parser-application-mbox.c \ + e-mail-parser-attachment-bar.c \ + e-mail-parser-headers.c \ + e-mail-parser-image.c \ + e-mail-parser-inlinepgp-encrypted.c \ + e-mail-parser-inlinepgp-signed.c \ + e-mail-parser-message.c \ + e-mail-parser-message-deliverystatus.c \ + e-mail-parser-message-external.c \ + e-mail-parser-message-rfc822.c \ + e-mail-parser-multipart-alternative.c \ + e-mail-parser-multipart-appledouble.c \ + e-mail-parser-multipart-digest.c \ + e-mail-parser-multipart-encrypted.c \ + e-mail-parser-multipart-mixed.c \ + e-mail-parser-multipart-related.c \ + e-mail-parser-multipart-signed.c \ + e-mail-parser-secure-button.c \ + e-mail-parser-source.c \ + e-mail-parser-text-enriched.c \ + e-mail-parser-text-html.c \ + e-mail-parser-text-plain.c \ + e-mail-part.c \ + e-mail-part-attachment.c \ + e-mail-part-list.c \ + e-mail-part-utils.c \ + e-mail-stripsig-filter.c \ + $(SMIME_EXTENSIONS) libemformat_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) + +if ENABLE_SMIME +SMIME_LIBS = \ + $(top_builddir)/smime/lib/libessmime.la \ + $(top_builddir)/smime/gui/libevolution-smime.la +endif + libemformat_la_LIBADD = \ $(top_builddir)/e-util/libeutil.la \ $(top_builddir)/shell/libeshell.la \ + $(top_builddir)/libemail-utils/libemail-utils.la \ + $(top_builddir)/libemail-engine/libemail-engine.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(LIBSOUP_LIBS) + $(LIBSOUP_LIBS) \ + $(SMIME_LIBS) -include $(top_srcdir)/git.mk diff --git a/em-format/e-mail-extension-registry.c b/em-format/e-mail-extension-registry.c new file mode 100644 index 0000000000..ef08761300 --- /dev/null +++ b/em-format/e-mail-extension-registry.c @@ -0,0 +1,294 @@ +/* + * e-mail-extension-registry.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 <glib-object.h> + +#include "e-mail-extension-registry.h" +#include "e-mail-extension.h" +#include "e-mail-format-extensions.h" +#include <libebackend/libebackend.h> +#include <camel/camel.h> + +#include <glib-object.h> + +#include <string.h> + +#define E_MAIL_EXTENSION_REGISTRY_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_EXTENSION_REGISTRY, EMailExtensionRegistryPrivate)) + +struct _EMailExtensionRegistryPrivate { + GHashTable *table; +}; + +static gconstpointer parent_class = 0; + +G_DEFINE_ABSTRACT_TYPE ( + EMailExtensionRegistry, + e_mail_extension_registry, + G_TYPE_OBJECT) + +/** + * EMailExtensionRegistry: + * + * The #EMailExtensionRegistry is an abstract class representing a registry + * for #EMailExtension<!-//>s. + * + * #EMailParser and #EMailFormatter both have internally a registry object + * based on the #EMailExtensionRegistry. + * + * One extension can registry itself for more mime-types. + */ + +static void +mail_extension_registry_finalize (GObject *object) +{ + EMailExtensionRegistry *reg = E_MAIL_EXTENSION_REGISTRY (object); + + if (reg->priv->table) { + g_hash_table_destroy (reg->priv->table); + reg->priv->table = NULL; + } + + /* Chain up to parent's finalize() */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +void +e_mail_extension_registry_class_init (EMailExtensionRegistryClass *klass) +{ + GObjectClass *object_class; + + g_type_class_add_private (klass, sizeof (EMailExtensionRegistryPrivate)); + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = mail_extension_registry_finalize; +} + +static void +destroy_queue (GQueue *queue) +{ + g_queue_free_full (queue, g_object_unref); +} + +void +e_mail_extension_registry_init (EMailExtensionRegistry *reg) +{ + reg->priv = E_MAIL_EXTENSION_REGISTRY_GET_PRIVATE (reg); + + reg->priv->table = g_hash_table_new_full ( + g_str_hash, g_str_equal, NULL, (GDestroyNotify) destroy_queue); +} + +/** + * e_mail_extension_registry_add_extension: + * @reg: An #EMailExtensionRegistry + * @extension: An #EMailExtension + * + * Registrys the @extension as a handler for all mime-types that it is able + * to handle. + */ +void +e_mail_extension_registry_add_extension (EMailExtensionRegistry *reg, + EMailExtension *extension) +{ + gint i; + const gchar **types; + + g_return_if_fail (E_IS_MAIL_EXTENSION_REGISTRY (reg)); + g_return_if_fail (E_IS_MAIL_EXTENSION (extension)); + + /* One reference per extension is enough */ + g_object_ref (extension); + + types = e_mail_extension_get_mime_types (extension); + for (i = 0; types && types[i]; i++) { + GQueue *queue; + + queue = g_hash_table_lookup (reg->priv->table, types[i]); + if (!queue) { + queue = g_queue_new (); + g_queue_push_head (queue, extension); + g_hash_table_insert (reg->priv->table, (gchar *) types[i], queue); + } else { + g_queue_push_head (queue, extension); + } + + if (camel_debug ("emformat:registry")) { + printf("Added extension '%s' for type '%s'\n", + G_OBJECT_TYPE_NAME (extension), types[i]); + } + } +} + +/** + * e_mail_extension_registry_remove_extension: + * @reg: An #EMailExtensionRegistry + * @extension: An #EMailExtension + * + * Removes @extension from the registry. + */ +void +e_mail_extension_registry_remove_extension (EMailExtensionRegistry *reg, + EMailExtension *extension) +{ + gint i; + const gchar **types; + + g_return_if_fail (E_IS_MAIL_EXTENSION_REGISTRY (reg)); + g_return_if_fail (E_IS_MAIL_EXTENSION (extension)); + + types = e_mail_extension_get_mime_types (extension); + for (i = 0; types && types[i]; i++) { + GQueue *queue; + + queue = g_hash_table_lookup (reg->priv->table, types[i]); + if (!queue) { + i++; + continue; + } + + g_queue_remove (queue, extension); + + if (camel_debug ("emformat:registry")) { + printf("Removed extension '%s' from type '%s'\n", + G_OBJECT_TYPE_NAME (extension), types[i]); + } + } + + g_object_unref (extension); +} + +/** + * e_mail_extension_registry_get_for_mime_type: + * @reg: An #EMailExtensionRegistry + * @mime_type: A string with mime-type to look up + * + * Tries to lookup list of #EMailExtension<!-//>s that has registryed themselves + * as handlers for the @mime_type. + * + * Return value: Returns #GQueue of #EMailExtension<!-//>s or %NULL when there + * are no extension registryed for given @mime_type. + */ +GQueue * +e_mail_extension_registry_get_for_mime_type (EMailExtensionRegistry *reg, + const gchar *mime_type) +{ + g_return_val_if_fail (E_IS_MAIL_EXTENSION_REGISTRY (reg), NULL); + g_return_val_if_fail (mime_type && *mime_type, NULL); + + return g_hash_table_lookup (reg->priv->table, mime_type); +} + +/** + * e_mail_extension_registry_get_fallback: + * @reg: An #EMailExtensionRegistry + * @mime_type: A string with mime-type whose fallback to look up + * + * Tries to lookup fallback parsers for given mime type. For instance, for + * multipart/alternative, it will try to lookup multipart/ * parser. + * + * Return Value: Returns #QGueue of #EMailExtension<!-//>>s or %NULL when there + * are no extensions registryed for the fallback type. + */ +GQueue * +e_mail_extension_registry_get_fallback (EMailExtensionRegistry *reg, + const gchar *mime_type) +{ + gchar *s, *type; + gsize len; + GQueue *parsers; + + g_return_val_if_fail (E_IS_MAIL_EXTENSION_REGISTRY (reg), NULL); + g_return_val_if_fail (mime_type && *mime_type, NULL); + + s = strchr (mime_type, '/'); + len = s - mime_type; + + s = g_alloca (len); + strncpy (s, mime_type, len); + type = g_ascii_strdown (s, len); + s = g_strdup_printf ("%s/*", type); + + parsers = g_hash_table_lookup (reg->priv->table, s); + + g_free (type); + g_free (s); + + return parsers; +} + +/******************************************************************************/ + +static void e_mail_parser_extension_registry_extensible_interface_init (EExtensibleInterface *iface); + +G_DEFINE_TYPE_WITH_CODE ( + EMailParserExtensionRegistry, + e_mail_parser_extension_registry, + E_TYPE_MAIL_EXTENSION_REGISTRY, + G_IMPLEMENT_INTERFACE ( + E_TYPE_EXTENSIBLE, + e_mail_parser_extension_registry_extensible_interface_init)); + +static void +e_mail_parser_extension_registry_init (EMailParserExtensionRegistry *parser_ereg) +{ + +} + +static void +e_mail_parser_extension_registry_class_init (EMailParserExtensionRegistryClass *klass) +{ + e_mail_parser_extension_registry_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_extension_registry_extensible_interface_init (EExtensibleInterface *interface) +{ + +} + +/******************************************************************************/ + +static void e_mail_formatter_extension_registry_extensible_interface_init (EExtensibleInterface *iface); + +G_DEFINE_TYPE_WITH_CODE ( + EMailFormatterExtensionRegistry, + e_mail_formatter_extension_registry, + E_TYPE_MAIL_EXTENSION_REGISTRY, + G_IMPLEMENT_INTERFACE ( + E_TYPE_EXTENSIBLE, + e_mail_formatter_extension_registry_extensible_interface_init)); + +static void +e_mail_formatter_extension_registry_init (EMailFormatterExtensionRegistry *formatter_ereg) +{ + +} + +static void +e_mail_formatter_extension_registry_class_init (EMailFormatterExtensionRegistryClass *klass) +{ + e_mail_formatter_extension_registry_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_extension_registry_extensible_interface_init (EExtensibleInterface *interface) +{ + +} diff --git a/em-format/e-mail-extension-registry.h b/em-format/e-mail-extension-registry.h new file mode 100644 index 0000000000..a6ddb441e0 --- /dev/null +++ b/em-format/e-mail-extension-registry.h @@ -0,0 +1,156 @@ +/* + * e-mail-extension-registry.h + * + * 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/> + * + */ + +#ifndef E_MAIL_EXTENSION_REGISTRY_H_ +#define E_MAIL_EXTENSION_REGISTRY_H_ + +#include <em-format/e-mail-extension.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_EXTENSION_REGISTRY \ + (e_mail_extension_registry_get_type ()) +#define E_MAIL_EXTENSION_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_EXTENSION_REGISTRY, EMailExtensionRegistry)) +#define E_MAIL_EXTENSION_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_EXTENSION_REGISTRY, EMailExtensionRegistryClass)) +#define E_IS_MAIL_EXTENSION_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_EXTENSION_REGISTRY)) +#define E_IS_MAIL_EXTENSION_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_EXTENSION_REGISTRY)) +#define E_MAIL_EXTENSION_REGISTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_EXTENSION_REGISTRY, EMailExtensionRegistryClass)) + +G_BEGIN_DECLS + +typedef struct _EMailExtensionRegistry EMailExtensionRegistry; +typedef struct _EMailExtensionRegistryClass EMailExtensionRegistryClass; +typedef struct _EMailExtensionRegistryPrivate EMailExtensionRegistryPrivate; + +struct _EMailExtensionRegistryClass { + GObjectClass parent_class; +}; + +struct _EMailExtensionRegistry { + GObject parent; + EMailExtensionRegistryPrivate *priv; +}; + +GType e_mail_extension_registry_get_type (void); + +void e_mail_extension_registry_add_extension (EMailExtensionRegistry *reg, + EMailExtension *extension); + +void e_mail_extension_registry_remove_extension + (EMailExtensionRegistry *reg, + EMailExtension *extension); + +GQueue * e_mail_extension_registry_get_for_mime_type + (EMailExtensionRegistry *reg, + const gchar *mime_type); + +GQueue * e_mail_extension_registry_get_fallback (EMailExtensionRegistry *reg, + const gchar *mime_type); + +G_END_DECLS + +/******************************************************************************/ + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY \ + (e_mail_parser_extension_registry_get_type ()) +#define E_MAIL_PARSER_EXTENSION_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, EMailParserExtensionRegistry)) +#define E_MAIL_PARSER_EXTENSION_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, EMailParserExtensionRegistryClass)) +#define E_IS_MAIL_PARSER_EXTENSION_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY)) +#define E_IS_MAIL_PARSER_EXTENSION_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY)) +#define E_MAIL_PARSER_EXTENSION_REGISTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, EMailParserExtensionRegistryClass)) + +G_BEGIN_DECLS + +typedef struct _EMailParserExtensionRegistry EMailParserExtensionRegistry; +typedef struct _EMailParserExtensionRegistryClass EMailParserExtensionRegistryClass; +typedef struct _EMailParserExtensionRegistryPrivate EMailParserExtensionRegistryPrivate; + +struct _EMailParserExtensionRegistryClass { + EMailExtensionRegistryClass parent_class; +}; + +struct _EMailParserExtensionRegistry { + EMailExtensionRegistry parent; + EMailParserExtensionRegistryPrivate *priv; +}; + +GType e_mail_parser_extension_registry_get_type (void); + +G_END_DECLS + +/******************************************************************************/ + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY \ + (e_mail_formatter_extension_registry_get_type ()) +#define E_MAIL_FORMATTER_EXTENSION_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, EMailFormatterExtensionRegistry)) +#define E_MAIL_FORMATTER_EXTENSION_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, EMailFormatterExtensionRegistryClass)) +#define E_IS_MAIL_FORMATTER_EXTENSION_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY)) +#define E_IS_MAIL_FORMATTER_EXTENSION_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY)) +#define E_MAIL_FORMATTER_EXTENSION_REGISTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, EMailFormatterExtensionRegistryClass)) + +G_BEGIN_DECLS + +typedef struct _EMailFormatterExtensionRegistry EMailFormatterExtensionRegistry; +typedef struct _EMailFormatterExtensionRegistryClass EMailFormatterExtensionRegistryClass; +typedef struct _EMailFormatterExtensionRegistryPrivate EMailFormatterExtensionRegistryPrivate; + +struct _EMailFormatterExtensionRegistryClass { + EMailExtensionRegistryClass parent_class; +}; + +struct _EMailFormatterExtensionRegistry { + EMailExtensionRegistry parent; + EMailFormatterExtensionRegistryPrivate *priv; +}; + +GType e_mail_formatter_extension_registry_get_type (void); + +G_END_DECLS + +#endif /* E_MAIL_EXTENSION_REGISTRY_H_ */ diff --git a/em-format/e-mail-extension.c b/em-format/e-mail-extension.c new file mode 100644 index 0000000000..9516974824 --- /dev/null +++ b/em-format/e-mail-extension.c @@ -0,0 +1,67 @@ +/* + * e-mail-extension.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-extension.h" + +#include <glib-object.h> + +G_DEFINE_INTERFACE (EMailExtension, e_mail_extension, G_TYPE_OBJECT) + +static void +e_mail_extension_default_init (EMailExtensionInterface *iface) +{ + +} + +/** + * EMailExtension: + * + * The #EMailExtension is an abstract interface for all extensions for + * #EMailParser and #EmailFormatter. + * + * The interface is further extended by #EMailParserExtension and + * #EMailFormatterExtension interfaces which define final API for both types + * of extensions. + */ + +/** + * e_mail_extension_get_mime_types: + * @extension: an #EMailExtension + * + * A virtual function reimplemented in all mail extensions that returns a + * @NULL-terminated array of mime types that the particular extension is able + * to process. + * + * The mime-types can be either full (like text/plain), or with common subtype, + * e.g. text/ *. User should try to find the best mathing mime-type handler and + * use the latter type only as a fallback. + * + * Return value: a @NULL-terminated array or @NULL + */ +const gchar ** +e_mail_extension_get_mime_types (EMailExtension *extension) +{ + EMailExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_EXTENSION (extension), NULL); + + interface = E_MAIL_EXTENSION_GET_INTERFACE (extension); + g_return_val_if_fail (interface->mime_types != NULL, NULL); + + return interface->mime_types (extension); +} diff --git a/em-format/e-mail-extension.h b/em-format/e-mail-extension.h new file mode 100644 index 0000000000..941638aed8 --- /dev/null +++ b/em-format/e-mail-extension.h @@ -0,0 +1,60 @@ +/* + * e-mail-extension.h + * + * 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/> + * + */ + +#ifndef E_MAIL_EXTENSION_H +#define E_MAIL_EXTENSION_H + +#include <glib-object.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_EXTENSION \ + (e_mail_extension_get_type ()) +#define E_MAIL_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_EXTENSION, EMailExtension)) +#define E_MAIL_EXTENSION_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_EXTENSION, EMailExtensionInterface)) +#define E_IS_MAIL_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_EXTENSION)) +#define E_IS_MAIL_EXTENSION_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_EXTENSION)) +#define E_MAIL_EXTENSION_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_MAIL_EXTENSION, EMailExtensionInterface)) + +G_BEGIN_DECLS + +typedef struct _EMailExtension EMailExtension; +typedef struct _EMailExtensionInterface EMailExtensionInterface; + +struct _EMailExtensionInterface { + GTypeInterface parent_interface; + + const gchar ** (*mime_types) (EMailExtension *extension); +}; + +GType e_mail_extension_get_type (void); + +const gchar ** e_mail_extension_get_mime_types (EMailExtension *extension); + +G_END_DECLS + +#endif /* E_MAIL_EXTENSION_H */ diff --git a/em-format/e-mail-format-extensions.c b/em-format/e-mail-format-extensions.c new file mode 100644 index 0000000000..fa7d5d8683 --- /dev/null +++ b/em-format/e-mail-format-extensions.c @@ -0,0 +1,124 @@ +/* + * e-mail-format-extensions.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-format-extensions.h" + +#include "em-format/e-mail-parser-extension.h" +#include "em-format/e-mail-formatter-extension.h" + +typedef GType (*TypeFunc) (void); + +TypeFunc parser_funcs[] = { + e_mail_parser_application_mbox_get_type, + e_mail_parser_attachment_bar_get_type, + e_mail_parser_headers_get_type, + e_mail_parser_message_get_type, + e_mail_parser_secure_button_get_type, + e_mail_parser_source_get_type, + e_mail_parser_image_get_type, + e_mail_parser_inline_pgp_encrypted_get_type, + e_mail_parser_inline_pgp_signed_get_type, + e_mail_parser_message_delivery_status_get_type, + e_mail_parser_message_external_get_type, + e_mail_parser_message_rfc822_get_type, + e_mail_parser_multipart_alternative_get_type, + e_mail_parser_multipart_apple_double_get_type, + e_mail_parser_multipart_digest_get_type, + e_mail_parser_multipart_encrypted_get_type, + e_mail_parser_multipart_mixed_get_type, + e_mail_parser_multipart_related_get_type, + e_mail_parser_multipart_signed_get_type, + e_mail_parser_text_enriched_get_type, + e_mail_parser_text_html_get_type, + e_mail_parser_text_plain_get_type, +#ifdef ENABLE_SMIME + e_mail_parser_application_smime_get_type, +#endif + NULL +}; + +TypeFunc formatter_funcs[] = { + e_mail_formatter_attachment_get_type, + e_mail_formatter_attachment_bar_get_type, + e_mail_formatter_error_get_type, + e_mail_formatter_headers_get_type, + e_mail_formatter_secure_button_get_type, + e_mail_formatter_source_get_type, + e_mail_formatter_image_get_type, + e_mail_formatter_message_rfc822_get_type, + e_mail_formatter_text_enriched_get_type, + e_mail_formatter_text_html_get_type, + e_mail_formatter_text_plain_get_type, + NULL +}; + +TypeFunc quote_formatter_funcs[] = { + e_mail_formatter_quote_attachment_get_type, + e_mail_formatter_quote_headers_get_type, + e_mail_formatter_quote_message_rfc822_get_type, + e_mail_formatter_quote_text_enriched_get_type, + e_mail_formatter_quote_text_html_get_type, + e_mail_formatter_quote_text_plain_get_type, + NULL +}; + +TypeFunc print_formatter_funcs[] = { + e_mail_formatter_print_headers_get_type, + NULL +}; + +static void +load (EMailExtensionRegistry *ereg, + TypeFunc *func_array) +{ + gint i = 0; + + for (i = 0; func_array[i] != NULL; i++) { + GType type; + EMailExtension *extension; + + type = func_array[i](); + extension = g_object_new (type, NULL); + + e_mail_extension_registry_add_extension (ereg, extension); + } +} + +void +e_mail_parser_internal_extensions_load (EMailExtensionRegistry *ereg) +{ + load (ereg, parser_funcs); +} + +void +e_mail_formatter_internal_extensions_load (EMailExtensionRegistry *ereg) +{ + load (ereg, formatter_funcs); +} + +void +e_mail_formatter_quote_internal_extensions_load (EMailExtensionRegistry *ereg) +{ + load (ereg, quote_formatter_funcs); +} + +void +e_mail_formatter_print_internal_extensions_load (EMailExtensionRegistry *ereg) +{ + load (ereg, print_formatter_funcs); +} diff --git a/em-format/e-mail-format-extensions.h b/em-format/e-mail-format-extensions.h new file mode 100644 index 0000000000..74638ef499 --- /dev/null +++ b/em-format/e-mail-format-extensions.h @@ -0,0 +1,88 @@ +/* + * extensions.h + * + * 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/> + * + */ + +#ifndef EXTENSIONS_H_ +#define EXTENSIONS_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <em-format/e-mail-extension-registry.h> +#include <em-format/e-mail-part.h> +#include <widgets/misc/e-attachment-view.h> + +G_BEGIN_DECLS + +void e_mail_formatter_internal_extensions_load (EMailExtensionRegistry *ereg); +void e_mail_parser_internal_extensions_load (EMailExtensionRegistry *ereg); +void e_mail_formatter_quote_internal_extensions_load (EMailExtensionRegistry *ereg); +void e_mail_formatter_print_internal_extensions_load (EMailExtensionRegistry *ereg); + +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_secure_button_get_type + (void); +GType e_mail_formatter_source_get_type (void); +GType e_mail_formatter_image_get_type (void); +GType e_mail_formatter_message_rfc822_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); + +GType e_mail_parser_application_mbox_get_type (void); +GType e_mail_parser_attachment_bar_get_type (void); +GType e_mail_parser_headers_get_type (void); +GType e_mail_parser_message_get_type (void); +GType e_mail_parser_secure_button_get_type (void); +GType e_mail_parser_source_get_type (void); +GType e_mail_parser_image_get_type (void); +GType e_mail_parser_inline_pgp_encrypted_get_type (void); +GType e_mail_parser_inline_pgp_signed_get_type (void); +GType e_mail_parser_message_delivery_status_get_type (void); +GType e_mail_parser_message_external_get_type (void); +GType e_mail_parser_message_rfc822_get_type (void); +GType e_mail_parser_multipart_alternative_get_type (void); +GType e_mail_parser_multipart_apple_double_get_type (void); +GType e_mail_parser_multipart_digest_get_type (void); +GType e_mail_parser_multipart_encrypted_get_type (void); +GType e_mail_parser_multipart_mixed_get_type (void); +GType e_mail_parser_multipart_related_get_type (void); +GType e_mail_parser_multipart_signed_get_type (void); +GType e_mail_parser_text_enriched_get_type (void); +GType e_mail_parser_text_html_get_type (void); +GType e_mail_parser_text_plain_get_type (void); +#ifdef ENABLE_SMIME +GType e_mail_parser_application_smime_get_type (void); +#endif + +GType e_mail_formatter_quote_attachment_get_type (void); +GType e_mail_formatter_quote_headers_get_type (void); +GType e_mail_formatter_quote_message_rfc822_get_type (void); +GType e_mail_formatter_quote_text_enriched_get_type (void); +GType e_mail_formatter_quote_text_html_get_type (void); +GType e_mail_formatter_quote_text_plain_get_type (void); + +GType e_mail_formatter_print_headers_get_type (void); + +G_END_DECLS + +#endif /* EXTENSIONS_H_ */ diff --git a/em-format/e-mail-formatter-attachment-bar.c b/em-format/e-mail-formatter-attachment-bar.c new file mode 100644 index 0000000000..c1fda1636c --- /dev/null +++ b/em-format/e-mail-formatter-attachment-bar.c @@ -0,0 +1,154 @@ +/* + * e-mail-formatter-attachment-bar.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "e-mail-format-extensions.h" +#include "e-mail-part-attachment-bar.h" +#include <misc/e-attachment-bar.h> + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> + +typedef struct _EMailFormatterAttachmentBar { + GObject parent; +} EMailFormatterAttachmentBar; + +typedef struct _EMailFormatterAttachmentBarClass { + GObjectClass parent_class; +} EMailFormatterAttachmentBarClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterAttachmentBar, + e_mail_formatter_attachment_bar, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.widget.attachment-bar", NULL }; + +static gboolean +emfe_attachment_bar_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *str; + + if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) && + (context->mode != E_MAIL_FORMATTER_MODE_RAW)) + return FALSE; + + str = g_strdup_printf ( + "<object type=\"application/vnd.evolution.widget.attachment-bar\" " + "height=\"0\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>", + part->id, part->id); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + return TRUE; +} + +static void +unset_bar_from_store_data (GObject *store, + EAttachmentBar *bar) +{ + /* + if (E_IS_ATTACHMENT_STORE (store)) + g_object_set_data (store, "attachment-bar", NULL); + */ +} + +static GtkWidget * +emfe_attachment_bar_get_widget (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params) +{ + EMailPartAttachmentBar *empab; + GtkWidget *widget; + + g_return_val_if_fail (E_MAIL_PART_IS (part, EMailPartAttachmentBar), NULL); + + empab = (EMailPartAttachmentBar *) part; + widget = e_attachment_bar_new (empab->store); + g_object_set_data (G_OBJECT (empab->store), "attachment-bar", widget); + g_object_weak_ref (G_OBJECT (widget), + (GWeakNotify) unset_bar_from_store_data, empab->store); + + return widget; +} + +static const gchar * +emfe_attachment_bar_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfe_attachment_bar_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfe_attachment_bar_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_attachment_bar_class_init (EMailFormatterAttachmentBarClass *klass) +{ + e_mail_formatter_attachment_bar_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_attachment_bar_format; + iface->get_widget = emfe_attachment_bar_get_widget; + iface->get_display_name = emfe_attachment_bar_get_display_name; + iface->get_description = emfe_attachment_bar_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_attachment_bar_mime_types; +} + +static void +e_mail_formatter_attachment_bar_init (EMailFormatterAttachmentBar *extension) +{ + +} diff --git a/em-format/e-mail-formatter-attachment.c b/em-format/e-mail-formatter-attachment.c new file mode 100644 index 0000000000..3d63413a69 --- /dev/null +++ b/em-format/e-mail-formatter-attachment.c @@ -0,0 +1,403 @@ +/* + * e-mail-formatter-attachment.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-attachment-bar.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-part-utils.h> +#include <em-format/e-mail-inline-filter.h> +#include <e-util/e-util.h> + +#include <shell/e-shell.h> +#include <shell/e-shell-window.h> + +#include <widgets/misc/e-attachment-button.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#define d(x) + +typedef struct _EMailFormatterAttachment { + GObject parent; +} EMailFormatterAttachment; + +typedef struct _EMailFormatterAttachmentClass { + GObjectClass parent_class; +} EMailFormatterAttachmentClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterAttachment, + e_mail_formatter_attachment, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init) +) + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.attachment", + "application/vnd.evolution.widget.attachment-button", + NULL }; + +static EAttachmentStore * +find_attachment_store (GSList *parts, + const gchar *start_id) +{ + gchar *tmp, *pos; + EMailPart *part; + gchar *id; + + id = g_strconcat (start_id, ".attachment-bar", NULL); + tmp = g_strdup (id); + part = NULL; + do { + GSList *iter; + + d(printf("Looking up attachment bar as %s\n", id)); + + for (iter = parts; iter; iter = iter->next) { + EMailPart *p = iter->data; + + if (!p) + continue; + + if (g_strcmp0 (p->id, id) == 0) { + part = p; + break; + } + } + + pos = g_strrstr (tmp, "."); + if (!pos) + break; + + g_free (id); + g_free (tmp); + tmp = g_strndup (start_id, pos - tmp); + id = g_strdup_printf ("%s.attachment-bar", tmp); + + } while (pos && !part); + + g_free (id); + g_free (tmp); + + if (part) { + return ((EMailPartAttachmentBar *) part)->store; + } + + return NULL; +} + +static gboolean +emfe_attachment_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *str, *text, *html; + gchar *button_id; + EAttachmentStore *store; + EMailExtensionRegistry *reg; + GQueue *extensions; + EMailPartAttachment *empa; + gchar *attachment_part_id; + + g_return_val_if_fail (E_MAIL_PART_IS (part, EMailPartAttachment), FALSE); + + empa = (EMailPartAttachment *) part; + + if ((context->mode == E_MAIL_FORMATTER_MODE_NORMAL) || + (context->mode == E_MAIL_FORMATTER_MODE_PRINTING)) { + if (part->validity) { + e_attachment_set_signed ( + empa->attachment, part->validity->sign.status); + e_attachment_set_encrypted ( + empa->attachment, part->validity->encrypt.status); + } + + store = find_attachment_store (context->parts, part->id); + if (store) { + GList *attachments = e_attachment_store_get_attachments (store); + if (!g_list_find (attachments, empa->attachment)) { + e_attachment_store_add_attachment ( + store, empa->attachment); + } + g_list_free (attachments); + } else { + g_warning ("Failed to locate attachment-bar for %s", part->id); + } + } + + /* If the attachment is requested as RAW, then call the handler directly + * and do not append any other code. */ + if ((context->mode == E_MAIL_FORMATTER_MODE_RAW) || + (context->mode == E_MAIL_FORMATTER_MODE_PRINTING)) { + EMailExtensionRegistry *reg; + GQueue *extensions; + GList *iter; + reg = e_mail_formatter_get_extension_registry (formatter); + + extensions = e_mail_extension_registry_get_for_mime_type ( + reg, empa->snoop_mime_type); + if (!extensions) { + extensions = e_mail_extension_registry_get_fallback ( + reg, empa->snoop_mime_type); + } + + if (!extensions) + return FALSE; + + if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) { + gchar *name; + EAttachment *attachment; + GFileInfo *fi; + const gchar *description; + + attachment = empa->attachment; + fi = e_attachment_get_file_info (attachment); + + description = e_attachment_get_description (attachment); + if (description && *description) { + name = g_strdup_printf ("<h2>Attachment: %s (%s)</h2>\n", + description, g_file_info_get_display_name (fi)); + } else { + name = g_strdup_printf ("<h2>Attachment: %s</h2>\n", + g_file_info_get_display_name (fi)); + } + + camel_stream_write_string (stream, name, cancellable, NULL); + g_free (name); + } + + for (iter = g_queue_peek_head_link (extensions); iter; iter = iter->next) { + + EMailFormatterExtension *ext; + ext = iter->data; + if (!ext) + continue; + + if (e_mail_formatter_extension_format (ext, formatter, + context, part, stream, cancellable)) { + return TRUE; + } + } + + return FALSE; + } + + /* E_MAIL_FORMATTER_MODE_NORMAL: */ + + reg = e_mail_formatter_get_extension_registry (formatter); + extensions = e_mail_extension_registry_get_for_mime_type ( + reg, empa->snoop_mime_type); + + if (!extensions) { + extensions = e_mail_extension_registry_get_fallback ( + reg, empa->snoop_mime_type); + } + + text = e_mail_part_describe (part->part, empa->snoop_mime_type); + html = camel_text_to_html ( + text, e_mail_formatter_get_text_format_flags (formatter) & + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); + g_free (text); + + if (empa->attachment_view_part_id) + attachment_part_id = empa->attachment_view_part_id; + else + attachment_part_id = part->id; + + button_id = g_strconcat (attachment_part_id, ".attachment_button", NULL); + + str = g_strdup_printf ( + "<div class=\"attachment\">" + "<table width=\"100%%\" border=\"0\">" + "<tr valign=\"middle\">" + "<td align=\"left\" width=\"100\">" + "<object type=\"application/vnd.evolution.widget.attachment-button\" " + "height=\"20\" width=\"100\" data=\"%s\" id=\"%s\"></object>" + "</td>" + "<td align=\"left\">%s</td>" + "</tr>", part->id, button_id, html); + + camel_stream_write_string (stream, str, cancellable, NULL); + g_free (button_id); + g_free (str); + g_free (html); + + if (extensions) { + GList *iter; + CamelStream *content_stream; + gboolean ok; + + content_stream = camel_stream_mem_new (); + ok = FALSE; + if (empa->attachment_view_part_id != NULL) { + + GSList *att_parts; + + att_parts = e_mail_part_list_get_iter ( + context->parts, + empa->attachment_view_part_id); + + if (att_parts && att_parts->data) { + ok = e_mail_formatter_format_as ( + formatter, context, att_parts->data, + content_stream, NULL, cancellable); + } + + } else { + + for (iter = g_queue_peek_head_link (extensions); iter; iter = iter->next) { + + EMailFormatterExtension *ext; + + ext = iter->data; + if (!ext) + continue; + + if (e_mail_formatter_extension_format ( + ext, formatter, context, + part, content_stream, + cancellable)) { + ok = TRUE; + break; + } + } + } + + if (ok) { + str = g_strdup_printf ( + "<tr><td colspan=\"2\">" + "<div class=\"attachment-wrapper\" id=\"%s\">", + attachment_part_id); + + camel_stream_write_string ( + stream, str, cancellable, NULL); + g_free (str); + + g_seekable_seek ( + G_SEEKABLE (content_stream), 0, + G_SEEK_SET, cancellable, NULL); + camel_stream_write_to_stream ( + content_stream, stream, + cancellable, NULL); + + camel_stream_write_string ( + stream, "</div></td></tr>", cancellable, NULL); + } + + g_object_unref (content_stream); + } + + camel_stream_write_string (stream, "</table></div>", cancellable, NULL); + + return TRUE; +} + +static GtkWidget * +emfe_attachment_get_widget (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params) +{ + EMailPartAttachment *empa; + EAttachmentStore *store; + EAttachmentView *view; + GtkWidget *widget; + + g_return_val_if_fail (E_MAIL_PART_IS (part, EMailPartAttachment), NULL); + empa = (EMailPartAttachment *) part; + + store = find_attachment_store (context->list, part->id); + widget = e_attachment_button_new (); + g_object_set_data (G_OBJECT (widget), "uri", part->id); + e_attachment_button_set_attachment ( + E_ATTACHMENT_BUTTON (widget), empa->attachment); + view = g_object_get_data (G_OBJECT (store), "attachment-bar"); + if (view) { + e_attachment_button_set_view ( + E_ATTACHMENT_BUTTON (widget), view); + } + + gtk_widget_set_can_focus (widget, TRUE); + gtk_widget_show (widget); + + return widget; +} + +static const gchar * +emfe_attachment_get_display_name (EMailFormatterExtension *extension) +{ + return _("Attachment"); +} + +static const gchar * +emfe_attachment_get_description (EMailFormatterExtension *extension) +{ + return _("Display as attachment"); +} + +static const gchar ** +emfe_attachment_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_attachment_class_init (EMailFormatterAttachmentClass *klass) +{ + e_mail_formatter_attachment_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_attachment_format; + iface->get_widget = emfe_attachment_get_widget; + iface->get_display_name = emfe_attachment_get_display_name; + iface->get_description = emfe_attachment_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_attachment_mime_types; +} + +static void +e_mail_formatter_attachment_init (EMailFormatterAttachment *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-error.c b/em-format/e-mail-formatter-error.c new file mode 100644 index 0000000000..95f201145b --- /dev/null +++ b/em-format/e-mail-formatter-error.c @@ -0,0 +1,160 @@ +/* + * e-mail-formatter-error.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +typedef struct _EMailFormatterError { + GObject parent; +} EMailFormatterError; + +typedef struct _EMailFormatterErrorClass { + GObjectClass parent_class; +} EMailFormatterErrorClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterError, + e_mail_formatter_error, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.error", NULL }; + +static gboolean +emfe_error_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *filter; + CamelDataWrapper *dw; + gchar *html; + + dw = camel_medium_get_content ((CamelMedium *) part->part); + + html = g_strdup_printf ( + "<div class=\"part-container\" style=\"" + "border-color: #%06x;" + "background-color: #%06x; color: #%06x;\">" + "<div class=\"part-container-inner-margin pre\">\n" + "<table border=\"0\" cellspacing=\"10\" " + "cellpadding=\"0\" width=\"100%%\">\n" + "<tr valign=\"top\"><td width=50>" + "<img src=\"gtk-stock://%s/?size=%d\" /></td>\n" + "<td style=\"color: red;\">", + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + 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)), + GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); + + camel_stream_write_string (stream, html, cancellable, NULL); + g_free (html); + + filtered_stream = camel_stream_filter_new (stream); + filter = camel_mime_filter_tohtml_new ( + CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); + camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), filter); + g_object_unref (filter); + + camel_data_wrapper_decode_to_stream_sync (dw, filtered_stream, cancellable, NULL); + camel_stream_flush (filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + camel_stream_write_string (stream, + "</td>\n" + "</tr>\n" + "</table>\n" + "</div>\n" + "</div>", + cancellable, NULL); + + return TRUE; +} + +static const gchar * +emfe_error_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfe_error_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfe_error_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_error_class_init (EMailFormatterErrorClass *klass) +{ + e_mail_formatter_error_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_error_format; + iface->get_display_name = emfe_error_get_display_name; + iface->get_description = emfe_error_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_error_mime_types; +} + +static void +e_mail_formatter_error_init (EMailFormatterError *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-extension.c b/em-format/e-mail-formatter-extension.c new file mode 100644 index 0000000000..381855bac5 --- /dev/null +++ b/em-format/e-mail-formatter-extension.c @@ -0,0 +1,189 @@ +/* + * e-mail-formatter-extension.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-extension.h" + +G_DEFINE_INTERFACE ( + EMailFormatterExtension, + e_mail_formatter_extension, + E_TYPE_MAIL_EXTENSION) + +/** + * EMailFormatterExtension: + * + * The #EMailFormatterExtension is an abstract interface for all extensions for + * #EmailFormatter. + */ + +static void +e_mail_formatter_extension_default_init (EMailFormatterExtensionInterface *iface) +{ + +} + +/** + * e_mail_formatter_extension_format + * @extension: an #EMailFormatterExtension + * @formatter: an #EMailFormatter + * @context: an #EMailFormatterContext + * @part: a #EMailPart to be formatter + * @stream: a #CamelStream to which the output should be written + * @cancellable: (allow-none) a #GCancellable + * + * A virtual function reimplemented in all mail formatter extensions. The function + * formats @part, generated HTML (or other format that can be displayed to user) + * and writes it to the @stream. + * + * When the function is unable to format the @part (either because it's broken + * or because it is a different mimetype then the extension is specialized for), the + * function will return @FALSE indicating the #EMailFormatter, that it should pick + * another extension. + * + * Implementation of this function must be thread-safe. + * + * Return value: Returns @TRUE when the @part was successfully formatted and + * data were written to the @stream, @FALSE otherwise. + */ +gboolean +e_mail_formatter_extension_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + EMailFormatterExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), FALSE); + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE); + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (part != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_STREAM (stream), FALSE); + + interface = E_MAIL_FORMATTER_EXTENSION_GET_INTERFACE (extension); + g_return_val_if_fail (interface->format != NULL, FALSE); + + return interface->format (extension, formatter, context, part, stream, cancellable); +} + +/** + * e_mail_formatter_extension_has_widget: + * @extension: an #EMailFormatterExtension + * + * Returns whether the extension can provide a GtkWidget. + * + * Return value: Returns %TRUE when @extension reimplements get_widget(), %FALSE + * otherwise. + */ +gboolean +e_mail_formatter_extension_has_widget (EMailFormatterExtension *extension) +{ + EMailFormatterExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), FALSE); + + interface = E_MAIL_FORMATTER_EXTENSION_GET_INTERFACE (extension); + + return (interface->get_widget != NULL); +} + +/** + * e_mail_formatter_extension_get_widget: + * @extension: an #EMailFormatterExtension + * @part: an #EMailPart + * @params: a #GHashTable + * + * A virtual function reimplemented in some mail formatter extensions. The function + * should construct a #GtkWidget for given @part. The @params hash table can contain + * additional parameters listed in the <object> HTML element that has requested + * the widget. + * + * When @bind_dom_func is not %NULL, the callee will set a callback function + * which should be called when the webpage is completely rendered to setup + * bindings between DOM events and the widget. + * + * Return value: Returns a #GtkWidget or %NULL, when error occurs or given @extension + * does not reimplement this method. + */ +GtkWidget * +e_mail_formatter_extension_get_widget (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params) +{ + EMailFormatterExtensionInterface *interface; + GtkWidget *widget; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), NULL); + g_return_val_if_fail (part != NULL, NULL); + g_return_val_if_fail (params != NULL, NULL); + + interface = E_MAIL_FORMATTER_EXTENSION_GET_INTERFACE (extension); + + widget = NULL; + if (interface->get_widget) { + widget = interface->get_widget ( + extension, context, part, params); + } + + return widget; +} + +/** + * e_mail_formatter_extension_get_display_name: + * @extension: an #EMailFormatterExtension + * + * A virtual function reimplemented in all formatter extensions. It returns a + * short name of the extension that can be displayed in user interface. + * + * Return value: A (localized) string with name of the extension + */ +const gchar * +e_mail_formatter_extension_get_display_name (EMailFormatterExtension *extension) +{ + EMailFormatterExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), NULL); + + interface = E_MAIL_FORMATTER_EXTENSION_GET_INTERFACE (extension); + g_return_val_if_fail (interface->get_display_name != NULL, NULL); + + return interface->get_display_name (extension); +} + +/** + * e_mail_formatter_extension_get_description: + * @extension: an #EMailFormatterExtension + * + * A virtual function reimplemented in all formatter extensions. It returns a + * longer description of capabilities of the extension. + * + * Return value: A (localized) string with description of the extension. + */ +const gchar * +e_mail_formatter_extension_get_description (EMailFormatterExtension *extension) +{ + EMailFormatterExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), NULL); + + interface = E_MAIL_FORMATTER_EXTENSION_GET_INTERFACE (extension); + g_return_val_if_fail (interface->get_description != NULL, NULL); + + return interface->get_description (extension); +} diff --git a/em-format/e-mail-formatter-extension.h b/em-format/e-mail-formatter-extension.h new file mode 100644 index 0000000000..e2c4ee6cf1 --- /dev/null +++ b/em-format/e-mail-formatter-extension.h @@ -0,0 +1,106 @@ +/* + * e-mail-formatter-extension.h + * + * 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/> + * + */ + +#ifndef E_MAIL_FORMATTER_EXTENSION_H +#define E_MAIL_FORMATTER_EXTENSION_H + +#include <em-format/e-mail-extension.h> +#include <em-format/e-mail-part.h> +#include <em-format/e-mail-formatter.h> +#include <camel/camel.h> +#include <gtk/gtk.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER_EXTENSION \ + (e_mail_formatter_extension_get_type ()) +#define E_MAIL_FORMATTER_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION, EMailFormatterExtension)) +#define E_MAIL_FORMATTER_EXTENSION_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_FORMATTER_EXTENSION, EMailFormatterExtensionInterface)) +#define E_IS_MAIL_FORMATTER_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION)) +#define E_IS_MAIL_FORMATTER_EXTENSION_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_FORMATTER_EXTENSION)) +#define E_MAIL_FORMATTER_EXTENSION_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION, EMailFormatterExtensionInterface)) + +#define EMF_EXTENSION_GET_FORMATTER(e) \ + E_MAIL_FORMATTER (e_extension_get_extensible (E_EXTENSION (e))) + +G_BEGIN_DECLS + +typedef struct _EMailFormatterExtension EMailFormatterExtension; +typedef struct _EMailFormatterExtensionInterface EMailFormatterExtensionInterface; + +struct _EMailFormatterExtensionInterface { + EMailExtensionInterface parent_interface; + + gboolean (*format) (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable); + + GtkWidget * (*get_widget) (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params); + + const gchar * (*get_display_name) + (EMailFormatterExtension *extension); + + const gchar * (*get_description) + (EMailFormatterExtension *extension); + +}; + +GType e_mail_formatter_extension_get_type + (void); + +gboolean e_mail_formatter_extension_format + (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable); + +gboolean e_mail_formatter_extension_has_widget + (EMailFormatterExtension *extension); + +GtkWidget * e_mail_formatter_extension_get_widget + (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params); + +const gchar * e_mail_formatter_extension_get_display_name + (EMailFormatterExtension *extension); + +const gchar * e_mail_formatter_extension_get_description + (EMailFormatterExtension *extension); + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_EXTENSION_H */ diff --git a/em-format/e-mail-formatter-headers.c b/em-format/e-mail-formatter-headers.c new file mode 100644 index 0000000000..24d27139b5 --- /dev/null +++ b/em-format/e-mail-formatter-headers.c @@ -0,0 +1,615 @@ +/* + * e-mail-formatter-headers.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-formatter-utils.h> +#include <em-format/e-mail-inline-filter.h> +#include <libemail-engine/e-mail-utils.h> +#include <libedataserver/libedataserver.h> +#include <e-util/e-util.h> +#include <shell/e-shell.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailFormatterHeaders { + GObject parent; +} EMailFormatterHeaders; + +typedef struct _EMailFormatterHeadersClass { + GObjectClass parent_class; +} EMailFormatterHeadersClass; + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.headers", NULL }; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterHeaders, + e_mail_formatter_headers, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)) + +static void +format_short_headers (EMailFormatter *formatter, + GString *buffer, + CamelMedium *part, + guint32 flags, + GCancellable *cancellable) +{ + const gchar *charset; + CamelContentType *ct; + const gchar *hdr_charset; + gchar *evolution_imagesdir; + gchar *subject = NULL; + struct _camel_header_address *addrs = NULL; + struct _camel_header_raw *header; + GString *from; + gboolean is_rtl; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + ct = camel_mime_part_get_content_type ((CamelMimePart *) part); + charset = camel_content_type_param (ct, "charset"); + charset = camel_iconv_charset_name (charset); + hdr_charset = e_mail_formatter_get_charset (formatter) ? + e_mail_formatter_get_charset (formatter) : + e_mail_formatter_get_default_charset (formatter); + + evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + from = g_string_new (""); + + g_string_append_printf (buffer, + "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" " + "id=\"__evo-short-headers\" style=\"display: %s\">", + flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED ? "block" : "none"); + + header = ((CamelMimePart *) part)->headers; + while (header) { + if (!g_ascii_strcasecmp (header->name, "From")) { + GString *tmp; + if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) { + header = header->next; + continue; + } + tmp = g_string_new (""); + e_mail_formatter_format_address ( + formatter, tmp, addrs, header->name, FALSE, + !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE)); + + if (tmp->len) + g_string_printf (from, _("From: %s"), tmp->str); + g_string_free (tmp, TRUE); + + } else if (!g_ascii_strcasecmp (header->name, "Subject")) { + gchar *buf = NULL; + subject = camel_header_unfold (header->value); + buf = camel_header_decode_string (subject, hdr_charset); + g_free (subject); + subject = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); + g_free (buf); + } + header = header->next; + } + + is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; + if (is_rtl) { + g_string_append_printf ( + buffer, + "<tr><td width=\"100%%\" align=\"right\">%s%s%s <strong>%s</strong></td></tr>", + from->len ? "(" : "", from->str, from->len ? ")" : "", + subject ? subject : _("(no subject)")); + } else { + g_string_append_printf ( + buffer, + "<tr><td><strong>%s</strong> %s%s%s</td></tr>", + subject ? subject : _("(no subject)"), + from->len ? "(" : "", from->str, from->len ? ")" : ""); + } + + g_string_append (buffer, "</table>"); + + g_free (subject); + if (addrs) + camel_header_address_list_clear (&addrs); + + g_string_free (from, TRUE); + g_free (evolution_imagesdir); +} + +static void +write_contact_picture (CamelMimePart *part, + gint size, + GString *buffer) +{ + gchar *b64, *content_type; + CamelDataWrapper *dw; + CamelContentType *ct; + GByteArray *ba; + + ba = NULL; + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + if (dw) { + ba = camel_data_wrapper_get_byte_array (dw); + } + + if (!ba || ba->len == 0) { + + if (camel_mime_part_get_filename (part)) { + + if (size >= 0) { + g_string_append_printf ( + buffer, + "<img width=\"%d\" src=\"evo-file://%s\" />", + size, camel_mime_part_get_filename (part)); + } else { + g_string_append_printf ( + buffer, + "<img src=\"evo-file://%s\" />", + camel_mime_part_get_filename (part)); + } + } + + return; + } + + b64 = g_base64_encode (ba->data, ba->len); + ct = camel_mime_part_get_content_type (part); + content_type = camel_content_type_simple (ct); + + if (size >= 0) { + g_string_append_printf ( + buffer, + "<img width=\"%d\" src=\"data:%s;base64,%s\">", + size, content_type, b64); + } else { + g_string_append_printf ( + buffer, + "<img src=\"data:%s;base64,%s\">", + content_type, b64); + } + + g_free (b64); + g_free (content_type); +} + +static CamelMimePart * +load_picture_from_file (const gchar *mime_type, + const gchar *filename, + GCancellable *cancellable) +{ + CamelMimePart *part; + CamelStream *stream; + CamelDataWrapper *dw; + gchar *basename; + + stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL); + if (stream == NULL) + return NULL; + + dw = camel_data_wrapper_new (); + camel_data_wrapper_construct_from_stream_sync ( + dw, stream, cancellable, NULL); + g_object_unref (stream); + if (mime_type) + camel_data_wrapper_set_mime_type (dw, mime_type); + part = camel_mime_part_new (); + camel_medium_set_content ((CamelMedium *) part, dw); + g_object_unref (dw); + basename = g_path_get_basename (filename); + camel_mime_part_set_filename (part, basename); + g_free (basename); + + return part; +} + +static void +format_full_headers (EMailFormatter *formatter, + GString *buffer, + CamelMedium *part, + guint32 flags, + GCancellable *cancellable) +{ + const gchar *charset; + CamelContentType *ct; + struct _camel_header_raw *header; + gboolean have_icon = FALSE; + const gchar *photo_name = NULL; + CamelInternetAddress *cia = NULL; + EShell *shell; + ESourceRegistry *registry; + gboolean face_decoded = FALSE, contact_has_photo = FALSE; + guchar *face_header_value = NULL; + gsize face_header_len = 0; + gchar *header_sender = NULL, *header_from = NULL, *name; + gboolean mail_from_delegate = FALSE; + const gchar *hdr_charset; + gchar *evolution_imagesdir; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + shell = e_shell_get_default (); + registry = e_shell_get_registry (shell); + + ct = camel_mime_part_get_content_type ((CamelMimePart *) part); + charset = camel_content_type_param (ct, "charset"); + charset = camel_iconv_charset_name (charset); + hdr_charset = e_mail_formatter_get_charset (formatter) ? + e_mail_formatter_get_charset (formatter) : + e_mail_formatter_get_default_charset (formatter); + + evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + + g_string_append_printf (buffer, + "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" " + "id=\"__evo-full-headers\" style=\"display: %s\" width=\"100%%\">", + flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED ? "none" : "block"); + + header = ((CamelMimePart *) part)->headers; + while (header) { + if (!g_ascii_strcasecmp (header->name, "Sender")) { + struct _camel_header_address *addrs; + GString *html; + + if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) + break; + + html = g_string_new(""); + name = e_mail_formatter_format_address ( + formatter, html, addrs, header->name, FALSE, + ~(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE)); + + header_sender = html->str; + camel_header_address_list_clear (&addrs); + + g_string_free (html, FALSE); + g_free (name); + } else if (!g_ascii_strcasecmp (header->name, "From")) { + struct _camel_header_address *addrs; + GString *html; + + if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) + break; + + html = g_string_new(""); + name = e_mail_formatter_format_address ( + formatter, html, addrs, header->name, FALSE, + !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE)); + + header_from = html->str; + camel_header_address_list_clear (&addrs); + + g_string_free (html, FALSE); + g_free (name); + } else if (!g_ascii_strcasecmp (header->name, "X-Evolution-Mail-From-Delegate")) { + mail_from_delegate = TRUE; + } + + header = header->next; + } + + if (header_sender && header_from && mail_from_delegate) { + gchar *bold_sender, *bold_from; + + g_string_append ( + buffer, + "<tr><td><table border=1 width=\"100%%\" " + "cellspacing=2 cellpadding=2><tr>"); + if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) + g_string_append ( + buffer, "<td align=\"right\" width=\"100%%\">"); + else + g_string_append ( + buffer, "<td align=\"left\" width=\"100%%\">"); + bold_sender = g_strconcat ("<b>", header_sender, "</b>", NULL); + bold_from = g_strconcat ("<b>", header_from, "</b>", NULL); + /* Translators: This message suggests to the receipients + * that the sender of the mail is different from the one + * listed in From field. */ + g_string_append_printf ( + buffer, + _("This message was sent by %s on behalf of %s"), + bold_sender, bold_from); + g_string_append (buffer, "</td></tr></table></td></tr>"); + g_free (bold_sender); + g_free (bold_from); + } + + g_free (header_sender); + g_free (header_from); + + g_string_append (buffer, "<tr><td width=\"100%%\"><table border=0 cellpadding=\"0\">\n"); + + g_free (evolution_imagesdir); + + /* dump selected headers */ + if (flags & E_MAIL_FORMATTER_MODE_ALL_HEADERS) { + header = ((CamelMimePart *) part)->headers; + while (header) { + e_mail_formatter_format_header ( + formatter, buffer, part, header, + E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS, charset); + header = header->next; + } + } else { + GList *link; + gint mailer_shown = FALSE; + + link = g_queue_peek_head_link ( + (GQueue *) e_mail_formatter_get_headers (formatter)); + + while (link != NULL) { + EMailFormatterHeader *h = link->data; + gint mailer, face; + + header = ((CamelMimePart *) part)->headers; + mailer = !g_ascii_strcasecmp (h->name, "X-Evolution-Mailer"); + face = !g_ascii_strcasecmp (h->name, "Face"); + + while (header) { + if (e_mail_formatter_get_show_sender_photo (formatter) && + !photo_name && !g_ascii_strcasecmp (header->name, "From")) + photo_name = header->value; + + if (!mailer_shown && mailer && ( + !g_ascii_strcasecmp (header->name, "X-Mailer") || + !g_ascii_strcasecmp (header->name, "User-Agent") || + !g_ascii_strcasecmp (header->name, "X-Newsreader") || + !g_ascii_strcasecmp (header->name, "X-MimeOLE"))) { + struct _camel_header_raw xmailer, *use_header = NULL; + + if (!g_ascii_strcasecmp (header->name, "X-MimeOLE")) { + for (use_header = header->next; use_header; use_header = use_header->next) { + if (!g_ascii_strcasecmp (use_header->name, "X-Mailer") || + !g_ascii_strcasecmp (use_header->name, "User-Agent") || + !g_ascii_strcasecmp (use_header->name, "X-Newsreader")) { + /* even we have X-MimeOLE, then use rather the standard one, when available */ + break; + } + } + } + + if (!use_header) + use_header = header; + + xmailer.name = (gchar *) "X-Evolution-Mailer"; + xmailer.value = use_header->value; + mailer_shown = TRUE; + + e_mail_formatter_format_header ( + formatter, buffer, part, + &xmailer, h->flags, charset); + if (strstr(use_header->value, "Evolution")) + have_icon = TRUE; + } else if (!face_decoded && face && !g_ascii_strcasecmp (header->name, "Face")) { + gchar *cp = header->value; + + /* Skip over spaces */ + while (*cp == ' ') + cp++; + + face_header_value = g_base64_decode ( + cp, &face_header_len); + face_header_value = g_realloc ( + face_header_value, + face_header_len + 1); + face_header_value[face_header_len] = 0; + face_decoded = TRUE; + /* Showing an encoded "Face" header makes little sense */ + } else if (!g_ascii_strcasecmp (header->name, h->name) && !face) { + e_mail_formatter_format_header ( + formatter, buffer, part, + header, h->flags, charset); + } + + header = header->next; + } + + link = g_list_next (link); + } + } + + g_string_append (buffer, "</table></td>"); + + if (photo_name) { + CamelMimePart *photopart; + gboolean only_local_photo; + + cia = camel_internet_address_new (); + camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name); + only_local_photo = e_mail_formatter_get_only_local_photos (formatter); + photopart = em_utils_contact_photo ( + registry, cia, only_local_photo); + + if (photopart) { + g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); + write_contact_picture (photopart, -1, buffer); + g_string_append (buffer, "</td>"); + g_object_unref (photopart); + } + g_object_unref (cia); + } + + if (!contact_has_photo && face_decoded) { + CamelMimePart *part; + + part = camel_mime_part_new (); + camel_mime_part_set_content ( + (CamelMimePart *) part, + (const gchar *) face_header_value, + face_header_len, "image/png"); + + g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); + write_contact_picture (part, 48, buffer); + g_string_append (buffer, "</td>"); + + g_object_unref (part); + g_free (face_header_value); + } + + if (have_icon) { + GtkIconInfo *icon_info; + CamelMimePart *iconpart = NULL; + + icon_info = gtk_icon_theme_lookup_icon ( + gtk_icon_theme_get_default (), + "evolution", 16, GTK_ICON_LOOKUP_NO_SVG); + if (icon_info != NULL) { + iconpart = load_picture_from_file ( + "image/png", gtk_icon_info_get_filename (icon_info), + cancellable); + gtk_icon_info_free (icon_info); + } + if (iconpart) { + g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); + write_contact_picture (iconpart, 16, buffer); + g_string_append (buffer, "</td>"); + + g_object_unref (iconpart); + } + } + + g_string_append (buffer, "</tr></table>"); +} + +static gboolean +emfe_headers_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + GString *buffer; + gint bg_color; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if (!part->part) + return FALSE; + + buffer = g_string_new (""); + + if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) { + GdkColor white = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 }; + bg_color = e_color_to_value (&white); + } else { + bg_color = e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY)); + } + + g_string_append_printf ( + buffer, + "<div class=\"headers\" style=\"background: #%06x;\">" + "<table border=\"0\" width=\"100%%\" style=\"color: #%06x;\">\n" + "<tr><td valign=\"top\" width=\"16\">\n", + bg_color, + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, + E_MAIL_FORMATTER_COLOR_HEADER))); + + if (context->flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE) { + g_string_append_printf (buffer, + "<img src=\"evo-file://%s/%s\" class=\"navigable\" " + "id=\"__evo-collapse-headers-img\" />" + "</td><td>", + EVOLUTION_IMAGESDIR, + (context->flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED) ? + "plus.png" : "minus.png"); + + format_short_headers (formatter, buffer, + (CamelMedium *) part->part, context->flags, cancellable); + } + + format_full_headers (formatter, buffer, + (CamelMedium *) part->part, context->flags, cancellable); + + g_string_append (buffer, "</td></tr></table></div>"); + + camel_stream_write_string (stream, buffer->str, cancellable, NULL); + + g_string_free (buffer, TRUE); + + return TRUE; +} + +static const gchar * +emfe_headers_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfe_headers_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfe_headers_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_headers_class_init (EMailFormatterHeadersClass *klass) +{ + e_mail_formatter_headers_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_headers_format; + iface->get_display_name = emfe_headers_get_display_name; + iface->get_description = emfe_headers_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_headers_mime_types; +} + +static void +e_mail_formatter_headers_init (EMailFormatterHeaders *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-image.c b/em-format/e-mail-formatter-image.c new file mode 100644 index 0000000000..890e9f3dcc --- /dev/null +++ b/em-format/e-mail-formatter-image.c @@ -0,0 +1,191 @@ +/* + * image-any.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-part-utils.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-inline-filter.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +static const gchar *formatter_mime_types[] = { "image/gif", "image/jpeg", + "image/png", "image/x-png", + "image/x-bmp", "image/bmp", + "image/svg", "image/x-cmu-raster", + "image/x-ico", + "image/x-portable-anymap", + "image/x-portable-bitmap", + "image/x-portable-graymap", + "image/x-portable-pixmap", + "image/x-xpixmap", + "image/jpg", "image/pjpeg", + "image/*", NULL }; + +typedef struct _EMailFormatterImage { + GObject parent; +} EMailFormatterImage; + +typedef struct _EMailFormatterImageClass { + GObjectClass parent_class; +} EMailFormatterImageClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterImage, + e_mail_formatter_image, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static gboolean +emfe_image_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *content; + CamelDataWrapper *dw; + GByteArray *ba; + CamelStream *raw_content; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + dw = camel_medium_get_content (CAMEL_MEDIUM (part->part)); + g_return_val_if_fail (dw, FALSE); + + raw_content = camel_stream_mem_new (); + camel_data_wrapper_decode_to_stream_sync (dw, raw_content, cancellable, NULL); + ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (raw_content)); + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + + if (!e_mail_formatter_get_animate_images (formatter)) { + + gchar *buff; + gsize len; + + e_mail_part_animation_extract_frame (ba, &buff, &len); + + camel_stream_write (stream, buff, len, cancellable, NULL); + + g_free (buff); + + } else { + + camel_stream_write ( + stream, (gchar *) ba->data, + ba->len, cancellable, NULL); + } + + } else { + + gchar *buffer; + + if (!e_mail_formatter_get_animate_images (formatter)) { + + gchar *buff; + gsize len; + + e_mail_part_animation_extract_frame (ba, &buff, &len); + + content = g_base64_encode ((guchar *) buff, len); + g_free (buff); + + } else { + content = g_base64_encode ((guchar *) ba->data, ba->len); + } + + /* The image is already base64-encrypted so we can directly + * paste it to the output */ + buffer = g_strdup_printf ( + "<img src=\"data:%s;base64,%s\" style=\"max-width: 100%%;\" />", + part->mime_type ? part->mime_type : "image/*", content); + + camel_stream_write_string (stream, buffer, cancellable, NULL); + g_free (buffer); + g_free (content); + } + + g_object_unref (raw_content); + + return TRUE; +} + +static const gchar * +emfe_image_get_display_name (EMailFormatterExtension *extension) +{ + return _("Regular Image"); +} + +static const gchar * +emfe_image_get_description (EMailFormatterExtension *extension) +{ + return _("Display part as an image"); +} + +static const gchar ** +emfe_image_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_image_class_init (EMailFormatterImageClass *klass) +{ + e_mail_formatter_image_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_image_format; + iface->get_display_name = emfe_image_get_display_name; + iface->get_description = emfe_image_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_image_mime_types; +} + +static void +e_mail_formatter_image_init (EMailFormatterImage *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-message-rfc822.c b/em-format/e-mail-formatter-message-rfc822.c new file mode 100644 index 0000000000..558579b00a --- /dev/null +++ b/em-format/e-mail-formatter-message-rfc822.c @@ -0,0 +1,276 @@ +/* + * e-mail-formatter-message-rfc822.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> +#include <glib-object.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-part-list.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +static const gchar* formatter_mime_types[] = { "message/rfc822", + "application/vnd.evolution.rfc822.end", + NULL }; + +typedef struct _EMailFormatterMessageRFC822 { + GObject parent; +} EMailFormatterMessageRFC822; + +typedef struct _EMailFormatterMessageRFC822Class { + GObjectClass parent_class; +} EMailFormatterMessageRFC822Class; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterMessageRFC822, + e_mail_formatter_message_rfc822, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static gboolean +emfe_message_rfc822_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + GSList *iter; + gchar *header, *end; + + header = e_mail_formatter_get_html_header (formatter); + camel_stream_write_string (stream, header, cancellable, NULL); + g_free (header); + + /* Print content of the message normally */ + context->mode = E_MAIL_FORMATTER_MODE_NORMAL; + + iter = e_mail_part_list_get_iter (context->parts, part->id); + + end = g_strconcat (part->id, ".end", NULL); + for (iter = iter->next; iter; iter = g_slist_next (iter)) { + EMailPart * p = iter->data; + if (!p) + continue; + + /* Check for nested rfc822 messages */ + if (g_str_has_suffix (p->id, ".rfc822")) { + gchar *sub_end = g_strconcat (p->id, ".end", NULL); + + while (iter) { + p = iter->data; + if (!p) { + iter = iter->next; + continue; + } + + if (g_strcmp0 (p->id, sub_end) == 0) { + break; + } + + iter = iter->next; + } + g_free (sub_end); + continue; + } + if ((g_strcmp0 (p->id, end) == 0)) + break; + + if (p->is_hidden) + continue; + + e_mail_formatter_format_as ( + formatter, context, p, + stream, NULL, cancellable); + + } + + g_free (end); + + context->mode = E_MAIL_FORMATTER_MODE_RAW; + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); + + } else if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) { + + GSList *iter; + gchar *end; + + /* Part is EMailPartAttachment */ + iter = e_mail_part_list_get_iter (context->parts, part->id); + iter = g_slist_next (iter); + + if (!iter || !iter->next || !iter->data) + return FALSE; + + part = iter->data; + end = g_strconcat (part->id, ".end", NULL); + + for (iter = iter->next; iter; iter = g_slist_next (iter)) { + EMailPart * p = iter->data; + if (!p) + continue; + + /* Skip attachment bar */ + if (g_str_has_suffix (part->id, ".attachment-bar")) + continue; + + /* Check for nested rfc822 messages */ + if (g_str_has_suffix (p->id, ".rfc822")) { + gchar *sub_end = g_strconcat (p->id, ".end", NULL); + + while (iter) { + p = iter->data; + if (!p) { + iter = iter->next; + continue; + } + + if (g_strcmp0 (p->id, sub_end) == 0) { + break; + } + + iter = iter->next; + } + g_free (sub_end); + continue; + } + + if ((g_strcmp0 (p->id, end) == 0)) + break; + + if (p->is_hidden) + continue; + + e_mail_formatter_format_as ( + formatter, context, p, + stream, NULL, cancellable); + } + + g_free (end); + + } else { + gchar *str; + gchar *uri; + + EMailPart *p; + GSList *iter; + + iter = e_mail_part_list_get_iter (context->parts, part->id); + if (!iter || !iter->next) + return FALSE; + + p = iter->data; + + uri = e_mail_part_build_uri (context->folder, context->message_uid, + "part_id", G_TYPE_STRING, p->id, + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, + "headers_collapsable", G_TYPE_INT, 0, + NULL); + + str = g_strdup_printf ( + "<div class=\"part-container\" style=\"border-color: #%06x; " + "background-color: #%06x;\">\n" + "<iframe width=\"100%%\" height=\"10\"" + " id=\"%s.iframe\" " + " frameborder=\"0\" src=\"%s\" name=\"%s\"></iframe>" + "</div>", + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY)), + part->id, uri, part->id); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + g_free (uri); + } + + return TRUE; +} + +static const gchar * +emfe_message_rfc822_get_display_name (EMailFormatterExtension *extension) +{ + return _("RFC822 message"); +} + +static const gchar * +emfe_message_rfc822_get_description (EMailFormatterExtension *extension) +{ + return _("Format part as an RFC822 message"); +} + +static const gchar ** +emfe_message_rfc822_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_message_rfc822_class_init (EMailFormatterMessageRFC822Class *klass) +{ + e_mail_formatter_message_rfc822_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_message_rfc822_format; + iface->get_display_name = emfe_message_rfc822_get_display_name; + iface->get_description = emfe_message_rfc822_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_message_rfc822_mime_types; +} + +static void +e_mail_formatter_message_rfc822_init (EMailFormatterMessageRFC822 *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-print-headers.c b/em-format/e-mail-formatter-print-headers.c new file mode 100644 index 0000000000..86fc85b063 --- /dev/null +++ b/em-format/e-mail-formatter-print-headers.c @@ -0,0 +1,258 @@ +/* + * e-mail-formatter-print-headers.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-formatter-utils.h> +#include <em-format/e-mail-inline-filter.h> +#include <libemail-engine/e-mail-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailFormatterPrintHeaders { + GObject parent; +} EMailFormatterPrintHeaders; + +typedef struct _EMailFormatterPrintHeadersClass { + GObjectClass parent_class; +} EMailFormatterPrintHeadersClass; + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.headers", NULL }; + +static void e_mail_formatter_print_formatter_extension_interface_init + (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_print_mail_extension_interface_init + (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterPrintHeaders, + e_mail_formatter_print_headers, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_print_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_print_formatter_extension_interface_init)) + +static gboolean +emfpe_headers_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + struct _camel_header_raw raw_header; + GString *str, *tmp; + gchar *subject; + const gchar *buf; + GSList *parts_iter; + GList *iter; + gint attachments_count; + gchar *part_id_prefix; + const GQueue *headers; + + buf = camel_medium_get_header (CAMEL_MEDIUM (part->part), "subject"); + subject = camel_header_decode_string (buf, "UTF-8"); + str = g_string_new (""); + g_string_append_printf (str, "<h1>%s</h1>\n", subject); + g_free (subject); + + g_string_append ( + str, + "<table border=\"0\" cellspacing=\"5\" " + "cellpadding=\"0\" class=\"printing-header\">\n"); + + headers = e_mail_formatter_get_headers (formatter); + for (iter = headers->head; iter; iter = iter->next) { + + EMailFormatterHeader *header = iter->data; + raw_header.name = header->name; + + /* Skip 'Subject' header, it's already displayed. */ + if (g_ascii_strncasecmp (header->name, "Subject", 7) == 0) + continue; + + if (header->value && *header->value) { + raw_header.value = header->value; + e_mail_formatter_format_header (formatter, str, + CAMEL_MEDIUM (part->part), &raw_header, + header->flags | E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, + "UTF-8"); + } else { + raw_header.value = g_strdup (camel_medium_get_header ( + CAMEL_MEDIUM (context->message), header->name)); + + if (raw_header.value && *raw_header.value) { + e_mail_formatter_format_header (formatter, str, + CAMEL_MEDIUM (part->part), &raw_header, + header->flags | E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, + "UTF-8"); + } + + if (raw_header.value) + g_free (raw_header.value); + } + } + + /* Get prefix of this PURI */ + part_id_prefix = g_strndup (part->id, g_strrstr (part->id, ".") - part->id); + + /* Add encryption/signature header */ + raw_header.name = _("Security"); + tmp = g_string_new (""); + /* Find first secured part. */ + for (parts_iter = context->parts; parts_iter; parts_iter = parts_iter->next) { + + EMailPart *mail_part = parts_iter->data; + if (mail_part == NULL) + continue; + + if (mail_part->validity_type == 0) + continue; + + if (!g_str_has_prefix (mail_part->id, part_id_prefix)) + continue; + + if ((mail_part->validity_type & E_MAIL_PART_VALIDITY_PGP) && + (mail_part->validity_type & E_MAIL_PART_VALIDITY_SIGNED)) { + g_string_append (tmp, _("GPG signed")); + } + if ((mail_part->validity_type & E_MAIL_PART_VALIDITY_PGP) && + (mail_part->validity_type & E_MAIL_PART_VALIDITY_ENCRYPTED)) { + if (tmp->len > 0) g_string_append (tmp, ", "); + g_string_append (tmp, _("GPG encrpyted")); + } + if ((mail_part->validity_type & E_MAIL_PART_VALIDITY_SMIME) && + (mail_part->validity_type & E_MAIL_PART_VALIDITY_SIGNED)) { + + if (tmp->len > 0) g_string_append (tmp, ", "); + g_string_append (tmp, _("S/MIME signed")); + } + if ((mail_part->validity_type & E_MAIL_PART_VALIDITY_SMIME) && + (mail_part->validity_type & E_MAIL_PART_VALIDITY_ENCRYPTED)) { + + if (tmp->len > 0) g_string_append (tmp, ", "); + g_string_append (tmp, _("S/MIME encrpyted")); + } + + break; + } + + if (tmp->len > 0) { + raw_header.value = tmp->str; + e_mail_formatter_format_header ( + formatter, str, CAMEL_MEDIUM (part->part), &raw_header, + E_MAIL_FORMATTER_HEADER_FLAG_BOLD | + E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, "UTF-8"); + } + g_string_free (tmp, TRUE); + + /* Count attachments and display the number as a header */ + attachments_count = 0; + + for (parts_iter = context->parts; parts_iter; parts_iter = parts_iter->next) { + + EMailPart *mail_part = parts_iter->data; + if (!mail_part) + continue; + + if (!g_str_has_prefix (mail_part->id, part_id_prefix)) + continue; + + if (mail_part->is_attachment && !mail_part->cid && + !mail_part->is_hidden) { + attachments_count++; + } + } + + if (attachments_count > 0) { + raw_header.name = _("Attachments"); + raw_header.value = g_strdup_printf ("%d", attachments_count); + e_mail_formatter_format_header ( + formatter, str, CAMEL_MEDIUM (part->part), &raw_header, + E_MAIL_FORMATTER_HEADER_FLAG_BOLD | + E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, "UTF-8"); + g_free (raw_header.value); + } + + g_string_append (str, "</table>"); + + camel_stream_write_string (stream, str->str, cancellable, NULL); + g_string_free (str, TRUE); + g_free (part_id_prefix); + + return TRUE; +} + +static const gchar * +emfpe_headers_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfpe_headers_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfpe_headers_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_print_headers_class_init (EMailFormatterPrintHeadersClass *klass) +{ + e_mail_formatter_print_headers_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_print_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfpe_headers_format; + iface->get_display_name = emfpe_headers_get_display_name; + iface->get_description = emfpe_headers_get_description; +} + +static void +e_mail_formatter_print_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfpe_headers_mime_types; +} + +static void +e_mail_formatter_print_headers_init (EMailFormatterPrintHeaders *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-print.c b/em-format/e-mail-formatter-print.c new file mode 100644 index 0000000000..a9109a647e --- /dev/null +++ b/em-format/e-mail-formatter-print.c @@ -0,0 +1,266 @@ +/* + * e-mail-formatter-print.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-print.h" + +#include <camel/camel.h> + +#include "e-mail-format-extensions.h" +#include "e-mail-part-attachment.h" +#include "e-mail-formatter-extension.h" +#include "e-mail-formatter-utils.h" +#include "e-mail-part.h" + +#include <gdk/gdk.h> +#include <glib/gi18n.h> + +static gpointer e_mail_formatter_print_parent_class = 0; + +static void +write_attachments_list (EMailFormatter *formatter, + EMailFormatterContext *context, + GSList *attachments, + CamelStream *stream, + GCancellable *cancellable) +{ + GString *str; + GSList *iter; + + if (!attachments) + return; + + str = g_string_new ( + "<table border=\"0\" cellspacing=\"5\" cellpadding=\"0\" " + "class=\"attachments-list\" >\n"); + g_string_append_printf (str, + "<tr><th colspan=\"2\"><h1>%s</h1></td></tr>\n" + "<tr><th>%s</th><th>%s</th></tr>\n", + _("Attachments"), _("Name"), _("Size")); + + for (iter = attachments; iter; iter = iter->next) { + EMailPartAttachment *part = iter->data; + EAttachment *attachment; + GFileInfo *fi; + gchar *name, *size; + + if (!part) + continue; + + attachment = part->attachment; + fi = e_attachment_get_file_info (attachment); + if (!fi) + continue; + + if (e_attachment_get_description (attachment) && + *e_attachment_get_description (attachment)) { + name = g_strdup_printf ("%s (%s)", + e_attachment_get_description (attachment), + g_file_info_get_display_name (fi)); + } else { + name = g_strdup (g_file_info_get_display_name (fi)); + } + + size = g_format_size (g_file_info_get_size (fi)); + + g_string_append_printf (str, "<tr><td>%s</td><td>%s</td></tr>\n", + name, size); + + g_free (name); + g_free (size); + } + + g_string_append (str, "</table>\n"); + + camel_stream_write_string (stream, str->str, cancellable, NULL); + g_string_free (str, TRUE); +} + +static void +mail_formatter_print_run (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable) +{ + GSList *iter; + GSList *attachments; + + context->mode = E_MAIL_FORMATTER_MODE_PRINTING; + + camel_stream_write_string (stream, + "<!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\" media=\"print\" " + "href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview-print.css\" />\n" + "</head>\n" + "<body style=\"background: #FFF; color: #000;\">", + cancellable, NULL); + + attachments = NULL; + for (iter = context->parts; iter; iter = g_slist_next (iter)) { + + EMailPart *part; + gboolean ok; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + part = iter->data; + if (!part) + continue; + + if (part->is_hidden && !part->is_error) { + if (g_str_has_suffix (part->id, ".rfc822")) { + iter = e_mail_formatter_find_rfc822_end_iter (iter); + } + + continue; + } + + if (!part->mime_type) + continue; + + if (part->is_attachment) { + if (part->cid != NULL) + continue; + + attachments = g_slist_append (attachments, part); + } + + 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")) { + iter = e_mail_formatter_find_rfc822_end_iter (iter); + + continue; + } + } + + write_attachments_list (formatter, context, attachments, stream, cancellable); + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); +} + +static void +mail_formatter_set_style (EMailFormatter *formatter, + GtkStyle *style, + GtkStateType state) +{ + EMailFormatterClass *formatter_class; + + /* White background */ + GdkColor body_color = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 }; + /* Black text */ + GdkColor text_color = { 0, 0, 0, 0 }; + + g_object_freeze_notify (G_OBJECT (formatter)); + + /* Set the other colors */ + formatter_class = E_MAIL_FORMATTER_CLASS (e_mail_formatter_print_parent_class); + formatter_class->set_style (formatter, style, state); + + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME, &body_color); + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_CONTENT, &body_color); + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT, &text_color); + + g_object_thaw_notify (G_OBJECT (formatter)); +} + +static void +e_mail_formatter_print_init (EMailFormatterPrint *formatter) +{ + +} + +static void +e_mail_formatter_print_finalize (GObject *object) +{ + /* Chain up to parent's finalize() */ + G_OBJECT_CLASS (e_mail_formatter_print_parent_class)->finalize (object); +} + +static void +e_mail_formatter_print_class_init (EMailFormatterPrintClass *klass) +{ + GObjectClass *object_class; + EMailFormatterClass *formatter_class; + + e_mail_formatter_print_parent_class = g_type_class_peek_parent (klass); + + formatter_class = E_MAIL_FORMATTER_CLASS (klass); + formatter_class->run = mail_formatter_print_run; + formatter_class->set_style = mail_formatter_set_style; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = e_mail_formatter_print_finalize; +} + +static void +e_mail_formatter_print_base_init (EMailFormatterPrintClass *klass) +{ + e_mail_formatter_print_internal_extensions_load ( + E_MAIL_EXTENSION_REGISTRY ( + E_MAIL_FORMATTER_CLASS (klass)->extension_registry)); + + E_MAIL_FORMATTER_CLASS (klass)->text_html_flags = + CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | + CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; +} + +EMailFormatter * +e_mail_formatter_print_new (void) +{ + return g_object_new (E_TYPE_MAIL_FORMATTER_PRINT, NULL); +} + +GType +e_mail_formatter_print_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + const GTypeInfo type_info = { + sizeof (EMailFormatterClass), + (GBaseInitFunc) e_mail_formatter_print_base_init, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) e_mail_formatter_print_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailFormatterPrint), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_mail_formatter_print_init, + NULL /* value_table */ + }; + + type = g_type_register_static (E_TYPE_MAIL_FORMATTER, + "EMailFormatterPrint", &type_info, 0); + } + + return type; +} + diff --git a/em-format/e-mail-formatter-print.h b/em-format/e-mail-formatter-print.h new file mode 100644 index 0000000000..1783cc068a --- /dev/null +++ b/em-format/e-mail-formatter-print.h @@ -0,0 +1,63 @@ +/* + * e-mail-formatter-print.h + * + * 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/> + * + */ + +#ifndef E_MAIL_FORMATTER_PRINT_H_ +#define E_MAIL_FORMATTER_PRINT_H_ + +#include <em-format/e-mail-formatter.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER_PRINT \ + (e_mail_formatter_print_get_type ()) +#define E_MAIL_FORMATTER_PRINT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_FORMATTER_PRINT, EMailFormatterPrint)) +#define E_MAIL_FORMATTER_PRINT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_FORMATTER_PRINT, EMailFormatterPrintClass)) +#define E_IS_MAIL_FORMATTER_PRINT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_FORMATTER_PRINT)) +#define E_IS_MAIL_FORMATTER_PRINT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_FORMATTER_PRINT)) +#define E_MAIL_FORMATTER_PRINT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_FORMATTER_PRINT, EMailFormatterPrintClass)) + +G_BEGIN_DECLS; + +typedef struct _EMailFormatterPrint EMailFormatterPrint; +typedef struct _EMailFormatterPrintClass EMailFormatterPrintClass; +typedef struct _EMailFormatterPrintContext EMailFormatterPrintContext; + +struct _EMailFormatterPrint { + EMailFormatter parent; +}; + +struct _EMailFormatterPrintClass { + EMailFormatterClass parent_class; +}; + +GType e_mail_formatter_print_get_type (void); + +EMailFormatter * e_mail_formatter_print_new (void); + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_PRINT_H_ */ diff --git a/em-format/e-mail-formatter-quote-attachment.c b/em-format/e-mail-formatter-quote-attachment.c new file mode 100644 index 0000000000..590f23ee6d --- /dev/null +++ b/em-format/e-mail-formatter-quote-attachment.c @@ -0,0 +1,163 @@ +/* + * e-mail-formatter-qoute-attachment.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" +#include "e-mail-part-attachment.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#define d(x) + +typedef struct _EMailFormatterQuoteAttachment { + GObject parent; +} EMailFormatterQuoteAttachment; + +typedef struct _EMailFormatterQuoteAttachmentClass { + GObjectClass parent_class; +} EMailFormatterQuoteAttachmentClass; + +static void e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterQuoteAttachment, + e_mail_formatter_quote_attachment, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_quote_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_quote_formatter_extension_interface_init) +) + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.attachment", + NULL }; + +static gboolean +emfqe_attachment_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *text, *html; + guint32 text_format_flags; + EMailPartAttachment *empa; + EMailPart *att_part; + GSList *iter; + + empa = E_MAIL_PART_ATTACHMENT (part); + + if (!empa->attachment_view_part_id) + return FALSE; + + iter = e_mail_part_list_get_iter ( + context->parts, empa->attachment_view_part_id); + if (!iter || !iter->data) + return FALSE; + + att_part = iter->data; + + camel_stream_write_string (stream, "<br><br>", cancellable, NULL); + + text_format_flags = + e_mail_formatter_get_text_format_flags (formatter); + text = e_mail_part_describe (part->part, + empa ? empa->snoop_mime_type : part->mime_type); + + html = camel_text_to_html ( + text, + text_format_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, + 0); + camel_stream_write_string (stream, html, cancellable, NULL); + camel_stream_write_string (stream, "<br>", cancellable, NULL); + g_free (html); + g_free (text); + + camel_stream_write_string (stream, + "<!--+GtkHTML:<DATA class=\"ClueFlow\" " + "key=\"orig\" value=\"1\">-->\n" + "<blockquote type=cite>\n", cancellable, NULL); + + e_mail_formatter_format_as ( + formatter, context, att_part, stream, NULL, cancellable); + + camel_stream_write_string (stream, + "</blockquote><!--+GtkHTML:" + "<DATA class=\"ClueFlow\" clear=\"orig\">-->", + cancellable, NULL); + + return TRUE; +} + +static const gchar * +emfqe_attachment_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfqe_attachment_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfqe_attachment_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_quote_attachment_class_init (EMailFormatterQuoteAttachmentClass *klass) +{ + e_mail_formatter_quote_attachment_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfqe_attachment_format; + iface->get_display_name = emfqe_attachment_get_display_name; + iface->get_description = emfqe_attachment_get_description; +} + +static void +e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfqe_attachment_mime_types; +} + +static void +e_mail_formatter_quote_attachment_init (EMailFormatterQuoteAttachment *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-quote-headers.c b/em-format/e-mail-formatter-quote-headers.c new file mode 100644 index 0000000000..f1b2b2e61f --- /dev/null +++ b/em-format/e-mail-formatter-quote-headers.c @@ -0,0 +1,162 @@ +/* + * e-mail-formatter-quote-headers.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-formatter-utils.h> +#include <em-format/e-mail-inline-filter.h> +#include <libemail-engine/e-mail-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailFormatterQuoteHeaders { + GObject parent; +} EMailFormatterQuoteHeaders; + +typedef struct _EMailFormatterQuoteHeadersClass { + GObjectClass parent_class; +} EMailFormatterQuoteHeadersClass; + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.headers", NULL }; + +static void e_mail_formatter_quote_formatter_extension_interface_init + (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_quote_mail_extension_interface_init + (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterQuoteHeaders, + e_mail_formatter_quote_headers, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_quote_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_quote_formatter_extension_interface_init)) + +static gboolean +emqfe_headers_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelContentType *ct; + const gchar *charset; + GList *iter; + GString *buffer; + const GQueue *default_headers; + + if (!part) + return FALSE; + + ct = camel_mime_part_get_content_type ((CamelMimePart *) part->part); + charset = camel_content_type_param (ct, "charset"); + charset = camel_iconv_charset_name (charset); + + buffer = g_string_new (""); + + /* dump selected headers */ + default_headers = e_mail_formatter_get_headers (formatter); + for (iter = default_headers->head; iter; iter = iter->next) { + struct _camel_header_raw *raw_header; + EMailFormatterHeader *h = iter->data; + guint32 flags; + + flags = h->flags & ~E_MAIL_FORMATTER_HEADER_FLAG_HTML; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE; + + for (raw_header = part->part->headers; raw_header; raw_header = raw_header->next) { + + if (g_strcmp0 (raw_header->name, h->name) == 0) { + + e_mail_formatter_format_header ( + formatter, buffer, (CamelMedium *) part->part, + raw_header, flags, charset); + + g_string_append (buffer, "<br>\n"); + break; + } + } + } + g_string_append (buffer, "<br>\n"); + + camel_stream_write_string (stream, buffer->str, cancellable, NULL); + + g_string_free (buffer, TRUE); + + return TRUE; +} + +static const gchar * +emqfe_headers_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emqfe_headers_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emqfe_headers_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_quote_headers_class_init (EMailFormatterQuoteHeadersClass *klass) +{ + e_mail_formatter_quote_headers_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emqfe_headers_format; + iface->get_display_name = emqfe_headers_get_display_name; + iface->get_description = emqfe_headers_get_description; +} + +static void +e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emqfe_headers_mime_types; +} + +static void +e_mail_formatter_quote_headers_init (EMailFormatterQuoteHeaders *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-quote-message-rfc822.c b/em-format/e-mail-formatter-quote-message-rfc822.c new file mode 100644 index 0000000000..847686240f --- /dev/null +++ b/em-format/e-mail-formatter-quote-message-rfc822.c @@ -0,0 +1,188 @@ +/* + * e-mail-formatter-quote-message-rfc822.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> +#include <glib-object.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter-quote.h> +#include <em-format/e-mail-part-list.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +static const gchar* formatter_mime_types[] = { "message/rfc822", + "application/vnd.evolution.rfc822.end", + NULL }; + +typedef struct _EMailFormatterQuoteMessageRFC822 { + GObject parent; +} EMailFormatterQuoteMessageRFC822; + +typedef struct _EMailFormatterQuoteMessageRFC822Class { + GObjectClass parent_class; +} EMailFormatterQuoteMessageRFC822Class; + +static void e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterQuoteMessageRFC822, + e_mail_formatter_quote_message_rfc822, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_quote_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_quote_formatter_extension_interface_init)); + +static gboolean +emfqe_message_rfc822_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + GSList *iter; + gchar *header, *end; + EMailFormatterQuoteContext *qc = (EMailFormatterQuoteContext *) context; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + header = e_mail_formatter_get_html_header (formatter); + camel_stream_write_string (stream, header, cancellable, NULL); + g_free (header); + + iter = e_mail_part_list_get_iter (context->parts, part->id); + + end = g_strconcat (part->id, ".end", NULL); + for (iter = iter->next; iter; iter = iter->next) { + EMailPart * p = iter->data; + if (!p) + continue; + + /* Skip attachment bar */ + if (g_str_has_suffix (p->id, ".attachment-bar")) + continue; + + if (g_str_has_suffix (p->id, ".headers.")) { + if (qc->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS) { + e_mail_formatter_format_as ( + formatter, context, part, stream, + "application/vnd.evolution.headers", + cancellable); + } + + continue; + } + + /* Check for nested rfc822 messages */ + if (g_str_has_suffix (p->id, ".rfc822")) { + gchar *sub_end = g_strconcat (p->id, ".end", NULL); + + while (iter) { + p = iter->data; + if (!p) { + iter = iter->next; + continue; + } + + if (g_strcmp0 (p->id, sub_end) == 0) { + break; + } + + iter = iter->next; + } + g_free (sub_end); + continue; + } + if ((g_strcmp0 (p->id, end) == 0)) + break; + + if (p->is_hidden) + continue; + + e_mail_formatter_format_as ( + formatter, context, p, + stream, NULL, cancellable); + + } + + g_free (end); + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); + + return TRUE; +} + +static const gchar * +emfqe_message_rfc822_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfqe_message_rfc822_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfqe_message_rfc822_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_quote_message_rfc822_class_init (EMailFormatterQuoteMessageRFC822Class *klass) +{ + e_mail_formatter_quote_message_rfc822_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfqe_message_rfc822_format; + iface->get_display_name = emfqe_message_rfc822_get_display_name; + iface->get_description = emfqe_message_rfc822_get_description; +} + +static void +e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfqe_message_rfc822_mime_types; +} + +static void +e_mail_formatter_quote_message_rfc822_init (EMailFormatterQuoteMessageRFC822 *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-quote-text-enriched.c b/em-format/e-mail-formatter-quote-text-enriched.c new file mode 100644 index 0000000000..e48154edd8 --- /dev/null +++ b/em-format/e-mail-formatter-quote-text-enriched.c @@ -0,0 +1,141 @@ +/* + * e-mail-formatter-quote-text-enriched.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-inline-filter.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +static const gchar *formatter_mime_types[] = { "text/enriched", + "text/richtext", + NULL }; + +typedef struct _EMailFormatterQuoteTextEnriched { + GObject parent; +} EMailFormatterQuoteTextEnriched; + +typedef struct _EMailFormatterQuoteTextEnrichedClass { + GObjectClass parent_class; +} EMailFormatterQuoteTextEnrichedClass; + +static void e_mail_formatter_quote_formatter_extension_interace_init + (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_quote_mail_extension_interface_init + (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterQuoteTextEnriched, + e_mail_formatter_quote_text_enriched, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_quote_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_quote_formatter_extension_interace_init)); + +static gboolean +emqfe_text_enriched_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *enriched; + guint32 camel_flags = 0; + + if (g_strcmp0 (part->mime_type, "text/richtext") == 0) { + camel_flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; + camel_stream_write_string ( + stream, "\n<!-- text/richtext -->\n", + cancellable, NULL); + } else { + camel_stream_write_string ( + stream, "\n<!-- text/enriched -->\n", + cancellable, NULL); + } + + enriched = camel_mime_filter_enriched_new (camel_flags); + filtered_stream = camel_stream_filter_new (stream); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), enriched); + g_object_unref (enriched); + + camel_stream_write_string (stream, "<br><hr><br>", cancellable, NULL); + e_mail_formatter_format_text (formatter, part, filtered_stream, cancellable); + camel_stream_flush (filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + return TRUE; +} + +static const gchar * +emqfe_text_enriched_get_display_name (EMailFormatterExtension *extension) +{ + return _("Richtext"); +} + +static const gchar * +emqfe_text_enriched_get_description (EMailFormatterExtension *extension) +{ + return _("Display part as enriched text"); +} + +static const gchar ** +emqfe_text_enriched_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_quote_text_enriched_class_init (EMailFormatterQuoteTextEnrichedClass *klass) +{ + e_mail_formatter_quote_text_enriched_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_quote_formatter_extension_interace_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emqfe_text_enriched_format; + iface->get_display_name = emqfe_text_enriched_get_display_name; + iface->get_description = emqfe_text_enriched_get_description; +} + +static void +e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emqfe_text_enriched_mime_types; +} + +static void +e_mail_formatter_quote_text_enriched_init (EMailFormatterQuoteTextEnriched *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-quote-text-html.c b/em-format/e-mail-formatter-quote-text-html.c new file mode 100644 index 0000000000..d4ef287878 --- /dev/null +++ b/em-format/e-mail-formatter-quote-text-html.c @@ -0,0 +1,143 @@ +/* + * e-mail-formatter-quote-text-html.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter-quote.h> +#include <em-format/e-mail-stripsig-filter.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#include <string.h> + +static const gchar *formatter_mime_types[] = { "text/html", NULL }; + +typedef struct _EMailFormatterQuoteTextHTML { + GObject parent; +} EMailFormatterQuoteTextHTML; + +typedef struct _EMailFormatterQuoteTextHTMLClass { + GObjectClass parent_class; +} EMailFormatterQuoteTextHTMLClass; + +static void e_mail_formatter_quote_formatter_extension_interface_init + (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_quote_mail_extension_interface_init + (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterQuoteTextHTML, + e_mail_formatter_quote_text_html, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_quote_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_quote_formatter_extension_interface_init)); + +static gboolean +emqfe_text_html_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + EMailFormatterQuoteContext *qf_context; + + qf_context = (EMailFormatterQuoteContext *) context; + + camel_stream_write_string ( + stream, "\n<!-- text/html -->\n", cancellable, NULL); + + if ((qf_context->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG) == 0) { + CamelMimeFilter *sig_strip; + CamelStream *filtered_stream; + + filtered_stream = camel_stream_filter_new (stream); + + sig_strip = e_mail_stripsig_filter_new (FALSE); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), sig_strip); + g_object_unref (sig_strip); + + e_mail_formatter_format_text ( + formatter, part, filtered_stream, cancellable); + camel_stream_flush (filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + } else { + e_mail_formatter_format_text ( + formatter, part, stream, cancellable); + } + + return TRUE; +} + +static const gchar * +emqfe_text_html_get_display_name (EMailFormatterExtension *extension) +{ + return _("HTML"); +} + +static const gchar * +emqfe_text_html_get_description (EMailFormatterExtension *extension) +{ + return _("Format part as HTML"); +} + +static const gchar ** +emqfe_text_html_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_quote_text_html_class_init (EMailFormatterQuoteTextHTMLClass *klass) +{ + e_mail_formatter_quote_text_html_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emqfe_text_html_format; + iface->get_display_name = emqfe_text_html_get_display_name; + iface->get_description = emqfe_text_html_get_description; +} + +static void +e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emqfe_text_html_mime_types; +} + +static void +e_mail_formatter_quote_text_html_init (EMailFormatterQuoteTextHTML *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-quote-text-plain.c b/em-format/e-mail-formatter-quote-text-plain.c new file mode 100644 index 0000000000..062e945fea --- /dev/null +++ b/em-format/e-mail-formatter-quote-text-plain.c @@ -0,0 +1,162 @@ +/* + * e-mail-formatter-quote-text-plain.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter-quote.h> +#include <em-format/e-mail-part-utils.h> +#include <em-format/e-mail-stripsig-filter.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +static const gchar *formatter_mime_types[] = { "text/plain", NULL }; + +typedef struct _EMailFormatterQuoteTextPlain { + GObject parent; +} EMailFormatterQuoteTextPlain; + +typedef struct _EMailFormatterQuoteTextPlainClass { + GObjectClass parent_class; +} EMailFormatterQuoteTextPlainClass; + +static void e_mail_formatter_quote_formatter_extension_interface_init + (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_quote_mail_extension_interface_init + (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterQuoteTextPlain, + e_mail_formatter_quote_text_plain, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_quote_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_quote_formatter_extension_interface_init)); + +static gboolean +emqfe_text_plain_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *html_filter; + CamelMimeFilter *sig_strip; + CamelContentType *type; + EMailFormatterQuoteContext *qf_context; + const gchar *format; + guint32 rgb = 0x737373, text_flags; + + if (!part->part) + return FALSE; + + qf_context = (EMailFormatterQuoteContext *) context; + + text_flags = CAMEL_MIME_FILTER_TOHTML_PRE | + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | + CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; + + if (e_mail_formatter_get_mark_citations (formatter)) { + text_flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; + } + + /* Check for RFC 2646 flowed text. */ + type = camel_mime_part_get_content_type (part->part); + if (camel_content_type_is(type, "text", "plain") + && (format = camel_content_type_param(type, "format")) + && !g_ascii_strcasecmp(format, "flowed")) + text_flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; + + filtered_stream = camel_stream_filter_new (stream); + + if ((qf_context->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG) == 0) { + sig_strip = e_mail_stripsig_filter_new (TRUE); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), sig_strip); + g_object_unref (sig_strip); + } + + html_filter = camel_mime_filter_tohtml_new (text_flags, rgb); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), html_filter); + g_object_unref (html_filter); + + e_mail_formatter_format_text ( + formatter, part, filtered_stream, cancellable); + + camel_stream_flush (filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + return TRUE; +} + +static const gchar * +emqfe_text_plain_get_display_name (EMailFormatterExtension *extension) +{ + return _("Plain Text"); +} + +static const gchar * +emqfe_text_plain_get_description (EMailFormatterExtension *extension) +{ + return _("Format part as plain text"); +} + +static const gchar ** +emqfe_text_plain_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_quote_text_plain_class_init (EMailFormatterQuoteTextPlainClass *klass) +{ + e_mail_formatter_quote_text_plain_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_quote_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emqfe_text_plain_format; + iface->get_display_name = emqfe_text_plain_get_display_name; + iface->get_description = emqfe_text_plain_get_description; +} + +static void +e_mail_formatter_quote_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emqfe_text_plain_mime_types; +} + +static void +e_mail_formatter_quote_text_plain_init (EMailFormatterQuoteTextPlain *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-quote.c b/em-format/e-mail-formatter-quote.c new file mode 100644 index 0000000000..e4a221e3d1 --- /dev/null +++ b/em-format/e-mail-formatter-quote.c @@ -0,0 +1,224 @@ +/* + * e-mail-formatter-quote.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-quote.h" + +#include <camel/camel.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-format-extensions.h" +#include "e-mail-part.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-utils.h" + +#include <libebackend/libebackend.h> +#include <gdk/gdk.h> +#include <glib/gi18n.h> + +struct _EMailFormatterQuotePrivate { + gchar *credits; + EMailFormatterQuoteFlags flags; +}; + +#define E_MAIL_FORMATTER_QUOTE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_FORMATTER_QUOTE, EMailFormatterQuotePrivate)) + +static gpointer e_mail_formatter_quote_parent_class = 0; + +static EMailFormatterContext * +mail_formatter_quote_create_context (EMailFormatter *formatter) +{ + return g_malloc0 (sizeof (EMailFormatterQuoteContext)); +} + +static void +mail_formatter_quote_free_context (EMailFormatter *formatter, + EMailFormatterContext *context) +{ + g_free ((EMailFormatterQuoteContext *) context); +} + +static void +mail_formatter_quote_run (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable) +{ + EMailFormatterQuote *qf; + EMailFormatterQuoteContext *qf_context; + GSettings *settings; + GSList *iter; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + qf = E_MAIL_FORMATTER_QUOTE (formatter); + + qf_context = (EMailFormatterQuoteContext *) context; + qf_context->qf_flags = qf->priv->flags; + + g_seekable_seek ( + G_SEEKABLE (stream), + 0, G_SEEK_SET, NULL, NULL); + + settings = g_settings_new ("org.gnome.evolution.mail"); + if (g_settings_get_boolean ( + settings, "composer-top-signature")) + camel_stream_write_string ( + stream, "<br>\n", cancellable, NULL); + g_object_unref (settings); + + if (qf->priv->credits && *qf->priv->credits) { + gchar *credits = g_strdup_printf ("%s<br/>", qf->priv->credits); + camel_stream_write_string (stream, credits, cancellable, NULL); + g_free (credits); + } else { + camel_stream_write_string (stream, "<br/>", cancellable, NULL); + } + + if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) { + camel_stream_write_string (stream, + "<!--+GtkHTML:<DATA class=\"ClueFlow\" " + "key=\"orig\" value=\"1\">-->\n" + "<blockquote type=cite>\n", cancellable, NULL); + } + + for (iter = context->parts; iter; iter = iter->next) { + EMailPart *part = iter->data; + + if (!part) + continue; + + if (g_str_has_suffix (part->id, ".rfc822")) { + gchar *end = g_strconcat (part->id, ".end", NULL); + + while (iter) { + EMailPart *p = iter->data; + if (!p) { + iter = iter->next; + continue; + } + + if (g_strcmp0 (p->id, end) == 0) + break; + + iter = iter->next; + } + g_free (end); + continue; + } + + if (part->is_hidden) + continue; + + e_mail_formatter_format_as ( + formatter, context, part, stream, + part->mime_type, cancellable); + } + + if (qf->priv->flags & E_MAIL_FORMATTER_QUOTE_FLAG_CITE) { + camel_stream_write_string ( + stream, "</blockquote><!--+GtkHTML:" + "<DATA class=\"ClueFlow\" clear=\"orig\">-->", + cancellable, NULL); + } +} + +static void +e_mail_formatter_quote_init (EMailFormatterQuote *formatter) +{ + formatter->priv = E_MAIL_FORMATTER_QUOTE_GET_PRIVATE (formatter); +} + +static void +e_mail_formatter_quote_finalize (GObject *object) +{ + /* Chain up to parent's finalize() */ + G_OBJECT_CLASS (e_mail_formatter_quote_parent_class)->finalize (object); +} + +static void +e_mail_formatter_quote_base_init (EMailFormatterQuoteClass *klass) +{ + e_mail_formatter_quote_internal_extensions_load ( + E_MAIL_EXTENSION_REGISTRY ( + E_MAIL_FORMATTER_CLASS (klass)->extension_registry)); + + E_MAIL_FORMATTER_CLASS (klass)->text_html_flags = + CAMEL_MIME_FILTER_TOHTML_PRE | + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | + CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; +} + +static void +e_mail_formatter_quote_class_init (EMailFormatterQuoteClass *klass) +{ + GObjectClass *object_class; + EMailFormatterClass *formatter_class; + + e_mail_formatter_quote_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, sizeof (EMailFormatterQuotePrivate)); + + formatter_class = E_MAIL_FORMATTER_CLASS (klass); + formatter_class->run = mail_formatter_quote_run; + formatter_class->create_context = mail_formatter_quote_create_context; + formatter_class->free_context = mail_formatter_quote_free_context; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = e_mail_formatter_quote_finalize; +} + +GType +e_mail_formatter_quote_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + const GTypeInfo type_info = { + sizeof (EMailFormatterClass), + (GBaseInitFunc) e_mail_formatter_quote_base_init, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) e_mail_formatter_quote_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailFormatterQuote), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_mail_formatter_quote_init, + NULL /* value_table */ + }; + + type = g_type_register_static (E_TYPE_MAIL_FORMATTER, + "EMailFormatterQuote", &type_info, 0); + } + + return type; +} + +EMailFormatter * +e_mail_formatter_quote_new (const gchar *credits, + EMailFormatterQuoteFlags flags) +{ + EMailFormatterQuote *formatter; + formatter = g_object_new (E_TYPE_MAIL_FORMATTER_QUOTE, NULL); + + formatter->priv->credits = g_strdup (credits); + formatter->priv->flags = flags; + + return (EMailFormatter *) formatter; +} diff --git a/em-format/e-mail-formatter-quote.h b/em-format/e-mail-formatter-quote.h new file mode 100644 index 0000000000..fa6730b964 --- /dev/null +++ b/em-format/e-mail-formatter-quote.h @@ -0,0 +1,79 @@ +/* + * e-mail-formatter-quote.h + * + * 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/> + * + */ + +#ifndef E_MAIL_FORMATTER_QUOTE_H_ +#define E_MAIL_FORMATTER_QUOTE_H_ + +#include <em-format/e-mail-formatter.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER_QUOTE \ + (e_mail_formatter_quote_get_type ()) +#define E_MAIL_FORMATTER_QUOTE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_FORMATTER_QUOTE, EMailFormatterQuote)) +#define E_MAIL_FORMATTER_QUOTE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_FORMATTER_QUOTE, EMailFormatterQuoteClass)) +#define E_IS_MAIL_FORMATTER_QUOTE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_FORMATTER_QUOTE)) +#define E_IS_MAIL_FORMATTER_QUOTE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_FORMATTER_QUOTE)) +#define E_MAIL_FORMATTER_QUOTE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_FORMATTER_QUOTE, EMailFormatterQuoteClass)) + +G_BEGIN_DECLS; + +typedef struct _EMailFormatterQuote EMailFormatterQuote; +typedef struct _EMailFormatterQuoteClass EMailFormatterQuoteClass; +typedef struct _EMailFormatterQuotePrivate EMailFormatterQuotePrivate; +typedef struct _EMailFormatterQuoteContext EMailFormatterQuoteContext; + +typedef enum { + E_MAIL_FORMATTER_QUOTE_FLAG_CITE = 1 << 0, + E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS = 1 << 1, + E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG = 1 << 2 /* do not strip signature */ +} EMailFormatterQuoteFlags; + +struct _EMailFormatterQuoteContext { + EMailFormatterContext parent; + + guint32 qf_flags; +}; + +struct _EMailFormatterQuote { + EMailFormatter parent; + + EMailFormatterQuotePrivate *priv; +}; + +struct _EMailFormatterQuoteClass { + EMailFormatterClass parent_class; +}; + +GType e_mail_formatter_quote_get_type (void); + +EMailFormatter * e_mail_formatter_quote_new (const gchar *credits, + EMailFormatterQuoteFlags flags); + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_QUOTE_H_ */ diff --git a/em-format/e-mail-formatter-secure-button.c b/em-format/e-mail-formatter-secure-button.c new file mode 100644 index 0000000000..05319ef7fc --- /dev/null +++ b/em-format/e-mail-formatter-secure-button.c @@ -0,0 +1,472 @@ +/* + * evolution-secure-button.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <e-util/e-util.h> + +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) +#include "certificate-viewer.h" +#include "e-cert-db.h" +#endif + +#include <camel/camel.h> + +typedef struct _EMailFormatterSecureButton { + GObject parent; +} EMailFormatterSecureButton; + +typedef struct _EMailFormatterSecureButtonClass { + GObjectClass parent_class; +} EMailFormatterSecureButtonClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterSecureButton, + e_mail_formatter_secure_button, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.widget.secure-button", NULL }; + +static const struct { + const gchar *icon, *shortdesc, *description; +} smime_sign_table[5] = { + { "stock_signature-bad", N_("Unsigned"), N_("This message is not signed. There is no guarantee that this message is authentic.") }, + { "stock_signature-ok", N_("Valid signature"), N_("This message is signed and is valid meaning that it is very likely that this message is authentic.") }, + { "stock_signature-bad", N_("Invalid signature"), N_("The signature of this message cannot be verified, it may have been altered in transit.") }, + { "stock_signature", N_("Valid signature, but cannot verify sender"), N_("This message is signed with a valid signature, but the sender of the message cannot be verified.") }, + { "stock_signature-bad", N_("Signature exists, but need public key"), N_("This message is signed with a signature, but there is no corresponding public key.") }, + +}; + +static const struct { + const gchar *icon, *shortdesc, *description; +} smime_encrypt_table[4] = { + { "stock_lock-broken", N_("Unencrypted"), N_("This message is not encrypted. Its content may be viewed in transit across the Internet.") }, + { "stock_lock-ok", N_("Encrypted, weak"), N_("This message is encrypted, but with a weak encryption algorithm. It would be difficult, but not impossible for an outsider to view the content of this message in a practical amount of time.") }, + { "stock_lock-ok", N_("Encrypted"), N_("This message is encrypted. It would be difficult for an outsider to view the content of this message.") }, + { "stock_lock-ok", N_("Encrypted, strong"), N_("This message is encrypted, with a strong encryption algorithm. It would be very difficult for an outsider to view the content of this message in a practical amount of time.") }, +}; + +static const GdkRGBA smime_sign_colour[5] = { + { 0 }, { 0.53, 0.73, 0.53, 1 }, { 0.73, 0.53, 0.53, 1 }, { 0.91, 0.82, 0.13, 1 }, { 0 }, +}; + +static gboolean +emfe_secure_button_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *str; + + if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) && + (context->mode != E_MAIL_FORMATTER_MODE_RAW)) + return FALSE; + + str = g_strdup_printf ( + "<object type=\"application/vnd.evolution.widget.secure-button\" " + "height=\"20\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>", + part->id, part->id); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + return TRUE; +} + +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) +static void +viewcert_clicked (GtkWidget *button, + GtkWidget *parent) +{ + CamelCipherCertInfo *info = g_object_get_data((GObject *) button, "e-cert-info"); + ECert *ec = NULL; + + if (info->cert_data) + ec = e_cert_new (CERT_DupCertificate (info->cert_data)); + + if (ec != NULL) { + GtkWidget *w = certificate_viewer_show (ec); + + /* oddly enough certificate_viewer_show doesn't ... */ + gtk_widget_show (w); + g_signal_connect ( + w, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + if (w && parent) + gtk_window_set_transient_for ( + (GtkWindow *) w, (GtkWindow *) parent); + + g_object_unref (ec); + } else { + g_warning("can't find certificate for %s <%s>", + info->name ? info->name : "", + info->email ? info->email : ""); + } +} +#endif + +static void +info_response (GtkWidget *widget, + guint button, + gpointer user_data) +{ + gtk_widget_destroy (widget); +} + +static void +add_cert_table (GtkWidget *grid, + GQueue *certlist, + gpointer user_data) +{ + GList *head, *link; + GtkTable *table; + gint n = 0; + + table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE); + + head = g_queue_peek_head_link (certlist); + + for (link = head; link != NULL; link = g_list_next (link)) { + CamelCipherCertInfo *info = link->data; + gchar *la = NULL; + const gchar *l = NULL; + + if (info->name) { + if (info->email && strcmp (info->name, info->email) != 0) + l = la = g_strdup_printf("%s <%s>", info->name, info->email); + else + l = info->name; + } else { + if (info->email) + l = info->email; + } + + if (l) { + GtkWidget *w; +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) + ECert *ec = NULL; +#endif + w = gtk_label_new (l); + gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); + g_free (la); + gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3); +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) + w = gtk_button_new_with_mnemonic(_("_View Certificate")); + gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); + g_object_set_data((GObject *)w, "e-cert-info", info); + g_signal_connect ( + w, "clicked", + G_CALLBACK (viewcert_clicked), grid); + + if (info->cert_data) + ec = e_cert_new (CERT_DupCertificate (info->cert_data)); + + if (ec == NULL) + gtk_widget_set_sensitive (w, FALSE); + else + g_object_unref (ec); +#else + w = gtk_label_new (_("This certificate is not viewable")); + gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); +#endif + n++; + } + } + + gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table)); +} + +static void +format_cert_infos (GQueue *cert_infos, + GString *output_buffer) +{ + GQueue valid = G_QUEUE_INIT; + GList *head, *link; + + head = g_queue_peek_head_link (cert_infos); + + /* Make sure we have a valid CamelCipherCertInfo before + * appending anything to the output buffer, so we don't + * end up with "()". */ + for (link = head; link != NULL; link = g_list_next (link)) { + CamelCipherCertInfo *cinfo = link->data; + + if ((cinfo->name != NULL && *cinfo->name != '\0') || + (cinfo->email != NULL && *cinfo->email != '\0')) { + g_queue_push_tail (&valid, cinfo); + } + } + + if (g_queue_is_empty (&valid)) + return; + + g_string_append (output_buffer, " ("); + + while (!g_queue_is_empty (&valid)) { + CamelCipherCertInfo *cinfo; + + cinfo = g_queue_pop_head (&valid); + + if (cinfo->name != NULL && *cinfo->name != '\0') { + g_string_append (output_buffer, cinfo->name); + + if (cinfo->email != NULL && *cinfo->email != '\0') { + g_string_append (output_buffer, " <"); + g_string_append (output_buffer, cinfo->email); + g_string_append (output_buffer, ">"); + } + + } else if (cinfo->email != NULL && *cinfo->email != '\0') { + g_string_append (output_buffer, cinfo->email); + } + + if (!g_queue_is_empty (&valid)) + g_string_append (output_buffer, ", "); + } + + g_string_append_c (output_buffer, ')'); +} + +static void +secure_button_clicked_cb (GtkWidget *widget, + EMailPart *part) +{ + GtkBuilder *builder; + GtkWidget *grid, *w; + GtkWidget *dialog; + + builder = gtk_builder_new (); + e_load_ui_builder_definition (builder, "mail-dialogs.ui"); + + dialog = e_builder_get_widget(builder, "message_security_dialog"); + + grid = e_builder_get_widget(builder, "signature_grid"); + w = gtk_label_new (_(smime_sign_table[part->validity->sign.status].description)); + gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_container_add (GTK_CONTAINER (grid), w); + if (part->validity->sign.description) { + GtkTextBuffer *buffer; + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text ( + buffer, part->validity->sign.description, + strlen (part->validity->sign.description)); + w = g_object_new (gtk_scrolled_window_get_type (), + "hscrollbar_policy", GTK_POLICY_AUTOMATIC, + "vscrollbar_policy", GTK_POLICY_AUTOMATIC, + "shadow_type", GTK_SHADOW_IN, + "expand", TRUE, + "child", g_object_new(gtk_text_view_get_type(), + "buffer", buffer, + "cursor_visible", FALSE, + "editable", FALSE, + "width_request", 500, + "height_request", 160, + NULL), + NULL); + g_object_unref (buffer); + + gtk_container_add (GTK_CONTAINER (grid), w); + } + + if (!g_queue_is_empty (&part->validity->sign.signers)) + add_cert_table ( + grid, &part->validity->sign.signers, NULL); + + gtk_widget_show_all (grid); + + grid = e_builder_get_widget(builder, "encryption_grid"); + w = gtk_label_new (_(smime_encrypt_table[part->validity->encrypt.status].description)); + gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_container_add (GTK_CONTAINER (grid), w); + if (part->validity->encrypt.description) { + GtkTextBuffer *buffer; + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text ( + buffer, part->validity->encrypt.description, + strlen (part->validity->encrypt.description)); + w = g_object_new (gtk_scrolled_window_get_type (), + "hscrollbar_policy", GTK_POLICY_AUTOMATIC, + "vscrollbar_policy", GTK_POLICY_AUTOMATIC, + "shadow_type", GTK_SHADOW_IN, + "expand", TRUE, + "child", g_object_new(gtk_text_view_get_type(), + "buffer", buffer, + "cursor_visible", FALSE, + "editable", FALSE, + "width_request", 500, + "height_request", 160, + NULL), + NULL); + g_object_unref (buffer); + + gtk_container_add (GTK_CONTAINER (grid), w); + } + + if (!g_queue_is_empty (&part->validity->encrypt.encrypters)) + add_cert_table (grid, &part->validity->encrypt.encrypters, NULL); + + gtk_widget_show_all (grid); + + g_object_unref (builder); + + g_signal_connect ( + dialog, "response", + G_CALLBACK (info_response), NULL); + + gtk_widget_show (dialog); +} + +static GtkWidget * +emfe_secure_button_get_widget (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params) +{ + GtkWidget *box, *button, *layout, *widget; + const gchar *icon_name; + gchar *description; + GString *buffer; + buffer = g_string_new (""); + + if (part->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) { + const gchar *desc; + gint status; + + status = part->validity->sign.status; + desc = smime_sign_table[status].shortdesc; + + g_string_append (buffer, gettext (desc)); + + format_cert_infos (&part->validity->sign.signers, buffer); + } + + if (part->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) { + const gchar *desc; + gint status; + + if (part->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) + g_string_append (buffer, "\n"); + + status = part->validity->encrypt.status; + desc = smime_encrypt_table[status].shortdesc; + g_string_append (buffer, gettext (desc)); + } + + description = g_string_free (buffer, FALSE); + + /* FIXME: need to have it based on encryption and signing too */ + if (part->validity->sign.status != 0) + icon_name = smime_sign_table[part->validity->sign.status].icon; + else + icon_name = smime_encrypt_table[part->validity->encrypt.status].icon; + + box = gtk_event_box_new (); + if (part->validity->sign.status != 0) + gtk_widget_override_background_color (box, GTK_STATE_FLAG_NORMAL, + &smime_sign_colour[part->validity->sign.status]); + + layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_container_add (GTK_CONTAINER (box), layout); + + button = gtk_button_new (); + gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0); + g_signal_connect (button, "clicked", + G_CALLBACK (secure_button_clicked_cb), part); + + widget = gtk_image_new_from_icon_name ( + icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_button_set_image (GTK_BUTTON (button), widget); + + widget = gtk_label_new (description); + gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0); + + gtk_widget_show_all (box); + + g_free (description); + return box; +} + +static const gchar * +emfe_secure_button_get_display_name (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar * +emfe_secure_button_get_description (EMailFormatterExtension *extension) +{ + return NULL; +} + +static const gchar ** +emfe_secure_button_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_secure_button_class_init (EMailFormatterSecureButtonClass *klass) +{ + e_mail_formatter_secure_button_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_secure_button_format; + iface->get_widget = emfe_secure_button_get_widget; + iface->get_display_name = emfe_secure_button_get_display_name; + iface->get_description = emfe_secure_button_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_secure_button_mime_types; +} + +static void +e_mail_formatter_secure_button_init (EMailFormatterSecureButton *extension) +{ + +} diff --git a/em-format/e-mail-formatter-source.c b/em-format/e-mail-formatter-source.c new file mode 100644 index 0000000000..92c3a925f1 --- /dev/null +++ b/em-format/e-mail-formatter-source.c @@ -0,0 +1,178 @@ +/* + * evolution-source.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-inline-filter.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +typedef struct _EMailFormatterSource { + GObject parent; +} EMailFormatterSource; + +typedef struct _EMailFormatterSourceClass { + GObjectClass parent_class; +} EMailFormatterSourceClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterSource, + e_mail_formatter_source, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init) +) + +static const gchar *formatter_mime_types[] = { "application/vnd.evolution.source", NULL }; + +static gboolean +emfe_source_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + GString *buffer; + CamelStream *filtered_stream; + CamelMimeFilter *filter; + CamelDataWrapper *dw = (CamelDataWrapper *) part->part; + + filtered_stream = camel_stream_filter_new (stream); + + filter = camel_mime_filter_tohtml_new ( + CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | + CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | + CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), filter); + g_object_unref (filter); + + buffer = g_string_new (""); + + if (CAMEL_IS_MIME_MESSAGE (part->part)) { + g_string_append_printf ( + buffer, + "<div class=\"part-container\" " + "style=\"border: 0; background: #%06x; color: #%06x;\" >", + 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))); + } else { + g_string_append_printf ( + buffer, + "<div class=\"part-container\" " + "style=\"border-color: #%06x; background: #%06x; color: #%06x;\">" + "<div class=\"part-container-inner-margin pre\">\n", + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + 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))); + } + + camel_stream_write_string ( + stream, buffer->str, cancellable, NULL); + camel_stream_write_string ( + stream, "<code class=\"pre\">", cancellable, NULL); + + camel_data_wrapper_write_to_stream_sync (dw, filtered_stream, + cancellable, NULL); + camel_stream_flush (filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + camel_stream_write_string ( + stream, "</code>", cancellable, NULL); + + g_string_free (buffer, TRUE); + + if (CAMEL_IS_MIME_MESSAGE (part->part)) { + camel_stream_write_string (stream, "</div>", cancellable, NULL); + } else { + camel_stream_write_string (stream, "</div></div>", cancellable, NULL); + } + + return TRUE; +} + +static const gchar * +emfe_source_get_display_name (EMailFormatterExtension *extension) +{ + return _("Source"); +} + +static const gchar * +emfe_source_get_description (EMailFormatterExtension *extension) +{ + return _("Display source of a MIME part"); +} + +static const gchar ** +emfe_source_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_source_class_init (EMailFormatterSourceClass *klass) +{ + e_mail_formatter_source_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_source_format; + iface->get_display_name = emfe_source_get_display_name; + iface->get_description = emfe_source_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_source_mime_types; +} + +static void +e_mail_formatter_source_init (EMailFormatterSource *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-text-enriched.c b/em-format/e-mail-formatter-text-enriched.c new file mode 100644 index 0000000000..fce7b317e2 --- /dev/null +++ b/em-format/e-mail-formatter-text-enriched.c @@ -0,0 +1,154 @@ +/* + * e-mail-formatter-text-enriched.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-inline-filter.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +static const gchar *formatter_mime_types[] = { "text/enriched", + "text/richtext", + NULL }; + +typedef struct _EMailFormatterTextEnriched { + GObject parent; +} EMailFormatterTextEnriched; + +typedef struct _EMailFormatterTextEnrichedClass { + GObjectClass parent_class; +} EMailFormatterTextEnrichedClass; + +static void e_mail_formatter_formatter_extension_interace_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterTextEnriched, + e_mail_formatter_text_enriched, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interace_init)); + +static gboolean +emfe_text_enriched_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *enriched; + guint32 filter_flags = 0; + GString *buffer; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if (!g_strcmp0(part->mime_type, "text/richtext")) { + filter_flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; + } + + enriched = camel_mime_filter_enriched_new (filter_flags); + filtered_stream = camel_stream_filter_new (stream); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), enriched); + g_object_unref (enriched); + + buffer = g_string_new (""); + + g_string_append_printf (buffer, + "<div class=\"part-container\" style=\"border-color: #%06x; " + "background-color: #%06x; color: #%06x;\">" + "<div class=\"part-container-inner-margin\">\n", + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color (formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color (formatter, E_MAIL_FORMATTER_COLOR_CONTENT)), + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color (formatter, E_MAIL_FORMATTER_COLOR_TEXT))); + + camel_stream_write_string (stream, buffer->str, cancellable, NULL); + g_string_free (buffer, TRUE); + + e_mail_formatter_format_text ( + formatter, part, filtered_stream, cancellable); + camel_stream_flush (filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + camel_stream_write_string (stream, "</div></div>", cancellable, NULL); + + return TRUE; +} + +static const gchar * +emfe_text_enriched_get_display_name (EMailFormatterExtension *extension) +{ + return _("Richtext"); +} + +static const gchar * +emfe_text_enriched_get_description (EMailFormatterExtension *extension) +{ + return _("Display part as enriched text"); +} + +static const gchar ** +emfe_text_enriched_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_text_enriched_class_init (EMailFormatterTextEnrichedClass *klass) +{ + e_mail_formatter_text_enriched_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interace_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_text_enriched_format; + iface->get_display_name = emfe_text_enriched_get_display_name; + iface->get_description = emfe_text_enriched_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_text_enriched_mime_types; +} + +static void +e_mail_formatter_text_enriched_init (EMailFormatterTextEnriched *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-text-html.c b/em-format/e-mail-formatter-text-html.c new file mode 100644 index 0000000000..28fb2644de --- /dev/null +++ b/em-format/e-mail-formatter-text-html.c @@ -0,0 +1,376 @@ +/* + * e-mail-formatter-text-html.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-inline-filter.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#include <ctype.h> +#include <string.h> + +static const gchar *formatter_mime_types[] = { "text/html", NULL }; + +typedef struct _EMailFormatterTextHTML { + GObject parent; +} EMailFormatterTextHTML; + +typedef struct _EMailFormatterTextHTMLClass { + GObjectClass parent_class; +} EMailFormatterTextHTMLClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterTextHTML, + e_mail_formatter_text_html, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static gchar * +get_tag (const gchar *utf8_string, + const gchar *tag_name, + gchar *opening, + gchar *closing) +{ + gchar *t; + gunichar c; + gboolean has_end; + + t = g_utf8_find_prev_char (utf8_string, closing); + while (t != opening) { + + c = g_utf8_get_char (t); + if (!g_unichar_isspace (c)) + break; + } + + /* Not a pair tag */ + if (c == '/') + return g_strndup (opening, closing - opening + 1); + + t = closing; + while (t) { + c = g_utf8_get_char (t); + if (c == '<') + break; + + t = g_utf8_find_next_char (t, NULL); + } + + do { + c = g_utf8_get_char (t); + + if (c == '/') { + has_end = TRUE; + break; + } + + if (c == '>') { + has_end = FALSE; + break; + } + + t = g_utf8_find_next_char (t, NULL); + + } while (t); + + /* Broken HTML? */ + if (!has_end) + return g_strndup (opening, closing - opening + 1); + + do { + c = g_utf8_get_char (t); + if ((c != ' ') && (c != '/')) + break; + + t = g_utf8_find_next_char (t, NULL); + } while (t); + + /* tag_name is always ASCII */ + if (g_ascii_strncasecmp (t, tag_name, strlen (tag_name)) == 0) { + + closing = g_utf8_strchr (t, -1, '>'); + + return g_strndup (opening, closing - opening + 1); + } + + /* Broken HTML? */ + return g_strndup (opening, closing - opening + 1); +} + +static gboolean +emfe_text_html_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + /* FORMATTER FIXME: Shouldn't we have some extra method for + * BASE64 and QP decoding?? */ + e_mail_formatter_format_text (formatter, part, stream, cancellable); + + } else if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) { + GString *string; + GByteArray *ba; + gchar *pos; + GList *tags, *iter; + gboolean valid; + gchar *tag; + const gchar *document_end; + gint length; + gint i; + CamelStream *decoded_stream; + + decoded_stream = camel_stream_mem_new (); + /* FORMATTER FIXME: See above */ + e_mail_formatter_format_text (formatter, part, decoded_stream, cancellable); + g_seekable_seek (G_SEEKABLE (decoded_stream), 0, G_SEEK_SET, cancellable, NULL); + + ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (decoded_stream)); + string = g_string_new_len ((gchar *) ba->data, ba->len); + + g_object_unref (decoded_stream); + + tags = NULL; + pos = string->str; + valid = FALSE; + + if (!g_utf8_validate (string->str, -1, NULL)) { + /* FIXME - What do we do if the string is not UTF-8 valid? */ + } + + do { + gchar *tmp; + gchar *closing; + gchar *opening; + + tmp = g_utf8_find_next_char (pos, NULL); + pos = g_utf8_strchr (tmp, -1, '<'); + if (!pos) + break; + + opening = pos; + closing = g_utf8_strchr (pos, -1, '>'); + + /* Find where the actual tag name begins */ + tag = g_utf8_find_next_char (pos, NULL); + while ((tag = g_utf8_find_next_char (pos, NULL)) != NULL) { + gunichar c = g_utf8_get_char (tag); + if (!g_unichar_isspace (c)) + break; + + } + + if (g_ascii_strncasecmp (tag, "style", 5) == 0) { + tags = g_list_append ( + tags, + get_tag (string->str, "style", opening, closing)); + } else if (g_ascii_strncasecmp (tag, "script", 6) == 0) { + tags = g_list_append ( + tags, + get_tag (string->str, "script", opening, closing)); + } else if (g_ascii_strncasecmp (tag, "link", 4) == 0) { + tags = g_list_append ( + tags, + get_tag (string->str, "link", opening, closing)); + } else if (g_ascii_strncasecmp (tag, "body", 4) == 0) { + valid = TRUE; + break; + } + + } while (pos); + + if (tags) + printf("\n\n**%s**\n\n", (gchar *) tags->data); + + /* Something's wrong, let's write the entire HTML and hope + * that WebKit can handle it */ + if (!valid) { + EMailFormatterContext c = { + .folder = context->folder, + .message = context->message, + .message_uid = context->message_uid, + .parts = context->parts, + .flags = context->flags, + .mode = E_MAIL_FORMATTER_MODE_RAW, + }; + + emfe_text_html_format ( + extension, formatter, &c, part, stream, cancellable); + return FALSE; + } + + /* include the "body" as well -----v */ + g_string_erase (string, 0, tag - string->str + 4); + g_string_prepend (string, "<div "); + + for (iter = tags; iter; iter = iter->next) { + g_string_prepend (string, iter->data); + } + + g_list_free_full (tags, g_free); + + document_end = NULL; + /* We can probably use ASCII functions here */ + if (g_strrstr (string->str, "</body>")) { + document_end = ">ydob/<"; + } + + if (g_strrstr (string->str, "</html>")) { + if (document_end) { + document_end = ">lmth/<>ydob/<"; + } else { + document_end = ">lmth/<"; + } + } + + if (document_end ) { + length = strlen (document_end); + tag = string->str + string->len - 1; + i = 0; + valid = FALSE; + while (i < length - 1) { + gunichar c; + + c = g_utf8_get_char (tag); + if (g_unichar_isspace (c)) { + tag = g_utf8_find_prev_char (string->str, tag); + continue; + } + + c = g_unichar_tolower (c); + + if (c == document_end[i]) { + tag = g_utf8_find_prev_char (string->str, tag); + i++; + valid = TRUE; + continue; + } + + tag = g_utf8_find_prev_char (string->str, tag); + valid = FALSE; + } + } + + if (valid) + g_string_truncate (string, tag - string->str); + + camel_stream_write_string (stream, string->str, cancellable, NULL); + + g_string_free (string, TRUE); + } else { + gchar *uri, *str; + + uri = e_mail_part_build_uri ( + context->folder, context->message_uid, + "part_id", G_TYPE_STRING, part->id, + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, + NULL); + + str = g_strdup_printf ( + "<div class=\"part-container-nostyle\">" + "<iframe width=\"100%%\" height=\"10\" " + " frameborder=\"0\" src=\"%s\" " + " id=\"%s.iframe\" " + " style=\"border: 1px solid #%06x; background-color: #%06x;\">" + "</iframe>" + "</div>", + uri, + part->id, + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_CONTENT))); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + g_free (uri); + } + + return TRUE; +} + +static const gchar * +emfe_text_html_get_display_name (EMailFormatterExtension *extension) +{ + return _("HTML"); +} + +static const gchar * +emfe_text_html_get_description (EMailFormatterExtension *extension) +{ + return _("Format part as HTML"); +} + +static const gchar ** +emfe_text_html_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_text_html_class_init (EMailFormatterTextHTMLClass *klass) +{ + e_mail_formatter_text_html_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_text_html_format; + iface->get_display_name = emfe_text_html_get_display_name; + iface->get_description = emfe_text_html_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_text_html_mime_types; +} + +static void +e_mail_formatter_text_html_init (EMailFormatterTextHTML *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-text-plain.c b/em-format/e-mail-formatter-text-plain.c new file mode 100644 index 0000000000..631c46a3bc --- /dev/null +++ b/em-format/e-mail-formatter-text-plain.c @@ -0,0 +1,212 @@ +/* + * e-mail-formatter-text-plain.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-inline-filter.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +static const gchar *formatter_mime_types[] = { "text/plain", "text/*", NULL }; + +typedef struct _EMailFormatterTextPlain { + GObject parent; +} EMailFormatterTextPlain; + +typedef struct _EMailFormatterTextPlainClass { + GObjectClass parent_class; +} EMailFormatterTextPlainClass; + +static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface); +static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailFormatterTextPlain, + e_mail_formatter_text_plain, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_formatter_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_FORMATTER_EXTENSION, + e_mail_formatter_formatter_extension_interface_init)); + +static gboolean +emfe_text_plain_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelDataWrapper *dw; + CamelStream *filtered_stream; + CamelMimeFilter *html_filter; + gchar *content; + const gchar *format; + guint32 filter_flags; + guint32 rgb; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if ((context->mode == E_MAIL_FORMATTER_MODE_RAW) || + (context->mode == E_MAIL_FORMATTER_MODE_PRINTING)) { + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + gchar *header; + header = e_mail_formatter_get_html_header (formatter); + camel_stream_write_string (stream, header, cancellable, NULL); + g_free (header); + + /* No need for body margins within <iframe> */ + camel_stream_write_string (stream, + "<style>body{ margin: 0; }</style>", + cancellable, NULL); + } + + filter_flags = e_mail_formatter_get_text_format_flags (formatter); + + dw = camel_medium_get_content (CAMEL_MEDIUM (part->part)); + if (!dw) + return FALSE; + + /* Check for RFC 2646 flowed text. */ + if (camel_content_type_is(dw->mime_type, "text", "plain") + && (format = camel_content_type_param(dw->mime_type, "format")) + && !g_ascii_strcasecmp(format, "flowed")) + filter_flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; + + rgb = e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_CITATION)); + + filtered_stream = camel_stream_filter_new (stream); + html_filter = camel_mime_filter_tohtml_new (filter_flags, rgb); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), html_filter); + g_object_unref (html_filter); + + content = g_strdup_printf ( + "<div class=\"part-container\" style=\"" + "border-color: #%06x;" + "background-color: #%06x; color: #%06x;\">" + "<div class=\"part-container-inner-margin pre\">\n", + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_CONTENT)), + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT))); + + camel_stream_write_string (stream, content, cancellable, NULL); + e_mail_formatter_format_text (formatter, part, filtered_stream, cancellable); + camel_stream_flush (filtered_stream, cancellable, NULL); + + g_object_unref (filtered_stream); + g_free (content); + + camel_stream_write_string (stream, "</div></div>\n", cancellable, NULL); + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + camel_stream_write_string (stream, "</body></html>", + cancellable, NULL); + } + + return TRUE; + + } else { + gchar *uri, *str; + + uri = e_mail_part_build_uri ( + context->folder, context->message_uid, + "part_id", G_TYPE_STRING, part->id, + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, + NULL); + + str = g_strdup_printf ( + "<iframe width=\"100%%\" height=\"10\"" + " id=\"%s.iframe\" " + " frameborder=\"0\" src=\"%s\"></iframe>", + part->id, uri); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + g_free (uri); + } + + return TRUE; +} + +static const gchar * +emfe_text_plain_get_display_name (EMailFormatterExtension *extension) +{ + return _("Plain Text"); +} + +static const gchar * +emfe_text_plain_get_description (EMailFormatterExtension *extension) +{ + return _("Format part as plain text"); +} + +static const gchar ** +emfe_text_plain_mime_types (EMailExtension *extension) +{ + return formatter_mime_types; +} + +static void +e_mail_formatter_text_plain_class_init (EMailFormatterTextPlainClass *klass) +{ + e_mail_formatter_text_plain_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface) +{ + iface->format = emfe_text_plain_format; + iface->get_display_name = emfe_text_plain_get_display_name; + iface->get_description = emfe_text_plain_get_description; +} + +static void +e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = emfe_text_plain_mime_types; +} + +static void +e_mail_formatter_text_plain_init (EMailFormatterTextPlain *formatter) +{ + +} diff --git a/em-format/e-mail-formatter-utils.c b/em-format/e-mail-formatter-utils.c new file mode 100644 index 0000000000..12ee042c90 --- /dev/null +++ b/em-format/e-mail-formatter-utils.c @@ -0,0 +1,434 @@ +/* + * e-mail-formatter-utils.h + * + * 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/> + * + */ + +#ifdef HAVE_CONFIG +#include <config.h> +#endif + +#include "e-mail-formatter-utils.h" + +#include <camel/camel.h> + +#include <libemail-engine/e-mail-utils.h> +#include <libemail-engine/mail-config.h> +#include <e-util/e-util.h> +#include <e-util/e-datetime-format.h> +#include <libedataserver/libedataserver.h> + +#include <glib/gi18n.h> + +#include <string.h> + +static const gchar *addrspec_hdrs[] = { + "Sender", "From", "Reply-To", "To", "Cc", "Bcc", + "Resent-Sender", "Resent-From", "Resent-Reply-To", + "Resent-To", "Resent-Cc", "Resent-Bcc", NULL +}; + +void +e_mail_formatter_format_text_header (EMailFormatter *formatter, + GString *buffer, + const gchar *label, + const gchar *value, + guint32 flags) +{ + const gchar *fmt, *html; + gchar *mhtml = NULL; + gboolean is_rtl; + + if (value == NULL) + return; + + while (*value == ' ') + value++; + + if (!(flags & E_MAIL_FORMATTER_HEADER_FLAG_HTML)) + html = mhtml = camel_text_to_html (value, + e_mail_formatter_get_text_format_flags (formatter), 0); + else + html = value; + + is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; + + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS) { + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) { + fmt = "<tr class=\"header-item\" style=\"display: %s\"><td><b>%s:</b> %s</td></tr>"; + } else { + fmt = "<tr class=\"header-item\" style=\"display: %s\"><td>%s: %s</td></tr>"; + } + } else if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NODEC) { + if (is_rtl) + fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b> </b></th></tr>"; + else + fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s<b> </b></th><td valign=top>%s</td></tr>"; + } else { + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) { + if (is_rtl) + fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b> </b></th></tr>"; + else + fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s:<b> </b></th><td>%s</td></tr>"; + } else { + if (is_rtl) + fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b> </b></td></tr>"; + else + fmt = "<tr class=\"header-item\" style=\"display: %s\"><td align=\"right\" valign=\"top\" nowrap>%s:<b> </b></td><td>%s</td></tr>"; + } + } + + g_string_append_printf (buffer, fmt, + (flags & E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN ? "none" : "table-row"), label, html); + + g_free (mhtml); +} + +gchar * +e_mail_formatter_format_address (EMailFormatter *formatter, + GString *out, + struct _camel_header_address *a, + gchar *field, + gboolean no_links, + gboolean elipsize) +{ + guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; + gchar *name, *mailto, *addr; + gint i = 0; + gchar *str = NULL; + gint limit = mail_config_get_address_count (); + + while (a) { + if (a->name) + name = camel_text_to_html (a->name, flags, 0); + else + name = NULL; + + switch (a->type) { + case CAMEL_HEADER_ADDRESS_NAME: + if (name && *name) { + gchar *real, *mailaddr; + + if (strchr (a->name, ',') || strchr (a->name, ';')) + g_string_append_printf (out, ""%s"", name); + else + g_string_append (out, name); + + g_string_append (out, " <"); + + /* rfc2368 for mailto syntax and url encoding extras */ + if ((real = camel_header_encode_phrase ((guchar *) a->name))) { + mailaddr = g_strdup_printf("%s <%s>", real, a->v.addr); + g_free (real); + mailto = camel_url_encode (mailaddr, "?=&()"); + g_free (mailaddr); + } else { + mailto = camel_url_encode (a->v.addr, "?=&()"); + } + } else { + mailto = camel_url_encode (a->v.addr, "?=&()"); + } + addr = camel_text_to_html (a->v.addr, flags, 0); + if (no_links) + g_string_append_printf (out, "%s", addr); + else + g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr); + g_free (mailto); + g_free (addr); + + if (name && *name) + g_string_append (out, ">"); + break; + case CAMEL_HEADER_ADDRESS_GROUP: + g_string_append_printf (out, "%s: ", name); + e_mail_formatter_format_address ( + formatter, out, a->v.members, field, + no_links, elipsize); + g_string_append_printf (out, ";"); + break; + default: + g_warning ("Invalid address type"); + break; + } + + g_free (name); + + i++; + a = a->next; + if (a) + g_string_append (out, ", "); + + if (!elipsize) + continue; + + /* Let us add a '...' if we have more addresses */ + if (limit > 0 && (i == limit - 1)) { + const gchar *id = NULL; + + if (strcmp (field, _("To")) == 0) { + id = "to"; + } else if (strcmp (field, _("Cc")) == 0) { + id = "cc"; + } else if (strcmp (field, _("Bcc")) == 0) { + id = "bcc"; + } + + if (id) { + g_string_append_printf (out, + "<span id=\"__evo-moreaddr-%s\" " + "style=\"display: none;\">", id); + str = g_strdup_printf ( + "<img src=\"evo-file://%s/plus.png\" " + "id=\"__evo-moreaddr-img-%s\" class=\"navigable\">", + EVOLUTION_IMAGESDIR, id); + } + } + } + + if (elipsize && str) { + const gchar *id = NULL; + + if (strcmp (field, _("To")) == 0) { + id = "to"; + } else if (strcmp (field, _("Cc")) == 0) { + id = "cc"; + } else if (strcmp (field, _("Bcc")) == 0) { + id = "bcc"; + } + + if (id) { + g_string_append_printf (out, + "</span>" + "<span class=\"navigable\" " + "id=\"__evo-moreaddr-ellipsis-%s\" " + "style=\"display: inline;\">...</span>", + id); + } + } + + return str; +} + +void +e_mail_formatter_canon_header_name (gchar *name) +{ + gchar *inptr = name; + + /* canonicalise the header name... first letter is + * capitalised and any letter following a '-' also gets + * capitalised */ + + if (*inptr >= 'a' && *inptr <= 'z') + *inptr -= 0x20; + + inptr++; + + while (*inptr) { + if (inptr[-1] == '-' && *inptr >= 'a' && *inptr <= 'z') + *inptr -= 0x20; + else if (*inptr >= 'A' && *inptr <= 'Z') + *inptr += 0x20; + + inptr++; + } +} + +void +e_mail_formatter_format_header (EMailFormatter *formatter, + GString *buffer, + CamelMedium *part, + struct _camel_header_raw *header, + guint32 flags, + const gchar *charset) +{ + gchar *name, *buf, *value = NULL; + const gchar *label, *txt; + gboolean addrspec = FALSE; + gchar *str_field = NULL; + gint i; + + name = g_alloca (strlen (header->name) + 1); + strcpy (name, header->name); + e_mail_formatter_canon_header_name (name); + + for (i = 0; addrspec_hdrs[i]; i++) { + if (!strcmp (name, addrspec_hdrs[i])) { + addrspec = TRUE; + break; + } + } + + label = _(name); + + if (addrspec) { + struct _camel_header_address *addrs; + GString *html; + gchar *img; + const gchar *charset = e_mail_formatter_get_charset (formatter) ? + e_mail_formatter_get_charset (formatter) : + e_mail_formatter_get_default_charset (formatter); + + buf = camel_header_unfold (header->value); + if (!(addrs = camel_header_address_decode (buf, charset))) { + g_free (buf); + return; + } + + g_free (buf); + + html = g_string_new(""); + img = e_mail_formatter_format_address (formatter, html, addrs, (gchar *) label, + (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS), + !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE)); + + if (img) { + str_field = g_strdup_printf ("%s%s:", img, label); + label = str_field; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_NODEC; + g_free (img); + } + + camel_header_address_list_clear (&addrs); + txt = value = html->str; + g_string_free (html, FALSE); + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML | E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + } else if (!strcmp (name, "Subject")) { + buf = camel_header_unfold (header->value); + txt = value = camel_header_decode_string (buf, charset); + g_free (buf); + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + } else if (!strcmp(name, "X-evolution-mailer")) { + /* pseudo-header */ + label = _("Mailer"); + txt = value = camel_header_format_ctext (header->value, charset); + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) { + gint msg_offset, local_tz; + time_t msg_date; + struct tm local; + gchar *html; + gboolean hide_real_date; + + hide_real_date = !e_mail_formatter_get_show_real_date (formatter); + + txt = header->value; + while (*txt == ' ' || *txt == '\t') + txt++; + + html = camel_text_to_html (txt, + e_mail_formatter_get_text_format_flags (formatter), 0); + + msg_date = camel_header_decode_date (txt, &msg_offset); + e_localtime_with_offset (msg_date, &local, &local_tz); + + /* Convert message offset to minutes (e.g. -0400 --> -240) */ + msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100); + /* Turn into offset from localtime, not UTC */ + msg_offset -= local_tz / 60; + + /* value will be freed at the end */ + if (!hide_real_date && !msg_offset) { + /* No timezone difference; just show the real Date: header */ + txt = value = html; + } else { + gchar *date_str; + + date_str = e_datetime_format_format ("mail", "header", + DTFormatKindDateTime, msg_date); + + if (hide_real_date) { + /* Show only the local-formatted date, losing all timezone + * information like Outlook does. Should we attempt to show + * it somehow? */ + txt = value = date_str; + } else { + txt = value = g_strdup_printf ("%s (<I>%s</I>)", html, date_str); + g_free (date_str); + } + g_free (html); + } + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML | + E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + } else if (!strcmp(name, "Newsgroups")) { + struct _camel_header_newsgroup *ng, *scan; + GString *html; + + buf = camel_header_unfold (header->value); + + if (!(ng = camel_header_newsgroups_decode (buf))) { + g_free (buf); + return; + } + + g_free (buf); + + html = g_string_new(""); + scan = ng; + while (scan) { + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS) + g_string_append_printf (html, "%s", scan->newsgroup); + else + g_string_append_printf(html, "<a href=\"news:%s\">%s</a>", + scan->newsgroup, scan->newsgroup); + scan = scan->next; + if (scan) + g_string_append_printf(html, ", "); + } + + camel_header_newsgroups_free (ng); + + txt = html->str; + g_string_free (html, FALSE); + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML | + E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + } else if (!strcmp (name, "Received") || !strncmp (name, "X-", 2)) { + /* don't unfold Received nor extension headers */ + txt = value = camel_header_decode_string (header->value, charset); + } else { + /* don't unfold Received nor extension headers */ + buf = camel_header_unfold (header->value); + txt = value = camel_header_decode_string (buf, charset); + g_free (buf); + } + + e_mail_formatter_format_text_header (formatter, buffer, label, txt, flags); + + g_free (value); + g_free (str_field); +} + +GSList * +e_mail_formatter_find_rfc822_end_iter (GSList *iter) +{ + EMailPart *part; + gchar *end; + + part = iter->data; + end = g_strconcat (part->id, ".end", NULL); + for (; iter != NULL; iter = g_slist_next (iter)) { + part = iter->data; + if (!part) + continue; + + if (g_strcmp0 (part->id, end) == 0) { + g_free (end); + return iter; + } + } + g_free (end); + return iter; +} diff --git a/em-format/e-mail-formatter-utils.h b/em-format/e-mail-formatter-utils.h new file mode 100644 index 0000000000..59d8e43257 --- /dev/null +++ b/em-format/e-mail-formatter-utils.h @@ -0,0 +1,56 @@ +/* + * e-mail-formatter-utils.h + * + * 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/> + * + */ + +#ifndef E_MAIL_FORMATTER_UTILS_H_ +#define E_MAIL_FORMATTER_UTILS_H_ + +#include <camel/camel.h> +#include <em-format/e-mail-formatter.h> + +G_BEGIN_DECLS + +void e_mail_formatter_format_header (EMailFormatter *formatter, + GString *buffer, + CamelMedium *part, + struct _camel_header_raw *header, + guint32 flags, + const gchar *charset); + +void e_mail_formatter_format_text_header + (EMailFormatter *formatter, + GString *buffer, + const gchar *label, + const gchar *value, + guint32 flags); + +gchar * e_mail_formatter_format_address (EMailFormatter *formatter, + GString *out, + struct _camel_header_address *a, + gchar *field, + gboolean no_links, + gboolean elipsize); + +void e_mail_formatter_canon_header_name + (gchar *name); + +GSList * e_mail_formatter_find_rfc822_end_iter + (GSList *rfc822_start_iter); + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_UTILS_H_ */ diff --git a/em-format/e-mail-formatter.c b/em-format/e-mail-formatter.c new file mode 100644 index 0000000000..a08504a21f --- /dev/null +++ b/em-format/e-mail-formatter.c @@ -0,0 +1,1510 @@ +/* + * 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 <camel/camel.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-formatter-utils.h" +#include "e-mail-part.h" + +#include "e-mail-format-extensions.h" + +#include <e-util/e-util.h> +#include <libebackend/libebackend.h> +#include <gdk/gdk.h> +#include <glib/gi18n.h> + +#define d(x) + +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 +}; + +static void +mail_formatter_run (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable) +{ + GSList *iter; + gchar *hdr; + + hdr = e_mail_formatter_get_html_header (formatter); + camel_stream_write_string (stream, hdr, cancellable, NULL); + g_free (hdr); + + for (iter = context->parts; iter; iter = iter->next) { + + EMailPart *part; + gboolean ok; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + part = iter->data; + if (!part) + continue; + + if (part->is_hidden && !part->is_error) { + if (g_str_has_suffix (part->id, ".rfc822")) { + iter = e_mail_formatter_find_rfc822_end_iter (iter); + } + + if (!iter) + 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")) { + iter = e_mail_formatter_find_rfc822_end_iter (iter); + + if (!iter) + 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 = iter->data; + if (part && g_str_has_suffix (part->id, ".rfc822.end")) + break; + + iter = iter->next; + } while (iter); + } + } + } + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); +} + +static EMailFormatterContext * +mail_formatter_create_context (EMailFormatter *formatter) +{ + EMailFormatterClass *formatter_class; + + formatter_class = E_MAIL_FORMATTER_GET_CLASS (formatter); + + if (formatter_class->create_context) { + if (!formatter_class->free_context) { + g_warning ("%s implements create_context() but " + "does not implement free_context()!", + G_OBJECT_TYPE_NAME (formatter)); + } + + return formatter_class->create_context (formatter); + } + + return g_new0 (EMailFormatterContext, 1); +} + +static void +mail_formatter_free_context (EMailFormatter *formatter, + EMailFormatterContext *context) +{ + EMailFormatterClass *formatter_class; + + formatter_class = E_MAIL_FORMATTER_GET_CLASS (formatter); + + if (formatter_class->free_context) { + formatter_class->free_context (formatter, context); + } else { + g_free (context); + } +} + +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_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_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_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_base_init (EMailFormatterClass *klass) +{ + klass->extension_registry = g_object_new ( + E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, NULL); + + e_mail_formatter_internal_extensions_load ( + E_MAIL_EXTENSION_REGISTRY (klass->extension_registry)); + + e_extensible_load_extensions ( + E_EXTENSIBLE (klass->extension_registry)); + + klass->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_MARK_CITATION; +} + +static void +e_mail_formatter_base_finalize (EMailFormatterClass *klass) +{ + g_object_unref (klass->extension_registry); +} + +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 +e_mail_formatter_class_init (EMailFormatterClass *klass) +{ + GObjectClass *object_class; + GdkColor *color; + + e_mail_formatter_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, sizeof (EMailFormatterPrivate)); + + klass->run = mail_formatter_run; + + /* EMailFormatter calls these directly */ + klass->create_context = NULL; + klass->free_context = NULL; + klass->set_style = mail_formatter_set_style; + + color = &klass->colors[E_MAIL_FORMATTER_COLOR_BODY]; + gdk_color_parse ("#eeeeee", color); + + color = &klass->colors[E_MAIL_FORMATTER_COLOR_CONTENT]; + gdk_color_parse ("#ffffff", color); + + color = &klass->colors[E_MAIL_FORMATTER_COLOR_FRAME]; + gdk_color_parse ("#3f3f3f", color); + + color = &klass->colors[E_MAIL_FORMATTER_COLOR_HEADER]; + gdk_color_parse ("#eeeeee", color); + + color = &klass->colors[E_MAIL_FORMATTER_COLOR_TEXT]; + gdk_color_parse ("#000000", color); + + object_class = G_OBJECT_CLASS (klass); + object_class->constructed = e_mail_formatter_constructed; + object_class->get_property = e_mail_formatter_get_property; + object_class->set_property = e_mail_formatter_set_property; + object_class->finalize = e_mail_formatter_finalize; + + 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)); +} + +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, + guint32 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); + context->message = parts->message; + context->folder = parts->folder; + context->message_uid = parts->message_uid; + context->parts = parts->list; + context->flags = flags; + context->mode = mode; + + formatter_class->run ( + formatter, context, stream, cancellable); + + mail_formatter_free_context (formatter, 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, + guint32 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 && callback) { + callback (G_OBJECT (formatter), G_ASYNC_RESULT (simple), user_data); + g_object_unref (simple); + return; + } + + context = mail_formatter_create_context (formatter); + context->message = g_object_ref (parts->message); + context->folder = g_object_ref (parts->folder); + context->message_uid = g_strdup (parts->message_uid); + context->parts = g_slist_copy (parts->list); + g_slist_foreach (context->parts, (GFunc) e_mail_part_ref, NULL); + context->flags = flags; + context->mode = mode; + + 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"); + + g_free (context->message_uid); + g_object_unref (context->message); + g_object_unref (context->folder); + g_slist_foreach (context->parts, (GFunc) e_mail_part_unref, NULL); + g_slist_free (context->parts); + mail_formatter_free_context (formatter, 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: #000; font-weight: bold; }\n" + "</style>\n" + "</head><body bgcolor=\"#%06x\">", + e_color_to_value ((GdkColor *) + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY))); +} + +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)); + + 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)); + + 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)); + + 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)); + + 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)); + g_return_if_fail (charset && *charset); + + if (formatter->priv->charset) + g_free (formatter->priv->charset); + + 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 (formatter->priv->default_charset) + 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: EM_FORMAT_HEAD_* defines 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, + guint32 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); +} + +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); +} diff --git a/em-format/e-mail-formatter.h b/em-format/e-mail-formatter.h new file mode 100644 index 0000000000..aea18439b2 --- /dev/null +++ b/em-format/e-mail-formatter.h @@ -0,0 +1,266 @@ +/* + * e-mail-formatter.h + * + * 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/> + * + */ + +#ifndef E_MAIL_FORMATTER_H_ +#define E_MAIL_FORMATTER_H_ + +#include <em-format/e-mail-part-list.h> +#include <em-format/e-mail-extension-registry.h> +#include <gdk/gdk.h> +#include <libemail-engine/e-mail-enums.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER \ + (e_mail_formatter_get_type ()) +#define E_MAIL_FORMATTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatter)) +#define E_MAIL_FORMATTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_FORMATTER, EMailFormatterClass)) +#define E_IS_MAIL_FORMATTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_FORMATTER)) +#define E_IS_MAIL_FORMATTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_FORMATTER)) +#define E_MAIL_FORMATTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterClass)) + +G_BEGIN_DECLS; + +typedef enum { + E_MAIL_FORMATTER_MODE_INVALID = -1, + E_MAIL_FORMATTER_MODE_NORMAL = 0, + E_MAIL_FORMATTER_MODE_SOURCE, + E_MAIL_FORMATTER_MODE_RAW, + E_MAIL_FORMATTER_MODE_PRINTING, + E_MAIL_FORMATTER_MODE_ALL_HEADERS +} EMailFormatterMode; + +typedef enum { + E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE = 1 << 0, + E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED = 1 << 1, + E_MAIL_FORMATTER_HEADER_FLAG_HTML = 1 << 2, + E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS = 1 << 3, + E_MAIL_FORMATTER_HEADER_FLAG_BOLD = 1 << 4, + E_MAIL_FORMATTER_HEADER_FLAG_NODEC = 1 << 5, + E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN = 1 << 6, + E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS = 1 << 7, + E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE = 1 << 8 +} EMailFormatterHeaderFlags; + +typedef enum { + E_MAIL_FORMATTER_COLOR_BODY, /* header area background */ + E_MAIL_FORMATTER_COLOR_CITATION, /* citation font color */ + E_MAIL_FORMATTER_COLOR_CONTENT, /* message area background */ + E_MAIL_FORMATTER_COLOR_FRAME, /* frame around message area */ + E_MAIL_FORMATTER_COLOR_HEADER, /* header font color */ + E_MAIL_FORMATTER_COLOR_TEXT, /* message font color */ + E_MAIL_FORMATTER_NUM_COLOR_TYPES +} EMailFormatterColorType; + +typedef struct _EMailFormatter EMailFormatter; +typedef struct _EMailFormatterClass EMailFormatterClass; +typedef struct _EMailFormatterPrivate EMailFormatterPrivate; +typedef struct _EMailFormatterHeader EMailFormatterHeader; +typedef struct _EMailFormatterContext EMailFormatterContext; + +struct _EMailFormatterHeader { + guint32 flags; /* E_MAIL_FORMATTER_HEADER_FLAG_ * */ + gchar *name; + gchar *value; +}; + +struct _EMailFormatterContext { + CamelMimeMessage *message; + CamelFolder *folder; + gchar *message_uid; + GSList *parts; + + EMailFormatterMode mode; + guint32 flags; +}; + +struct _EMailFormatter { + GObject parent; + EMailFormatterPrivate *priv; +}; + +struct _EMailFormatterClass { + GObjectClass parent_class; + + EMailFormatterExtensionRegistry *extension_registry; + guint32 text_html_flags; + + /* Colors should apply globally */ + GdkColor colors[E_MAIL_FORMATTER_NUM_COLOR_TYPES]; + + void (*run) (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable); + + EMailFormatterContext * (*create_context) (EMailFormatter *formatter); + + void (*free_context) (EMailFormatter *formatter, + EMailFormatterContext *context); + + void (*set_style) (EMailFormatter *formatter, + GtkStyle *style, + GtkStateType state); + +}; + +GType e_mail_formatter_get_type (void); + +EMailFormatter * + e_mail_formatter_new (void); + +void e_mail_formatter_format_sync (EMailFormatter *formatter, + EMailPartList *parts_list, + CamelStream *stream, + guint32 flags, + EMailFormatterMode mode, + GCancellable *cancellable); + +void e_mail_formatter_format (EMailFormatter *formatter, + EMailPartList *parts_list, + CamelStream *stream, + guint32 flags, + EMailFormatterMode mode, + GAsyncReadyCallback callback, + GCancellable *cancellable, + gpointer user_data); + +CamelStream * e_mail_formatter_format_finished + (EMailFormatter *formatter, + GAsyncResult *result, + GError *error); + +gboolean e_mail_formatter_format_as (EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + const gchar *as_mime_type, + GCancellable *cancellable); + +void e_mail_formatter_format_text (EMailFormatter *formatter, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable); +gchar * e_mail_formatter_get_html_header + (EMailFormatter *formatter); +EMailExtensionRegistry * + e_mail_formatter_get_extension_registry + (EMailFormatter *formatter); + +guint32 e_mail_formatter_get_text_format_flags + (EMailFormatter *formatter); + +const GdkColor * + e_mail_formatter_get_color (EMailFormatter *formatter, + EMailFormatterColorType type); +void e_mail_formatter_set_color (EMailFormatter *efh, + EMailFormatterColorType type, + const GdkColor *color); +void e_mail_formatter_set_style (EMailFormatter *formatter, + GtkStyle *style, + GtkStateType state); + +EMailImageLoadingPolicy + e_mail_formatter_get_image_loading_policy + (EMailFormatter *formatter); +void e_mail_formatter_set_image_loading_policy + (EMailFormatter *formatter, + EMailImageLoadingPolicy policy); + +gboolean e_mail_formatter_get_mark_citations + (EMailFormatter *formatter); +void e_mail_formatter_set_mark_citations + (EMailFormatter *formatter, + gboolean mark_citations); + +gboolean e_mail_formatter_get_only_local_photos + (EMailFormatter *formatter); +void e_mail_formatter_set_only_local_photos + (EMailFormatter *formatter, + gboolean only_local_photos); + +gboolean e_mail_formatter_get_show_sender_photo + (EMailFormatter *formatter); +void e_mail_formatter_set_show_sender_photo + (EMailFormatter *formatter, + gboolean show_sender_photo); + +gboolean e_mail_formatter_get_animate_images + (EMailFormatter *formatter); +void e_mail_formatter_set_animate_images + (EMailFormatter *formatter, + gboolean animate_images); + +gboolean e_mail_formatter_get_show_real_date + (EMailFormatter *formatter); +void e_mail_formatter_set_show_real_date + (EMailFormatter *formatter, + gboolean show_real_date); + +const gchar * e_mail_formatter_get_charset (EMailFormatter *formatter); +void e_mail_formatter_set_charset (EMailFormatter *formatter, + const gchar *charset); + +const gchar * e_mail_formatter_get_default_charset + (EMailFormatter *formatter); +void e_mail_formatter_set_default_charset + (EMailFormatter *formatter, + const gchar *charset); + +const GQueue * e_mail_formatter_get_headers (EMailFormatter *formatter); + +void e_mail_formatter_clear_headers (EMailFormatter *formatter); + +void e_mail_formatter_set_default_headers + (EMailFormatter *formatter); + +void e_mail_formatter_add_header (EMailFormatter *formatter, + const gchar *name, + const gchar *value, + guint32 flags); + +void e_mail_formatter_add_header_struct + (EMailFormatter *formatter, + const EMailFormatterHeader *header); + +void e_mail_formatter_remove_header (EMailFormatter *formatter, + const gchar *name, + const gchar *value); + +void e_mail_formatter_remove_header_struct + (EMailFormatter *formatter, + const EMailFormatterHeader *header); + +EMailFormatterHeader * + e_mail_formatter_header_new (const gchar *name, + const gchar *value); + +void e_mail_formatter_header_free (EMailFormatterHeader *header); + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_H_ */ diff --git a/em-format/em-inline-filter.c b/em-format/e-mail-inline-filter.c index f70915148f..aa65708f7e 100644 --- a/em-format/em-inline-filter.c +++ b/em-format/e-mail-inline-filter.c @@ -26,13 +26,12 @@ #include <string.h> -#include "em-inline-filter.h" - -#include "em-format/em-format.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-utils.h" #define d(x) -G_DEFINE_TYPE (EMInlineFilter, em_inline_filter, CAMEL_TYPE_MIME_FILTER) +G_DEFINE_TYPE (EMailInlineFilter, e_mail_inline_filter, CAMEL_TYPE_MIME_FILTER) enum { EMIF_PLAIN, @@ -95,7 +94,7 @@ construct_part_from_stream (CamelStream *mem, } static void -inline_filter_add_part (EMInlineFilter *emif, +inline_filter_add_part (EMailInlineFilter *emif, const gchar *data, gint len) { @@ -186,7 +185,7 @@ inline_filter_add_part (EMInlineFilter *emif, /* pre-snoop the mime type of unknown objects, and poke and hack it into place */ if (camel_content_type_is(dw->mime_type, "application", "octet-stream") - && (mimetype = em_format_snoop_type (part)) + && (mimetype = e_mail_part_snoop_type (part)) && strcmp(mimetype, "application/octet-stream") != 0) { camel_data_wrapper_set_mime_type (dw, mimetype); camel_mime_part_set_content_type (part, mimetype); @@ -206,7 +205,7 @@ inline_filter_scan (CamelMimeFilter *f, gsize len, gint final) { - EMInlineFilter *emif = (EMInlineFilter *) f; + EMailInlineFilter *emif = (EMailInlineFilter *) f; gchar *inptr = in, *inend = in + len; gchar *data_start = in; gchar *start = in; @@ -323,7 +322,7 @@ inline_filter_scan (CamelMimeFilter *f, static void inline_filter_finalize (GObject *object) { - EMInlineFilter *emif = EM_INLINE_FILTER (object); + EMailInlineFilter *emif = E_MAIL_INLINE_FILTER (object); if (emif->base_type) camel_content_type_unref (emif->base_type); @@ -333,7 +332,7 @@ inline_filter_finalize (GObject *object) g_free (emif->filename); /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (em_inline_filter_parent_class)->finalize (object); + G_OBJECT_CLASS (e_mail_inline_filter_parent_class)->finalize (object); } static void @@ -371,7 +370,7 @@ inline_filter_complete (CamelMimeFilter *filter, static void inline_filter_reset (CamelMimeFilter *filter) { - EMInlineFilter *emif = EM_INLINE_FILTER (filter); + EMailInlineFilter *emif = E_MAIL_INLINE_FILTER (filter); GSList *l; l = emif->parts; @@ -389,7 +388,7 @@ inline_filter_reset (CamelMimeFilter *filter) } static void -em_inline_filter_class_init (EMInlineFilterClass *class) +e_mail_inline_filter_class_init (EMailInlineFilterClass *class) { GObjectClass *object_class; CamelMimeFilterClass *mime_filter_class; @@ -404,7 +403,7 @@ em_inline_filter_class_init (EMInlineFilterClass *class) } static void -em_inline_filter_init (EMInlineFilter *emif) +e_mail_inline_filter_init (EMailInlineFilter *emif) { emif->data = g_byte_array_new (); emif->found_any = FALSE; @@ -423,13 +422,13 @@ em_inline_filter_init (EMInlineFilter *emif) * * Return value: **/ -EMInlineFilter * -em_inline_filter_new (CamelTransferEncoding base_encoding, - CamelContentType *base_type) +EMailInlineFilter * +e_mail_inline_filter_new (CamelTransferEncoding base_encoding, + CamelContentType *base_type) { - EMInlineFilter *emif; + EMailInlineFilter *emif; - emif = g_object_new (EM_TYPE_INLINE_FILTER, NULL); + emif = g_object_new (E_TYPE_MAIL_INLINE_FILTER, NULL); emif->base_encoding = base_encoding; if (base_type) { emif->base_type = base_type; @@ -440,7 +439,7 @@ em_inline_filter_new (CamelTransferEncoding base_encoding, } CamelMultipart * -em_inline_filter_get_multipart (EMInlineFilter *emif) +e_mail_inline_filter_get_multipart (EMailInlineFilter *emif) { GSList *l = emif->parts; CamelMultipart *mp; @@ -455,7 +454,7 @@ em_inline_filter_get_multipart (EMInlineFilter *emif) } gboolean -em_inline_filter_found_any (EMInlineFilter *emif) +e_mail_inline_filter_found_any (EMailInlineFilter *emif) { g_return_val_if_fail (emif != NULL, FALSE); diff --git a/em-format/em-inline-filter.h b/em-format/e-mail-inline-filter.h index 503ec7c66c..ff8248c681 100644 --- a/em-format/em-inline-filter.h +++ b/em-format/e-mail-inline-filter.h @@ -21,36 +21,36 @@ * */ -#ifndef EM_INLINE_FILTER_H -#define EM_INLINE_FILTER_H +#ifndef E_MAIL_INLINE_FILTER_H +#define E_MAIL_INLINE_FILTER_H #include <camel/camel.h> /* Standard GObject macros */ -#define EM_TYPE_INLINE_FILTER \ - (em_inline_filter_get_type ()) -#define EM_INLINE_FILTER(obj) \ +#define E_TYPE_MAIL_INLINE_FILTER \ + (e_mail_inline_filter_get_type ()) +#define E_MAIL_INLINE_FILTER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_INLINE_FILTER, EMInlineFilter)) -#define EM_INLINE_FILTER_CLASS(cls) \ + ((obj), E_TYPE_MAIL_INLINE_FILTER, EMailInlineFilter)) +#define E_MAIL_INLINE_FILTER_CLASS(cls) \ (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_INLINE_FILTER, EMInlineFilterClass)) -#define EM_IS_INLINE_FILTER(obj) \ + ((cls), E_TYPE_MAIL_INLINE_FILTER, EMailInlineFilterClass)) +#define E_MAIL_IS_INLINE_FILTER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_INLINE_FILTER)) -#define EM_IS_INLINE_FILTER_CLASS(cls) \ + ((obj), E_TYPE_MAIL_INLINE_FILTER)) +#define E_MAIL_IS_INLINE_FILTER_CLASS(cls) \ (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_INLINE_FILTER)) -#define EM_INLINE_FILTER_GET_CLASS(obj) \ + ((cls), E_TYPE_MAIL_INLINE_FILTER)) +#define E_MAIL_INLINE_FILTER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_INLINE_FILTER, EMInlineFilterClass)) + ((obj), E_TYPE_MAIL_INLINE_FILTER, EMailInlineFilterClass)) G_BEGIN_DECLS -typedef struct _EMInlineFilter EMInlineFilter; -typedef struct _EMInlineFilterClass EMInlineFilterClass; +typedef struct _EMailInlineFilter EMailInlineFilter; +typedef struct _EMailInlineFilterClass EMailInlineFilterClass; -struct _EMInlineFilter { +struct _EMailInlineFilter { CamelMimeFilter filter; gint state; @@ -65,16 +65,18 @@ struct _EMInlineFilter { gboolean found_any; }; -struct _EMInlineFilterClass { +struct _EMailInlineFilterClass { CamelMimeFilterClass filter_class; }; -GType em_inline_filter_get_type (void); -EMInlineFilter *em_inline_filter_new (CamelTransferEncoding base_encoding, +GType e_mail_inline_filter_get_type (void); +EMailInlineFilter * + e_mail_inline_filter_new (CamelTransferEncoding base_encoding, CamelContentType *type); -CamelMultipart *em_inline_filter_get_multipart (EMInlineFilter *emif); -gboolean em_inline_filter_found_any (EMInlineFilter *emif); +CamelMultipart *e_mail_inline_filter_get_multipart + (EMailInlineFilter *emif); +gboolean e_mail_inline_filter_found_any (EMailInlineFilter *emif); G_END_DECLS -#endif /* EM_INLINE_FILTER_H */ +#endif /* E_MAIL_INLINE_FILTER_H */ diff --git a/em-format/e-mail-parser-application-mbox.c b/em-format/e-mail-parser-application-mbox.c new file mode 100644 index 0000000000..c396c48416 --- /dev/null +++ b/em-format/e-mail-parser-application-mbox.c @@ -0,0 +1,213 @@ +/* + * e-mail-parser-application-mbox.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib-object.h> +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserApplicationMBox { + GObject parent; +} EMailParserApplicationMBox; + +typedef struct _EMailParserApplicationMBoxClass { + GObjectClass parent_class; +} EMailParserApplicationMBoxClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserApplicationMBox, + e_mail_parser_application_mbox, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "application/mbox", + NULL }; + +static GSList * +empe_app_mbox_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMimeParser *mime_parser; + CamelStream *mem_stream; + camel_mime_parser_state_t state; + gint old_len; + gint messages; + GSList *parts; + GError *error; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + /* Extract messages from the application/mbox part and + * render them as a flat list of messages. */ + + /* XXX If the mbox has multiple messages, maybe render them + * as a multipart/digest so each message can be expanded + * or collapsed individually. + * + * See attachment_handler_mail_x_uid_list() for example. */ + + /* XXX This is based on em_utils_read_messages_from_stream(). + * Perhaps refactor that function to return an array of + * messages instead of assuming we want to append them + * to a folder? */ + + mime_parser = camel_mime_parser_new (); + camel_mime_parser_scan_from (mime_parser, TRUE); + + mem_stream = camel_stream_mem_new (); + camel_data_wrapper_decode_to_stream_sync ( + camel_medium_get_content (CAMEL_MEDIUM (part)), + mem_stream, NULL, NULL); + g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, cancellable, NULL); + + error = NULL; + camel_mime_parser_init_with_stream (mime_parser, mem_stream, &error); + if (error) { + parts = e_mail_parser_error (parser, cancellable, + _("Error parsing MBOX part: %s"), + error->message ? + error->message : + _("Unknown error")); + g_clear_error (&error); + g_object_unref (mem_stream); + g_object_unref (mime_parser); + return parts; + } + + g_object_unref (mem_stream); + + old_len = part_id->len; + + /* Extract messages from the mbox. */ + messages = 0; + state = camel_mime_parser_step (mime_parser, NULL, NULL); + + parts = NULL; + while (state == CAMEL_MIME_PARSER_STATE_FROM) { + CamelMimeMessage *message; + CamelMimePart *opart; + GSList *new_parts; + + message = camel_mime_message_new (); + opart = CAMEL_MIME_PART (message); + + if (!camel_mime_part_construct_from_parser_sync ( + opart, mime_parser, NULL, NULL)) { + g_object_unref (message); + break; + } + + g_string_append_printf (part_id, ".mbox.%d", messages); + + new_parts = e_mail_parser_parse_part_as ( + parser, CAMEL_MIME_PART (message), + part_id, "message/rfc822", cancellable); + + /* Wrap every message as attachment */ + new_parts = e_mail_parser_wrap_as_attachment ( + parser, CAMEL_MIME_PART (message), + new_parts, part_id, cancellable); + + /* Inline all messages in mbox */ + if (new_parts && new_parts->data) { + EMailPart *p = new_parts->data; + + p->force_inline = TRUE; + } + + parts = g_slist_concat (parts, new_parts); + + g_string_truncate (part_id, old_len); + + g_object_unref (message); + + /* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */ + camel_mime_parser_step (mime_parser, NULL, NULL); + + state = camel_mime_parser_step (mime_parser, NULL, NULL); + + messages++; + } + + g_object_unref (mime_parser); + + return parts; +} + +static guint32 +empe_app_mbox_get_flags (EMailParserExtension *extension) +{ + return E_MAIL_PARSER_EXTENSION_INLINE | + E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; +} + +static const gchar ** +empe_app_mbox_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_application_mbox_class_init (EMailParserApplicationMBoxClass *klass) +{ + e_mail_parser_application_mbox_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *interface) +{ + interface->parse = empe_app_mbox_parse; + interface->get_flags = empe_app_mbox_get_flags; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *interface) +{ + interface->mime_types = empe_app_mbox_mime_types; +} + +static void +e_mail_parser_application_mbox_init (EMailParserApplicationMBox *self) +{ +} diff --git a/em-format/e-mail-parser-application-smime.c b/em-format/e-mail-parser-application-smime.c new file mode 100644 index 0000000000..12bce2fa56 --- /dev/null +++ b/em-format/e-mail-parser-application-smime.c @@ -0,0 +1,199 @@ +/* + * e-mail-parser-application-xpkcs7mime.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib-object.h> +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserApplicationSMIME { + GObject parent; +} EMailParserApplicationSMIME; + +typedef struct _EMailParserAppplicationSMIMEClass { + GObjectClass parent_class; +} EMailParserApplicationSMIMEClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserApplicationSMIME, + e_mail_parser_application_smime, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "application/xpkcs7mime", + "application/x-pkcs7-mime", + "application/pkcs7-mime", + "application/pkcs7-signature", + "application/xpkcs7-signature", + NULL }; + +static GSList * +empe_app_smime_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelCipherContext *context; + CamelMimePart *opart; + CamelCipherValidity *valid; + GError *local_error = NULL; + GSList *parts, *iter; + CamelContentType *ct; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + ct = camel_mime_part_get_content_type (part); + if (camel_content_type_is (ct, "application", "pkcs7-signature") || + camel_content_type_is (ct, "application", "xpkcs7-signature")) { + return g_slist_alloc (); + } + + context = camel_smime_context_new (e_mail_parser_get_session (parser)); + + opart = camel_mime_part_new (); + valid = camel_cipher_context_decrypt_sync ( + context, part, opart, + cancellable, &local_error); + + e_mail_part_preserve_charset_in_content_type (part, opart); + + if (valid == NULL) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Could not parse S/MIME message: %s"), + local_error->message ? + local_error->message : + _("Unknown error")); + g_clear_error (&local_error); + } else { + gint len = part_id->len; + + g_string_append (part_id, ".encrypted"); + + parts = e_mail_parser_parse_part ( + parser, opart, part_id, cancellable); + + g_string_truncate (part_id, len); + + /* Update validity flags of all the involved subp-arts */ + for (iter = parts; iter; iter = iter->next) { + + EMailPart *mail_part = iter->data; + if (!mail_part) + continue; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_SMIME); + + } + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself */ + if (!e_mail_part_is_secured (opart)) { + GSList *button; + EMailPart *mail_part; + g_string_append (part_id, ".encrypted.button"); + + button = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable); + if (button && button->data) { + mail_part = button->data; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_SMIME); + } + + parts = g_slist_concat (parts, button); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + } + + g_object_unref (opart); + g_object_unref (context); + + return parts; +} + +static guint32 +empe_app_smime_get_flags (EMailParserExtension *extension) +{ + return E_MAIL_PARSER_EXTENSION_INLINE; +} + +static const gchar ** +empe_application_smime_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_application_smime_class_init (EMailParserApplicationSMIMEClass *klass) +{ + e_mail_parser_application_smime_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *interface) +{ + interface->parse = empe_app_smime_parse; + interface->get_flags = empe_app_smime_get_flags; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *interface) +{ + interface->mime_types = empe_application_smime_mime_types; +} + +static void +e_mail_parser_application_smime_init (EMailParserApplicationSMIME *parser) +{ + +} diff --git a/em-format/e-mail-parser-attachment-bar.c b/em-format/e-mail-parser-attachment-bar.c new file mode 100644 index 0000000000..009869d43c --- /dev/null +++ b/em-format/e-mail-parser-attachment-bar.c @@ -0,0 +1,120 @@ +/* + * e-mail-parser-attachment-bar.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> +#include "e-mail-part-attachment-bar.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <widgets/misc/e-attachment-bar.h> + +#include <camel/camel.h> + +static void +mail_part_attachment_bar_free (EMailPart *part) +{ + EMailPartAttachmentBar *empab = (EMailPartAttachmentBar *) part; + + g_clear_object (&empab->store); +} + +/******************************************************************************/ + +typedef struct _EMailParserAttachmentBar { + GObject parent; +} EMailParserAttachmentBar; + +typedef struct _EMailParserAttachmentBarClass { + GObjectClass parent_class; +} EMailParserAttachmentBarClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserAttachmentBar, + e_mail_parser_attachment_bar, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)) + +static const gchar *parser_mime_types[] = { "application/vnd.evolution.widget.attachment-bar", NULL }; + +static GSList * +empe_attachment_bar_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPartAttachmentBar *empab; + gint len; + + len = part_id->len; + g_string_append (part_id, ".attachment-bar"); + empab = (EMailPartAttachmentBar *) e_mail_part_subclass_new ( + part, part_id->str, sizeof (EMailPartAttachmentBar), + (GFreeFunc) mail_part_attachment_bar_free); + empab->parent.mime_type = g_strdup ("application/vnd.evolution.widget.attachment-bar"); + empab->store = E_ATTACHMENT_STORE (e_attachment_store_new ()); + g_string_truncate (part_id, len); + + return g_slist_append (NULL, empab); +} + +static const gchar ** +empe_attachment_bar_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_attachment_bar_class_init (EMailParserAttachmentBarClass *klass) +{ + e_mail_parser_attachment_bar_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_attachment_bar_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_attachment_bar_mime_types; +} + +static void +e_mail_parser_attachment_bar_init (EMailParserAttachmentBar *parser) +{ + +} diff --git a/em-format/e-mail-parser-extension.c b/em-format/e-mail-parser-extension.c new file mode 100644 index 0000000000..589fae31f7 --- /dev/null +++ b/em-format/e-mail-parser-extension.c @@ -0,0 +1,101 @@ +/* + * e-mail-parser-extension.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 <camel/camel.h> + +#include "e-mail-parser-extension.h" + +G_DEFINE_INTERFACE ( + EMailParserExtension, + e_mail_parser_extension, + E_TYPE_MAIL_EXTENSION) + +/** + * EMailParserExtension: + * + * The #EMailParserExtension is an abstract interface for all extensions for + * #EMailParser. + */ + +static void +e_mail_parser_extension_default_init (EMailParserExtensionInterface *iface) +{ + +} + +/** + * e_mail_parser_extension_parse + * @extension: an #EMailParserExtension + * @parser: a #EMailParser + * @mime_part: (allow-none) a #CamelMimePart to parse + * @part_id: a #GString to which parser will append ID of the parsed part. + * @flags: #EMailParserFlags + * @cancellable: (allow-none) A #GCancellable + * + * A virtual function reimplemented in all mail parser extensions. The function + * decodes and parses the @mime_part, creating one or more #EMailPart<!-//>s. + * + * When the function is unable to parse the @mime_part (either because it's broken + * or because it is a different mimetype then the extension is specialized for), the + * function will return @NULL indicating the #EMailParser, that it should pick + * another extension. + * + * When the @mime_part contains for example multipart/mixed of one RFC822 message + * with an attachment and of one image, then parser must make sure that the + * returned #GSList is correctly ordered: + * + * part1.rfc822.plain_text + * part1.rfc822.attachment + * part2.image + * + * Implementation of this function must be thread-safe. + * + * Return value: Returns #GSList of #EMailPart<!-//>s when the part was succesfully + * parsed, returns @NULL when the parser is not able to parse the part. + */ +GSList * +e_mail_parser_extension_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *mime_part, + GString *part_id, + GCancellable *cancellable) +{ + EMailParserExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_PARSER_EXTENSION (extension), NULL); + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + + interface = E_MAIL_PARSER_EXTENSION_GET_INTERFACE (extension); + g_return_val_if_fail (interface->parse != NULL, NULL); + + return interface->parse (extension, parser, mime_part, part_id, cancellable); +} + +guint32 +e_mail_parser_extension_get_flags (EMailParserExtension *extension) +{ + EMailParserExtensionInterface *interface; + + g_return_val_if_fail (E_IS_MAIL_PARSER_EXTENSION (extension), 0); + + interface = E_MAIL_PARSER_EXTENSION_GET_INTERFACE (extension); + if (interface->get_flags == NULL) + return 0; + + return interface->get_flags (extension); +} diff --git a/em-format/e-mail-parser-extension.h b/em-format/e-mail-parser-extension.h new file mode 100644 index 0000000000..0dcff76212 --- /dev/null +++ b/em-format/e-mail-parser-extension.h @@ -0,0 +1,86 @@ +/* + * e-mail-parser-extension.h + * + * 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/> + * + */ + +#ifndef E_MAIL_PARSER_EXTENSION_H +#define E_MAIL_PARSER_EXTENSION_H + +#include <em-format/e-mail-extension.h> +#include <em-format/e-mail-parser.h> +#include <camel/camel.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PARSER_EXTENSION \ + (e_mail_parser_extension_get_type ()) +#define E_MAIL_PARSER_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION, EMailParserExtension)) +#define E_MAIL_PARSER_EXTENSION_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PARSER_EXTENSION, EMailParserExtensionInterface)) +#define E_IS_MAIL_PARSER_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION)) +#define E_IS_MAIL_PARSER_EXTENSION_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PARSER_EXTENSION)) +#define E_MAIL_PARSER_EXTENSION_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION, EMailParserExtensionInterface)) + +#define EMP_EXTENSION_GET_PARSER(e) \ + E_MAIL_PARSER (e_extension_get_extensible (E_EXTENSION (e))) + +G_BEGIN_DECLS + +typedef struct _EMailParserExtension EMailParserExtension; +typedef struct _EMailParserExtensionInterface EMailParserExtensionInterface; + +typedef enum { + E_MAIL_PARSER_EXTENSION_INLINE = 1 << 0, /* Don't parse as attachment */ + E_MAIL_PARSER_EXTENSION_INLINE_DISPOSITION = 1 << 1, /* Always expand */ + E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE = 1 << 2 /* Always check what's inside */ +} EMailParserExtensionFlags; + +struct _EMailParserExtensionInterface { + EMailExtensionInterface parent_interface; + + GSList * (*parse) (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *mime_part, + GString *part_id, + GCancellable *cancellable); + + guint32 (*get_flags) (EMailParserExtension *extension); + +}; + +GType e_mail_parser_extension_get_type + (void); + +GSList * e_mail_parser_extension_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *mime_part, + GString *part_id, + GCancellable *cancellable); + +guint32 e_mail_parser_extension_get_flags + (EMailParserExtension *extension); + +G_END_DECLS + +#endif /* E_MAIL_PARSER_EXTENSION_H */ diff --git a/em-format/e-mail-parser-headers.c b/em-format/e-mail-parser-headers.c new file mode 100644 index 0000000000..06f28746f0 --- /dev/null +++ b/em-format/e-mail-parser-headers.c @@ -0,0 +1,112 @@ +/* + * e-mail-parser-headers.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <libemail-engine/e-mail-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserHeaders { + GObject parent; +} EMailParserHeaders; + +typedef struct _EMailParserHeadersClass { + GObjectClass parent_class; +} EMailParserHeadersClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserHeaders, + e_mail_parser_headers, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "application/vnd.evolution.headers", NULL }; + +static GSList * +empe_headers_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + gint len; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + len = part_id->len; + g_string_append (part_id, ".headers"); + + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = g_strdup ("application/vnd.evolution.headers"); + g_string_truncate (part_id, len); + + return g_slist_append (NULL, mail_part); +} + +static const gchar ** +empe_headers_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_headers_class_init (EMailParserHeadersClass *klass) +{ + e_mail_parser_headers_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_headers_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_headers_mime_types; +} + +static void +e_mail_parser_headers_init (EMailParserHeaders *parser) +{ + +} diff --git a/em-format/e-mail-parser-image.c b/em-format/e-mail-parser-image.c new file mode 100644 index 0000000000..2fb1fdff2f --- /dev/null +++ b/em-format/e-mail-parser-image.c @@ -0,0 +1,147 @@ +/* + * e-mail-parser-image.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-part-utils.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +typedef struct _EMailParserImage { + GObject parent; +} EMailParserImage; + +typedef struct _EMailParserImageClass { + GObjectClass parent_class; +} EMailParserImageClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserImage, + e_mail_parser_image, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "image/gif", + "image/jpeg", + "image/png", + "image/x-png", + "image/x-bmp", + "image/bmp", + "image/svg", + "image/x-cmu-raster", + "image/x-ico", + "image/x-portable-anymap", + "image/x-portable-bitmap", + "image/x-portable-graymap", + "image/x-portable-pixmap", + "image/x-xpixmap", + "image/jpg", + "image/pjpeg", + NULL }; + +static GSList * +empe_image_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + const gchar *tmp; + gchar *cid; + gint len; + CamelContentType *ct; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + tmp = camel_mime_part_get_content_id (part); + if (tmp) { + cid = g_strdup_printf ("cid:%s", tmp); + } else { + cid = NULL; + } + + len = part_id->len; + g_string_append (part_id, ".image"); + + ct = camel_mime_part_get_content_type (part); + + mail_part = e_mail_part_new (part, part_id->str); + mail_part->is_attachment = TRUE; + mail_part->cid = cid; + mail_part->mime_type = ct ? camel_content_type_simple (ct) : g_strdup ("image/*"); + mail_part->is_hidden = (cid != NULL); + + g_string_truncate (part_id, len); + + if (!cid) { + return e_mail_parser_wrap_as_attachment ( + parser, part, g_slist_append (NULL, mail_part), + part_id, cancellable); + } + + return g_slist_append (NULL, mail_part); +} + +static const gchar ** +empe_image_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_image_class_init (EMailParserImageClass *klass) +{ + e_mail_parser_image_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_image_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_image_mime_types; +} + +static void +e_mail_parser_image_init (EMailParserImage *parser) +{ + +} diff --git a/em-format/e-mail-parser-inlinepgp-encrypted.c b/em-format/e-mail-parser-inlinepgp-encrypted.c new file mode 100644 index 0000000000..5395b7be7e --- /dev/null +++ b/em-format/e-mail-parser-inlinepgp-encrypted.c @@ -0,0 +1,205 @@ +/* + * e-mail-parser-inlinepgp-encrypted.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserInlinePGPEncrypted { + GObject parent; +} EMailParserInlinePGPEncrypted; + +typedef struct _EMailParserInlinePGPEncryptedClass { + GObjectClass parent_class; +} EMailParserInlinePGPEncryptedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserInlinePGPEncrypted, + e_mail_parser_inline_pgp_encrypted, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "application/x-inlinepgp-encrypted", + NULL }; + +static GSList * +empe_inlinepgp_encrypted_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelCipherContext *cipher; + CamelCipherValidity *valid; + CamelMimePart *opart; + CamelDataWrapper *dw; + gchar *mime_type; + gint len; + GError *local_error = NULL; + GSList *parts, *iter; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + cipher = camel_gpg_context_new (e_mail_parser_get_session (parser)); + + opart = camel_mime_part_new (); + + /* Decrypt the message */ + valid = camel_cipher_context_decrypt_sync ( + cipher, part, opart, cancellable, &local_error); + + if (!valid) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Could not parse PGP message: %s"), + local_error->message ? + local_error->message : + _("Unknown error")); + g_clear_error (&local_error); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as (parser, + part, part_id, + "application/vnd.evolution.source", + cancellable)); + + g_object_unref (cipher); + g_object_unref (opart); + return parts; + } + + dw = camel_medium_get_content ((CamelMedium *) opart); + mime_type = camel_data_wrapper_get_mime_type (dw); + + /* this ensures to show the 'opart' as inlined, if possible */ + if (mime_type && g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) { + const gchar *snoop = e_mail_part_snoop_type (opart); + + if (snoop) + camel_data_wrapper_set_mime_type (dw, snoop); + } + + e_mail_part_preserve_charset_in_content_type (part, opart); + g_free (mime_type); + + /* Pass it off to the real formatter */ + len = part_id->len; + g_string_append (part_id, ".inlinepgp_encrypted"); + + parts = e_mail_parser_parse_part_as ( + parser, opart, part_id, + camel_data_wrapper_get_mime_type (dw), cancellable); + + g_string_truncate (part_id, len); + + for (iter = parts; iter; iter = iter->next) { + EMailPart *mail_part; + + mail_part = iter->data; + if (!mail_part) + continue; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself */ + if (!e_mail_part_is_secured (opart)) { + GSList *button; + EMailPart *mail_part; + g_string_append (part_id, ".inlinepgp_encrypted.button"); + + button = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable); + if (button && button->data) { + mail_part = button->data; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + parts = g_slist_concat (parts, button); + + g_string_truncate (part_id, len); + } + + /* Clean Up */ + camel_cipher_validity_free (valid); + g_object_unref (opart); + g_object_unref (cipher); + + return parts; +} + +static const gchar ** +empe_inlinepgp_encrypted_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_inline_pgp_encrypted_class_init (EMailParserInlinePGPEncryptedClass *klass) +{ + e_mail_parser_inline_pgp_encrypted_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_inlinepgp_encrypted_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_inlinepgp_encrypted_mime_types; +} + +static void +e_mail_parser_inline_pgp_encrypted_init (EMailParserInlinePGPEncrypted *parser) +{ + +} diff --git a/em-format/e-mail-parser-inlinepgp-signed.c b/em-format/e-mail-parser-inlinepgp-signed.c new file mode 100644 index 0000000000..d90dcddd2e --- /dev/null +++ b/em-format/e-mail-parser-inlinepgp-signed.c @@ -0,0 +1,230 @@ +/* + * e-mail-parser-inlinepgp-signed.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserInlinePGPSigned { + GObject parent; +} EMailParserInlinePGPSigned; + +typedef struct _EMailParserInlinePGPSignedClass { + GObjectClass parent_class; +} EMailParserInlinePGPSignedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserInlinePGPSigned, + e_mail_parser_inline_pgp_signed, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "application/x-inlinepgp-signed", + NULL }; + +static GSList * +empe_inlinepgp_signed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilterPgp *pgp_filter; + CamelContentType *content_type; + CamelCipherContext *cipher; + CamelCipherValidity *valid; + CamelDataWrapper *dw; + CamelMimePart *opart; + CamelStream *ostream; + gchar *type; + gint len; + GError *local_error = NULL; + GByteArray *ba; + GSList *parts, *iter; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + cipher = camel_gpg_context_new (e_mail_parser_get_session (parser)); + + /* Verify the signature of the message */ + valid = camel_cipher_context_verify_sync ( + cipher, part, cancellable, &local_error); + if (!valid) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Error verifying signature: %s"), + local_error->message ? + local_error->message : + _("Unknown error")); + + g_clear_error (&local_error); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable)); + + g_object_unref (cipher); + return parts; + } + + /* Setup output stream */ + ostream = camel_stream_mem_new (); + filtered_stream = camel_stream_filter_new (ostream); + + /* Add PGP header / footer filter */ + pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new (); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), + CAMEL_MIME_FILTER (pgp_filter)); + g_object_unref (pgp_filter); + + /* Pass through the filters that have been setup */ + dw = camel_medium_get_content ((CamelMedium *) part); + camel_data_wrapper_decode_to_stream_sync ( + dw, (CamelStream *) filtered_stream, cancellable, NULL); + camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + /* Create a new text/plain MIME part containing the signed + * content preserving the original part's Content-Type params. */ + content_type = camel_mime_part_get_content_type (part); + type = camel_content_type_format (content_type); + content_type = camel_content_type_decode (type); + g_free (type); + + g_free (content_type->type); + content_type->type = g_strdup ("text"); + g_free (content_type->subtype); + content_type->subtype = g_strdup ("plain"); + type = camel_content_type_format (content_type); + camel_content_type_unref (content_type); + + ba = camel_stream_mem_get_byte_array ((CamelStreamMem *) ostream); + opart = camel_mime_part_new (); + camel_mime_part_set_content (opart, (gchar *) ba->data, ba->len, type); + g_free (type); + + len = part_id->len; + g_string_append (part_id, ".inlinepgp_signed"); + + parts = e_mail_parser_parse_part ( + parser, opart, part_id, cancellable); + + for (iter = parts; iter; iter = iter->next) { + EMailPart *mail_part; + + mail_part = iter->data; + if (!mail_part) + continue; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_SIGNED | + E_MAIL_PART_VALIDITY_PGP); + } + + g_string_truncate (part_id, len); + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself */ + if (!e_mail_part_is_secured (opart)) { + GSList *button; + EMailPart *mail_part; + g_string_append (part_id, ".inlinepgp_signed.button"); + + button = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable); + if (button && button->data) { + mail_part = button->data; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_SIGNED | + E_MAIL_PART_VALIDITY_PGP); + } + + parts = g_slist_concat (parts, button); + + g_string_truncate (part_id, len); + } + + /* Clean Up */ + camel_cipher_validity_free (valid); + g_object_unref (dw); + g_object_unref (opart); + g_object_unref (ostream); + g_object_unref (cipher); + + return parts; +} + +static const gchar ** +empe_inlinepgp_signed_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_inline_pgp_signed_class_init (EMailParserInlinePGPSignedClass *klass) +{ + e_mail_parser_inline_pgp_signed_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_inlinepgp_signed_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_inlinepgp_signed_mime_types; +} + +static void +e_mail_parser_inline_pgp_signed_init (EMailParserInlinePGPSigned *parser) +{ + +} diff --git a/em-format/e-mail-parser-message-deliverystatus.c b/em-format/e-mail-parser-message-deliverystatus.c new file mode 100644 index 0000000000..feae7c2cf5 --- /dev/null +++ b/em-format/e-mail-parser-message-deliverystatus.c @@ -0,0 +1,116 @@ +/* + * e-mail-parser-message-deliverystatus.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib-object.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMessageDeliveryStatus { + GObject parent; +} EMailParserMessageDeliveryStatus; + +typedef struct _EMailParserMessageDeliveryStatusClass { + GObjectClass parent_class; +} EMailParserMessageDeliveryStatusClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMessageDeliveryStatus, + e_mail_parser_message_delivery_status, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "message/delivery-status", + NULL }; + +static GSList * +empe_msg_deliverystatus_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + gsize len; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + len = part_id->len; + g_string_append (part_id, ".delivery-status"); + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = g_strdup ("text/plain"); + + g_string_truncate (part_id, len); + + /* The only reason for having a separate parser for + * message/delivery-status is to display the part as an attachment */ + return e_mail_parser_wrap_as_attachment ( + parser, part, g_slist_append (NULL, mail_part), + part_id, cancellable); +} + +static const gchar ** +empe_msg_deliverystatus_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_message_delivery_status_class_init (EMailParserMessageDeliveryStatusClass *klass) +{ + e_mail_parser_message_delivery_status_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_msg_deliverystatus_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_msg_deliverystatus_mime_types; +} + +static void +e_mail_parser_message_delivery_status_init (EMailParserMessageDeliveryStatus *parser) +{ + +} diff --git a/em-format/e-mail-parser-message-external.c b/em-format/e-mail-parser-message-external.c new file mode 100644 index 0000000000..8c02a3ef3b --- /dev/null +++ b/em-format/e-mail-parser-message-external.c @@ -0,0 +1,211 @@ +/* + * e-mail-parser-message-external.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#include <string.h> +#include <ctype.h> + +typedef struct _EMailParserMessageExternal { + GObject parent; +} EMailParserMessageExternal; + +typedef struct _EMailParserMessageExternalClass { + GObjectClass parent_class; +} EMailParserMessageExternalClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMessageExternal, + e_mail_parser_message_external, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "message/external-body", + NULL }; + +static GSList * +empe_msg_external_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + CamelMimePart *newpart; + CamelContentType *type; + const gchar *access_type; + gchar *url = NULL, *desc = NULL; + gchar *content; + gint len; + gchar *mime_type; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + newpart = camel_mime_part_new (); + + /* needs to be cleaner */ + type = camel_mime_part_get_content_type (part); + access_type = camel_content_type_param (type, "access-type"); + if (!access_type) { + const gchar *msg = _("Malformed external-body part"); + mime_type = g_strdup ("text/plain"); + camel_mime_part_set_content (newpart, msg, strlen (msg), mime_type); + goto addPart; + } + + if (!g_ascii_strcasecmp(access_type, "ftp") || + !g_ascii_strcasecmp(access_type, "anon-ftp")) { + const gchar *name, *site, *dir, *mode; + gchar *path; + gchar ftype[16]; + + name = camel_content_type_param (type, "name"); + site = camel_content_type_param (type, "site"); + dir = camel_content_type_param (type, "directory"); + mode = camel_content_type_param (type, "mode"); + if (name == NULL || site == NULL) + goto fail; + + /* Generate the path. */ + if (dir) + path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name); + else + path = g_strdup_printf("/%s", *name=='/'?name+1:name); + + if (mode && *mode) + sprintf(ftype, ";type=%c", *mode); + else + ftype[0] = 0; + + url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype); + g_free (path); + desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url); + } else if (!g_ascii_strcasecmp (access_type, "local-file")) { + const gchar *name, *site; + + name = camel_content_type_param (type, "name"); + site = camel_content_type_param (type, "site"); + if (name == NULL) + goto fail; + + url = g_filename_to_uri (name, NULL, NULL); + if (site) + desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site); + else + desc = g_strdup_printf(_("Pointer to local file (%s)"), name); + } else if (!g_ascii_strcasecmp (access_type, "URL")) { + const gchar *urlparam; + gchar *s, *d; + + /* RFC 2017 */ + urlparam = camel_content_type_param (type, "url"); + if (urlparam == NULL) + goto fail; + + /* For obscure MIMEy reasons, the URL may be split into words */ + url = g_strdup (urlparam); + s = d = url; + while (*s) { + if (!isspace ((guchar) * s)) + *d++ = *s; + s++; + } + *d = 0; + desc = g_strdup_printf (_("Pointer to remote data (%s)"), url); + } else { + goto fail; + } + + mime_type = g_strdup ("text/html"); + content = g_strdup_printf ("<a href=\"%s\">%s</a>", url, desc); + camel_mime_part_set_content (newpart, content, strlen (content), mime_type); + g_free (content); + + g_free (url); + g_free (desc); + + goto addPart; + +fail: + content = g_strdup_printf ( + _("Pointer to unknown external data (\"%s\" type)"), + access_type); + mime_type = g_strdup ("text/plain"); + camel_mime_part_set_content (newpart, content, strlen (content), mime_type); + g_free (content); + +addPart: + len = part_id->len; + g_string_append (part_id, ".msg_external"); + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = mime_type; + g_string_truncate (part_id, len); + + return g_slist_append (NULL, mail_part); +} + +static const gchar ** +empe_msg_external_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_message_external_class_init (EMailParserMessageExternalClass *klass) +{ + e_mail_parser_message_external_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_msg_external_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_msg_external_mime_types; +} + +static void +e_mail_parser_message_external_init (EMailParserMessageExternal *parser) +{ + +} diff --git a/em-format/e-mail-parser-message-rfc822.c b/em-format/e-mail-parser-message-rfc822.c new file mode 100644 index 0000000000..75b0306d84 --- /dev/null +++ b/em-format/e-mail-parser-message-rfc822.c @@ -0,0 +1,174 @@ +/* + * e-mail-parser-message-rfc822.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> +#include <glib-object.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-list.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMessageRFC822 { + GObject parent; +} EMailParserMessageRFC822; + +typedef struct _EMailParserMessageRFC822Class { + GObjectClass parent_class; +} EMailParserMessageRFC822Class; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMessageRFC822, + e_mail_parser_message_rfc822, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "message/rfc822", + "message/news", + "message/*", + NULL }; + +static GSList * +empe_msg_rfc822_parse (EMailParserExtension *extension, + EMailParser *eparser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + GSList *parts = NULL; + EMailPart *mail_part; + gint len; + CamelMimePart *message; + CamelDataWrapper *dw; + CamelStream *new_stream; + CamelMimeParser *mime_parser; + CamelContentType *ct; + + len = part_id->len; + g_string_append (part_id, ".rfc822"); + + /* Create an empty PURI that will represent start of the RFC message */ + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = g_strdup ("message/rfc822"); + parts = g_slist_append (NULL, mail_part); + + /* Sometime the _actual_ message is encapsulated in another CamelMimePart, + * sometimes the CamelMimePart actually represents the RFC822 message */ + ct = camel_mime_part_get_content_type (part); + if (camel_content_type_is (ct, "message", "rfc822")) { + new_stream = camel_stream_mem_new (); + mime_parser = camel_mime_parser_new (); + message = (CamelMimePart *) camel_mime_message_new (); + + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + camel_data_wrapper_decode_to_stream_sync ( + dw, new_stream, cancellable, NULL); + g_seekable_seek ( + G_SEEKABLE (new_stream), 0, G_SEEK_SET, cancellable, NULL); + camel_mime_parser_init_with_stream ( + mime_parser, new_stream, NULL); + camel_mime_part_construct_from_parser_sync ( + message, mime_parser, cancellable, NULL); + + g_object_unref (mime_parser); + g_object_unref (new_stream); + } else { + message = g_object_ref (part); + } + + parts = g_slist_concat (parts, e_mail_parser_parse_part_as ( + eparser, message, part_id, + "application/vnd.evolution.message", + cancellable)); + + g_object_unref (message); + + /* Add another generic EMailPart that represents end of the RFC message. + * The em_format_write() function will skip all parts between the ".rfc822" + * part and ".rfc822.end" part as they will be rendered in an <iframe> */ + g_string_append (part_id, ".end"); + mail_part = e_mail_part_new (message, part_id->str); + mail_part->is_hidden = TRUE; + parts = g_slist_append (parts, mail_part); + g_string_truncate (part_id, len); + + if (e_mail_part_is_attachment (message)) { + return e_mail_parser_wrap_as_attachment ( + eparser, message, parts, part_id, cancellable); + } + + return parts; +} + +static guint32 +empe_msg_rfc822_get_flags (EMailParserExtension *extension) +{ + return E_MAIL_PARSER_EXTENSION_INLINE | + E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; +} + +static const gchar ** +empe_msg_rfc822_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_message_rfc822_class_init (EMailParserMessageRFC822Class *klass) +{ + e_mail_parser_message_rfc822_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_msg_rfc822_parse; + iface->get_flags = empe_msg_rfc822_get_flags; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_msg_rfc822_mime_types; +} + +static void +e_mail_parser_message_rfc822_init (EMailParserMessageRFC822 *parser) +{ + +} diff --git a/em-format/e-mail-parser-message.c b/em-format/e-mail-parser-message.c new file mode 100644 index 0000000000..acfebb8977 --- /dev/null +++ b/em-format/e-mail-parser-message.c @@ -0,0 +1,129 @@ +/* + * e-mail-parser-message.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include "e-mail-part-utils.h" +#include <libemail-engine/e-mail-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMessage { + GObject parent; +} EMailParserMessage; + +typedef struct _EMailParserMessageClass { + GObjectClass parent_class; +} EMailParserMessageClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMessage, + e_mail_parser_message, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "application/vnd.evolution.message", NULL }; + +static GSList * +empe_message_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + GSList *parts; + CamelContentType *ct; + gchar *mime_type; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + /* Headers */ + parts = g_slist_concat (NULL, e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.headers", + cancellable)); + + /* Attachment Bar */ + parts = g_slist_concat (parts, e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.attachment-bar", + cancellable)); + + ct = camel_mime_part_get_content_type (part); + mime_type = camel_content_type_simple (ct); + + /* Actual message body */ + parts = g_slist_concat (parts, e_mail_parser_parse_part_as ( + parser, part, part_id, mime_type, + cancellable)); + + g_free (mime_type); + + return parts; +} + +static const gchar ** +empe_message_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_message_class_init (EMailParserMessageClass *klass) +{ + e_mail_parser_message_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_message_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_message_mime_types; +} + +static void +e_mail_parser_message_init (EMailParserMessage *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-alternative.c b/em-format/e-mail-parser-multipart-alternative.c new file mode 100644 index 0000000000..c8bbfdc31d --- /dev/null +++ b/em-format/e-mail-parser-multipart-alternative.c @@ -0,0 +1,191 @@ +/* + * e-mail-parser-multipart-alternative.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMultipartAlternative { + GObject parent; +} EMailParserMultipartAlternative; + +typedef struct _EMailParserMultipartAlternativeClass { + GObjectClass parent_class; +} EMailParserMultipartAlternativeClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartAlternative, + e_mail_parser_multipart_alternative, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "multipart/alternative", NULL }; + +static gboolean +related_display_part_is_attachment (CamelMimePart *part) +{ + CamelMimePart *display_part; + + display_part = e_mail_part_get_related_display_part (part, NULL); + return display_part && e_mail_part_is_attachment (display_part); +} + +static GSList * +empe_mp_alternative_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMultipart *mp; + gint i, nparts, bestid = 0; + CamelMimePart *best = NULL; + GSList *parts; + EMailExtensionRegistry *reg; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + reg = e_mail_parser_get_extension_registry (parser); + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) { + return e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable); + } + + /* as per rfc, find the last part we know how to display */ + nparts = camel_multipart_get_number (mp); + for (i = 0; i < nparts; i++) { + CamelMimePart *mpart; + CamelDataWrapper *data_wrapper; + CamelContentType *type; + CamelStream *null_stream; + gchar *mime_type; + gsize content_size; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + /* is it correct to use the passed in *part here? */ + mpart = camel_multipart_get_part (mp, i); + + if (mpart == NULL) + continue; + + /* This may block even though the stream does not. + * XXX Pretty inefficient way to test if the MIME part + * is empty. Surely there's a quicker way? */ + null_stream = camel_stream_null_new (); + data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (mpart)); + camel_data_wrapper_decode_to_stream_sync ( + data_wrapper, null_stream, cancellable, NULL); + content_size = CAMEL_STREAM_NULL (null_stream)->written; + g_object_unref (null_stream); + + if (content_size == 0) + continue; + + type = camel_mime_part_get_content_type (mpart); + mime_type = camel_content_type_simple (type); + + camel_strdown (mime_type); + + if (!e_mail_part_is_attachment (mpart) && + ((camel_content_type_is (type, "multipart", "related") == 0) || + !related_display_part_is_attachment (mpart)) && + (e_mail_extension_registry_get_for_mime_type (reg, mime_type) || + ((best == NULL) && + (e_mail_extension_registry_get_fallback (reg, mime_type))))) + { + best = mpart; + bestid = i; + } + + g_free (mime_type); + } + + if (best) { + gint len = part_id->len; + + g_string_append_printf(part_id, ".alternative.%d", bestid); + + parts = e_mail_parser_parse_part ( + parser, best, part_id, cancellable); + + g_string_truncate (part_id, len); + } else { + parts = e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", cancellable); + } + + return parts; +} + +static const gchar ** +empe_mp_alternative_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_alternative_class_init (EMailParserMultipartAlternativeClass *klass) +{ + e_mail_parser_multipart_alternative_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_alternative_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_alternative_mime_types; +} + +static void +e_mail_parser_multipart_alternative_init (EMailParserMultipartAlternative *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-appledouble.c b/em-format/e-mail-parser-multipart-appledouble.c new file mode 100644 index 0000000000..5591b10dd5 --- /dev/null +++ b/em-format/e-mail-parser-multipart-appledouble.c @@ -0,0 +1,130 @@ +/* + * e-mail-parser-multipart-appledouble.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> + +#include <camel/camel.h> + +typedef struct _EMailParserMultipartAppleDouble { + GObject parent; +} EMailParserMultipartAppleDouble; + +typedef struct _EMailParserMultipartAppleDoubleClass { + GObjectClass parent_class; +} EMailParserMultipartAppleDoubleClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartAppleDouble, + e_mail_parser_multipart_apple_double, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "multipart/appledouble", NULL }; + +static GSList * +empe_mp_appledouble_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMultipart *mp; + GSList *parts; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) { + parts = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable); + } else { + CamelMimePart *mime_part; + mime_part = camel_multipart_get_part (mp, 1); + + if (mime_part) { + gint len; + /* try the data fork for something useful, doubtful but who knows */ + len = part_id->len; + g_string_append_printf(part_id, ".appledouble.1"); + + parts = e_mail_parser_parse_part ( + parser, mime_part, part_id, cancellable); + + g_string_truncate (part_id, len); + + } else { + + parts = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable); + } + } + + return parts; +} + +static const gchar ** +empe_mp_appledouble_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_apple_double_class_init (EMailParserMultipartAppleDoubleClass *klass) +{ + e_mail_parser_multipart_apple_double_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_appledouble_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_appledouble_mime_types; +} + +static void +e_mail_parser_multipart_apple_double_init (EMailParserMultipartAppleDouble *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-digest.c b/em-format/e-mail-parser-multipart-digest.c new file mode 100644 index 0000000000..5b96d1fa23 --- /dev/null +++ b/em-format/e-mail-parser-multipart-digest.c @@ -0,0 +1,175 @@ +/* + * e-mail-parser-multipart-digest.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMultipartDigest { + GObject parent; +} EMailParserMultipartDigest; + +typedef struct _EMailParserMultipartDigestClass { + GObjectClass parent_class; +} EMailParserMultipartDigestClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartDigest, + e_mail_parser_multipart_digest, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "multipart/digest", + NULL }; + +static GSList * +empe_mp_digest_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMultipart *mp; + gint i, nparts, len; + GSList *parts; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) { + return e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", cancellable); + } + + len = part_id->len; + nparts = camel_multipart_get_number (mp); + parts = NULL; + for (i = 0; i < nparts; i++) { + CamelMimePart *subpart; + CamelContentType *ct; + gchar *cts; + + subpart = camel_multipart_get_part (mp, i); + + if (!subpart) + continue; + + g_string_append_printf(part_id, ".digest.%d", i); + + ct = camel_mime_part_get_content_type (subpart); + + /* According to RFC this shouldn't happen, but who knows... */ + if (ct && !camel_content_type_is (ct, "message", "rfc822")) { + cts = camel_content_type_simple (ct); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as ( + parser, subpart, part_id, + cts, cancellable)); + + g_free (cts); + } else { + GSList *new_parts; + + new_parts = e_mail_parser_parse_part_as ( + parser, subpart, part_id, + "message/rfc822", cancellable); + + /* Force the message to be collapsable */ + if (new_parts && new_parts->data && + !E_MAIL_PART (new_parts->data)->is_attachment) { + new_parts = e_mail_parser_wrap_as_attachment ( + parser, subpart, new_parts, part_id, + cancellable); + } + + /* Force the message to be expanded */ + if (new_parts) { + EMailPart *p = new_parts->data; + if (p) { + p->force_inline = TRUE; + } + } + + parts = g_slist_concat (parts, new_parts); + } + + g_string_truncate (part_id, len); + } + + return parts; +} + +static guint32 +empe_mp_digest_get_flags (EMailParserExtension *extension) +{ + return E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; +} + +static const gchar ** +empe_mp_digest_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_digest_class_init (EMailParserMultipartDigestClass *klass) +{ + e_mail_parser_multipart_digest_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_digest_parse; + iface->get_flags = empe_mp_digest_get_flags; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_digest_mime_types; +} + +static void +e_mail_parser_multipart_digest_init (EMailParserMultipartDigest *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-encrypted.c b/em-format/e-mail-parser-multipart-encrypted.c new file mode 100644 index 0000000000..f03588ac82 --- /dev/null +++ b/em-format/e-mail-parser-multipart-encrypted.c @@ -0,0 +1,220 @@ +/* + * e-mail-parser-multipart-encrypted.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +typedef struct _EMailParserMultipartEncrypted { + GObject parent; +} EMailParserMultipartEncrypted; + +typedef struct _EMailParserMultipartEncryptedClass { + GObjectClass parent_class; +} EMailParserMultipartEncryptedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartEncrypted, + e_mail_parser_multipart_encrypted, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)) + +static const gchar* parser_mime_types[] = { "multipart/encrypted", NULL }; + +static GSList * +empe_mp_encrypted_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelCipherContext *context; + const gchar *protocol; + CamelMimePart *opart; + CamelCipherValidity *valid; + CamelMultipartEncrypted *mpe; + GError *local_error = NULL; + GSList *parts; + gint len; + GSList *iter; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part); + if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Could not parse MIME message. " + "Displaying as source.")); + parts = g_slist_concat ( + parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution/source", + cancellable)); + + return parts; + } + + /* Currently we only handle RFC2015-style PGP encryption. */ + protocol = camel_content_type_param ( + ((CamelDataWrapper *)mpe)->mime_type, "protocol"); + if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Unsupported encryption type for multipart/encrypted")); + + parts = g_slist_concat ( + parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "multipart/mixed", cancellable)); + return parts; + } + + context = camel_gpg_context_new (e_mail_parser_get_session (parser)); + + opart = camel_mime_part_new (); + valid = camel_cipher_context_decrypt_sync ( + context, part, opart, cancellable, &local_error); + + e_mail_part_preserve_charset_in_content_type (part, opart); + if (valid == NULL) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Could not parse PGP/MIME message: %s"), + local_error->message ? + local_error->message : + _("Unknown error")); + + g_clear_error (&local_error); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "multipart/mixed", cancellable)); + + g_object_unref (opart); + g_object_unref (context); + + return parts; + } + + len = part_id->len; + g_string_append (part_id, ".encrypted"); + + parts = e_mail_parser_parse_part ( + parser, opart, part_id, cancellable); + + g_string_truncate (part_id, len); + + /* Update validity of all encrypted sub-parts */ + for (iter = parts; iter; iter = iter->next) { + EMailPart *mail_part; + + mail_part = iter->data; + if (!mail_part) + continue; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + /* Add a widget with details about the encryption, but only when + * the decrypted part isn't itself secured, in that case it has created + * the button itself */ + if (!e_mail_part_is_secured (opart)) { + GSList *button; + EMailPart *mail_part; + g_string_append (part_id, ".encrypted.button"); + + button = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable); + if (button && button->data) { + mail_part = button->data; + + e_mail_part_update_validity (mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + parts = g_slist_concat (parts, button); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + + /* TODO: Make sure when we finalize this part, it is zero'd out */ + g_object_unref (opart); + g_object_unref (context); + + return parts; +} + +static const gchar ** +empe_mp_encrypted_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_encrypted_class_init (EMailParserMultipartEncryptedClass *klass) +{ + e_mail_parser_multipart_encrypted_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_encrypted_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_encrypted_mime_types; +} + +static void +e_mail_parser_multipart_encrypted_init (EMailParserMultipartEncrypted *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-mixed.c b/em-format/e-mail-parser-multipart-mixed.c new file mode 100644 index 0000000000..0da450f747 --- /dev/null +++ b/em-format/e-mail-parser-multipart-mixed.c @@ -0,0 +1,158 @@ +/* + * e-mail-parser-multipart-mixed.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> +#include <em-format/e-mail-part-utils.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMultipartMixed { + GObject parent; +} EMailParserMultipartMixed; + +typedef struct _EMailParserMultipartMixedClass { + GObjectClass parent_class; +} EMailParserMultipartMixedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartMixed, + e_mail_parser_multipart_mixed, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "multipart/mixed", + "multipart/report", + "multipart/*", + NULL }; + +static GSList * +empe_mp_mixed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMultipart *mp; + gint i, nparts, len; + GSList *parts; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) { + parts = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", cancellable); + return parts; + } + + len = part_id->len; + parts = NULL; + nparts = camel_multipart_get_number (mp); + for (i = 0; i < nparts; i++) { + CamelMimePart *subpart; + CamelContentType *ct; + GSList *new_parts; + + subpart = camel_multipart_get_part (mp, i); + + g_string_append_printf(part_id, ".mixed.%d", i); + + new_parts = e_mail_parser_parse_part ( + parser, subpart, part_id, cancellable); + + /* Force messages to be expandable */ + ct = camel_mime_part_get_content_type (subpart); + if (!new_parts || + (camel_content_type_is (ct, "message", "rfc822") && + new_parts && new_parts->data && + !E_MAIL_PART (new_parts->data)->is_attachment)) { + + parts = g_slist_concat (parts, + e_mail_parser_wrap_as_attachment ( + parser, subpart, new_parts, + part_id, cancellable)); + if (parts && parts->data) + E_MAIL_PART (parts->data)->force_inline = TRUE; + } else { + parts = g_slist_concat (parts, new_parts); + } + + g_string_truncate (part_id, len); + } + + return parts; +} + +static guint32 +empe_mp_mixed_get_flags (EMailParserExtension *extension) +{ + return E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; +} + +static const gchar ** +empe_mp_mixed_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_mixed_class_init (EMailParserMultipartMixedClass *klass) +{ + e_mail_parser_multipart_mixed_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_mixed_parse; + iface->get_flags = empe_mp_mixed_get_flags; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_mixed_mime_types; +} + +static void +e_mail_parser_multipart_mixed_init (EMailParserMultipartMixed *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-related.c b/em-format/e-mail-parser-multipart-related.c new file mode 100644 index 0000000000..ff7621202d --- /dev/null +++ b/em-format/e-mail-parser-multipart-related.c @@ -0,0 +1,162 @@ +/* + * e-mail-parser-multipart-related.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserMultipartRelated { + GObject parent; +} EMailParserMultipartRelated; + +typedef struct _EMailParserMultipartRelatedClass { + GObjectClass parent_class; +} EMailParserMultipartRelatedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartRelated, + e_mail_parser_multipart_related, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "multipart/related", + NULL }; + +static GSList * +empe_mp_related_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMultipart *mp; + CamelMimePart *body_part, *display_part = NULL; + gint i, nparts, partidlen, displayid = 0; + GSList *parts; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) { + return e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", cancellable); + } + + display_part = e_mail_part_get_related_display_part (part, &displayid); + + if (display_part == NULL) { + return e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable); + } + + /* The to-be-displayed part goes first */ + partidlen = part_id->len; + g_string_append_printf(part_id, ".related.%d", displayid); + + parts = e_mail_parser_parse_part ( + parser, display_part, part_id, cancellable); + + g_string_truncate (part_id, partidlen); + + /* Process the related parts */ + nparts = camel_multipart_get_number (mp); + for (i = 0; i < nparts; i++) { + GSList *list, *iter; + body_part = camel_multipart_get_part (mp, i); + list = NULL; + + if (body_part == display_part) + continue; + + g_string_append_printf(part_id, ".related.%d", i); + + list = e_mail_parser_parse_part ( + parser, body_part, part_id, cancellable); + + g_string_truncate (part_id, partidlen); + + for (iter = list; iter; iter = iter->next) { + EMailPart *mail_part; + + mail_part = iter->data; + if (!mail_part) + continue; + + /* Don't render the part on it's own! */ + mail_part->is_hidden = TRUE; + } + + parts = g_slist_concat (parts, list); + } + + return parts; +} + +static const gchar ** +empe_mp_related_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_related_class_init (EMailParserMultipartRelatedClass *klass) +{ + e_mail_parser_multipart_related_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_related_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_related_mime_types; +} + +static void +e_mail_parser_multipart_related_init (EMailParserMultipartRelated *parser) +{ + +} diff --git a/em-format/e-mail-parser-multipart-signed.c b/em-format/e-mail-parser-multipart-signed.c new file mode 100644 index 0000000000..37bedc91f0 --- /dev/null +++ b/em-format/e-mail-parser-multipart-signed.c @@ -0,0 +1,252 @@ +/* + * e-mail-parser-multipart-signed.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +typedef struct _EMailParserMultipartSigned { + GObject parent; +} EMailParserMultipartSigned; + +typedef struct _EMailParserMultipartSignedClass { + GObjectClass parent_class; +} EMailParserMultipartSignedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserMultipartSigned, + e_mail_parser_multipart_signed, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar* parser_mime_types[] = { "multipart/signed", + "application/pgp-signature", + NULL }; + +static GSList * +empe_mp_signed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelMimePart *cpart; + CamelMultipartSigned *mps; + CamelCipherContext *cipher = NULL; + CamelSession *session; + guint32 validity_type; + GSList *parts; + CamelCipherValidity *valid; + GError *local_error = NULL; + gint i, nparts, len; + gboolean secured; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + /* If the part is application/pgp-signature sub-part then skip it. */ + if (!CAMEL_IS_MULTIPART (part)) { + CamelContentType *ct; + ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (part)); + if (camel_content_type_is (ct, "application", "pgp-signature")) { + return g_slist_alloc (); + } + } + + mps = (CamelMultipartSigned *) camel_medium_get_content ((CamelMedium *) part); + if (!CAMEL_IS_MULTIPART_SIGNED (mps) + || (cpart = camel_multipart_get_part ((CamelMultipart *) mps, + CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Could not parse MIME message. " + "Displaying as source.")); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable)); + return parts; + } + + session = e_mail_parser_get_session (parser); + /* FIXME: Should be done via a plugin interface */ + /* FIXME: duplicated in em-format-html-display.c */ + if (mps->protocol) { +#ifdef ENABLE_SMIME + if (g_ascii_strcasecmp("application/x-pkcs7-signature", mps->protocol) == 0 + || g_ascii_strcasecmp("application/pkcs7-signature", mps->protocol) == 0) { + cipher = camel_smime_context_new (session); + validity_type = E_MAIL_PART_VALIDITY_SMIME; + } else { +#endif + if (g_ascii_strcasecmp("application/pgp-signature", mps->protocol) == 0) { + cipher = camel_gpg_context_new (session); + validity_type = E_MAIL_PART_VALIDITY_PGP; + } +#ifdef ENABLE_SMIME + } +#endif + } + + if (cipher == NULL) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Unsupported signature format")); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "multipart/mixed", cancellable)); + + return parts; + } + + valid = camel_cipher_context_verify_sync ( + cipher, part, cancellable, &local_error); + if (valid == NULL) { + parts = e_mail_parser_error ( + parser, cancellable, + _("Error verifying signature: %s"), + local_error->message ? + local_error->message : + _("Unknown error")); + + g_clear_error (&local_error); + + parts = g_slist_concat (parts, + e_mail_parser_parse_part_as ( + parser, part, part_id, + "multipart/mixed", cancellable)); + + g_object_unref (cipher); + return parts; + } + + nparts = camel_multipart_get_number (CAMEL_MULTIPART (mps)); + secured = FALSE; + len = part_id->len; + parts = NULL; + for (i = 0; i < nparts; i++) { + CamelMimePart *subpart; + GSList *mail_parts, *iter; + subpart = camel_multipart_get_part (CAMEL_MULTIPART (mps), i); + + g_string_append_printf(part_id, ".signed.%d", i); + + mail_parts = e_mail_parser_parse_part ( + parser, subpart, part_id, cancellable); + + g_string_truncate (part_id, len); + + if (!secured) + secured = e_mail_part_is_secured (subpart); + + for (iter = mail_parts; iter; iter = iter->next) { + EMailPart *mail_part; + + mail_part = iter->data; + if (!mail_part) + continue; + + e_mail_part_update_validity (mail_part, valid, + validity_type | E_MAIL_PART_VALIDITY_SIGNED); + } + + parts = g_slist_concat (parts, mail_parts); + } + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself */ + if (!secured) { + GSList *button; + EMailPart *mail_part; + g_string_append (part_id, ".signed.button"); + + button = e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable); + if (button && button->data) { + mail_part = button->data; + + e_mail_part_update_validity (mail_part, valid, + validity_type | E_MAIL_PART_VALIDITY_SIGNED); + } + + parts = g_slist_concat (parts, button); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + + g_object_unref (cipher); + + return parts; +} + +static const gchar ** +empe_mp_signed_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_multipart_signed_class_init (EMailParserMultipartSignedClass *klass) +{ + e_mail_parser_multipart_signed_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_mp_signed_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_mp_signed_mime_types; +} + +static void +e_mail_parser_multipart_signed_init (EMailParserMultipartSigned *parser) +{ + +} diff --git a/em-format/e-mail-parser-secure-button.c b/em-format/e-mail-parser-secure-button.c new file mode 100644 index 0000000000..6c8a0e69c6 --- /dev/null +++ b/em-format/e-mail-parser-secure-button.c @@ -0,0 +1,104 @@ +/* + * e-mail-parser-secure-button.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "e-mail-format-extensions.h" + +#include <glib/gi18n-lib.h> + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <camel/camel.h> + +typedef struct _EMailParserSecureButton { + GObject parent; +} EMailParserSecureButton; + +typedef struct _EMailParserSecureButtonClass { + GObjectClass parent_class; +} EMailParserSecureButtonClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserSecureButton, + e_mail_parser_secure_button, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)) + +static const gchar *parser_mime_types[] = { "application/vnd.evolution.widget.secure-button", NULL }; + +static GSList * +empe_secure_button_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + gint len; + + len = part_id->len; + g_string_append (part_id, ".secure_button"); + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = g_strdup ("application/vnd.evolution.widget.secure-button"); + g_string_truncate (part_id, len); + + return g_slist_append (NULL, mail_part); +} + +static const gchar ** +empe_secure_button_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_secure_button_class_init (EMailParserSecureButtonClass *klass) +{ + e_mail_parser_secure_button_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_secure_button_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_secure_button_mime_types; +} + +static void +e_mail_parser_secure_button_init (EMailParserSecureButton *parser) +{ + +} diff --git a/em-format/e-mail-parser-source.c b/em-format/e-mail-parser-source.c new file mode 100644 index 0000000000..ae1a9a3a51 --- /dev/null +++ b/em-format/e-mail-parser-source.c @@ -0,0 +1,108 @@ +/* + * e-mail-parser-source.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +typedef struct _EMailParserSource { + GObject parent; +} EMailParserSource; + +typedef struct _EMailParserSourceClass { + GObjectClass parent_class; +} EMailParserSourceClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserSource, + e_mail_parser_source, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "application/vnd.evolution.source", NULL }; + +static GSList * +empe_source_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + gint len; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + len = part_id->len; + g_string_append (part_id, ".source"); + + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = g_strdup ("application/vnd.evolution.source"); + g_string_truncate (part_id, len); + + return g_slist_append (NULL, mail_part); +} + +static const gchar ** +empe_source_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_source_class_init (EMailParserSourceClass *klass) +{ + e_mail_parser_source_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_source_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_source_mime_types; +} + +static void +e_mail_parser_source_init (EMailParserSource *parser) +{ + +} diff --git a/em-format/e-mail-parser-text-enriched.c b/em-format/e-mail-parser-text-enriched.c new file mode 100644 index 0000000000..fad71f825b --- /dev/null +++ b/em-format/e-mail-parser-text-enriched.c @@ -0,0 +1,128 @@ +/* + * e-mail-parser-text-enriched.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +typedef struct _EMailParserTextEnriched { + GObject parent; +} EMailParserTextEnriched; + +typedef struct _EMailParserTextEnrichedClass { + GObjectClass parent_class; +} EMailParserTextEnrichedClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserTextEnriched, + e_mail_parser_text_enriched, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "text/richtext", + "text/enriched", + NULL }; + +static GSList * +empe_text_enriched_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *mail_part; + const gchar *tmp; + gint len; + CamelContentType *ct; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + len = part_id->len; + g_string_append (part_id, ".text_enriched"); + + ct = camel_mime_part_get_content_type (part); + + mail_part = e_mail_part_new (part, part_id->str); + mail_part->mime_type = ct ? camel_content_type_simple (ct) : g_strdup ("text/enriched"); + tmp = camel_mime_part_get_content_id (part); + if (!tmp) { + mail_part->cid = NULL; + } else { + mail_part->cid = g_strdup_printf ("cid:%s", tmp); + } + + g_string_truncate (part_id, len); + + if (e_mail_part_is_attachment (part)) { + return e_mail_parser_wrap_as_attachment ( + parser, part, g_slist_append (NULL, mail_part), + part_id, cancellable); + } + + return g_slist_append (NULL, mail_part); +} + +static const gchar ** +empe_text_enriched_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_text_enriched_class_init (EMailParserTextEnrichedClass *klass) +{ + e_mail_parser_text_enriched_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_text_enriched_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_text_enriched_mime_types; +} + +static void +e_mail_parser_text_enriched_init (EMailParserTextEnriched *parser) +{ + +} diff --git a/em-format/e-mail-parser-text-html.c b/em-format/e-mail-parser-text-html.c new file mode 100644 index 0000000000..05c7bcd242 --- /dev/null +++ b/em-format/e-mail-parser-text-html.c @@ -0,0 +1,138 @@ +/* + * e-mail-parser-text-html.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> + +#include <string.h> + +typedef struct _EMailParserTextHTML { + GObject parent; +} EMailParserTextHTML; + +typedef struct _EMailParserTextHTMLClass { + GObjectClass parent_class; +} EMailParserTextHTMLClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserTextHTML, + e_mail_parser_text_html, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "text/html", NULL }; + +static GSList * +empe_text_html_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + EMailPart *empart; + const gchar *location; + gchar *cid = NULL; + const gchar *base; + gint len; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + cid = NULL; + base = camel_medium_get_header (CAMEL_MEDIUM (part), "content-base"); + location = camel_mime_part_get_content_location (part); + if (location != NULL) { + if (strchr (location, ':') == NULL && base != NULL) { + CamelURL *uri; + CamelURL *base_url = camel_url_new (base, NULL); + + uri = camel_url_new_with_base (base_url, location); + cid = camel_url_to_string (uri, 0); + camel_url_free (uri); + camel_url_free (base_url); + } else { + cid = g_strdup (location); + } + } + + len = part_id->len; + g_string_append (part_id, ".text_html"); + + empart = e_mail_part_new (part, part_id->str); + empart->mime_type = g_strdup ("text/html"); + empart->cid = cid; + g_string_truncate (part_id, len); + + if (e_mail_part_is_attachment (part)) { + return e_mail_parser_wrap_as_attachment ( + parser, part, g_slist_append (NULL, empart), + part_id, cancellable); + } + + return g_slist_append (NULL, empart); +} + +static const gchar ** +empe_text_html_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_text_html_class_init (EMailParserTextHTMLClass *klass) +{ + e_mail_parser_text_html_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_text_html_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_text_html_mime_types; +} + +static void +e_mail_parser_text_html_init (EMailParserTextHTML *parser) +{ + +} diff --git a/em-format/e-mail-parser-text-plain.c b/em-format/e-mail-parser-text-plain.c new file mode 100644 index 0000000000..999030447b --- /dev/null +++ b/em-format/e-mail-parser-text-plain.c @@ -0,0 +1,253 @@ +/* + * e-mail-parser-text-plain.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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-mail-format-extensions.h" + +#include <em-format/e-mail-parser-extension.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-inline-filter.h> +#include <em-format/e-mail-part-utils.h> +#include <e-util/e-util.h> + +#include <glib/gi18n-lib.h> +#include <camel/camel.h> +#include <ctype.h> + +typedef struct _EMailParserTextPlain { + GObject parent; +} EMailParserTextPlain; + +typedef struct _EMailParserTextPlainClass { + GObjectClass parent_class; +} EMailParserTextPlainClass; + +static void e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface); +static void e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface); + +G_DEFINE_TYPE_EXTENDED ( + EMailParserTextPlain, + e_mail_parser_text_plain, + G_TYPE_OBJECT, + 0, + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_EXTENSION, + e_mail_parser_mail_extension_interface_init) + G_IMPLEMENT_INTERFACE ( + E_TYPE_MAIL_PARSER_EXTENSION, + e_mail_parser_parser_extension_interface_init)); + +static const gchar *parser_mime_types[] = { "text/plain", "text/*", NULL }; + +static gboolean +part_is_empty (CamelMimePart *part) +{ + CamelDataWrapper *dw; + GByteArray *ba; + guint i; + + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + ba = camel_data_wrapper_get_byte_array (dw); + + if (!ba) + return TRUE; + + for (i = 0; i < ba->len; i++) { + + /* Checks for \n, \t, \f, \r, \v and space */ + if (!isspace (ba->data[i])) + return FALSE; + } + + return TRUE; +} + +static GSList * +process_part (EMailParser *parser, + GString *part_id, + gint part_number, + CamelMimePart *part, + gboolean is_attachment, + GCancellable *cancellable) +{ + CamelContentType *type; + EMailPart *empart; + gint s_len = part_id->len; + GSList *parts; + + if (part_is_empty (part)) { + return g_slist_alloc (); + } + + type = camel_mime_part_get_content_type (part); + if (camel_content_type_is (type, "text", "*") && + (!camel_content_type_is (type, "text", "calendar"))) { + + g_string_append_printf (part_id, ".plain_text.%d", part_number); + + empart = e_mail_part_new (part, part_id->str); + empart->mime_type = camel_content_type_simple (type); + + g_string_truncate (part_id, s_len); + + if (is_attachment) { + + return e_mail_parser_wrap_as_attachment ( + parser, part, + g_slist_append (NULL, empart), + part_id, cancellable); + + } + + return g_slist_append (NULL, empart); + } + + g_string_append_printf (part_id, ".inline.%d", part_number); + + parts = e_mail_parser_parse_part ( + parser, CAMEL_MIME_PART (part), + part_id, cancellable); + + g_string_truncate (part_id, s_len); + + return parts; +} + +static GSList * +empe_text_plain_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + GSList *parts; + CamelStream *filtered_stream, *null; + CamelMultipart *mp; + CamelDataWrapper *dw; + CamelContentType *type; + gint i, count; + EMailInlineFilter *inline_filter; + gboolean charset_added = FALSE; + const gchar *snoop_type = NULL; + gboolean is_attachment; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + dw = camel_medium_get_content ((CamelMedium *) part); + if (!dw) + return NULL; + + /* This scans the text part for inline-encoded data, creates + * a multipart of all the parts inside it. */ + + /* FIXME: We should discard this multipart if it only contains + * the original text, but it makes this hash lookup more complex */ + if (!dw->mime_type) + snoop_type = e_mail_part_snoop_type (part); + + /* if we had to snoop the part type to get here, then + * use that as the base type, yuck */ + if (snoop_type == NULL + || (type = camel_content_type_decode (snoop_type)) == NULL) { + type = dw->mime_type; + camel_content_type_ref (type); + } + + if (dw->mime_type && type != dw->mime_type && camel_content_type_param (dw->mime_type, "charset")) { + camel_content_type_set_param (type, "charset", camel_content_type_param (dw->mime_type, "charset")); + charset_added = TRUE; + } + + null = camel_stream_null_new (); + filtered_stream = camel_stream_filter_new (null); + g_object_unref (null); + inline_filter = e_mail_inline_filter_new (camel_mime_part_get_encoding (part), type); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), + CAMEL_MIME_FILTER (inline_filter)); + camel_data_wrapper_decode_to_stream_sync ( + dw, (CamelStream *) filtered_stream, cancellable, NULL); + camel_stream_close ((CamelStream *) filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + mp = e_mail_inline_filter_get_multipart (inline_filter); + + if (charset_added) { + camel_content_type_set_param (type, "charset", NULL); + } + + g_object_unref (inline_filter); + camel_content_type_unref (type); + + /* We handle our made-up multipart here, so we don't recursively call ourselves */ + count = camel_multipart_get_number (mp); + parts = NULL; + + is_attachment = ((count == 1) && (e_mail_part_is_attachment (part))); + + for (i = 0; i < count; i++) { + CamelMimePart *newpart = camel_multipart_get_part (mp, i); + + if (!newpart) + continue; + + parts = g_slist_concat (parts, + process_part ( + parser, part_id, i, + newpart, is_attachment, + cancellable)); + } + + g_object_unref (mp); + + return parts; +} + +static const gchar ** +empe_text_plain_mime_types (EMailExtension *extension) +{ + return parser_mime_types; +} + +static void +e_mail_parser_text_plain_class_init (EMailParserTextPlainClass *klass) +{ + e_mail_parser_text_plain_parent_class = g_type_class_peek_parent (klass); +} + +static void +e_mail_parser_parser_extension_interface_init (EMailParserExtensionInterface *iface) +{ + iface->parse = empe_text_plain_parse; +} + +static void +e_mail_parser_mail_extension_interface_init (EMailExtensionInterface *iface) +{ + iface->mime_types = empe_text_plain_mime_types; +} + +static void +e_mail_parser_text_plain_init (EMailParserTextPlain *parser) +{ + +} diff --git a/em-format/e-mail-parser.c b/em-format/e-mail-parser.c new file mode 100644 index 0000000000..98b31b73ad --- /dev/null +++ b/em-format/e-mail-parser.c @@ -0,0 +1,693 @@ +/* + * e-mail-parser.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-parser.h" +#include "e-mail-parser-extension.h" +#include "e-mail-format-extensions.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-utils.h" + +#include <camel/camel.h> +#include <libebackend/libebackend.h> + +#include <e-util/e-util.h> + +#include <shell/e-shell.h> +#include <shell/e-shell-window.h> + +#include <widgets/misc/e-attachment.h> + +#include <string.h> + +static gpointer parent_class = 0; + +struct _EMailParserPrivate { + GMutex *mutex; + + gint last_error; + + CamelSession *session; +}; + +#define E_MAIL_PARSER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PARSER, EMailParserPrivate)) + +#define d(x) + +enum { + PROP_0, + PROP_SESSION +}; + +static GSList * +mail_parser_run (EMailParser *parser, + CamelMimeMessage *message, + GCancellable *cancellable) +{ + GSList *parts; + EMailExtensionRegistry *reg; + GQueue *parsers; + GList *iter; + GString *part_id; + + reg = e_mail_parser_get_extension_registry (parser); + + parsers = e_mail_extension_registry_get_for_mime_type ( + reg, "application/vnd.evolution.message"); + + if (!parsers) + parsers = e_mail_extension_registry_get_for_mime_type ( + reg, "message/*"); + + /* parsers == NULL means, that the internal Evolution parser extensions + * were not loaded. Something is terribly wrong. */ + g_return_val_if_fail (parsers != NULL, NULL); + + part_id = g_string_new (".message"); + parts = NULL; + + if (!parsers) { + parts = e_mail_parser_wrap_as_attachment ( + parser, CAMEL_MIME_PART (message), + NULL, part_id, cancellable); + } else { + for (iter = parsers->head; iter; iter = iter->next) { + + EMailParserExtension *extension; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + extension = iter->data; + if (!extension) + continue; + + parts = e_mail_parser_extension_parse ( + extension, parser, CAMEL_MIME_PART (message), + part_id, cancellable); + + if (parts != NULL) + break; + } + + parts = g_slist_prepend ( + parts, + e_mail_part_new ( + CAMEL_MIME_PART (message), + ".message")); + } + + g_string_free (part_id, TRUE); + + return parts; +} + +static void +mail_parser_set_session (EMailParser *parser, + CamelSession *session) +{ + g_return_if_fail (E_IS_MAIL_PARSER (parser)); + g_return_if_fail (CAMEL_IS_SESSION (session)); + + g_object_ref (session); + + if (parser->priv->session) + g_object_unref (parser->priv->session); + + parser->priv->session = session; +} + +static void +e_mail_parser_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EMailParser *parser = E_MAIL_PARSER (object); + + switch (property_id) { + case PROP_SESSION: + mail_parser_set_session (parser, + CAMEL_SESSION (g_value_get_object (value))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_mail_parser_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EMailParser *parser = E_MAIL_PARSER (object); + + switch (property_id) { + case PROP_SESSION: + g_value_set_object (value, + G_OBJECT (e_mail_parser_get_session (parser))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_mail_parser_finalize (GObject *object) +{ + EMailParserPrivate *priv; + + priv = E_MAIL_PARSER (object)->priv; + + if (priv->mutex) { + g_mutex_free (priv->mutex); + priv->mutex = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} +static void +e_mail_parser_init (EMailParser *parser) +{ + parser->priv = E_MAIL_PARSER_GET_PRIVATE (parser); + + parser->priv->mutex = g_mutex_new (); +} + +static void +e_mail_parser_base_init (EMailParserClass *klass) +{ + klass->extension_registry = g_object_new ( + E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, NULL); + + e_mail_parser_internal_extensions_load ( + E_MAIL_EXTENSION_REGISTRY (klass->extension_registry)); + + e_extensible_load_extensions (E_EXTENSIBLE (klass->extension_registry)); +} + +static void +e_mail_parser_base_finalize (EMailParserClass *klass) +{ + g_object_unref (klass->extension_registry); +} + +static void +e_mail_parser_class_init (EMailParserClass *klass) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, sizeof (EMailParserPrivate)); + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = e_mail_parser_finalize; + object_class->set_property = e_mail_parser_set_property; + object_class->get_property = e_mail_parser_get_property; + + g_object_class_install_property ( + object_class, + PROP_SESSION, + g_param_spec_object ( + "session", + "Camel Session", + NULL, + CAMEL_TYPE_SESSION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +GType +e_mail_parser_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMailParserClass), + (GBaseInitFunc) e_mail_parser_base_init, + (GBaseFinalizeFunc) e_mail_parser_base_finalize, + (GClassInitFunc) e_mail_parser_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailParser), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_mail_parser_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + G_TYPE_OBJECT, "EMailParser", + &type_info, 0); + } + + return type; +} + +EMailParser * +e_mail_parser_new (CamelSession *session) +{ + return g_object_new (E_TYPE_MAIL_PARSER, + "session", session, NULL); +} + +/** + * e_mail_parser_parse_sync: + * @parser: an #EMailParser + * @folder: (allow none) a #CamelFolder containing the @message or %NULL + * @message_uid: (allow none) UID of the @message within the @folder or %NULL + * @message: a #CamelMimeMessage + * @cancellable: (allow-none) a #GCancellable + * + * Parses the @message synchronously. Returns a list of #EMailPart<!-//>s which + * represents structure of the message and additional properties of each part. + * + * Note that this function can block for a while, so it's not a good idea to call + * it from main thread. + * + * Return Value: An #EMailPartsList + */ +EMailPartList * +e_mail_parser_parse_sync (EMailParser *parser, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GCancellable *cancellable) +{ + EMailPartList *parts_list; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + + parts_list = e_mail_part_list_new (); + + if (folder) + parts_list->folder = g_object_ref (folder); + + if (message_uid) + parts_list->message_uid = g_strdup (message_uid); + + parts_list->message = g_object_ref (message); + + parts_list->list = mail_parser_run (parser, message, cancellable); + + return parts_list; +} + +static void +mail_parser_prepare_async (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + CamelMimeMessage *message; + GSList *list; + + message = g_object_get_data (G_OBJECT (res), "message"); + + list = mail_parser_run (E_MAIL_PARSER (object), message, cancellable); + + g_simple_async_result_set_op_res_gpointer (res, list, NULL); +} + +/** + * e_mail_parser_parse: + * @parser: an #EMailParser + * @message: a #CamelMimeMessage + * @callback: a #GAsyncReadyCallback + * @cancellable: (allow-none) a #GCancellable + * @user_data: (allow-none) user data passed to the callback + * + * Asynchronous version of #e_mail_parser_parse_sync(). + */ +void +e_mail_parser_parse (EMailParser *parser, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GAsyncReadyCallback callback, + GCancellable *cancellable, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (E_IS_MAIL_PARSER (parser)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + result = g_simple_async_result_new ( + G_OBJECT (parser), callback, + user_data, e_mail_parser_parse); + + g_object_set_data (G_OBJECT (result), "message", g_object_ref (message)); + + if (folder) + g_object_set_data (G_OBJECT (result), "folder", g_object_ref (folder)); + if (message_uid) + g_object_set_data (G_OBJECT (result), "message_uid", g_strdup (message_uid)); + + g_simple_async_result_run_in_thread ( + result, mail_parser_prepare_async, G_PRIORITY_DEFAULT, cancellable); +} + +EMailPartList * +e_mail_parser_parse_finish (EMailParser *parser, + GAsyncResult *result, + GError **error) +{ + EMailPartList *parts_list; + + parts_list = e_mail_part_list_new (); + + /* The data were ref'ed or copied in e_mail_parser_parse_async */ + parts_list->message = g_object_get_data (G_OBJECT (result), "message"); + parts_list->folder = g_object_get_data (G_OBJECT (result), "folder"); + parts_list->message_uid = g_object_get_data (G_OBJECT (result), "message_uid"); + + parts_list->list = g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (result)); + + if (camel_debug_start ("emformat:parser")) { + GSList *iter; + + printf("%s finished with EMailPartList:\n", + G_OBJECT_TYPE_NAME (parser)); + + for (iter = parts_list->list; iter; iter = iter->next) { + EMailPart *part = iter->data; + if (!part) continue; + printf(" id: %s | cid: %s | mime_type: %s | is_hidden: %d | is_attachment: %d\n", + part->id, part->cid, part->mime_type, + part->is_hidden ? 1 : 0, part->is_attachment ? 1 : 0); + } + + camel_debug_end (); + } + + return parts_list; +} + +GSList * +e_mail_parser_parse_part (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable) +{ + CamelContentType *ct; + gchar *mime_type; + GSList *list; + + ct = camel_mime_part_get_content_type (part); + if (!ct) { + mime_type = (gchar *) "application/vnd.evolution.error"; + } else { + gchar *tmp; + tmp = camel_content_type_simple (ct); + mime_type = g_ascii_strdown (tmp, -1); + g_free (tmp); + } + + list = e_mail_parser_parse_part_as ( + parser, part, part_id, mime_type, cancellable); + + if (ct) { + g_free (mime_type); + } + + return list; +} + +GSList * +e_mail_parser_parse_part_as (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + const gchar *mime_type, + GCancellable *cancellable) +{ + GQueue *parsers; + GList *iter; + EMailExtensionRegistry *reg; + EMailParserClass *parser_class; + GSList *part_list; + gchar *as_mime_type; + + if (g_cancellable_is_cancelled (cancellable)) + return NULL; + + if (mime_type) + as_mime_type = g_ascii_strdown (mime_type, -1); + else + as_mime_type = NULL; + + parser_class = E_MAIL_PARSER_GET_CLASS (parser); + reg = E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry); + + parsers = e_mail_extension_registry_get_for_mime_type (reg, as_mime_type); + if (!parsers) { + parsers = e_mail_extension_registry_get_fallback (reg, as_mime_type); + } + + if (as_mime_type) + g_free (as_mime_type); + + if (!parsers) { + return e_mail_parser_wrap_as_attachment ( + parser, part, NULL, part_id, cancellable); + } + + for (iter = parsers->head; iter; iter = iter->next) { + EMailParserExtension *extension; + + extension = iter->data; + if (!extension) + continue; + + part_list = e_mail_parser_extension_parse ( + extension, parser, part, part_id, cancellable); + + if (part_list) + break; + } + + return part_list; +} + +GSList * +e_mail_parser_error (EMailParser *parser, + GCancellable *cancellable, + const gchar *format, + ...) +{ + EMailPart *mail_part; + CamelMimePart *part; + gchar *errmsg; + gchar *uri; + va_list ap; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + g_return_val_if_fail (format != NULL, NULL); + + va_start (ap, format); + errmsg = g_strdup_vprintf (format, ap); + + part = camel_mime_part_new (); + camel_mime_part_set_content (part, + errmsg, strlen (errmsg), + "application/vnd.evolution.error"); + g_free (errmsg); + va_end (ap); + + g_mutex_lock (parser->priv->mutex); + parser->priv->last_error++; + uri = g_strdup_printf (".error.%d", parser->priv->last_error); + g_mutex_unlock (parser->priv->mutex); + + mail_part = e_mail_part_new (part, uri); + mail_part->mime_type = g_strdup ("application/vnd.evolution.error"); + mail_part->is_error = TRUE; + + g_free (uri); + g_object_unref (part); + + return g_slist_append (NULL, mail_part); +} + +static void +attachment_loaded (EAttachment *attachment, + GAsyncResult *res, + gpointer user_data) +{ + EShell *shell; + GtkWindow *window; + + shell = e_shell_get_default (); + window = e_shell_get_active_window (shell); + + e_attachment_load_handle_error (attachment, res, window); + + g_object_unref (attachment); +} + +/* Idle callback */ +static gboolean +load_attachment_idle (EAttachment *attachment) +{ + e_attachment_load_async (attachment, + (GAsyncReadyCallback) attachment_loaded, NULL); + + return FALSE; +} + +GSList * +e_mail_parser_wrap_as_attachment (EMailParser *parser, + CamelMimePart *part, + GSList *parts, + GString *part_id, + GCancellable *cancellable) +{ + EMailPartAttachment *empa; + const gchar *snoop_mime_type, *cid; + GQueue *extensions; + CamelContentType *ct; + gchar *mime_type; + CamelDataWrapper *dw; + GByteArray *ba; + gsize size; + gint part_id_len; + + ct = camel_mime_part_get_content_type (part); + extensions = NULL; + snoop_mime_type = NULL; + if (ct) { + EMailExtensionRegistry *reg; + mime_type = camel_content_type_simple (ct); + + reg = e_mail_parser_get_extension_registry (parser); + extensions = e_mail_extension_registry_get_for_mime_type ( + reg, mime_type); + + if (camel_content_type_is (ct, "text", "*") || + camel_content_type_is (ct, "message", "rfc822")) + snoop_mime_type = mime_type; + else + g_free (mime_type); + } + + if (!snoop_mime_type) + snoop_mime_type = e_mail_part_snoop_type (part); + + if (parts) + printf("WRAPPING %s AS %s\n", E_MAIL_PART (parts->data)->id, snoop_mime_type); + + if (!extensions) { + EMailExtensionRegistry *reg; + + reg = e_mail_parser_get_extension_registry (parser); + extensions = e_mail_extension_registry_get_for_mime_type ( + reg, snoop_mime_type); + + if (!extensions) { + extensions = e_mail_extension_registry_get_fallback ( + reg, snoop_mime_type); + } + } + + part_id_len = part_id->len; + g_string_append (part_id, ".attachment"); + + empa = (EMailPartAttachment *) e_mail_part_subclass_new ( + part, part_id->str, sizeof (EMailPartAttachment), + (GFreeFunc) e_mail_part_attachment_free); + empa->parent.mime_type = g_strdup ("application/vnd.evolution.attachment"); + empa->parent.is_attachment = TRUE; + empa->shown = extensions && (!g_queue_is_empty (extensions) && + e_mail_part_is_inline (part, extensions)); + empa->snoop_mime_type = snoop_mime_type; + empa->attachment = e_attachment_new (); + empa->attachment_view_part_id = parts ? g_strdup (E_MAIL_PART (parts->data)->id) : NULL; + + cid = camel_mime_part_get_content_id (part); + if (cid) + empa->parent.cid = g_strdup_printf ("cid:%s", cid); + + e_attachment_set_mime_part (empa->attachment, part); + e_attachment_set_shown (empa->attachment, empa->shown); + e_attachment_set_can_show (empa->attachment, + extensions && !g_queue_is_empty (extensions)); + + /* Try to guess size of the attachments */ + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + ba = camel_data_wrapper_get_byte_array (dw); + if (ba) { + size = ba->len; + + if (camel_mime_part_get_encoding (part) == CAMEL_TRANSFER_ENCODING_BASE64) + size = size / 1.37; + } + + /* e_attachment_load_async must be called from main thread */ + g_idle_add ( + (GSourceFunc) load_attachment_idle, + g_object_ref (empa->attachment)); + + if (size != 0) { + GFileInfo *fileinfo; + + fileinfo = e_attachment_get_file_info (empa->attachment); + + if (!fileinfo) { + fileinfo = g_file_info_new (); + g_file_info_set_content_type ( + fileinfo, empa->snoop_mime_type); + } else { + g_object_ref (fileinfo); + } + + g_file_info_set_size (fileinfo, size); + e_attachment_set_file_info (empa->attachment, fileinfo); + + g_object_unref (fileinfo); + } + + if (parts && parts->data) { + E_MAIL_PART (parts->data)->is_hidden = TRUE; + } + + g_string_truncate (part_id, part_id_len); + + return g_slist_prepend (parts, empa); +} + +CamelSession * +e_mail_parser_get_session (EMailParser *parser) +{ + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + + return parser->priv->session; +} + +EMailExtensionRegistry * +e_mail_parser_get_extension_registry (EMailParser *parser) +{ + EMailParserClass *parser_class; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + + parser_class = E_MAIL_PARSER_GET_CLASS (parser); + return E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry); +} diff --git a/em-format/e-mail-parser.h b/em-format/e-mail-parser.h new file mode 100644 index 0000000000..202e28d267 --- /dev/null +++ b/em-format/e-mail-parser.h @@ -0,0 +1,114 @@ +/* + * e-mail-parser.h + * + * 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/> + * + */ + +#ifndef E_MAIL_PARSER_H_ +#define E_MAIL_PARSER_H_ + +#include <em-format/e-mail-part-list.h> +#include <em-format/e-mail-extension-registry.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PARSER \ + (e_mail_parser_get_type ()) +#define E_MAIL_PARSER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PARSER, EMailParser)) +#define E_MAIL_PARSER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PARSER, EMailParserClass)) +#define E_IS_MAIL_PARSER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PARSER)) +#define E_IS_MAIL_PARSER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PARSER)) +#define E_MAIL_PARSER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PARSER, EMailParserClass)) + +G_BEGIN_DECLS + +typedef struct _EMailParser EMailParser; +typedef struct _EMailParserClass EMailParserClass; +typedef struct _EMailParserPrivate EMailParserPrivate; + +struct _EMailParser { + GObject parent; + EMailParserPrivate *priv; +}; + +struct _EMailParserClass { + GObjectClass parent_class; + + EMailParserExtensionRegistry *extension_registry; +}; + +GType e_mail_parser_get_type (void); + +EMailParser * e_mail_parser_new (CamelSession *session); + +EMailPartList * e_mail_parser_parse_sync (EMailParser *parser, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GCancellable *cancellable); + +void e_mail_parser_parse (EMailParser *parser, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GAsyncReadyCallback callback, + GCancellable *cancellable, + gpointer user_data); + +EMailPartList * e_mail_parser_parse_finish (EMailParser *parser, + GAsyncResult *result, + GError **error); + +GSList * e_mail_parser_parse_part (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable); + +GSList * e_mail_parser_parse_part_as (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + const gchar *mime_type, + GCancellable *cancellable); + +GSList * e_mail_parser_error (EMailParser *parser, + GCancellable *cancellable, + const gchar *format, + ...) G_GNUC_PRINTF (3, 4); + +GSList * e_mail_parser_wrap_as_attachment + (EMailParser *parser, + CamelMimePart *part, + GSList *parts, + GString *part_id, + GCancellable *cancellable); + +CamelSession * e_mail_parser_get_session (EMailParser *parser); + +EMailExtensionRegistry * + e_mail_parser_get_extension_registry + (EMailParser *parser); + +G_END_DECLS + +#endif /* E_MAIL_PARSER_H_ */ diff --git a/em-format/e-mail-part-attachment-bar.h b/em-format/e-mail-part-attachment-bar.h new file mode 100644 index 0000000000..e6d65428cb --- /dev/null +++ b/em-format/e-mail-part-attachment-bar.h @@ -0,0 +1,34 @@ +/* + * 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/> + * + */ + +#ifndef E_MAIL_PART_ATTACHMENT_BAR_H +#define E_MAIL_PART_ATTACHMENT_BAR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <em-format/e-mail-part.h> + +#include <widgets/misc/e-attachment-store.h> + +typedef struct _EMailPartAttachmentBar { + EMailPart parent; + + EAttachmentStore *store; +} EMailPartAttachmentBar; + +#endif /* E_MAIL_PART_ATTACHMENT_BAR_H */ diff --git a/em-format/e-mail-part-attachment.c b/em-format/e-mail-part-attachment.c new file mode 100644 index 0000000000..2047f9d6b2 --- /dev/null +++ b/em-format/e-mail-part-attachment.c @@ -0,0 +1,30 @@ +/* + * e-mail-part-attachment.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-part-attachment.h" + +void +e_mail_part_attachment_free (EMailPartAttachment *empa) +{ + g_clear_object (&empa->attachment); + + if (empa->attachment_view_part_id) { + g_free (empa->attachment_view_part_id); + empa->attachment_view_part_id = NULL; + } +} diff --git a/em-format/e-mail-part-attachment.h b/em-format/e-mail-part-attachment.h new file mode 100644 index 0000000000..cd07e3634b --- /dev/null +++ b/em-format/e-mail-part-attachment.h @@ -0,0 +1,45 @@ +/* + * 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/> + * + */ + +#ifndef E_MAIL_PART_ATTACHMENT_H +#define E_MAIL_PART_ATTACHMENT_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <em-format/e-mail-part.h> + +#define E_MAIL_PART_ATTACHMENT(p) ((EMailPartAttachment *) p) + +G_BEGIN_DECLS + +typedef struct _EMailPartAttachment { + EMailPart parent; + + EAttachment *attachment; + gchar *attachment_view_part_id; + + gboolean shown; + const gchar *snoop_mime_type; + +} EMailPartAttachment; + +void e_mail_part_attachment_free (EMailPartAttachment *empa); + +G_END_DECLS + +#endif /* E_MAIL_PART_ATTACHMENT_H */ diff --git a/em-format/e-mail-part-list.c b/em-format/e-mail-part-list.c new file mode 100644 index 0000000000..743834beef --- /dev/null +++ b/em-format/e-mail-part-list.c @@ -0,0 +1,130 @@ +/* + * e-mail-part-list.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 <camel/camel.h> + +#include "e-mail-part-list.h" + +G_DEFINE_TYPE (EMailPartList, e_mail_part_list, G_TYPE_OBJECT) + +static void +unref_mail_part (gpointer user_data) +{ + if (user_data) + e_mail_part_unref (user_data); +} + +static void +e_mail_part_list_finalize (GObject *object) +{ + EMailPartList *part_list = E_MAIL_PART_LIST (object); + + g_clear_object (&part_list->folder); + g_clear_object (&part_list->message); + + if (part_list->list) { + g_slist_free_full (part_list->list, unref_mail_part); + part_list->list = NULL; + } + + if (part_list->message_uid) { + g_free (part_list->message_uid); + part_list->message_uid = NULL; + } + + G_OBJECT_CLASS (e_mail_part_list_parent_class)->finalize (object); +} + +static void +e_mail_part_list_class_init (EMailPartListClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = e_mail_part_list_finalize; +} + +static void +e_mail_part_list_init (EMailPartList *part_list) +{ + +} + +EMailPartList * +e_mail_part_list_new () +{ + return g_object_new (E_TYPE_MAIL_PART_LIST, NULL); +} + +EMailPart * +e_mail_part_list_find_part (EMailPartList *part_list, + const gchar *id) +{ + GSList *iter; + gboolean by_cid; + + g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL); + g_return_val_if_fail (id && *id, NULL); + + by_cid = (g_str_has_prefix (id, "cid:") || g_str_has_prefix (id, "CID:")); + + for (iter = part_list->list; iter; iter = iter->next) { + + EMailPart *part = iter->data; + if (!part) + continue; + + if ((by_cid && (g_strcmp0 (part->cid, id) == 0)) || + (!by_cid && (g_strcmp0 (part->id, id) == 0))) + return part; + } + + return NULL; +} + +/** + * e_mail_part_list_get_iter: + * @part_list: a #GSList of #EMailPart + * @id: id of #EMailPart to lookup + * + * Returns iter of an #EMailPart within the @part_list. + * + * Return Value: a #GSList sublist. The list is owned by #EMailPartList and + * must not be freed or altered. + */ +GSList * +e_mail_part_list_get_iter (GSList *list, + const gchar *id) +{ + GSList *iter; + + g_return_val_if_fail (list != NULL, NULL); + g_return_val_if_fail (id && *id, NULL); + + for (iter = list; iter; iter = iter->next) { + + EMailPart *part = iter->data; + if (!part) + continue; + + if (g_strcmp0 (part->id, id) == 0) + return iter; + } + + return NULL; +} diff --git a/em-format/e-mail-part-list.h b/em-format/e-mail-part-list.h new file mode 100644 index 0000000000..12a24f365f --- /dev/null +++ b/em-format/e-mail-part-list.h @@ -0,0 +1,76 @@ +/* + * e-mail-part-list.h + * + * 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/> + * + */ + +#ifndef E_MAIL_PART_LIST_H_ +#define E_MAIL_PART_LIST_H_ + +#include <camel/camel.h> +#include <em-format/e-mail-part.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART_LIST \ + (e_mail_part_list_get_type ()) +#define E_MAIL_PART_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART_LIST, EMailPartList)) +#define E_MAIL_PART_LIST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART_LIST, EMailPartListClass)) +#define E_IS_MAIL_PART_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART_LIST)) +#define E_IS_MAIL_PART_LIST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART_LIST)) +#define E_MAIL_PART_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART_LIST, EMailPartListClass)) + +G_BEGIN_DECLS + +typedef struct _EMailPartList EMailPartList; +typedef struct _EMailPartListClass EMailPartListClass; + +struct _EMailPartList { + GObject parent; + + CamelMimeMessage *message; + CamelFolder *folder; + gchar *message_uid; + + /* GSList of EMailPart's */ + GSList *list; +}; + +struct _EMailPartListClass { + GObjectClass parent_class; +}; + +EMailPartList * e_mail_part_list_new (); + +GType e_mail_part_list_get_type (); + +EMailPart * e_mail_part_list_find_part (EMailPartList *part_list, + const gchar *id); + +GSList * e_mail_part_list_get_iter (GSList *list, + const gchar *id); + +G_END_DECLS + +#endif /* E_MAIL_PART_LIST_H_ */ diff --git a/em-format/e-mail-part-utils.c b/em-format/e-mail-part-utils.c new file mode 100644 index 0000000000..858209b7df --- /dev/null +++ b/em-format/e-mail-part-utils.c @@ -0,0 +1,546 @@ +/* + * e-mail-part-utils.h + * + * 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/> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n-lib.h> + +#include "e-mail-part-utils.h" +#include "e-mail-parser-extension.h" + +#include <camel/camel.h> +#include <e-util/e-util.h> +#include <gdk/gdk.h> + +#include <libsoup/soup.h> + +#include <string.h> + +#define d(x) + +/** + * e_mail_parst_is_secured: + * @part: a #CamelMimePart + * + * Whether @part is signed or encrypted or not. + * + * Return Value: TRUE/FALSE + */ +gboolean +e_mail_part_is_secured (CamelMimePart *part) +{ + CamelContentType *ct = camel_mime_part_get_content_type (part); + + return (camel_content_type_is (ct, "multipart", "signed") || + camel_content_type_is (ct, "multipart", "encrypted") || + camel_content_type_is (ct, "application", "x-inlinepgp-signed") || + camel_content_type_is (ct, "application", "x-inlinepgp-encrypted") || + camel_content_type_is (ct, "application", "x-pkcs7-mime") || + camel_content_type_is (ct, "application", "pkcs7-mime")); +} + +/** + * e_mail_partr_snoop_type: + * @part: a #CamelMimePart + * + * Tries to snoop the mime type of a part. + * + * Return value: %NULL if unknown (more likely application/octet-stream). + **/ +const gchar * +e_mail_part_snoop_type (CamelMimePart *part) +{ + /* cache is here only to be able still return const gchar * */ + static GHashTable *types_cache = NULL; + + const gchar *filename; + gchar *name_type = NULL, *magic_type = NULL, *res, *tmp; + CamelDataWrapper *dw; + + filename = camel_mime_part_get_filename (part); + if (filename != NULL) + name_type = e_util_guess_mime_type (filename, FALSE); + + dw = camel_medium_get_content ((CamelMedium *) part); + if (!camel_data_wrapper_is_offline (dw)) { + GByteArray *byte_array; + CamelStream *stream; + + byte_array = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (byte_array); + + if (camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL) > 0) { + gchar *content_type; + + content_type = g_content_type_guess ( + filename, byte_array->data, + byte_array->len, NULL); + + if (content_type != NULL) + magic_type = g_content_type_get_mime_type (content_type); + + g_free (content_type); + } + + g_object_unref (stream); + } + + /* If gvfs doesn't recognize the data by magic, but it + * contains English words, it will call it text/plain. If the + * filename-based check came up with something different, use + * that instead and if it returns "application/octet-stream" + * try to do better with the filename check. + */ + + if (magic_type) { + if (name_type + && (!strcmp(magic_type, "text/plain") + || !strcmp(magic_type, "application/octet-stream"))) + res = name_type; + else + res = magic_type; + } else + res = name_type; + + if (res != name_type) + g_free (name_type); + + if (res != magic_type) + g_free (magic_type); + + if (!types_cache) + types_cache = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + + if (res) { + tmp = g_hash_table_lookup (types_cache, res); + if (tmp) { + g_free (res); + res = tmp; + } else { + g_hash_table_insert (types_cache, res, res); + } + } + + d(printf("Snooped mime type %s\n", res)); + return res; + + /* We used to load parts to check their type, we don't anymore, + * see bug #211778 for some discussion */ +} + +/** + * e_mail_part_is_attachment + * @part: Part to check. + * + * Returns true if the part is an attachment. + * + * A part is not considered an attachment if it is a + * multipart, or a text part with no filename. It is used + * to determine if an attachment header should be displayed for + * the part. + * + * Content-Disposition is not checked. + * + * Return value: TRUE/FALSE + **/ +gboolean +e_mail_part_is_attachment (CamelMimePart *part) +{ + /*CamelContentType *ct = camel_mime_part_get_content_type(part);*/ + CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part); + + if (!dw) + return 0; + + d(printf("checking is attachment %s/%s\n", dw->mime_type->type, dw->mime_type->subtype)); + return !(camel_content_type_is (dw->mime_type, "multipart", "*") + || camel_content_type_is ( + dw->mime_type, "application", "x-pkcs7-mime") + || camel_content_type_is ( + dw->mime_type, "application", "pkcs7-mime") + || camel_content_type_is ( + dw->mime_type, "application", "x-inlinepgp-signed") + || camel_content_type_is ( + dw->mime_type, "application", "x-inlinepgp-encrypted") + || camel_content_type_is ( + dw->mime_type, "x-evolution", "evolution-rss-feed") + || camel_content_type_is (dw->mime_type, "text", "calendar") + || camel_content_type_is (dw->mime_type, "text", "x-calendar") + || (camel_content_type_is (dw->mime_type, "text", "*") + && camel_mime_part_get_filename (part) == NULL)); +} + +/** + * e_mail_part_preserve_charset_in_content_type: + * @ipart: Source #CamelMimePart + * @opart: Target #CamelMimePart + * + * Copies 'charset' part of content-type header from @ipart to @opart. + */ +void +e_mail_part_preserve_charset_in_content_type (CamelMimePart *ipart, + CamelMimePart *opart) +{ + CamelDataWrapper *data_wrapper; + CamelContentType *content_type; + const gchar *charset; + + g_return_if_fail (ipart != NULL); + g_return_if_fail (opart != NULL); + + data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart)); + content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); + + if (content_type == NULL) + return; + + charset = camel_content_type_param (content_type, "charset"); + + if (charset == NULL || *charset == '\0') + return; + + data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart)); + content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); + + if (content_type) + camel_content_type_set_param (content_type, "charset", charset); + + /* update charset also on the part itself */ + data_wrapper = CAMEL_DATA_WRAPPER (opart); + content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); + if (content_type) + camel_content_type_set_param (content_type, "charset", charset); +} + +/** + * e_mail_part_get_related_display_part: + * @part: a multipart/related or multipart/alternative #CamelMimePart + * @out_displayid: (out) returns index of the returned part + * + * Goes through all subparts of given @part and tries to determine which + * part should be displayed and which parts are just attachments to the + * part. + * + * Return Value: A #CamelMimePart that should be displayed + */ +CamelMimePart * +e_mail_part_get_related_display_part (CamelMimePart *part, + gint *out_displayid) +{ + CamelMultipart *mp; + CamelMimePart *body_part, *display_part = NULL; + CamelContentType *content_type; + const gchar *start; + gint i, nparts, displayid = 0; + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) + return NULL; + + nparts = camel_multipart_get_number (mp); + content_type = camel_mime_part_get_content_type (part); + start = camel_content_type_param (content_type, "start"); + if (start && strlen (start) > 2) { + gint len; + const gchar *cid; + + /* strip <>'s from CID */ + len = strlen (start) - 2; + start++; + + for (i = 0; i < nparts; i++) { + body_part = camel_multipart_get_part (mp, i); + cid = camel_mime_part_get_content_id (body_part); + + if (cid && !strncmp (cid, start, len) && strlen (cid) == len) { + display_part = body_part; + displayid = i; + break; + } + } + } else { + display_part = camel_multipart_get_part (mp, 0); + } + + if (out_displayid) + *out_displayid = displayid; + + return display_part; +} + +void +e_mail_part_animation_extract_frame (const GByteArray *anim, + gchar **frame, + gsize *len) +{ + GdkPixbufLoader *loader; + GdkPixbufAnimation *animation; + GdkPixbuf *frame_buf; + + /* GIF89a (GIF image signature) */ + const gchar GIF_HEADER[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; + const gint GIF_HEADER_LEN = sizeof (GIF_HEADER); + + /* NETSCAPE2.0 (extension describing animated GIF, starts on 0x310) */ + const gchar GIF_APPEXT[] = { 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, + 0x50, 0x45, 0x32, 0x2E, 0x30 }; + const gint GIF_APPEXT_LEN = sizeof (GIF_APPEXT); + + if ((anim == NULL) || (anim->data == NULL)) { + *frame = NULL; + *len = 0; + return; + } + + /* Check if the image is an animated GIF. We don't care about any + * other animated formats (APNG or MNG) as WebKit does not support them + * and displays only the first frame. */ + if ((anim->len < 0x331) + || (memcmp (anim->data, GIF_HEADER, GIF_HEADER_LEN) != 0) + || (memcmp (&anim->data[0x310], GIF_APPEXT, GIF_APPEXT_LEN) != 0)) { + + *frame = g_memdup (anim->data, anim->len); + *len = anim->len; + return; + } + + loader = gdk_pixbuf_loader_new (); + gdk_pixbuf_loader_write (loader, (guchar *) anim->data, anim->len, NULL); + gdk_pixbuf_loader_close (loader, NULL); + animation = gdk_pixbuf_loader_get_animation (loader); + if (!animation) { + + *frame = g_memdup (anim->data, anim->len); + *len = anim->len; + g_object_unref (loader); + return; + } + + /* Extract first frame */ + frame_buf = gdk_pixbuf_animation_get_static_image (animation); + if (!frame_buf) { + *frame = g_memdup (anim->data, anim->len); + *len = anim->len; + g_object_unref (loader); + g_object_unref (animation); + return; + } + + /* Unforunatelly, GdkPixbuf cannot save to GIF, but WebKit does not + * have any trouble displaying PNG image despite the part having + * image/gif mime-type */ + gdk_pixbuf_save_to_buffer (frame_buf, frame, len, "png", NULL, NULL); + + g_object_unref (loader); +} + +/** + * e_mail_part_build_url: + * @folder: (allow-none) a #CamelFolder with the message or %NULL + * @message_uid: (allow-none) uid of the message within the @folder or %NULL + * @first_param_name: Name of first query parameter followed by GType of it's value and value + * terminated by %NULL. + * + * Construct a URI for message. + * + * The URI can contain multiple query parameters. The list of parameters must be + * NULL-terminated. Each query must contain name, GType of value and value. + * + * Return Value: a URL of a message or part + */ +gchar * +e_mail_part_build_uri (CamelFolder *folder, + const gchar *message_uid, + const gchar *first_param_name, + ...) +{ + CamelStore *store; + gchar *uri, *tmp; + va_list ap; + const gchar *name; + const gchar *service_uid, *folder_name; + gchar separator; + + g_return_val_if_fail (message_uid && *message_uid, NULL); + + if (!folder) { + folder_name = "generic"; + service_uid = "generic"; + } else { + tmp = (gchar *) camel_folder_get_full_name (folder); + folder_name = (const gchar *) soup_uri_encode (tmp, NULL); + store = camel_folder_get_parent_store (folder); + if (store) + service_uid = camel_service_get_uid (CAMEL_SERVICE (store)); + else + service_uid = "generic"; + } + + tmp = g_strdup_printf ("mail://%s/%s/%s", + service_uid, + folder_name, + message_uid); + + if (folder) { + g_free ((gchar *) folder_name); + } + + va_start (ap, first_param_name); + name = first_param_name; + separator = '?'; + while (name) { + gchar *tmp2; + gint type = va_arg (ap, gint); + switch (type) { + case G_TYPE_INT: + case G_TYPE_BOOLEAN: { + gint val = va_arg (ap, gint); + tmp2 = g_strdup_printf ("%s%c%s=%d", tmp, + separator, name, val); + break; + } + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: { + gdouble val = va_arg (ap, double); + tmp2 = g_strdup_printf ("%s%c%s=%f", tmp, + separator, name, val); + break; + } + case G_TYPE_STRING: { + gchar *val = va_arg (ap, gchar *); + gchar *escaped = soup_uri_encode (val, NULL); + tmp2 = g_strdup_printf ("%s%c%s=%s", tmp, + separator, name, escaped); + g_free (escaped); + break; + } + default: + g_warning ("Invalid param type %s", g_type_name (type)); + return NULL; + } + + g_free (tmp); + tmp = tmp2; + + if (separator == '?') + separator = '&'; + + name = va_arg (ap, gchar *); + } + va_end (ap); + + uri = tmp; + if (uri == NULL) + return NULL; + + /* For some reason, webkit won't accept URL with username, but + * without password (mail://store@host/folder/mail), so we + * will replace the '@' symbol by '/' to get URL like + * mail://store/host/folder/mail which is OK + */ + while ((tmp = strchr (uri, '@')) != NULL) { + tmp[0] = '/'; + } + + return uri; +} + +/** + * e_mail_part_describe: + * @part: a #CamelMimePart + * @mimetype: mimetype of the content + * + * Generate a simple textual description of a part, @mime_type represents + * the content. + * + * Return value: + **/ +gchar * +e_mail_part_describe (CamelMimePart *part, + const gchar *mime_type) +{ + GString *stext; + const gchar *filename, *description; + gchar *content_type, *desc; + + stext = g_string_new(""); + content_type = g_content_type_from_mime_type (mime_type); + desc = g_content_type_get_description ( + content_type != NULL ? content_type : mime_type); + g_free (content_type); + g_string_append_printf ( + stext, _("%s attachment"), desc ? desc : mime_type); + g_free (desc); + + filename = camel_mime_part_get_filename (part); + description = camel_mime_part_get_description (part); + + if (!filename || !*filename) { + CamelDataWrapper *content; + + content = camel_medium_get_content (CAMEL_MEDIUM (part)); + + if (CAMEL_IS_MIME_MESSAGE (content)) + filename = camel_mime_message_get_subject ( + CAMEL_MIME_MESSAGE (content)); + } + + if (filename != NULL && *filename != '\0') { + gchar *basename = g_path_get_basename (filename); + g_string_append_printf (stext, " (%s)", basename); + g_free (basename); + } + + if (description != NULL && *description != '\0' && + g_strcmp0 (filename, description) != 0) + g_string_append_printf (stext, ", \"%s\"", description); + + return g_string_free (stext, FALSE); +} + +gboolean +e_mail_part_is_inline (CamelMimePart *mime_part, + GQueue *extensions) +{ + const gchar *disposition; + EMailParserExtension *extension; + + if ((extensions == NULL) || g_queue_is_empty (extensions)) + return FALSE; + + extension = g_queue_peek_head (extensions); + /* Some types need to override the disposition. + * e.g. application/x-pkcs7-mime */ + if (e_mail_parser_extension_get_flags (extension) & + E_MAIL_PARSER_EXTENSION_INLINE_DISPOSITION) + return TRUE; + + disposition = camel_mime_part_get_disposition (mime_part); + if (disposition != NULL) + return g_ascii_strcasecmp (disposition, "inline") == 0; + + /* Otherwise, use the default for this handler type. */ + return (e_mail_parser_extension_get_flags (extension) & + E_MAIL_PARSER_EXTENSION_INLINE) != 0; +} diff --git a/em-format/e-mail-part-utils.h b/em-format/e-mail-part-utils.h new file mode 100644 index 0000000000..97c483db94 --- /dev/null +++ b/em-format/e-mail-part-utils.h @@ -0,0 +1,58 @@ +/* + * e-mail-part-utils.h + * + * 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/> + * + */ + +#ifndef E_MAIL_PART_UTILS_H_ +#define E_MAIL_PART_UTILS_H_ + +#include <camel/camel.h> + +G_BEGIN_DECLS + +gboolean e_mail_part_is_secured (CamelMimePart *part); + +const gchar * e_mail_part_snoop_type (CamelMimePart *part); + +gboolean e_mail_part_is_attachment (CamelMimePart *part); + +void e_mail_part_preserve_charset_in_content_type + (CamelMimePart *ipart, + CamelMimePart *opart); + +CamelMimePart * e_mail_part_get_related_display_part + (CamelMimePart *part, + gint *out_displayid); + +void e_mail_part_animation_extract_frame ( + const GByteArray *anim, + gchar **frame, + gsize *len); + +gchar * e_mail_part_build_uri (CamelFolder *folder, + const gchar *message_uid, + const gchar *first_param_name, + ...); + +gchar * e_mail_part_describe (CamelMimePart *part, + const gchar *mime_type); + +gboolean e_mail_part_is_inline (CamelMimePart *part, + GQueue *extensions); + +G_END_DECLS + +#endif /* E_MAIL_PART_UTILS_H_ */ diff --git a/em-format/e-mail-part.c b/em-format/e-mail-part.c new file mode 100644 index 0000000000..a0d493afe4 --- /dev/null +++ b/em-format/e-mail-part.c @@ -0,0 +1,196 @@ +/* + * e-mail-part.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 <camel/camel.h> + +#include "e-mail-part.h" + +/** + * EMailPart: + * + * The #EMailPart is a wrapper around #CamelMimePart which holds additional + * information about the mime part, like it's ID, encryption type etc. + * + * #EMailPart is not GObject-based, but has a simple reference counting. + * + * Each #EMailPart must have a unique ID. The ID is a dot-separated hierarchical + * description of the location of the part within the email message. + */ + +struct _EMailPartPrivate { + guint ref_cnt; + gsize instance_size; + GFreeFunc free_func; +}; + +static void +mail_part_free (EMailPart *part) +{ + if (!part) + return; + + if (part->part) { + g_object_unref (part->part); + part->part = NULL; + } + + if (part->cid) { + g_free (part->cid); + part->cid = NULL; + } + + if (part->mime_type) { + g_free (part->mime_type); + part->mime_type = NULL; + } + + if (part->validity) { + camel_cipher_validity_free (part->validity); + part->validity = NULL; + } + + if (part->validity_parent) { + camel_cipher_validity_free (part->validity_parent); + part->validity_parent = NULL; + } + + if (part->priv->free_func) { + part->priv->free_func (part); + part->priv->free_func = NULL; + } + + if (part->id) { + g_free (part->id); + part->id = NULL; + } + + g_free (part->priv); + part->priv = NULL; + + g_free (part); +} + +/** + * e_mail_part_new: + * @part: (allow-none) a #CamelMimePart or %NULL + * @id: part ID + * + * Creates a new #EMailPart for given mime part. + * + * Return value: a new #EMailPart + */ +EMailPart * +e_mail_part_new (CamelMimePart *part, + const gchar *id) +{ + return e_mail_part_subclass_new (part, id, sizeof (EMailPart), NULL); +} + +/** + * e_mail_part_new: + * @part: (allow-none) a #CamelMimePart or %NULL + * @id: part ID + * @size: Size of the EMailPart subclass + * + * Allocates a @size bytes representing an #EMailPart subclass. + * + * Return value: a new #EMailPart-based object + */ +EMailPart * +e_mail_part_subclass_new (CamelMimePart *part, + const gchar *id, + gsize size, + GFreeFunc free_func) +{ + EMailPart *mail_part; + + g_return_val_if_fail (size >= sizeof (EMailPart), NULL); + + mail_part = g_malloc0 (size); + mail_part->priv = g_new0 (EMailPartPrivate, 1); + + mail_part->priv->ref_cnt = 1; + mail_part->priv->free_func = free_func; + mail_part->priv->instance_size = size; + + if (part) { + mail_part->part = g_object_ref (part); + } + + if (id) { + mail_part->id = g_strdup (id); + } + + return mail_part; +} + +EMailPart * +e_mail_part_ref (EMailPart *part) +{ + g_return_val_if_fail (part != NULL, NULL); + g_return_val_if_fail (part->priv != NULL, NULL); + + g_atomic_int_inc (&part->priv->ref_cnt); + + return part; +} + +void +e_mail_part_unref (EMailPart *part) +{ + g_return_if_fail (part != NULL); + g_return_if_fail (part->priv != NULL); + + if (g_atomic_int_dec_and_test (&part->priv->ref_cnt)) { + mail_part_free (part); + } +} + +gsize +e_mail_part_get_instance_size (EMailPart *part) +{ + g_return_val_if_fail (part != NULL, 0); + + return part->priv->instance_size; +} + +/** + * e_mail_part_update_validity: + * @part: An #EMailPart + * @validity_type: E_MAIL_PART_VALIDITY_* flags + * @validity: a #CamelCipherValidity + * + * Updates validity of the @part. When the part already has some validity + * set, the new @validity and @validity_type are just appended, preserving + * the original validity. + */ +void +e_mail_part_update_validity (EMailPart *part, + CamelCipherValidity *validity, + guint32 validity_type) +{ + g_return_if_fail (part != NULL); + + part->validity_type &= validity_type; + + if (part->validity) { + camel_cipher_validity_envelope (part->validity, validity); + } else { + part->validity = camel_cipher_validity_clone (validity); + } +} diff --git a/em-format/e-mail-part.h b/em-format/e-mail-part.h new file mode 100644 index 0000000000..3d2e9628eb --- /dev/null +++ b/em-format/e-mail-part.h @@ -0,0 +1,98 @@ +/* + * e-mail-part.h + * + * 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/> + * + */ + +#ifndef E_MAIL_PART_H_ +#define E_MAIL_PART_H_ + +#include <camel/camel.h> +#include <misc/e-attachment.h> +#include <webkit/webkitdom.h> + +#define E_MAIL_PART_IS(p,s_t) \ + ((p != NULL) && (e_mail_part_get_instance_size (p) == sizeof (s_t))) +#define E_MAIL_PART(o) ((EMailPart *) o) + +G_BEGIN_DECLS + +typedef struct _EMailPart EMailPart; +typedef struct _EMailPartPrivate EMailPartPrivate; + +typedef void (*EMailPartDOMBindFunc) (EMailPart *part, + WebKitDOMElement *element); + +enum { + E_MAIL_PART_VALIDITY_NONE = 0, + E_MAIL_PART_VALIDITY_PGP = 1 << 0, + E_MAIL_PART_VALIDITY_SMIME = 1 << 1, + E_MAIL_PART_VALIDITY_SIGNED = 1 << 2, + E_MAIL_PART_VALIDITY_ENCRYPTED = 1 << 3 +} EMailPartValidityFlags; + +struct _EMailPart { + EMailPartPrivate *priv; + + EMailPartDOMBindFunc bind_func; + + CamelMimePart *part; + gchar *id; + gchar *cid; + gchar *mime_type; + + guint32 validity_type; /* E_MAIL_PART_VALIDITY_ * flags */ + CamelCipherValidity *validity; + CamelCipherValidity *validity_parent; + + gint is_attachment: 1; + + /* Whether the part should be rendered or not. + * This is used for example to prevent images + * related to text/html parts from being + * rendered as attachments. */ + gint is_hidden: 1; + + /* Force attachment to be expanded, even without + * content-disposition: inline */ + gint force_inline: 1; + + /* Force attachment to be collapsed, even with + * content-disposition: inline */ + gint force_collapse: 1; + + /* Does part contain an error message? */ + gint is_error: 1; +}; + +EMailPart * e_mail_part_new (CamelMimePart *part, + const gchar *id); +EMailPart * e_mail_part_subclass_new (CamelMimePart *part, + const gchar *id, + gsize size, + GFreeFunc free_func); + +EMailPart * e_mail_part_ref (EMailPart *part); +void e_mail_part_unref (EMailPart *part); + +gsize e_mail_part_get_instance_size (EMailPart *part); + +void e_mail_part_update_validity (EMailPart *part, + CamelCipherValidity *validity, + guint32 validity_type); + +G_END_DECLS + +#endif /* E_MAIL_PART_H_ */ diff --git a/em-format/em-stripsig-filter.c b/em-format/e-mail-stripsig-filter.c index fe08383d5c..e861a13a53 100644 --- a/em-format/em-stripsig-filter.c +++ b/em-format/e-mail-stripsig-filter.c @@ -28,9 +28,9 @@ #include <stdio.h> #include <string.h> -#include "em-stripsig-filter.h" +#include "e-mail-stripsig-filter.h" -G_DEFINE_TYPE (EMStripSigFilter, em_stripsig_filter, CAMEL_TYPE_MIME_FILTER) +G_DEFINE_TYPE (EMailStripSigFilter, e_mail_stripsig_filter, CAMEL_TYPE_MIME_FILTER) static void strip_signature (CamelMimeFilter *filter, @@ -42,7 +42,7 @@ strip_signature (CamelMimeFilter *filter, gsize *outprespace, gint flush) { - EMStripSigFilter *stripsig = (EMStripSigFilter *) filter; + EMailStripSigFilter *stripsig = (EMailStripSigFilter *) filter; register const gchar *inptr = in; const gchar *inend = in + len; const gchar *start = NULL; @@ -124,13 +124,13 @@ filter_complete (CamelMimeFilter *filter, static void filter_reset (CamelMimeFilter *filter) { - EMStripSigFilter *stripsig = (EMStripSigFilter *) filter; + EMailStripSigFilter *stripsig = (EMailStripSigFilter *) filter; stripsig->midline = FALSE; } static void -em_stripsig_filter_class_init (EMStripSigFilterClass *class) +e_mail_stripsig_filter_class_init (EMailStripSigFilterClass *class) { CamelMimeFilterClass *mime_filter_class; @@ -141,12 +141,12 @@ em_stripsig_filter_class_init (EMStripSigFilterClass *class) } static void -em_stripsig_filter_init (EMStripSigFilter *filter) +e_mail_stripsig_filter_init (EMailStripSigFilter *filter) { } /** - * em_stripsig_filter_new: + * e_mail_stripsig_filter_new: * @text_plain_only: Whether should look for a text/plain signature * delimiter "-- \n" only or also an HTML signature delimiter "-- <BR>". * @@ -155,9 +155,9 @@ em_stripsig_filter_init (EMStripSigFilter *filter) * Returns a new stripsig filter. **/ CamelMimeFilter * -em_stripsig_filter_new (gboolean text_plain_only) +e_mail_stripsig_filter_new (gboolean text_plain_only) { - EMStripSigFilter *filter = g_object_new (EM_TYPE_STRIPSIG_FILTER, NULL); + EMailStripSigFilter *filter = g_object_new (E_TYPE_MAIL_STRIPSIG_FILTER, NULL); filter->text_plain_only = text_plain_only; diff --git a/em-format/em-stripsig-filter.h b/em-format/e-mail-stripsig-filter.h index 47017f79fe..730d55eaaf 100644 --- a/em-format/em-stripsig-filter.h +++ b/em-format/e-mail-stripsig-filter.h @@ -20,50 +20,50 @@ * */ -#ifndef EM_STRIPSIG_FILTER_H -#define EM_STRIPSIG_FILTER_H +#ifndef E_MAIL_STRIPSIG_FILTER_H +#define E_MAIL_STRIPSIG_FILTER_H #include <camel/camel.h> /* Standard GObject macros */ -#define EM_TYPE_STRIPSIG_FILTER \ - (em_stripsig_filter_get_type ()) -#define EM_STRIPSIG_FILTER(obj) \ +#define E_TYPE_MAIL_STRIPSIG_FILTER \ + (e_mail_stripsig_filter_get_type ()) +#define E_MAIL_STRIPSIG_FILTER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_STRIPSIG_FILTER, EMStripSigFilter)) -#define EM_STRIPSIG_FILTER_CLASS(cls) \ + ((obj), E_TYPE_MAIL_STRIPSIG_FILTER, EMailStripSigFilter)) +#define E_MAIL_STRIPSIG_FILTER_CLASS(cls) \ (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_STRIPSIG_FILTER, EMStripSigFilterClass)) -#define EM_IS_STRIPSIG_FILTER(obj) \ + ((cls), E_TYPE_MAIL_STRIPSIG_FILTER, EMailStripSigFilterClass)) +#define E_MAIL_IS_STRIPSIG_FILTER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_STRIPSIG_FILTER)) -#define EM_IS_STRIPSIG_FILTER_CLASS(cls) \ + ((obj), E_TYPE_MAIL_STRIPSIG_FILTER)) +#define E_MAIL_IS_STRIPSIG_FILTER_CLASS(cls) \ (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_STRIPSIG_FILTER)) -#define EM_STRIPSIG_FILTER_GET_CLASS(obj) \ + ((cls), E_TYPE_MAIL_STRIPSIG_FILTER)) +#define E_MAIL_STRIPSIG_FILTER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_STRIPSIG_FILTER, EMStripSigFilterClass)) + ((obj), E_TYPE_MAIL_STRIPSIG_FILTER, EMailStripSigFilterClass)) G_BEGIN_DECLS -typedef struct _EMStripSigFilter EMStripSigFilter; -typedef struct _EMStripSigFilterClass EMStripSigFilterClass; +typedef struct _EMailStripSigFilter EMailStripSigFilter; +typedef struct _EMailStripSigFilterClass EMailStripSigFilterClass; -struct _EMStripSigFilter { +struct _EMailStripSigFilter { CamelMimeFilter parent; guint32 midline : 1; guint32 text_plain_only : 1; }; -struct _EMStripSigFilterClass { +struct _EMailStripSigFilterClass { CamelMimeFilterClass parent_class; }; -GType em_stripsig_filter_get_type (void); +GType e_mail_stripsig_filter_get_type (void); CamelMimeFilter * - em_stripsig_filter_new (gboolean text_plain_only); + e_mail_stripsig_filter_new (gboolean text_plain_only); G_END_DECLS -#endif /* EM_STRIPSIG_FILTER_H */ +#endif /* E_MAIL_STRIPSIG_FILTER_H */ diff --git a/em-format/em-format-quote.c b/em-format/em-format-quote.c deleted file mode 100644 index b74da215f4..0000000000 --- a/em-format/em-format-quote.c +++ /dev/null @@ -1,844 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Jeffrey Stedfast <fejj@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> - -#include <glib/gi18n.h> - -#include "em-inline-filter.h" -#include "em-stripsig-filter.h" -#include "em-format-quote.h" - -#define EM_FORMAT_QUOTE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), EM_TYPE_FORMAT_QUOTE, EMFormatQuotePrivate)) - -struct _EMFormatQuotePrivate { - gchar *credits; - EMFormatQuoteFlags flags; - guint32 text_html_flags; -}; - -static void emfq_builtin_init (EMFormatQuoteClass *efhc); - -static CamelMimePart * decode_inline_parts (CamelMimePart *part, GCancellable *cancellable); - -static void emfq_parse_text_plain (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emfq_parse_text_enriched (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emfq_parse_text_html (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emfq_parse_attachment (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); - -static void emfq_write_text_plain (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void emfq_write_text_enriched (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void emfq_write_text_html (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -static gpointer parent_class; - -/* Decodes inline encoded parts of 'part'. The returned pointer, - * if not NULL, should be unreffed with g_object_unref(). */ -static CamelMimePart * -decode_inline_parts (CamelMimePart *part, - GCancellable *cancellable) -{ - CamelMultipart *mp; - CamelStream *null; - CamelStream *filtered_stream; - EMInlineFilter *inline_filter; - - g_return_val_if_fail (part != NULL, NULL); - - null = camel_stream_null_new (); - filtered_stream = camel_stream_filter_new (null); - g_object_unref (null); - - inline_filter = em_inline_filter_new ( - camel_mime_part_get_encoding (part), - camel_mime_part_get_content_type (part)); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), - CAMEL_MIME_FILTER (inline_filter)); - camel_data_wrapper_decode_to_stream_sync ( - camel_medium_get_content (CAMEL_MEDIUM (part)), - filtered_stream, cancellable, NULL); - camel_stream_close (filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); - - if (!em_inline_filter_found_any (inline_filter)) { - g_object_unref (inline_filter); - return NULL; - } - - mp = em_inline_filter_get_multipart (inline_filter); - - g_object_unref (inline_filter); - - if (mp) { - part = camel_mime_part_new (); - camel_medium_set_content ( - CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp)); - g_object_unref (mp); - } else { - g_object_ref (part); - } - - return part; -} - -static void -emfq_format_text_header (EMFormatQuote *emfq, - GString *buffer, - const gchar *label, - const gchar *value, - guint32 flags, - gint is_html) -{ - const gchar *html; - gchar *mhtml = NULL; - - if (value == NULL) - return; - - while (*value == ' ') - value++; - - if (!is_html) - html = mhtml = camel_text_to_html (value, 0, 0); - else - html = value; - - if (flags & EM_FORMAT_HEADER_BOLD) - g_string_append_printf ( - buffer, "<b>%s</b>: %s<br>", label, html); - else - g_string_append_printf ( - buffer, "%s: %s<br>", label, html); - - g_free (mhtml); -} - -static const gchar *addrspec_hdrs[] = { - "Sender", "From", "Reply-To", "To", "Cc", "Bcc", - "Resent-Sender", "Resent-from", "Resent-Reply-To", - "Resent-To", "Resent-cc", "Resent-Bcc", NULL -}; - -#if 0 -/* FIXME: include Sender and Resent-* headers too? */ -/* For Translators only: The following strings are - * used in the header table in the preview pane. */ -static gchar *i18n_hdrs[] = { - N_("From"), N_("Reply-To"), N_("To"), N_("Cc"), N_("Bcc") -}; -#endif - -static void -emfq_format_address (GString *out, - struct _camel_header_address *a) -{ - guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; - gchar *name, *mailto, *addr; - - while (a) { - if (a->name) - name = camel_text_to_html (a->name, flags, 0); - else - name = NULL; - - switch (a->type) { - case CAMEL_HEADER_ADDRESS_NAME: - if (name && *name) { - gchar *real, *mailaddr; - - g_string_append_printf (out, "%s <", name); - /* rfc2368 for mailto syntax and url encoding extras */ - if ((real = camel_header_encode_phrase ((guchar *) a->name))) { - mailaddr = g_strdup_printf ("%s <%s>", real, a->v.addr); - g_free (real); - mailto = camel_url_encode (mailaddr, "?=&()"); - g_free (mailaddr); - } else { - mailto = camel_url_encode (a->v.addr, "?=&()"); - } - } else { - mailto = camel_url_encode (a->v.addr, "?=&()"); - } - addr = camel_text_to_html (a->v.addr, flags, 0); - g_string_append_printf ( - out, "<a href=\"mailto:%s\">%s</a>", - mailto, addr); - g_free (mailto); - g_free (addr); - - if (name && *name) - g_string_append (out, ">"); - break; - case CAMEL_HEADER_ADDRESS_GROUP: - g_string_append_printf (out, "%s: ", name); - emfq_format_address (out, a->v.members); - g_string_append_printf (out, ";"); - break; - default: - g_warning ("Invalid address type"); - break; - } - - g_free (name); - - a = a->next; - if (a) - g_string_append (out, ", "); - } -} - -static void -canon_header_name (gchar *name) -{ - gchar *inptr = name; - - /* canonicalise the header name... first letter is - * capitalised and any letter following a '-' also gets - * capitalised */ - - if (g_ascii_islower (*inptr)) - *inptr = g_ascii_toupper (*inptr); - - inptr++; - - while (*inptr) { - if (inptr[-1] == '-' && g_ascii_islower (*inptr)) - *inptr = g_ascii_toupper (*inptr); - else if (g_ascii_isupper (*inptr)) - *inptr = g_ascii_tolower (*inptr); - - inptr++; - } -} - -static void -emfq_format_header (EMFormat *emf, - GString *buffer, - CamelMedium *part, - const gchar *namein, - guint32 flags, - const gchar *charset) -{ - CamelMimeMessage *msg = (CamelMimeMessage *) part; - EMFormatQuote *emfq = (EMFormatQuote *) emf; - gchar *name, *buf, *value = NULL; - const gchar *txt, *label; - gboolean addrspec = FALSE; - gint is_html = FALSE; - gint i; - - name = g_alloca (strlen (namein) + 1); - strcpy (name, namein); - canon_header_name (name); - - /* Never quote Bcc headers */ - if (g_str_equal (name, "Bcc") || g_str_equal (name, "Resent-Bcc")) - return; - - for (i = 0; addrspec_hdrs[i]; i++) { - if (!strcmp (name, addrspec_hdrs[i])) { - addrspec = TRUE; - break; - } - } - - label = _(name); - - if (addrspec) { - struct _camel_header_address *addrs; - GString *html; - - if (!(txt = camel_medium_get_header (part, name))) - return; - - buf = camel_header_unfold (txt); - addrs = camel_header_address_decode ( - txt, em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf)); - if (addrs == NULL) { - g_free (buf); - return; - } - - g_free (buf); - - html = g_string_new (""); - emfq_format_address (html, addrs); - camel_header_address_unref (addrs); - txt = value = html->str; - g_string_free (html, FALSE); - flags |= EM_FORMAT_HEADER_BOLD; - is_html = TRUE; - } else if (!strcmp (name, "Subject")) { - txt = camel_mime_message_get_subject (msg); - label = _("Subject"); - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp (name, "X-Evolution-Mailer")) { /* pseudo-header */ - if (!(txt = camel_medium_get_header (part, "x-mailer"))) - if (!(txt = camel_medium_get_header (part, "user-agent"))) - if (!(txt = camel_medium_get_header (part, "x-newsreader"))) - if (!(txt = camel_medium_get_header (part, "x-mimeole"))) - return; - - txt = value = camel_header_format_ctext (txt, charset); - - label = _("Mailer"); - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) { - if (!(txt = camel_medium_get_header (part, name))) - return; - - flags |= EM_FORMAT_HEADER_BOLD; - } else { - txt = camel_medium_get_header (part, name); - buf = camel_header_unfold (txt); - txt = value = camel_header_decode_string (txt, charset); - g_free (buf); - } - - emfq_format_text_header (emfq, buffer, label, txt, flags, is_html); - - g_free (value); -} - -static void -emfq_format_headers (EMFormatQuote *emfq, - GString *buffer, - CamelMedium *part) -{ - EMFormat *emf = (EMFormat *) emfq; - CamelContentType *ct; - const gchar *charset; - GList *link; - - if (!part) - return; - - ct = camel_mime_part_get_content_type ((CamelMimePart *) part); - charset = camel_content_type_param (ct, "charset"); - charset = camel_iconv_charset_name (charset); - - /* dump selected headers */ - link = g_queue_peek_head_link (&emf->header_list); - while (link != NULL) { - EMFormatHeader *h = link->data; - emfq_format_header ( - emf, buffer, part, h->name, h->flags, charset); - link = g_list_next (link); - } - - g_string_append (buffer, "<br>\n"); -} - -static void -emfq_dispose (GObject *object) -{ - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -emfq_finalize (GObject *object) -{ - EMFormatQuotePrivate *priv; - - priv = EM_FORMAT_QUOTE_GET_PRIVATE (object); - - g_free (priv->credits); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/******************************************************************************/ -static void -emfq_parse_text_plain (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - CamelMimePart *mp; - gint len; - - len = part_id->len; - g_string_append (part_id, ".text_plain"); - - mp = decode_inline_parts (part, cancellable); - if (mp) { - - if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) { - em_format_parse_part (emf, mp, part_id, info, cancellable); - } - - g_object_unref (mp); - } - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = emfq_write_text_plain; - puri->mime_type = g_strdup ("text/html"); - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); -} - -static void -emfq_parse_text_html (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - len = part_id->len; - g_string_append (part_id, ".text_html"); - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = emfq_write_text_html; - puri->mime_type = g_strdup ("text/html"); - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); -} - -static void -emfq_parse_text_enriched (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - len = part_id->len; - g_string_append (part_id, ".text_enriched"); - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = emfq_write_text_enriched; - puri->mime_type = g_strdup ("text/html"); - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); -} - -static void -emfq_parse_attachment (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - len = part_id->len; - g_string_append (part_id, ".attachment"); - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = emfq_write_text_html; - puri->mime_type = g_strdup ("text/html"); - puri->is_attachment = TRUE; - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); -} - -/******************************************************************************/ - -static void -emfq_write_attachment (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf); - const EMFormatHandler *handler; - gchar *text, *html; - CamelContentType *ct; - const gchar *mime_type; - - ct = camel_mime_part_get_content_type (puri->part); - if (ct) { - mime_type = camel_content_type_simple (ct); - camel_content_type_unref (ct); - } else { - mime_type = "application/octet-stream"; - } - - handler = em_format_find_handler (emf, mime_type); - - if (!em_format_is_inline (emf, puri->uri, puri->part, handler)) - return; - - camel_stream_write_string ( - stream, "<table border=1 cellspacing=0 cellpadding=0>" - "<tr><td><font size=-1>\n", cancellable, NULL); - - /* output some info about it */ - text = em_format_describe_part (puri->part, mime_type); - html = camel_text_to_html ( - text, emfq->priv->text_html_flags & - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_write_string (stream, html, cancellable, NULL); - g_free (html); - g_free (text); - - camel_stream_write_string ( - stream, "</font></td></tr></table>", cancellable, NULL); - - if (handler && handler->write_func) - handler->write_func (emf, puri, stream, info, cancellable); -} - -static void -emfq_write (EMFormat *emf, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GList *iter; - - for (iter = emf->mail_part_list; iter; iter = iter->next) { - - EMFormatPURI *puri = iter->data; - if (!puri || !puri->write_func) - continue; - - puri->write_func (emf, puri, stream, info, cancellable); - } -} - -static void -emfq_base_init (EMFormatQuoteClass *klass) -{ - emfq_builtin_init (klass); -} - -static void -emfq_class_init (EMFormatQuoteClass *klass) -{ - GObjectClass *object_class; - EMFormatClass *format_class; - - parent_class = g_type_class_peek_parent (klass); - g_type_class_add_private (klass, sizeof (EMFormatQuotePrivate)); - - format_class = EM_FORMAT_CLASS (klass); - format_class->write = emfq_write; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = emfq_dispose; - object_class->finalize = emfq_finalize; -} - -static void -emfq_init (EMFormatQuote *emfq) -{ - emfq->priv = EM_FORMAT_QUOTE_GET_PRIVATE (emfq); - - /* we want to convert url's etc */ - emfq->priv->text_html_flags = - CAMEL_MIME_FILTER_TOHTML_PRE | - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | - CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; -} - -GType -em_format_quote_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatQuoteClass), - (GBaseInitFunc) emfq_base_init, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) emfq_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormatQuote), - 0, /* n_preallocs */ - (GInstanceInitFunc) emfq_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - EM_TYPE_FORMAT, "EMFormatQuote", &type_info, 0); - } - - return type; -} - -EMFormatQuote * -em_format_quote_new (CamelSession *session, - const gchar *credits, - CamelStream *stream, - EMFormatQuoteFlags flags) -{ - EMFormatQuote *emfq; - - g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); - g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL); - - /* Steam must also be seekable so we can reset its position. */ - g_return_val_if_fail (G_IS_SEEKABLE (stream), NULL); - - emfq = g_object_new ( - EM_TYPE_FORMAT_QUOTE, - "session", session, NULL); - - emfq->priv->credits = g_strdup (credits); - emfq->priv->flags = flags; - - return emfq; -} - -void -em_format_quote_write (EMFormatQuote *emfq, - CamelStream *stream, - GCancellable *cancellable) -{ - EMFormat *emf; - GSettings *settings; - GList *iter; - EMFormatWriterInfo info = { 0 }; - - emf = (EMFormat *) emfq; - - g_seekable_seek ( - G_SEEKABLE (stream), - 0, G_SEEK_SET, NULL, NULL); - - settings = g_settings_new ("org.gnome.evolution.mail"); - if (g_settings_get_boolean ( - settings, "composer-top-signature")) - camel_stream_write_string ( - stream, "<br>\n", cancellable, NULL); - g_object_unref (settings); - - if (emfq->priv->credits && *emfq->priv->credits) { - gchar *credits = g_strdup_printf ("%s<br/>", emfq->priv->credits); - camel_stream_write_string (stream, credits, cancellable, NULL); - g_free (credits); - } else { - camel_stream_write_string (stream, "<br/>", cancellable, NULL); - } - - if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE) - camel_stream_write_string (stream, - "<!--+GtkHTML:<DATA class=\"ClueFlow\" " - "key=\"orig\" value=\"1\">-->\n" - "<blockquote type=cite>\n", cancellable, NULL); - - for (iter = emf->mail_part_list; iter; iter = iter->next) { - EMFormatPURI *puri = iter->data; - - if (puri->is_attachment || !puri->write_func) - continue; - - puri = iter->data; - - if (emfq->priv->flags & EM_FORMAT_QUOTE_HEADERS) { - GString *buffer = g_string_new (""); - emfq_format_headers (emfq, buffer, (CamelMedium *) puri->part); - camel_stream_write_string (stream, buffer->str, cancellable, NULL); - g_string_free (buffer, TRUE); - } - - puri->write_func (emf, puri, stream, &info, cancellable); - } - - if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE) - camel_stream_write_string ( - stream, "</blockquote><!--+GtkHTML:" - "<DATA class=\"ClueFlow\" clear=\"orig\">-->", - cancellable, NULL); -} - -static void -emfq_write_text_plain (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf); - CamelStream *filtered_stream; - CamelMimeFilter *html_filter; - CamelMimeFilter *sig_strip; - CamelContentType *type; - const gchar *format; - guint32 rgb = 0x737373, flags; - - if (!puri->part) - return; - - flags = emfq->priv->text_html_flags; - - /* Check for RFC 2646 flowed text. */ - type = camel_mime_part_get_content_type (puri->part); - if (camel_content_type_is(type, "text", "plain") - && (format = camel_content_type_param(type, "format")) - && !g_ascii_strcasecmp(format, "flowed")) - flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; - - filtered_stream = camel_stream_filter_new (stream); - - if ((emfq->priv->flags & EM_FORMAT_QUOTE_KEEP_SIG) == 0) { - sig_strip = em_stripsig_filter_new (TRUE); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), sig_strip); - g_object_unref (sig_strip); - } - - html_filter = camel_mime_filter_tohtml_new (flags, rgb); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), html_filter); - g_object_unref (html_filter); - - em_format_format_text ( - EM_FORMAT (emfq), filtered_stream, - CAMEL_DATA_WRAPPER (puri->part), cancellable); - - camel_stream_flush (filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); -} - -static void -emfq_write_text_enriched (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - CamelStream *filtered_stream; - CamelMimeFilter *enriched; - guint32 flags = 0; - CamelContentType *ct; - const gchar *mime_type = NULL; - - ct = camel_mime_part_get_content_type (puri->part); - if (ct) { - mime_type = camel_content_type_simple (ct); - camel_content_type_unref (ct); - } - - if (g_strcmp0 (mime_type, "text/richtext") == 0) { - flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; - camel_stream_write_string ( - stream, "\n<!-- text/richtext -->\n", - cancellable, NULL); - } else { - camel_stream_write_string ( - stream, "\n<!-- text/enriched -->\n", - cancellable, NULL); - } - - enriched = camel_mime_filter_enriched_new (flags); - filtered_stream = camel_stream_filter_new (stream); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), enriched); - g_object_unref (enriched); - - camel_stream_write_string (stream, "<br><hr><br>", cancellable, NULL); - em_format_format_text ( - emf, filtered_stream, CAMEL_DATA_WRAPPER (puri->part), cancellable); - camel_stream_flush (filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); -} - -static void -emfq_write_text_html (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatQuotePrivate *priv; - - priv = EM_FORMAT_QUOTE_GET_PRIVATE (emf); - - camel_stream_write_string ( - stream, "\n<!-- text/html -->\n", cancellable, NULL); - - if ((priv->flags & EM_FORMAT_QUOTE_KEEP_SIG) == 0) { - CamelMimeFilter *sig_strip; - CamelStream *filtered_stream; - - filtered_stream = camel_stream_filter_new (stream); - - sig_strip = em_stripsig_filter_new (FALSE); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), sig_strip); - g_object_unref (sig_strip); - - em_format_format_text ( - emf, filtered_stream, - (CamelDataWrapper *) puri->part, cancellable); - camel_stream_flush (filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); - } else { - em_format_format_text ( - emf, stream, - (CamelDataWrapper *) puri->part, cancellable); - } -} - -/****************************************************************************/ -static EMFormatHandler type_builtin_table[] = { - { (gchar *) "text/plain", emfq_parse_text_plain, emfq_write_text_plain, }, - { (gchar *) "text/enriched", emfq_parse_text_enriched, emfq_write_text_enriched, }, - { (gchar *) "text/richtext", emfq_parse_text_enriched, emfq_write_text_enriched, }, - { (gchar *) "text/html", emfq_parse_text_html, emfq_write_text_html, }, - { (gchar *) "text/*", emfq_parse_text_plain, emfq_write_text_plain, }, - { (gchar *) "message/external-body", em_format_empty_parser, em_format_empty_writer, }, - { (gchar *) "multipart/appledouble", em_format_empty_parser, em_format_empty_writer, }, - - /* internal evolution types */ - { (gchar *) "x-evolution/evolution-rss-feed", 0, emfq_write_text_html, }, - { (gchar *) "x-evolution/message/attachment", emfq_parse_attachment, emfq_write_attachment, }, -}; - -static void -emfq_builtin_init (EMFormatQuoteClass *efhc) -{ - gint ii; - - EMFormatClass *emfc = (EMFormatClass *) efhc; - - for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++) - em_format_class_add_handler ( - emfc, &type_builtin_table[ii]); -} diff --git a/em-format/em-format-quote.h b/em-format/em-format-quote.h deleted file mode 100644 index 4ad66a5d2a..0000000000 --- a/em-format/em-format-quote.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Jeffrey Stedfast <fejj@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef EM_FORMAT_QUOTE_H -#define EM_FORMAT_QUOTE_H - -#include "em-format.h" - -/* Standard GObject macros */ -#define EM_TYPE_FORMAT_QUOTE \ - (em_format_quote_get_type ()) -#define EM_FORMAT_QUOTE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_FORMAT_QUOTE, EMFormatQuote)) -#define EM_FORMAT_QUOTE_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_FORMAT_QUOTE, EMFormatQuoteClass)) -#define EM_IS_FORMAT_QUOTE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_FORMAT_QUOTE)) -#define EM_IS_FORMAT_QUOTE_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_FORMAT_QUOTE)) -#define EM_FORMAT_QUOTE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_FORMAT_QUOTE, EMFormatQuoteClass)) - -G_BEGIN_DECLS - -typedef struct _EMFormatQuote EMFormatQuote; -typedef struct _EMFormatQuoteClass EMFormatQuoteClass; -typedef struct _EMFormatQuotePrivate EMFormatQuotePrivate; - -typedef enum { - EM_FORMAT_QUOTE_CITE = 1 << 0, - EM_FORMAT_QUOTE_HEADERS = 1 << 1, - EM_FORMAT_QUOTE_KEEP_SIG = 1 << 2 /* do not strip signature */ -} EMFormatQuoteFlags; - -struct _EMFormatQuote { - EMFormat format; - EMFormatQuotePrivate *priv; -}; - -struct _EMFormatQuoteClass { - EMFormatClass format_class; -}; - -GType em_format_quote_get_type (void); -EMFormatQuote * em_format_quote_new (CamelSession *session, - const gchar *credits, - CamelStream *stream, - EMFormatQuoteFlags flags); -void em_format_quote_write (EMFormatQuote *emfq, - CamelStream *stream, - GCancellable *cancellable); - -G_END_DECLS - -#endif /* EM_FORMAT_QUOTE_H */ diff --git a/em-format/em-format.c b/em-format/em-format.c deleted file mode 100644 index 9603dbd4cb..0000000000 --- a/em-format/em-format.c +++ /dev/null @@ -1,2676 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * Jeffrey Stedfast <fejj@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <gio/gio.h> -#include <glib/gi18n-lib.h> -#include <libsoup/soup-uri.h> - -#include "em-format.h" -#include "e-util/e-util.h" -#include "shell/e-shell.h" -#include "shell/e-shell-settings.h" - -#define d(x) - -#define EM_FORMAT_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), EM_TYPE_FORMAT, EMFormatPrivate)) - -struct _EMFormatPrivate { - GNode *current_node; - - CamelSession *session; - - CamelURL *base_url; - - gchar *charset; - gchar *default_charset; - gboolean composer; - - gint last_error; -}; - -enum { - PROP_0, - PROP_BASE_URL, - PROP_CHARSET, - PROP_COMPOSER, - PROP_DEFAULT_CHARSET, - PROP_SESSION -}; - -enum { - REDRAW_REQUESTED, - LAST_SIGNAL -}; - -gint signals[LAST_SIGNAL]; - -static gpointer parent_class; - -/* PARSERS */ -static void emf_parse_application_xpkcs7mime (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_application_mbox (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_alternative (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_appledouble (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_encrypted (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_mixed (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_signed (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_related (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_multipart_digest (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_message_deliverystatus (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_inlinepgp_signed (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_inlinepgp_encrypted (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_message (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_headers (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_post_headers (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void emf_parse_source (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); - -/* WRITERS */ -static void emf_write_text (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void emf_write_source (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void emf_write_error (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -/**************************************************************************/ - -static gboolean -is_secured (CamelMimePart *part) -{ - CamelContentType *ct = camel_mime_part_get_content_type (part); - - return (camel_content_type_is (ct, "multipart", "signed") || - camel_content_type_is (ct, "multipart", "encrypted") || - camel_content_type_is (ct, "application", "x-inlinepgp-signed") || - camel_content_type_is (ct, "application", "x-inlinepgp-encrypted") || - camel_content_type_is (ct, "application", "x-pkcs7-mime") || - camel_content_type_is (ct, "application", "pkcs7-mime")); -} - -static void -preserve_charset_in_content_type (CamelMimePart *ipart, - CamelMimePart *opart) -{ - CamelDataWrapper *data_wrapper; - CamelContentType *content_type; - const gchar *charset; - - g_return_if_fail (ipart != NULL); - g_return_if_fail (opart != NULL); - - data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart)); - content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); - - if (content_type == NULL) - return; - - charset = camel_content_type_param (content_type, "charset"); - - if (charset == NULL || *charset == '\0') - return; - - data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart)); - content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); - if (content_type) - camel_content_type_set_param (content_type, "charset", charset); - - /* update charset also on the part itself */ - data_wrapper = CAMEL_DATA_WRAPPER (opart); - content_type = camel_data_wrapper_get_mime_type_field (data_wrapper); - if (content_type) - camel_content_type_set_param (content_type, "charset", charset); -} - -static CamelMimePart * -get_related_display_part (CamelMimePart *part, - gint *out_displayid) -{ - CamelMultipart *mp; - CamelMimePart *body_part, *display_part = NULL; - CamelContentType *content_type; - const gchar *start; - gint i, nparts, displayid = 0; - - mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - - if (!CAMEL_IS_MULTIPART (mp)) - return NULL; - - nparts = camel_multipart_get_number (mp); - content_type = camel_mime_part_get_content_type (part); - start = camel_content_type_param (content_type, "start"); - if (start && strlen (start) > 2) { - gint len; - const gchar *cid; - - /* strip <>'s from CID */ - len = strlen (start) - 2; - start++; - - for (i = 0; i < nparts; i++) { - body_part = camel_multipart_get_part (mp, i); - cid = camel_mime_part_get_content_id (body_part); - - if (cid && !strncmp (cid, start, len) && strlen (cid) == len) { - display_part = body_part; - displayid = i; - break; - } - } - } else { - display_part = camel_multipart_get_part (mp, 0); - } - - if (out_displayid) - *out_displayid = displayid; - - return display_part; -} - -static gboolean -related_display_part_is_attachment (EMFormat *emf, - CamelMimePart *part) -{ - CamelMimePart *display_part; - - display_part = get_related_display_part (part, NULL); - return display_part && em_format_is_attachment (emf, display_part); -} - -/**************************************************************************/ -void -em_format_empty_parser (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - /* DO NOTHING */ -} - -#ifdef ENABLE_SMIME -static void -emf_parse_application_xpkcs7mime (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelCipherContext *context; - CamelMimePart *opart; - CamelCipherValidity *valid; - GError *local_error = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - context = camel_smime_context_new (emf->priv->session); - - opart = camel_mime_part_new (); - valid = camel_cipher_context_decrypt_sync ( - context, part, opart, cancellable, &local_error); - preserve_charset_in_content_type (part, opart); - if (valid == NULL) { - em_format_format_error ( - emf, "%s", - local_error->message ? local_error->message : - _("Could not parse S/MIME message: Unknown error")); - g_clear_error (&local_error); - } else { - EMFormatParserInfo encinfo = { - info->handler, - info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_SMIME, - valid - }; - gint len = part_id->len; - - g_string_append (part_id, ".encrypted"); - em_format_parse_part (emf, opart, part_id, &encinfo, cancellable); - g_string_truncate (part_id, len); - - /* Add a widget with details about the encryption, but only when - * the encrypted isn't itself secured, in that case it has created - * the button itself */ - if (!is_secured (opart)) { - g_string_append (part_id, ".encrypted.button"); - em_format_parse_part_as (emf, part, part_id, &encinfo, - "x-evolution/message/x-secure-button", cancellable); - g_string_truncate (part_id, len); - } - - camel_cipher_validity_free (valid); - } - - g_object_unref (opart); - g_object_unref (context); -} -#endif - -/* RFC 4155 */ -static void -emf_parse_application_mbox (EMFormat *emf, - CamelMimePart *mime_part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMimeParser *parser; - CamelStream *mem_stream; - camel_mime_parser_state_t state; - gint old_len; - gint messages; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - /* Extract messages from the application/mbox part and - * render them as a flat list of messages. */ - - /* XXX If the mbox has multiple messages, maybe render them - * as a multipart/digest so each message can be expanded - * or collapsed individually. - * - * See attachment_handler_mail_x_uid_list() for example. */ - - /* XXX This is based on em_utils_read_messages_from_stream(). - * Perhaps refactor that function to return an array of - * messages instead of assuming we want to append them - * to a folder? */ - - parser = camel_mime_parser_new (); - camel_mime_parser_scan_from (parser, TRUE); - - mem_stream = camel_stream_mem_new (); - camel_data_wrapper_decode_to_stream_sync ( - camel_medium_get_content (CAMEL_MEDIUM (mime_part)), - mem_stream, NULL, NULL); - g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL); - camel_mime_parser_init_with_stream (parser, mem_stream, NULL); - g_object_unref (mem_stream); - - old_len = part_id->len; - - /* Extract messages from the mbox. */ - messages = 0; - state = camel_mime_parser_step (parser, NULL, NULL); - - while (state == CAMEL_MIME_PARSER_STATE_FROM) { - CamelMimeMessage *message; - - message = camel_mime_message_new (); - mime_part = CAMEL_MIME_PART (message); - - if (!camel_mime_part_construct_from_parser_sync ( - mime_part, parser, NULL, NULL)) { - g_object_unref (message); - break; - } - - g_string_append_printf (part_id, ".mbox.%d", messages); - em_format_parse_part_as (emf, CAMEL_MIME_PART (message), - part_id, info, "message/rfc822", cancellable); - g_string_truncate (part_id, old_len); - - g_object_unref (message); - - /* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */ - camel_mime_parser_step (parser, NULL, NULL); - - state = camel_mime_parser_step (parser, NULL, NULL); - - messages++; - } - - g_object_unref (parser); -} - -/* RFC 1740 */ -static void -emf_parse_multipart_alternative (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMultipart *mp; - gint i, nparts, bestid = 0; - CamelMimePart *best = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - - if (!CAMEL_IS_MULTIPART (mp)) { - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - /* as per rfc, find the last part we know how to display */ - nparts = camel_multipart_get_number (mp); - for (i = 0; i < nparts; i++) { - CamelMimePart *mpart; - CamelDataWrapper *data_wrapper; - CamelContentType *type; - CamelStream *null_stream; - gchar *mime_type; - gsize content_size; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - /* is it correct to use the passed in *part here? */ - mpart = camel_multipart_get_part (mp, i); - - if (mpart == NULL) - continue; - - /* This may block even though the stream does not. - * XXX Pretty inefficient way to test if the MIME part - * is empty. Surely there's a quicker way? */ - null_stream = camel_stream_null_new (); - data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (mpart)); - camel_data_wrapper_decode_to_stream_sync ( - data_wrapper, null_stream, cancellable, NULL); - content_size = CAMEL_STREAM_NULL (null_stream)->written; - g_object_unref (null_stream); - - if (content_size == 0) - continue; - - type = camel_mime_part_get_content_type (mpart); - mime_type = camel_content_type_simple (type); - - camel_strdown (mime_type); - - if (!em_format_is_attachment (emf, mpart) && - ((camel_content_type_is (type, "multipart", "related") == 0) || - !related_display_part_is_attachment (emf, mpart)) && - (em_format_find_handler (emf, mime_type) - || (best == NULL && em_format_fallback_handler (emf, mime_type)))) { - best = mpart; - bestid = i; - } - - g_free (mime_type); - } - - if (best) { - gint len = part_id->len; - - g_string_append_printf(part_id, ".alternative.%d", bestid); - em_format_parse_part (emf, best, part_id, info, cancellable); - g_string_truncate (part_id, len); - } else - emf_parse_multipart_mixed (emf, part, part_id, info, cancellable); -} - -/* RFC 1740 */ -static void -emf_parse_multipart_appledouble (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMultipart *mp; - CamelMimePart *mime_part; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - - if (!CAMEL_IS_MULTIPART (mp)) { - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - mime_part = camel_multipart_get_part (mp, 1); - if (mime_part) { - gint len; - /* try the data fork for something useful, doubtful but who knows */ - len = part_id->len; - g_string_append_printf(part_id, ".appledouble.1"); - em_format_parse_part (emf, mime_part, part_id, info, cancellable); - g_string_truncate (part_id, len); - } else { - emf_parse_source (emf, part, part_id, info, cancellable); - } -} - -static void -emf_parse_multipart_encrypted (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelCipherContext *context; - const gchar *protocol; - CamelMimePart *opart; - CamelCipherValidity *valid; - CamelMultipartEncrypted *mpe; - GError *local_error = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part); - if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) { - em_format_format_error ( - emf, _("Could not parse MIME message. " - "Displaying as source.")); - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - /* Currently we only handle RFC2015-style PGP encryption. */ - protocol = camel_content_type_param ( - ((CamelDataWrapper *)mpe)->mime_type, "protocol"); - if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) { - em_format_format_error (emf, _("Unsupported encryption type for multipart/encrypted")); - emf_parse_multipart_mixed (emf, part, part_id, info, cancellable); - return; - } - - context = camel_gpg_context_new (emf->priv->session); - opart = camel_mime_part_new (); - valid = camel_cipher_context_decrypt_sync ( - context, part, opart, cancellable, &local_error); - preserve_charset_in_content_type (part, opart); - if (valid == NULL) { - em_format_format_error ( - emf, local_error->message ? - _("Could not parse PGP/MIME message") : - _("Could not parse PGP/MIME message: Unknown error")); - if (local_error->message != NULL) - em_format_format_error ( - emf, "%s", local_error->message); - g_clear_error (&local_error); - emf_parse_multipart_mixed (emf, part, part_id, info, cancellable); - } else { - gint len = part_id->len; - - EMFormatParserInfo encinfo = { - info->handler, - info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP, - }; - - if (info->validity) - camel_cipher_validity_envelope (valid, info->validity); - - encinfo.validity = valid; - - g_string_append (part_id, ".encrypted"); - em_format_parse_part (emf, opart, part_id, &encinfo, cancellable); - g_string_truncate (part_id, len); - - /* Add a widget with details about the encryption, but only when - * the encrypted isn't itself secured, in that case it has created - * the button itself */ - if (!is_secured (opart)) { - g_string_append (part_id, ".encrypted.button"); - em_format_parse_part_as (emf, part, part_id, &encinfo, - "x-evolution/message/x-secure-button", cancellable); - g_string_truncate (part_id, len); - } - - camel_cipher_validity_free (valid); - } - - /* TODO: Make sure when we finalize this part, it is zero'd out */ - g_object_unref (opart); - g_object_unref (context); -} - -/* RFC 2046 */ -static void -emf_parse_multipart_mixed (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMultipart *mp; - gint i, nparts, len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - - if (!CAMEL_IS_MULTIPART (mp)) { - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - len = part_id->len; - nparts = camel_multipart_get_number (mp); - for (i = 0; i < nparts; i++) { - CamelMimePart *subpart; - - subpart = camel_multipart_get_part (mp, i); - - g_string_append_printf(part_id, ".mixed.%d", i); - em_format_parse_part (emf, subpart, part_id, info, cancellable); - g_string_truncate (part_id, len); - } -} - -static void -emf_parse_multipart_signed (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMimePart *cpart; - CamelMultipartSigned *mps; - CamelCipherContext *cipher = NULL; - guint32 validity_type; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mps = (CamelMultipartSigned *) camel_medium_get_content ((CamelMedium *) part); - if (!CAMEL_IS_MULTIPART_SIGNED (mps) - || (cpart = camel_multipart_get_part ((CamelMultipart *) mps, - CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) { - em_format_format_error ( - emf, _("Could not parse MIME message. " - "Displaying as source.")); - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - /* FIXME: Should be done via a plugin interface */ - /* FIXME: duplicated in em-format-html-display.c */ - if (mps->protocol) { -#ifdef ENABLE_SMIME - if (g_ascii_strcasecmp("application/x-pkcs7-signature", mps->protocol) == 0 - || g_ascii_strcasecmp("application/pkcs7-signature", mps->protocol) == 0) { - cipher = camel_smime_context_new (emf->priv->session); - validity_type = EM_FORMAT_VALIDITY_FOUND_SMIME; - } else -#endif - if (g_ascii_strcasecmp("application/pgp-signature", mps->protocol) == 0) { - cipher = camel_gpg_context_new (emf->priv->session); - validity_type = EM_FORMAT_VALIDITY_FOUND_PGP; - } - } - - if (cipher == NULL) { - em_format_format_error(emf, _("Unsupported signature format")); - emf_parse_multipart_mixed (emf, part, part_id, info, cancellable); - } else { - CamelCipherValidity *valid; - GError *local_error = NULL; - - valid = camel_cipher_context_verify_sync ( - cipher, part, cancellable, &local_error); - if (valid == NULL) { - em_format_format_error ( - emf, local_error->message ? - _("Error verifying signature") : - _("Unknown error verifying signature")); - if (local_error->message != NULL) - em_format_format_error ( - emf, "%s", - local_error->message); - g_clear_error (&local_error); - emf_parse_multipart_mixed (emf, part, part_id,info, cancellable); - } else { - gint i, nparts, len = part_id->len; - gboolean secured; - - EMFormatParserInfo signinfo = { - info->handler, - info->validity_type | validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED, - }; - - if (info->validity) - camel_cipher_validity_envelope (valid, info->validity); - signinfo.validity = valid; - - nparts = camel_multipart_get_number (CAMEL_MULTIPART (mps)); - secured = FALSE; - for (i = 0; i < nparts; i++) { - CamelMimePart *subpart; - subpart = camel_multipart_get_part (CAMEL_MULTIPART (mps), i); - - g_string_append_printf(part_id, ".signed.%d", i); - em_format_parse_part (emf, subpart, part_id, &signinfo, cancellable); - g_string_truncate (part_id, len); - - if (!secured) - secured = is_secured (subpart); - } - - /* Add a widget with details about the encryption, but only when - * the encrypted isn't itself secured, in that case it has created - * the button itself */ - if (!secured) { - g_string_append (part_id, ".signed.button"); - em_format_parse_part_as (emf, part, part_id, &signinfo, - "x-evolution/message/x-secure-button", cancellable); - g_string_truncate (part_id, len); - } - - camel_cipher_validity_free (valid); - } - } - - g_object_unref (cipher); -} - -/* RFC 2046 */ -static void -emf_parse_multipart_digest (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMultipart *mp; - gint i, nparts, len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - - if (!CAMEL_IS_MULTIPART (mp)) { - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - len = part_id->len; - nparts = camel_multipart_get_number (mp); - for (i = 0; i < nparts; i++) { - CamelMimePart *subpart; - CamelContentType *ct; - gchar *cts; - const EMFormatHandler *handler; - - subpart = camel_multipart_get_part (mp, i); - - if (!subpart) - continue; - - g_string_append_printf(part_id, ".digest.%d", i); - - ct = camel_mime_part_get_content_type (subpart); - /* According to RFC this shouldn't happen, but who knows... */ - if (ct && !camel_content_type_is (ct, "message", "rfc822")) { - cts = camel_content_type_simple (ct); - em_format_parse_part_as (emf, part, part_id, info, cts, cancellable); - g_free (cts); - g_string_truncate (part_id, len); - continue; - } - - handler = em_format_find_handler (emf, "message/rfc822"); - if (handler && handler->parse_func) - handler->parse_func (emf, subpart, part_id, info, cancellable); - - g_string_truncate (part_id, len); - } -} - -/* RFC 2387 */ -static void -emf_parse_multipart_related (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelMultipart *mp; - CamelMimePart *body_part, *display_part = NULL; - gint i, nparts, partidlen, displayid = 0; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); - - if (!CAMEL_IS_MULTIPART (mp)) { - emf_parse_source (emf, part, part_id, info, cancellable); - return; - } - - display_part = get_related_display_part (part, &displayid); - - if (display_part == NULL) { - emf_parse_multipart_mixed ( - emf, part, part_id, info, cancellable); - return; - } - - /* The to-be-displayed part goes first */ - partidlen = part_id->len; - g_string_append_printf(part_id, ".related.%d", displayid); - em_format_parse_part (emf, display_part, part_id, info, cancellable); - g_string_truncate (part_id, partidlen); - - /* Process the related parts */ - nparts = camel_multipart_get_number (mp); - for (i = 0; i < nparts; i++) { - body_part = camel_multipart_get_part (mp, i); - if (body_part != display_part) { - g_string_append_printf(part_id, ".related.%d", i); - em_format_parse_part (emf, body_part, part_id, info, cancellable); - g_string_truncate (part_id, partidlen); - } - } -} - -static void -emf_parse_message_deliverystatus (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - len = part_id->len; - g_string_append (part_id, ".deliverystatus"); - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = emf_write_text; - puri->mime_type = g_strdup ("text/html"); - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - - g_string_truncate (part_id, len); - - em_format_add_puri (emf, puri); -} - -static void -emf_parse_inlinepgp_signed (EMFormat *emf, - CamelMimePart *ipart, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelStream *filtered_stream; - CamelMimeFilterPgp *pgp_filter; - CamelContentType *content_type; - CamelCipherContext *cipher; - CamelCipherValidity *valid; - CamelDataWrapper *dw; - CamelMimePart *opart; - CamelStream *ostream; - gchar *type; - gint len; - GError *local_error = NULL; - EMFormatParserInfo signinfo; - GByteArray *ba; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - if (!ipart) { - em_format_format_error(emf, _("Unknown error verifying signature")); - return; - } - - cipher = camel_gpg_context_new (emf->priv->session); - /* Verify the signature of the message */ - valid = camel_cipher_context_verify_sync ( - cipher, ipart, cancellable, &local_error); - if (!valid) { - em_format_format_error ( - emf, local_error->message ? - _("Error verifying signature") : - _("Unknown error verifying signature")); - if (local_error->message) - em_format_format_error ( - emf, "%s", local_error->message); - emf_parse_source (emf, ipart, part_id, info, cancellable); - /* XXX I think this will loop: - * em_format_part_as(emf, stream, part, "text/plain"); */ - g_clear_error (&local_error); - g_object_unref (cipher); - return; - } - - /* Setup output stream */ - ostream = camel_stream_mem_new (); - filtered_stream = camel_stream_filter_new (ostream); - - /* Add PGP header / footer filter */ - pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new (); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), - CAMEL_MIME_FILTER (pgp_filter)); - g_object_unref (pgp_filter); - - /* Pass through the filters that have been setup */ - dw = camel_medium_get_content ((CamelMedium *) ipart); - camel_data_wrapper_decode_to_stream_sync ( - dw, (CamelStream *) filtered_stream, cancellable, NULL); - camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); - - /* Create a new text/plain MIME part containing the signed - * content preserving the original part's Content-Type params. */ - content_type = camel_mime_part_get_content_type (ipart); - type = camel_content_type_format (content_type); - content_type = camel_content_type_decode (type); - g_free (type); - - g_free (content_type->type); - content_type->type = g_strdup ("text"); - g_free (content_type->subtype); - content_type->subtype = g_strdup ("plain"); - type = camel_content_type_format (content_type); - camel_content_type_unref (content_type); - - ba = camel_stream_mem_get_byte_array ((CamelStreamMem *) ostream); - opart = camel_mime_part_new (); - camel_mime_part_set_content (opart, (gchar *) ba->data, ba->len, type); - g_free (type); - - if (info->validity) - camel_cipher_validity_envelope (valid, info->validity); - - /* Pass it off to the real formatter */ - len = part_id->len; - g_string_append (part_id, ".inlinepgp_signed"); - signinfo.handler = info->handler; - signinfo.validity_type = info->validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED | EM_FORMAT_VALIDITY_FOUND_PGP; - signinfo.validity = valid; - em_format_parse_part (emf, opart, part_id, &signinfo, cancellable); - g_string_truncate (part_id, len); - - /* Add a widget with details about the encryption, but only when - * the encrypted isn't itself secured, in that case it has created - * the button itself */ - if (!is_secured (opart)) { - g_string_append (part_id, ".inlinepgp_signed.button"); - em_format_parse_part_as (emf, opart, part_id, &signinfo, - "x-evolution/message/x-secure-button", cancellable); - g_string_truncate (part_id, len); - } - - /* Clean Up */ - camel_cipher_validity_free (valid); - g_object_unref (dw); - g_object_unref (opart); - g_object_unref (ostream); - g_object_unref (cipher); -} - -static void -emf_parse_inlinepgp_encrypted (EMFormat *emf, - CamelMimePart *ipart, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelCipherContext *cipher; - CamelCipherValidity *valid; - CamelMimePart *opart; - CamelDataWrapper *dw; - gchar *mime_type; - gint len; - GError *local_error = NULL; - EMFormatParserInfo encinfo; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - cipher = camel_gpg_context_new (emf->priv->session); - opart = camel_mime_part_new (); - - /* Decrypt the message */ - valid = camel_cipher_context_decrypt_sync ( - cipher, ipart, opart, cancellable, &local_error); - - if (!valid) { - em_format_format_error ( - emf, _("Could not parse PGP message: ")); - if (local_error->message != NULL) - em_format_format_error ( - emf, "%s", local_error->message); - else - em_format_format_error ( - emf, _("Unknown error")); - emf_parse_source (emf, ipart, part_id, info, cancellable); - /* XXX I think this will loop: - * em_format_part_as(emf, stream, part, "text/plain"); */ - - g_clear_error (&local_error); - g_object_unref (cipher); - g_object_unref (opart); - return; - } - - dw = camel_medium_get_content ((CamelMedium *) opart); - mime_type = camel_data_wrapper_get_mime_type (dw); - - /* this ensures to show the 'opart' as inlined, if possible */ - if (mime_type && g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) { - const gchar *snoop = em_format_snoop_type (opart); - - if (snoop) - camel_data_wrapper_set_mime_type (dw, snoop); - } - - preserve_charset_in_content_type (ipart, opart); - g_free (mime_type); - - if (info->validity) - camel_cipher_validity_envelope (valid, info->validity); - - /* Pass it off to the real formatter */ - len = part_id->len; - g_string_append (part_id, ".inlinepgp_encrypted"); - encinfo.handler = info->handler; - encinfo.validity_type = info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP; - encinfo.validity = valid; - em_format_parse_part (emf, opart, part_id, &encinfo, cancellable); - g_string_truncate (part_id, len); - - /* Add a widget with details about the encryption, but only when - * the encrypted isn't itself secured, in that case it has created - * the button itself */ - if (!is_secured (opart)) { - g_string_append (part_id, ".inlinepgp_encrypted.button"); - em_format_parse_part_as (emf, opart, part_id, &encinfo, - "x-evolution/message/x-secure-button", cancellable); - g_string_truncate (part_id, len); - } - - /* Clean Up */ - camel_cipher_validity_free (valid); - g_object_unref (opart); - g_object_unref (cipher); -} - -static void -emf_parse_message (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - /* Headers */ - info->force_handler = TRUE; - em_format_parse_part_as (emf, part, part_id, info, - "x-evolution/message/headers", cancellable); - - /* Anything that comes between headers and message body */ - info->force_handler = TRUE; - em_format_parse_part_as (emf, part, part_id, info, - "x-evolution/message/post-headers", cancellable); - - /* Begin parsing the message */ - info->force_handler = FALSE; - em_format_parse_part (emf, part, part_id, info, cancellable); -} - -static void -emf_parse_headers (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - len = part_id->len; - g_string_append (part_id, ".headers"); - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = info->handler->write_func; - puri->mime_type = g_strdup ("text/html"); - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); -} - -static void -emf_parse_post_headers (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - /* Add attachment bar */ - info->force_handler = TRUE; - em_format_parse_part_as (emf, part, part_id, info, - "x-evolution/message/attachment-bar", cancellable); -} - -static void -emf_parse_source (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - len = part_id->len; - g_string_append (part_id, ".source"); - - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = info->handler->write_func; - puri->mime_type = g_strdup ("text/html"); - g_string_truncate (part_id, len); - - em_format_add_puri (emf, puri); -} - -/**************************************************************************/ - -void -em_format_empty_writer (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - /* DO NOTHING */ -} - -static void -emf_write_error (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) puri->part, - stream, cancellable, NULL); -} - -static void -emf_write_text (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - CamelContentType *ct; - - ct = camel_mime_part_get_content_type (puri->part); - if (!camel_content_type_is (ct, "text", "plain")) { - camel_stream_write_string (stream, _("Cannot proccess non-text mime/part"), - cancellable, NULL); - return; - } - - camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) puri->part, - stream, cancellable, NULL); -} - -static void -emf_write_source (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GByteArray *ba; - gchar *data; - - g_return_if_fail (EM_IS_FORMAT (emf)); - - ba = camel_data_wrapper_get_byte_array ((CamelDataWrapper *) puri->part); - - data = g_strndup ((gchar *) ba->data, ba->len); - camel_stream_write_string (stream, data, cancellable, NULL); - g_free (data); -} - -/**************************************************************************/ - -static gboolean -emf_is_inline (EMFormat *emf, - const gchar *part_id, - CamelMimePart *mime_part, - const EMFormatHandler *handle) -{ - //EMFormatCache *emfc; - const gchar *disposition; - - if (handle == NULL) - return FALSE; - - /* Some types need to override the disposition. - * e.g. application/x-pkcs7-mime */ - if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION) - return TRUE; - - disposition = camel_mime_part_get_disposition (mime_part); - if (disposition != NULL) - return g_ascii_strcasecmp (disposition, "inline") == 0; - - /* Otherwise, use the default for this handler type. */ - return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0; -} - -/**************************************************************************/ - -static EMFormatHandler type_handlers[] = { -#ifdef ENABLE_SMIME - { (gchar *) "application/x-pkcs7-mime", emf_parse_application_xpkcs7mime, 0, EM_FORMAT_HANDLER_INLINE_DISPOSITION }, -#endif - { (gchar *) "application/mbox", emf_parse_application_mbox, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "multipart/alternative", emf_parse_multipart_alternative, }, - { (gchar *) "multipart/appledouble", emf_parse_multipart_appledouble, }, - { (gchar *) "multipart/encrypted", emf_parse_multipart_encrypted, }, - { (gchar *) "multipart/mixed", emf_parse_multipart_mixed, }, - { (gchar *) "multipart/signed", emf_parse_multipart_signed, }, - { (gchar *) "multipart/related", emf_parse_multipart_related, }, - { (gchar *) "multipart/digest", emf_parse_multipart_digest, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "multipart/*", emf_parse_multipart_mixed, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "message/deliverystatus", emf_parse_message_deliverystatus, 0, }, - - /* Ignore PGP signature part */ - { (gchar *) "application/pgp-signature", em_format_empty_parser, }, - - /* Insert brokenly-named parts here */ -#ifdef ENABLE_SMIME - { (gchar *) "application/pkcs7-mime", emf_parse_application_xpkcs7mime, 0, EM_FORMAT_HANDLER_INLINE_DISPOSITION }, -#endif - - /* internal types */ - { (gchar *) "application/x-inlinepgp-signed", emf_parse_inlinepgp_signed, }, - { (gchar *) "application/x-inlinepgp-encrypted", emf_parse_inlinepgp_encrypted, }, - { (gchar *) "x-evolution/message", emf_parse_message, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "x-evolution/message/headers", emf_parse_headers, }, - { (gchar *) "x-evolution/message/post-headers", emf_parse_post_headers, }, - { (gchar *) "x-evolution/message/source", emf_parse_source, emf_write_source }, -}; - -/* note: also copied in em-mailer-prefs.c */ -static const struct { - const gchar *name; - guint32 flags; -} default_headers[] = { - { N_("From"), EM_FORMAT_HEADER_BOLD }, - { N_("Reply-To"), EM_FORMAT_HEADER_BOLD }, - { N_("To"), EM_FORMAT_HEADER_BOLD }, - { N_("Cc"), EM_FORMAT_HEADER_BOLD }, - { N_("Bcc"), EM_FORMAT_HEADER_BOLD }, - { N_("Subject"), EM_FORMAT_HEADER_BOLD }, - { N_("Date"), EM_FORMAT_HEADER_BOLD }, - { N_("Newsgroups"), EM_FORMAT_HEADER_BOLD }, - { N_("Face"), 0 }, -}; - -static void -em_format_set_session (EMFormat *emf, - CamelSession *session) -{ - g_return_if_fail (CAMEL_IS_SESSION (session)); - g_return_if_fail (emf->priv->session == NULL); - - emf->priv->session = g_object_ref (session); -} - -static void -em_format_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_BASE_URL: - em_format_set_base_url ( - EM_FORMAT (object), - g_value_get_object (value)); - return; - - case PROP_CHARSET: - em_format_set_charset ( - EM_FORMAT (object), - g_value_get_string (value)); - return; - - case PROP_COMPOSER: - em_format_set_composer ( - EM_FORMAT (object), - g_value_get_boolean (value)); - return; - - case PROP_DEFAULT_CHARSET: - em_format_set_default_charset ( - EM_FORMAT (object), - g_value_get_string (value)); - return; - - case PROP_SESSION: - em_format_set_session ( - EM_FORMAT (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - -} - -static void -em_format_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_BASE_URL: - g_value_set_object ( - value, em_format_get_base_url ( - EM_FORMAT (object))); - return; - - case PROP_CHARSET: - g_value_set_string ( - value, em_format_get_charset ( - EM_FORMAT (object))); - return; - - case PROP_COMPOSER: - g_value_set_boolean ( - value, em_format_get_composer ( - EM_FORMAT (object))); - return; - - case PROP_DEFAULT_CHARSET: - g_value_set_string ( - value, em_format_get_default_charset ( - EM_FORMAT (object))); - return; - - case PROP_SESSION: - g_value_set_object ( - value, em_format_get_session ( - EM_FORMAT (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -em_format_finalize (GObject *object) -{ - EMFormat *emf = EM_FORMAT (object); - - if (emf->message_uid) { - g_free (emf->message_uid); - emf->message_uid = NULL; - } - - if (emf->uri_base) { - g_free (emf->uri_base); - emf->uri_base = NULL; - } - - if (emf->message) { - g_object_unref (emf->message); - emf->message = NULL; - } - - if (emf->folder) { - g_object_unref (emf->folder); - emf->folder = NULL; - } - - if (emf->mail_part_table) { - /* This will destroy all the EMFormatPURI objects stored - * inside!!!! */ - g_hash_table_destroy (emf->mail_part_table); - emf->mail_part_table = NULL; - } - - if (emf->mail_part_list) { - g_list_free (emf->mail_part_list); - emf->mail_part_list = NULL; - } - - if (emf->priv->base_url) { - camel_url_free (emf->priv->base_url); - emf->priv->base_url = NULL; - } - - if (emf->priv->session) { - g_object_unref (emf->priv->session); - emf->priv->session = NULL; - } - - if (emf->priv->charset) { - g_free (emf->priv->charset); - emf->priv->charset = NULL; - } - - em_format_clear_headers (emf); - - /* Chain up to parent's finalize() method */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -em_format_base_init (EMFormatClass *class) -{ - gint i; - - class->type_handlers = g_hash_table_new (g_str_hash, g_str_equal); - - for (i = 0; i < G_N_ELEMENTS (type_handlers); i++) { - g_hash_table_insert (class->type_handlers, - type_handlers[i].mime_type, - &type_handlers[i]); - } -} - -static void -em_format_class_init (EMFormatClass *class) -{ - GObjectClass *object_class; - - parent_class = g_type_class_peek_parent (class); - - g_type_class_add_private (class, sizeof (EMFormatPrivate)); - - class->is_inline = emf_is_inline; - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = em_format_set_property; - object_class->get_property = em_format_get_property; - object_class->finalize = em_format_finalize; - - g_object_class_install_property ( - object_class, - PROP_BASE_URL, - g_param_spec_pointer ( - "base-url", - NULL, - NULL, - 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_COMPOSER, - g_param_spec_boolean ( - "composer", - NULL, - NULL, - FALSE, - 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)); - - g_object_class_install_property ( - object_class, - PROP_SESSION, - g_param_spec_object ( - "session", - "Session", - "A CamelSession", - CAMEL_TYPE_SESSION, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); - - signals[REDRAW_REQUESTED] = g_signal_new ( - "redraw-requested", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EMFormatClass, redraw_requested), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE,0); -} - -static void -mail_part_table_item_free (gpointer data) -{ - GList *iter = data; - EMFormatPURI *puri = iter->data; - - em_format_puri_free (puri); -} - -static void -em_format_init (EMFormat *emf) -{ - emf->priv = EM_FORMAT_GET_PRIVATE (emf); - - emf->message = NULL; - emf->folder = NULL; - emf->mail_part_list = NULL; - emf->mail_part_table = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify) mail_part_table_item_free); - /* No need to free the key, because it's owned and free'd by the PURI */ - - emf->priv->last_error = 0; - - em_format_default_headers (emf); -} - -EMFormat * -em_format_new (void) -{ - return g_object_new (EM_TYPE_FORMAT, NULL); -} - -GType -em_format_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatClass), - (GBaseInitFunc) em_format_base_init, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) em_format_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormat), - 0, /* n_preallocs */ - (GInstanceInitFunc) em_format_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - G_TYPE_OBJECT, "EMFormat", &type_info, 0); - } - - return type; -} - -void -em_format_set_charset (EMFormat *emf, - const gchar *charset) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - - if (emf->priv->charset) - g_free (emf->priv->charset); - - emf->priv->charset = g_strdup (charset); - - g_object_notify (G_OBJECT (emf), "charset"); -} - -const gchar * -em_format_get_charset (EMFormat *emf) -{ - g_return_val_if_fail (EM_IS_FORMAT (emf), NULL); - - return emf->priv->charset; -} - -void -em_format_set_default_charset (EMFormat *emf, - const gchar *charset) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - - if (emf->priv->default_charset) - g_free (emf->priv->default_charset); - - emf->priv->default_charset = g_strdup (charset); - - g_object_notify (G_OBJECT (emf), "default-charset"); -} - -const gchar * -em_format_get_default_charset (EMFormat *emf) -{ - g_return_val_if_fail (EM_IS_FORMAT (emf), NULL); - - return emf->priv->default_charset; -} - -void -em_format_set_composer (EMFormat *emf, - gboolean composer) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - - if (emf->priv->composer && composer) - return; - - emf->priv->composer = composer; - - g_object_notify (G_OBJECT (emf), "composer"); -} - -gboolean -em_format_get_composer (EMFormat *emf) -{ - g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE); - - return emf->priv->composer; -} - -CamelSession * -em_format_get_session (EMFormat *emf) -{ - g_return_val_if_fail (EM_IS_FORMAT (emf), NULL); - - return emf->priv->session; -} - -void -em_format_set_base_url (EMFormat *emf, - CamelURL *url) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (url); - - if (emf->priv->base_url) - camel_url_free (emf->priv->base_url); - - emf->priv->base_url = camel_url_copy (url); - - g_object_notify (G_OBJECT (emf), "base-url"); -} - -void -em_format_set_base_url_string (EMFormat *emf, - const gchar *url_string) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (url_string && *url_string); - - if (emf->priv->base_url) - camel_url_free (emf->priv->base_url); - - emf->priv->base_url = camel_url_new (url_string, NULL); - - g_object_notify (G_OBJECT (emf), "base-url"); -} - -CamelURL * -em_format_get_base_url (EMFormat *emf) -{ - g_return_val_if_fail (EM_IS_FORMAT (emf), NULL); - - return emf->priv->base_url; -} - -/** - * em_format_clear_headers: - * @emf: - * - * Clear the list of headers to be displayed. This will force all headers to - * be shown. - **/ -void -em_format_clear_headers (EMFormat *emf) -{ - EMFormatHeader *eh; - - g_return_if_fail (EM_IS_FORMAT (emf)); - - while ((eh = g_queue_pop_head (&emf->header_list)) != NULL) { - em_format_header_free (eh); - } - -} - -void -em_format_default_headers (EMFormat *emf) -{ - gint ii; - - g_return_if_fail (EM_IS_FORMAT (emf)); - - /* Set the default headers */ - em_format_clear_headers (emf); - for (ii = 0; ii < G_N_ELEMENTS (default_headers); ii++) - em_format_add_header ( - emf, default_headers[ii].name, NULL, - default_headers[ii].flags); -} - -/** - * em_format_add_header: - * @emf: - * @name: The name of the header, as it will appear during output. - * @value: Value of the header. Can be NULL. - * @flags: EM_FORMAT_HEAD_* defines 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 -em_format_add_header (EMFormat *emf, - const gchar *name, - const gchar *value, - guint32 flags) -{ - EMFormatHeader *h; - - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (name && *name); - - h = em_format_header_new (name, value); - h->flags = flags; - g_queue_push_tail (&emf->header_list, h); -} - -void -em_format_add_header_struct (EMFormat *emf, - EMFormatHeader *header) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (header && header->name); - - em_format_add_header (emf, header->name, header->value, header->flags); -} - -void -em_format_remove_header (EMFormat *emf, - const gchar *name, - const gchar *value) -{ - GList *iter = NULL; - - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (name && *name); - - iter = g_queue_peek_head_link (&emf->header_list); - while (iter) { - EMFormatHeader *header = iter->data; - - if (!header->value || !*header->value) { - GList *next = iter->next; - if (g_strcmp0 (name, header->name) == 0) - g_queue_delete_link (&emf->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) { - em_format_header_free (iter->data); - g_queue_delete_link (&emf->header_list, iter); - } -} - -void -em_format_remove_header_struct (EMFormat *emf, - const EMFormatHeader *header) -{ - g_return_if_fail (header); - - em_format_remove_header (emf, header->name, header->value); -} - -void -em_format_add_puri (EMFormat *emf, - EMFormatPURI *puri) -{ - GList *item; - - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (puri != NULL); - - emf->mail_part_list = g_list_append (emf->mail_part_list, puri); - item = g_list_last (emf->mail_part_list); - - g_hash_table_insert (emf->mail_part_table, - puri->uri, item); - - d(printf("Added PURI %s\n", puri->uri)); -} - -EMFormatPURI * -em_format_find_puri (EMFormat *emf, - const gchar *id) -{ - GList *list_iter; - - /* First handle CIDs... */ - if (g_str_has_prefix (id, "CID:") || g_str_has_prefix (id, "cid:")) { - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init (&iter, emf->mail_part_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - EMFormatPURI *puri = ((GList *) value)->data; - if (g_strcmp0 (puri->cid, id) == 0) - return puri; - } - - return NULL; - } - - list_iter = g_hash_table_lookup (emf->mail_part_table, id); - if (list_iter) - return list_iter->data; - - return NULL; -} - -void -em_format_class_add_handler (EMFormatClass *emfc, - EMFormatHandler *handler) -{ - EMFormatHandler *old_handler; - - g_return_if_fail (EM_IS_FORMAT_CLASS (emfc)); - g_return_if_fail (handler); - - old_handler = g_hash_table_lookup ( - emfc->type_handlers, handler->mime_type); - - handler->old = old_handler; - - /* If parse_func or write_func of the new handler is not set, - * use function from the old handler (if it exists). - * This way we can assign a new write_func for to an existing - * parse_func */ - if (old_handler && handler->parse_func == NULL) { - handler->parse_func = old_handler->parse_func; - } - - if (old_handler && handler->write_func == NULL) { - handler->write_func = old_handler->write_func; - } - - g_hash_table_insert (emfc->type_handlers, - handler->mime_type, handler); -} - -void -em_format_class_remove_handler (EMFormatClass *emfc, - EMFormatHandler *handler) -{ - g_return_if_fail (EM_IS_FORMAT_CLASS (emfc)); - g_return_if_fail (handler); - - g_hash_table_remove (emfc->type_handlers, handler->mime_type); -} - -const EMFormatHandler * -em_format_find_handler (EMFormat *emf, - const gchar *mime_type) -{ - EMFormatClass *emfc; - gchar *s; - const EMFormatHandler *handler; - - g_return_val_if_fail (EM_IS_FORMAT (emf), NULL); - g_return_val_if_fail (mime_type && *mime_type, NULL); - - emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf); - - s = g_ascii_strdown (mime_type, -1); - - handler = g_hash_table_lookup ( - emfc->type_handlers, s); - - g_free (s); - - return handler; -} - -/** - * em_format_fallback_handler: - * @emf: - * @mime_type: - * - * Try to find a format handler based on the major type of the @mime_type. - * - * The subtype is replaced with "*" and a lookup performed. - * - * Return value: - **/ -const EMFormatHandler * -em_format_fallback_handler (EMFormat *emf, - const gchar *mime_type) -{ - gchar *mime, *s; - - s = strchr (mime_type, '/'); - if (s == NULL) - mime = (gchar *) mime_type; - else { - gsize len = (s - mime_type) + 1; - - mime = g_alloca (len + 2); - strncpy (mime, mime_type, len); - strcpy(mime+len, "*"); - } - - return em_format_find_handler (emf, mime); -} - -void -em_format_parse (EMFormat *emf, - CamelMimeMessage *message, - CamelFolder *folder, - GCancellable *cancellable) -{ - GString *part_id; - EMFormatPURI *puri; - EMFormatParserInfo info = { 0 }; - - g_return_if_fail (EM_IS_FORMAT (emf)); - - if (g_cancellable_is_cancelled (cancellable)) - return; - - if (message) { - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - - if (emf->message) - g_object_unref (emf->message); - emf->message = g_object_ref (message); - } - - if (folder) { - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - - if (emf->folder) - g_object_unref (emf->folder); - emf->folder = g_object_ref (folder); - } - - /* Before the actual parsing starts, - * let child classes prepare themselves. */ - if (EM_FORMAT_GET_CLASS (emf)->preparse) - EM_FORMAT_GET_CLASS (emf)->preparse (emf); - - part_id = g_string_new (".message"); - - /* Create a special PURI with entire message */ - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), - (CamelMimePart *) emf->message, part_id->str); - puri->mime_type = g_strdup ("text/html"); - em_format_add_puri (emf, puri); - - info.force_handler = TRUE; - em_format_parse_part_as (emf, CAMEL_MIME_PART (emf->message), part_id, &info, - "x-evolution/message", cancellable); - - g_string_free (part_id, TRUE); -} - -void -em_format_write (EMFormat *emf, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatClass *emf_class; - - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (CAMEL_IS_STREAM (stream)); - - emf_class = EM_FORMAT_GET_CLASS (emf); - if (emf_class->write) - emf_class->write (emf, stream, info, cancellable); -} - -static void -emf_start_async_parser (GSimpleAsyncResult *result, - GObject *object, - GCancellable *cancellable) -{ - em_format_parse (EM_FORMAT (object), NULL, NULL, cancellable); -} - -void -em_format_parse_async (EMFormat *emf, - CamelMimeMessage *message, - CamelFolder *folder, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - g_return_if_fail (EM_IS_FORMAT (emf)); - - if (g_cancellable_is_cancelled (cancellable)) - return; - - if (message) { - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - - if (emf->message) - g_object_unref (emf->message); - - emf->message = g_object_ref (message); - - } - - if (folder) { - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - - if (emf->folder) - g_object_unref (emf->folder); - - emf->folder = g_object_ref (folder); - - } - - simple = g_simple_async_result_new ( - G_OBJECT (emf), callback, - user_data, em_format_parse_async); - - g_simple_async_result_set_check_cancellable (simple, cancellable); - - g_simple_async_result_run_in_thread ( - simple, emf_start_async_parser, - G_PRIORITY_DEFAULT, cancellable); - - g_object_unref (simple); -} - -void -em_format_parse_part_as (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - const gchar *mime_type, - GCancellable *cancellable) -{ - const EMFormatHandler *handler; - const CamelContentDisposition *disposition; - EMFormatParserInfo ninfo = { - .handler = 0, - .validity_type = info ? info->validity_type : 0, - .validity = info ? info->validity : 0, - .force_handler = 0 - }; - - /* Let everything that claims to be an attachment or inlined - * part to be parsed as an attachment. The parser will decide - * how to display it. */ - disposition = camel_mime_part_get_content_disposition (part); - if (!info->force_handler && disposition && - (g_strcmp0 (disposition->disposition, "attachment") == 0)) { - ninfo.is_attachment = TRUE; - handler = em_format_find_handler (emf, "x-evolution/message/attachment"); - ninfo.handler = handler; - - if (handler && handler->parse_func) - handler->parse_func (emf, part, part_id, &ninfo, cancellable); - - return; - } - - handler = em_format_find_handler (emf, mime_type); - if (handler && handler->parse_func) { - ninfo.handler = handler; - handler->parse_func (emf, part, part_id, &ninfo, cancellable); - } else { - handler = em_format_find_handler (emf, "x-evolution/message/attachment"); - ninfo.handler = handler; - - /* When this fails, something is probably very wrong...*/ - if (handler && handler->parse_func) - handler->parse_func (emf, part, part_id, &ninfo, cancellable); - } -} - -void -em_format_parse_part (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelContentType *ct; - gchar *mime_type; - - ct = camel_mime_part_get_content_type (part); - if (ct) { - mime_type = camel_content_type_simple (ct); - } else { - mime_type = (gchar *) "text/plain"; - } - - em_format_parse_part_as (emf, part, part_id, info, mime_type, cancellable); - - if (ct) - g_free (mime_type); -} - -gboolean -em_format_is_inline (EMFormat *emf, - const gchar *part_id, - CamelMimePart *part, - const EMFormatHandler *handler) -{ - EMFormatClass *class; - - g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE); - g_return_val_if_fail (part_id && *part_id, FALSE); - g_return_val_if_fail (CAMEL_IS_MIME_PART (part), FALSE); - g_return_val_if_fail (handler, FALSE); - - class = EM_FORMAT_GET_CLASS (emf); - g_return_val_if_fail (class->is_inline != NULL, FALSE); - - return class->is_inline (emf, part_id, part, handler); - -} - -void -em_format_format_error (EMFormat *emf, - const gchar *format, - ...) -{ - EMFormatPURI *puri; - CamelMimePart *part; - const EMFormatHandler *handler; - gchar *errmsg; - gchar *uri; - va_list ap; - - g_return_if_fail (EM_IS_FORMAT (emf)); - g_return_if_fail (format != NULL); - - va_start (ap, format); - errmsg = g_strdup_vprintf (format, ap); - - part = camel_mime_part_new (); - camel_mime_part_set_content (part, errmsg, strlen (errmsg), "text/plain"); - g_free (errmsg); - va_end (ap); - - handler = em_format_find_handler (emf, "x-evolution/error"); - - emf->priv->last_error++; - uri = g_strdup_printf (".error.%d", emf->priv->last_error); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, uri); - puri->mime_type = g_strdup ("text/html"); - if (handler && handler->write_func) - puri->write_func = handler->write_func; - else - puri->write_func = emf_write_error; - - em_format_add_puri (emf, puri); - - g_free (uri); - g_object_unref (part); -} - -/** - * em_format_format_text: - * @emf: - * @stream: Where to write the converted text - * @part: Part whose container is to be formatted - * @cancellable: optional #GCancellable object, or %NULL - * - * Decode/output a part's content to @stream. - **/ -void -em_format_format_text (EMFormat *emf, - CamelStream *stream, - CamelDataWrapper *dw, - GCancellable *cancellable) -{ - CamelStream *filter_stream; - CamelMimeFilter *filter; - const gchar *charset = NULL; - CamelMimeFilterWindows *windows = NULL; - CamelStream *mem_stream = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - if (emf->priv->charset) { - charset = emf->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 = emf->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); -} - -/** - * em_format_describe_part: - * @part: - * @mimetype: - * - * Generate a simple textual description of a part, @mime_type represents - * the content. - * - * Return value: - **/ -gchar * -em_format_describe_part (CamelMimePart *part, - const gchar *mime_type) -{ - GString *stext; - const gchar *filename, *description; - gchar *content_type, *desc; - - stext = g_string_new(""); - content_type = g_content_type_from_mime_type (mime_type); - desc = g_content_type_get_description ( - content_type != NULL ? content_type : mime_type); - g_free (content_type); - g_string_append_printf ( - stext, _("%s attachment"), desc ? desc : mime_type); - g_free (desc); - - filename = camel_mime_part_get_filename (part); - description = camel_mime_part_get_description (part); - - if (!filename || !*filename) { - CamelDataWrapper *content; - - content = camel_medium_get_content (CAMEL_MEDIUM (part)); - - if (CAMEL_IS_MIME_MESSAGE (content)) - filename = camel_mime_message_get_subject ( - CAMEL_MIME_MESSAGE (content)); - } - - if (filename != NULL && *filename != '\0') { - gchar *basename = g_path_get_basename (filename); - g_string_append_printf (stext, " (%s)", basename); - g_free (basename); - } - - if (description != NULL && *description != '\0' && - g_strcmp0 (filename, description) != 0) - g_string_append_printf (stext, ", \"%s\"", description); - - return g_string_free (stext, FALSE); -} - -/** - * em_format_is_attachment: - * @emf: - * @part: Part to check. - * - * Returns true if the part is an attachment. - * - * A part is not considered an attachment if it is a - * multipart, or a text part with no filename. It is used - * to determine if an attachment header should be displayed for - * the part. - * - * Content-Disposition is not checked. - * - * Return value: TRUE/FALSE - **/ -gint -em_format_is_attachment (EMFormat *emf, - CamelMimePart *part) -{ - /*CamelContentType *ct = camel_mime_part_get_content_type(part);*/ - CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part); - - if (!dw) - return 0; - - d(printf("checking is attachment %s/%s\n", dw->mime_type->type, dw->mime_type->subtype)); - return !(camel_content_type_is (dw->mime_type, "multipart", "*") - || camel_content_type_is ( - dw->mime_type, "application", "x-pkcs7-mime") - || camel_content_type_is ( - dw->mime_type, "application", "pkcs7-mime") - || camel_content_type_is ( - dw->mime_type, "application", "x-inlinepgp-signed") - || camel_content_type_is ( - dw->mime_type, "application", "x-inlinepgp-encrypted") - || camel_content_type_is ( - dw->mime_type, "x-evolution", "evolution-rss-feed") - || camel_content_type_is (dw->mime_type, "text", "calendar") - || camel_content_type_is (dw->mime_type, "text", "x-calendar") - || (camel_content_type_is (dw->mime_type, "text", "*") - && camel_mime_part_get_filename (part) == NULL)); -} - -/** - * em_format_snoop_type: - * @part: - * - * Tries to snoop the mime type of a part. - * - * Return value: NULL if unknown (more likely application/octet-stream). - **/ -const gchar * -em_format_snoop_type (CamelMimePart *part) -{ - /* cache is here only to be able still return const gchar * */ - static GHashTable *types_cache = NULL; - - const gchar *filename; - gchar *name_type = NULL, *magic_type = NULL, *res, *tmp; - CamelDataWrapper *dw; - - filename = camel_mime_part_get_filename (part); - if (filename != NULL) - name_type = e_util_guess_mime_type (filename, FALSE); - - dw = camel_medium_get_content ((CamelMedium *) part); - if (!camel_data_wrapper_is_offline (dw)) { - GByteArray *byte_array; - CamelStream *stream; - - byte_array = g_byte_array_new (); - stream = camel_stream_mem_new_with_byte_array (byte_array); - - if (camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL) > 0) { - gchar *content_type; - - content_type = g_content_type_guess ( - filename, byte_array->data, - byte_array->len, NULL); - - if (content_type != NULL) - magic_type = g_content_type_get_mime_type (content_type); - - g_free (content_type); - } - - g_object_unref (stream); - } - - /* If gvfs doesn't recognize the data by magic, but it - * contains English words, it will call it text/plain. If the - * filename-based check came up with something different, use - * that instead and if it returns "application/octet-stream" - * try to do better with the filename check. - */ - - if (magic_type) { - if (name_type - && (!strcmp(magic_type, "text/plain") - || !strcmp(magic_type, "application/octet-stream"))) - res = name_type; - else - res = magic_type; - } else - res = name_type; - - if (res != name_type) - g_free (name_type); - - if (res != magic_type) - g_free (magic_type); - - if (!types_cache) - types_cache = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) NULL); - - if (res) { - tmp = g_hash_table_lookup (types_cache, res); - if (tmp) { - g_free (res); - res = tmp; - } else { - g_hash_table_insert (types_cache, res, res); - } - } - - return res; - - /* We used to load parts to check their type, we don't anymore, - * see bug #211778 for some discussion */ -} - -/** - * Construct a URI for message. - * - * The URI can contain multiple query parameters. The list of parameters must be - * NULL-terminated. Each query must contain name, GType of value and value. - * - * @param folder Folder wit the message - * @param message_uid ID of message within the \p folder - * @param first_param_name Name of first query parameter followed by GType of it's value and value. - */ -gchar * -em_format_build_mail_uri (CamelFolder *folder, - const gchar *message_uid, - const gchar *first_param_name, - ...) -{ - CamelStore *store; - gchar *uri, *tmp; - va_list ap; - const gchar *name; - const gchar *service_uid, *folder_name; - gchar separator; - - g_return_val_if_fail (message_uid && *message_uid, NULL); - - if (!folder) { - folder_name = "generic"; - service_uid = "generic"; - } else { - tmp = (gchar *) camel_folder_get_full_name (folder); - folder_name = (const gchar *) soup_uri_encode (tmp, NULL); - store = camel_folder_get_parent_store (folder); - if (store) - service_uid = camel_service_get_uid (CAMEL_SERVICE (store)); - else - service_uid = "generic"; - } - - tmp = g_strdup_printf ("mail://%s/%s/%s", - service_uid, - folder_name, - message_uid); - - if (folder) { - g_free ((gchar *) folder_name); - } - - va_start (ap, first_param_name); - name = first_param_name; - separator = '?'; - while (name) { - gchar *tmp2; - gint type = va_arg (ap, gint); - switch (type) { - case G_TYPE_INT: - case G_TYPE_BOOLEAN: { - gint val = va_arg (ap, gint); - tmp2 = g_strdup_printf ("%s%c%s=%d", tmp, - separator, name, val); - break; - } - case G_TYPE_FLOAT: - case G_TYPE_DOUBLE: { - gdouble val = va_arg (ap, double); - tmp2 = g_strdup_printf ("%s%c%s=%f", tmp, - separator, name, val); - break; - } - case G_TYPE_STRING: { - gchar *val = va_arg (ap, gchar *); - gchar *escaped = soup_uri_encode (val, NULL); - tmp2 = g_strdup_printf ("%s%c%s=%s", tmp, - separator, name, escaped); - g_free (escaped); - break; - } - default: - g_warning ("Invalid param type %s", g_type_name (type)); - return NULL; - } - - g_free (tmp); - tmp = tmp2; - - if (separator == '?') - separator = '&'; - - name = va_arg (ap, gchar *); - } - va_end (ap); - - uri = tmp; - if (uri == NULL) - return NULL; - - /* For some reason, webkit won't accept URL with username, but - * without password (mail://store@host/folder/mail), so we - * will replace the '@' symbol by '/' to get URL like - * mail://store/host/folder/mail which is OK - */ - while ((tmp = strchr (uri, '@')) != NULL) { - tmp[0] = '/'; - } - - return uri; -} - -void -em_format_redraw (EMFormat *emf) -{ - g_return_if_fail (EM_IS_FORMAT (emf)); - - g_signal_emit (emf, signals[REDRAW_REQUESTED], 0); -} - -/**************************************************************************/ -EMFormatPURI * -em_format_puri_new (EMFormat *emf, - gsize puri_size, - CamelMimePart *part, - const gchar *uri) -{ - EMFormatPURI *puri; - - g_return_val_if_fail (EM_IS_FORMAT (emf), NULL); - g_return_val_if_fail (puri_size >= sizeof (EMFormatPURI), NULL); - - puri = (EMFormatPURI *) g_malloc0 (puri_size); - puri->emf = emf; - - if (part) - puri->part = g_object_ref (part); - - if (uri) - puri->uri = g_strdup (uri); - - return puri; -} - -void -em_format_puri_free (EMFormatPURI *puri) -{ - g_return_if_fail (puri); - - if (puri->part) - g_object_unref (puri->part); - - if (puri->uri) - g_free (puri->uri); - - if (puri->cid) - g_free (puri->cid); - - if (puri->mime_type) - g_free (puri->mime_type); - - if (puri->validity) - camel_cipher_validity_free (puri->validity); - - if (puri->validity_parent) - camel_cipher_validity_free (puri->validity_parent); - - if (puri->free) - puri->free (puri); - - g_free (puri); -} - -void -em_format_puri_write (EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - g_return_if_fail (puri); - g_return_if_fail (CAMEL_IS_STREAM (stream)); - - if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) { - const EMFormatHandler *handler; - handler = em_format_find_handler (puri->emf, "x-evolution/message/source"); - handler->write_func (puri->emf, puri, stream, info, cancellable); - return; - } - - if (puri->write_func) { - puri->write_func (puri->emf, puri, stream, info, cancellable); - } else { - const EMFormatHandler *handler; - const gchar *mime_type; - - if (puri->mime_type) { - mime_type = puri->mime_type; - } else { - mime_type = (gchar *) "plain/text"; - } - - handler = em_format_find_handler (puri->emf, mime_type); - if (handler && handler->write_func) { - handler->write_func (puri->emf, - puri, stream, info, cancellable); - } - } -} - -EMFormatHeader * -em_format_header_new (const gchar *name, - const gchar *value) -{ - EMFormatHeader *header; - - g_return_val_if_fail (name && *name, NULL); - - header = g_new0 (EMFormatHeader, 1); - header->name = g_strdup (name); - if (value && *value) - header->value = g_strdup (value); - - return header; -} - -void -em_format_header_free (EMFormatHeader *header) -{ - g_return_if_fail (header != NULL); - - if (header->name) { - g_free (header->name); - header->name = NULL; - } - - if (header->value) { - g_free (header->value); - header->value = NULL; - } - - g_free (header); -} diff --git a/em-format/em-format.h b/em-format/em-format.h deleted file mode 100644 index 5b97cb799c..0000000000 --- a/em-format/em-format.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef EM_FORMAT_H -#define EM_FORMAT_H - -#include <camel/camel.h> -#include <gtk/gtk.h> -#include <webkit/webkitdom.h> - -/* Standard GObject macros */ -#define EM_TYPE_FORMAT \ - (em_format_get_type ()) -#define EM_FORMAT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_FORMAT, EMFormat)) -#define EM_FORMAT_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_FORMAT, EMFormatClass)) -#define EM_IS_FORMAT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_FORMAT)) -#define EM_IS_FORMAT_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_FORMAT)) -#define EM_FORMAT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_FORMAT, EMFormatClass)) - -G_BEGIN_DECLS - -#define EM_FORMAT_HEADER_BOLD (1<<0) -#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */ - -#define EM_FORMAT_VALIDITY_FOUND_PGP (1<<0) -#define EM_FORMAT_VALIDITY_FOUND_SMIME (1<<1) -#define EM_FORMAT_VALIDITY_FOUND_SIGNED (1<<2) -#define EM_FORMAT_VALIDITY_FOUND_ENCRYPTED (1<<3) - -typedef struct _EMFormat EMFormat; -typedef struct _EMFormatClass EMFormatClass; -typedef struct _EMFormatPrivate EMFormatPrivate; - -typedef struct _EMFormatPURI EMFormatPURI; -typedef struct _EMFormatHeader EMFormatHeader; -typedef struct _EMFormatHandler EMFormatHandler; -typedef struct _EMFormatParserInfo EMFormatParserInfo; -typedef struct _EMFormatWriterInfo EMFormatWriterInfo; - -typedef void (*EMFormatParseFunc) (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable); -typedef void (*EMFormatWriteFunc) (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable); -typedef GtkWidget * (*EMFormatWidgetFunc) (EMFormat *emf, - EMFormatPURI *puri, - GCancellable *cancellable); -typedef void (*EMailDisplayBindFunc) (WebKitDOMElement *root, - EMFormatPURI *puri); - -typedef enum { - EM_FORMAT_HANDLER_INLINE = 1 << 0, - EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1 << 1, - EM_FORMAT_HANDLER_COMPOUND_TYPE = 1 << 2 -} EMFormatHandlerFlags; - -typedef enum { - EM_FORMAT_WRITE_MODE_NORMAL= 1 << 0, - EM_FORMAT_WRITE_MODE_ALL_HEADERS = 1 << 1, - EM_FORMAT_WRITE_MODE_SOURCE = 1 << 2, - EM_FORMAT_WRITE_MODE_PRINTING = 1 << 3, - EM_FORMAT_WRITE_MODE_RAW = 1 << 4 -} EMFormatWriteMode; - -struct _EMFormatHandler { - gchar *mime_type; - EMFormatParseFunc parse_func; - EMFormatWriteFunc write_func; - EMFormatHandlerFlags flags; - - EMFormatHandler *old; -}; - -/** - * Use this struct to pass additional information between - * EMFormatParseFunc's. - * Much cleaner then setting public property of EMFormat. - */ -struct _EMFormatParserInfo { - const EMFormatHandler *handler; - - /* EM_FORMAT_VALIDITY_* flags */ - guint32 validity_type; - CamelCipherValidity *validity; - - gint is_attachment : 1; - gint force_handler: 1; -}; - -struct _EMFormatWriterInfo { - EMFormatWriteMode mode; - gboolean headers_collapsable; - gboolean headers_collapsed; -}; - -struct _EMFormatHeader { - guint32 flags; /* E_FORMAT_HEADER_ * */ - gchar *name; - gchar *value; -}; - -#define EM_FORMAT_HEADER_BOLD (1<<0) -#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */ - -struct _EMFormatPURI { - CamelMimePart *part; - - EMFormat *emf; - EMFormatWriteFunc write_func; - EMFormatWidgetFunc widget_func; - - /** - * Called by #EMailDisplay whenever document/frame is reloaded. - * Modules and plugins can create bindings to events of DOM - * objects they created. - */ - EMailDisplayBindFunc bind_func; - - gchar *uri; - gchar *cid; - gchar *mime_type; - - /* EM_FORMAT_VALIDITY_* flags */ - guint32 validity_type; - CamelCipherValidity *validity; - CamelCipherValidity *validity_parent; - - gboolean is_attachment; - - void (*free)(EMFormatPURI *puri); /* optional callback for freeing user-fields */ -}; - -struct _EMFormat { - GObject parent; - EMFormatPrivate *priv; - - CamelMimeMessage *message; - CamelFolder *folder; - gchar *message_uid; - gchar *uri_base; - - /* Defines order in which parts should be displayed */ - GList *mail_part_list; - /* For quick search for parts by their URI/ID */ - GHashTable *mail_part_table; - - /* If empty, then all. */ - GQueue header_list; -}; - -struct _EMFormatClass { - GObjectClass parent_class; - - GHashTable *type_handlers; - - gboolean (*is_inline) (EMFormat *emf, - const gchar *part_id, - CamelMimePart *part, - const EMFormatHandler *handler); - - /* Write the entire message to stream */ - void (*write) (EMFormat *emf, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable); - - void (*preparse) (EMFormat *emf); - - /* signals */ - void (*redraw_requested) (EMFormat *emf); - -}; - -GType em_format_get_type (void); -EMFormat * em_format_new (void); -void em_format_set_charset (EMFormat *emf, - const gchar *charset); -const gchar * em_format_get_charset (EMFormat *emf); -const gchar * em_format_get_default_charset (EMFormat *emf); -void em_format_set_default_charset (EMFormat *emf, - const gchar *charset); -gboolean em_format_get_composer (EMFormat *emf); -void em_format_set_composer (EMFormat *emf, - gboolean composer); -CamelSession * em_format_get_session (EMFormat *emf); -void em_format_set_base_url (EMFormat *emf, - CamelURL *url); -void em_format_set_base_url_string (EMFormat *emf, - const gchar *url_string); -CamelURL * em_format_get_base_url (EMFormat *emf); -void em_format_clear_headers (EMFormat *emf); -void em_format_default_headers (EMFormat *emf); -void em_format_add_header (EMFormat *emf, - const gchar *name, - const gchar *value, - guint32 flags); -void em_format_add_header_struct (EMFormat *emf, - EMFormatHeader *header); -void em_format_remove_header (EMFormat *emf, - const gchar *name, - const gchar *value); -void em_format_remove_header_struct (EMFormat *emf, - const EMFormatHeader *header); -void em_format_add_puri (EMFormat *emf, - EMFormatPURI *puri); -EMFormatPURI * em_format_find_puri (EMFormat *emf, - const gchar *id); -void em_format_class_add_handler (EMFormatClass *emfc, - EMFormatHandler *handler); -void em_format_class_remove_handler (EMFormatClass *emfc, - EMFormatHandler *handler); -const EMFormatHandler * - em_format_find_handler (EMFormat *emf, - const gchar *mime_type); -const EMFormatHandler * - em_format_fallback_handler (EMFormat *emf, - const gchar *mime_type); -void em_format_parse (EMFormat *emf, - CamelMimeMessage *message, - CamelFolder *folder, - GCancellable *cancellable); -void em_format_write (EMFormat *emf, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable); -void em_format_parse_async (EMFormat *emf, - CamelMimeMessage *message, - CamelFolder *folder, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -void em_format_parse_part (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable); -void em_format_parse_part_as (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - const gchar *mime_type, - GCancellable *cancellable); -gboolean em_format_is_inline (EMFormat *emf, - const gchar *part_id, - CamelMimePart *part, - const EMFormatHandler *handler); -gchar * em_format_get_error_id (EMFormat *emf); -void em_format_format_error (EMFormat *emf, - const gchar *format, - ...) G_GNUC_PRINTF (2, 3); -void em_format_format_text (EMFormat *emf, - CamelStream *stream, - CamelDataWrapper *dw, - GCancellable *cancellable); -gchar * em_format_describe_part (CamelMimePart *part, - const gchar *mime_type); -gint em_format_is_attachment (EMFormat *emf, - CamelMimePart *part); -const gchar * em_format_snoop_type (CamelMimePart *part); -gchar * em_format_build_mail_uri (CamelFolder *folder, - const gchar *message_uid, - const gchar *part_uid, - ...) G_GNUC_NULL_TERMINATED; - -/* EMFormatParseFunc that does nothing. Use it to disable - * parsing of a specific mime type parts */ -void em_format_empty_parser (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable); - -/* EMFormatWriteFunc that does nothing. Use it to disable - * writing of a specific mime type parts */ -void em_format_empty_writer (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable); - -void em_format_redraw (EMFormat *emf); - -EMFormatPURI * em_format_puri_new (EMFormat *emf, - gsize puri_size, - CamelMimePart *part, - const gchar *uri); -void em_format_puri_free (EMFormatPURI *puri); -void em_format_puri_write (EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable); -EMFormatHeader * - em_format_header_new (const gchar *name, - const gchar *value); -void em_format_header_free (EMFormatHeader *header); - -#endif /* EM_FORMAT_H */ - diff --git a/mail/Makefile.am b/mail/Makefile.am index da2c0eb692..7fa3dbab9c 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -44,7 +44,6 @@ mailinclude_HEADERS = \ e-mail-account-manager.h \ e-mail-account-store.h \ e-mail-account-tree-view.h \ - e-mail-attachment-bar.h \ e-mail-autoconfig.h \ e-mail-backend.h \ e-mail-browser.h \ @@ -101,10 +100,6 @@ mailinclude_HEADERS = \ em-folder-tree-model.h \ em-folder-tree.h \ em-folder-utils.h \ - em-format-hook.h \ - em-format-html-display.h \ - em-format-html-print.h \ - em-format-html.h \ em-search-context.h \ em-subscription-editor.h \ em-utils.h \ @@ -128,7 +123,6 @@ libevolution_mail_la_SOURCES = \ e-mail-account-manager.c \ e-mail-account-store.c \ e-mail-account-tree-view.c \ - e-mail-attachment-bar.c \ e-mail-autoconfig.c \ e-mail-backend.c \ e-mail-browser.c \ @@ -185,10 +179,6 @@ libevolution_mail_la_SOURCES = \ em-folder-tree-model.c \ em-folder-tree.c \ em-folder-utils.c \ - em-format-hook.c \ - em-format-html-display.c \ - em-format-html-print.c \ - em-format-html.c \ em-search-context.c \ em-subscription-editor.c \ em-utils.c \ diff --git a/mail/e-http-request.c b/mail/e-http-request.c index a9415b248a..95a97e62f6 100644 --- a/mail/e-http-request.c +++ b/mail/e-http-request.c @@ -26,10 +26,13 @@ #include <webkit/webkit.h> #include <e-util/e-util.h> +#include <mail/em-utils.h> +#include <libemail-engine/e-mail-enumtypes.h> #include <string.h> -#include "em-format-html.h" +#include <em-format/e-mail-formatter.h> +#include <shell/e-shell.h> #define d(x) @@ -41,7 +44,7 @@ struct _EHTTPRequestPrivate { gchar *content_type; gint content_length; - EMFormatHTML *efh; + EMailPartList *parts_list; }; G_DEFINE_TYPE (EHTTPRequest, e_http_request, SOUP_TYPE_REQUEST) @@ -208,9 +211,12 @@ handle_http_request (GSimpleAsyncResult *res, EHTTPRequest *request = E_HTTP_REQUEST (object); SoupURI *soup_uri; gchar *evo_uri, *uri; + gchar *mail_uri; GInputStream *stream; gboolean force_load_images = FALSE; + EMailImageLoadingPolicy image_policy; gchar *uri_md5; + EMailFormatter *formatter; const gchar *user_cache_dir; CamelDataCache *cache; @@ -222,9 +228,15 @@ handle_http_request (GSimpleAsyncResult *res, return; } + formatter = e_mail_formatter_new (); + /* Remove the __evo-mail query */ soup_uri = soup_request_get_uri (SOUP_REQUEST (request)); query = soup_form_decode (soup_uri->query); + mail_uri = g_hash_table_lookup (query, "__evo-mail"); + if (mail_uri) + mail_uri = g_strdup (mail_uri); + g_hash_table_remove (query, "__evo-mail"); /* Remove __evo-load-images if present (and in such case set @@ -306,7 +318,36 @@ handle_http_request (GSimpleAsyncResult *res, /* Item not found in cache, but image loading policy allows us to fetch * it from the interwebs */ - if (force_load_images || em_format_html_can_load_images (request->priv->efh)) { + image_policy = e_mail_formatter_get_image_loading_policy (formatter); + if (!force_load_images && mail_uri && + (image_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES)) { + SoupSession *session; + GHashTable *parts; + gchar *decoded_uri; + EMailPartList *part_list; + + session = webkit_get_default_session (); + parts = g_object_get_data (G_OBJECT (session), "mails"); + decoded_uri = soup_uri_decode (mail_uri); + + part_list = g_hash_table_lookup (parts, decoded_uri); + if (part_list) { + EShell *shell; + ESourceRegistry *registry; + CamelInternetAddress *addr; + + shell = e_shell_get_default (); + registry = e_shell_get_registry (shell); + addr = camel_mime_message_get_from (part_list->message); + force_load_images = em_utils_in_addressbook ( + registry, addr, FALSE); + } + + g_free (decoded_uri); + } + + if ((image_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) || + force_load_images) { SoupRequester *requester; SoupRequest *http_request; @@ -393,6 +434,8 @@ handle_http_request (GSimpleAsyncResult *res, cleanup: g_free (uri); g_free (uri_md5); + if (mail_uri) + g_free (mail_uri); } static void @@ -405,9 +448,9 @@ http_request_finalize (GObject *object) request->priv->content_type = NULL; } - if (request->priv->efh) { - g_object_unref (request->priv->efh); - request->priv->efh = NULL; + if (request->priv->parts_list) { + g_object_unref (request->priv->parts_list); + request->priv->parts_list = NULL; } G_OBJECT_CLASS (e_http_request_parent_class)->finalize (object); @@ -434,7 +477,7 @@ http_request_send_async (SoupRequest *request, SoupURI *uri; const gchar *enc; SoupSession *session; - GHashTable *formatters, *query; + GHashTable *mails, *query; ehr = E_HTTP_REQUEST (request); uri = soup_request_get_uri (request); @@ -452,16 +495,16 @@ http_request_send_async (SoupRequest *request, mail_uri = soup_uri_decode (enc); session = webkit_get_default_session (); - formatters = g_object_get_data (G_OBJECT (session), "formatters"); - g_return_if_fail (formatters != NULL); + mails = g_object_get_data (G_OBJECT (session), "mails"); + g_return_if_fail (mails != NULL); - ehr->priv->efh = g_hash_table_lookup (formatters, mail_uri); + ehr->priv->parts_list = g_hash_table_lookup (mails, mail_uri); g_free (mail_uri); - g_return_if_fail (ehr->priv->efh); + g_return_if_fail (ehr->priv->parts_list); /* Make sure the formatter lives until we are finished here */ - g_object_ref (ehr->priv->efh); + g_object_ref (ehr->priv->parts_list); simple = g_simple_async_result_new ( G_OBJECT (request), callback, diff --git a/mail/e-mail-attachment-bar.h b/mail/e-mail-attachment-bar.h deleted file mode 100644 index b83d9733e0..0000000000 --- a/mail/e-mail-attachment-bar.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * e-mail-attachment-bar.h - * - * 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/> - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef E_MAIL_ATTACHMENT_BAR_H -#define E_MAIL_ATTACHMENT_BAR_H - -#include <gtk/gtk.h> -#include <misc/e-attachment-view.h> - -/* Standard GObject macros */ -#define E_TYPE_MAIL_ATTACHMENT_BAR \ - (e_mail_attachment_bar_get_type ()) -#define E_MAIL_ATTACHMENT_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBar)) -#define E_MAIL_ATTACHMENT_BAR_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarClass)) -#define E_IS_MAIL_ATTACHMENT_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_MAIL_ATTACHMENT_BAR)) -#define E_IS_MAIL_ATTACHMENT_BAR_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_MAIL_ATTACHMENT_BAR)) -#define E_MAIL_ATTACHMENT_BAR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarClass)) - -G_BEGIN_DECLS - -typedef struct _EMailAttachmentBar EMailAttachmentBar; -typedef struct _EMailAttachmentBarClass EMailAttachmentBarClass; -typedef struct _EMailAttachmentBarPrivate EMailAttachmentBarPrivate; - -struct _EMailAttachmentBar { - GtkVBox parent; - EMailAttachmentBarPrivate *priv; -}; - -struct _EMailAttachmentBarClass { - GtkVBoxClass parent_class; -}; - -GType e_mail_attachment_bar_get_type (void); -GtkWidget * e_mail_attachment_bar_new (EAttachmentStore *store); -gint e_mail_attachment_bar_get_active_view - (EMailAttachmentBar *bar); -void e_mail_attachment_bar_set_active_view - (EMailAttachmentBar *bar, - gint active_view); -gboolean e_mail_attachment_bar_get_expanded - (EMailAttachmentBar *bar); -void e_mail_attachment_bar_set_expanded - (EMailAttachmentBar *bar, - gboolean expanded); -EAttachmentStore * - e_mail_attachment_bar_get_store (EMailAttachmentBar *bar); - -G_END_DECLS - -#endif /* E_MAIL_ATTACHMENT_BAR_H */ diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c index 806980d602..eda9d6cbc3 100644 --- a/mail/e-mail-browser.c +++ b/mail/e-mail-browser.c @@ -39,7 +39,6 @@ #include "mail/e-mail-reader.h" #include "mail/e-mail-reader-utils.h" #include "mail/em-folder-tree-model.h" -#include "mail/em-format-html-display.h" #include "mail/message-list.h" #define E_MAIL_BROWSER_GET_PRIVATE(obj) \ @@ -56,7 +55,7 @@ struct _EMailBrowserPrivate { GtkUIManager *ui_manager; EFocusTracker *focus_tracker; - EMFormatWriteMode mode; + EMailFormatterMode mode; GtkWidget *main_menu; GtkWidget *main_toolbar; @@ -915,7 +914,7 @@ e_mail_browser_class_init (EMailBrowserClass *class) NULL, 0, G_MAXINT, - EM_FORMAT_WRITE_MODE_NORMAL, + E_MAIL_FORMATTER_MODE_NORMAL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } @@ -951,7 +950,7 @@ GtkWidget * e_mail_browser_new (EMailBackend *backend, CamelFolder *folder, const gchar *msg_uid, - EMFormatWriteMode mode) + EMailFormatterMode mode) { GtkWidget *widget; diff --git a/mail/e-mail-browser.h b/mail/e-mail-browser.h index 88f8174a9d..849daebce4 100644 --- a/mail/e-mail-browser.h +++ b/mail/e-mail-browser.h @@ -64,7 +64,7 @@ GType e_mail_browser_get_type (void); GtkWidget * e_mail_browser_new (EMailBackend *backend, CamelFolder *folder, const gchar *message_uid, - EMFormatWriteMode mode); + EMailFormatterMode mode); void e_mail_browser_close (EMailBrowser *browser); gboolean e_mail_browser_get_show_deleted (EMailBrowser *browser); void e_mail_browser_set_show_deleted (EMailBrowser *browser, diff --git a/mail/e-mail-config-lookup-page.c b/mail/e-mail-config-lookup-page.c index 0154a053b1..d200c3cba0 100644 --- a/mail/e-mail-config-lookup-page.c +++ b/mail/e-mail-config-lookup-page.c @@ -29,7 +29,7 @@ G_DEFINE_TYPE_WITH_CODE ( EMailConfigLookupPage, e_mail_config_lookup_page, GTK_TYPE_BOX, - G_IMPLEMENT_INTERFACE( + G_IMPLEMENT_INTERFACE ( E_TYPE_MAIL_CONFIG_PAGE, e_mail_config_lookup_page_interface_init)) diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c index ca807a6899..4f72e843f6 100644 --- a/mail/e-mail-display.c +++ b/mail/e-mail-display.c @@ -28,6 +28,12 @@ #include <glib/gi18n.h> #include <gdk/gdk.h> +#include <em-format/e-mail-part-utils.h> +#include <em-format/e-mail-formatter-extension.h> +#include <em-format/e-mail-extension-registry.h> +#include <em-format/e-mail-part-attachment.h> +#include <em-format/e-mail-formatter-print.h> + #include "e-util/e-marshal.h" #include "e-util/e-util.h" #include "e-util/e-plugin-ui.h" @@ -37,8 +43,7 @@ #include "mail/em-utils.h" #include "mail/e-mail-request.h" #include "mail/e-http-request.h" -#include "mail/em-format-html-display.h" -#include "mail/e-mail-attachment-bar.h" +#include "widgets/misc/e-attachment-bar.h" #include "widgets/misc/e-attachment-button.h" #include <camel/camel.h> @@ -54,9 +59,10 @@ G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW) ((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate)) struct _EMailDisplayPrivate { - EMFormatHTML *formatter; + EMailPartList *part_list; + EMailFormatterMode mode; + EMailFormatter *formatter; - EMFormatWriteMode mode; gboolean headers_collapsable; gboolean headers_collapsed; @@ -66,12 +72,14 @@ struct _EMailDisplayPrivate { gint force_image_load: 1; GSettings *settings; + + GHashTable *widgets; }; enum { PROP_0, - PROP_FORMATTER, PROP_MODE, + PROP_PART_LIST, PROP_HEADERS_COLLAPSABLE, PROP_HEADERS_COLLAPSED, }; @@ -197,45 +205,15 @@ formatter_image_loading_policy_changed_cb (GObject *object, static void mail_display_update_formatter_colors (EMailDisplay *display) { - EMFormatHTMLColorType type; - EMFormatHTML *formatter; - GdkColor *color; - GtkStateType state; GtkStyle *style; - - state = gtk_widget_get_state (GTK_WIDGET (display)); - formatter = display->priv->formatter; + GtkStateType state; if (!display->priv->formatter) return; style = gtk_widget_get_style (GTK_WIDGET (display)); - if (style == NULL) - return; - - g_object_freeze_notify (G_OBJECT (formatter)); - - color = &style->bg[state]; - type = EM_FORMAT_HTML_COLOR_BODY; - em_format_html_set_color (formatter, type, color); - - color = &style->base[GTK_STATE_NORMAL]; - type = EM_FORMAT_HTML_COLOR_CONTENT; - em_format_html_set_color (formatter, type, color); - - color = &style->dark[state]; - type = EM_FORMAT_HTML_COLOR_FRAME; - em_format_html_set_color (formatter, type, color); - - color = &style->fg[state]; - type = EM_FORMAT_HTML_COLOR_HEADER; - em_format_html_set_color (formatter, type, color); - - color = &style->text[state]; - type = EM_FORMAT_HTML_COLOR_TEXT; - em_format_html_set_color (formatter, type, color); - - g_object_thaw_notify (G_OBJECT (formatter)); + state = gtk_widget_get_state (GTK_WIDGET (display)); + e_mail_formatter_set_style (display->priv->formatter, style, state); } static void @@ -245,10 +223,10 @@ mail_display_set_property (GObject *object, GParamSpec *pspec) { switch (property_id) { - case PROP_FORMATTER: - e_mail_display_set_formatter ( + case PROP_PART_LIST: + e_mail_display_set_parts_list ( E_MAIL_DISPLAY (object), - g_value_get_object (value)); + g_value_get_pointer (value)); return; case PROP_MODE: e_mail_display_set_mode ( @@ -277,9 +255,9 @@ mail_display_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { - case PROP_FORMATTER: - g_value_set_object ( - value, e_mail_display_get_formatter ( + case PROP_PART_LIST: + g_value_set_pointer ( + value, e_mail_display_get_parts_list ( E_MAIL_DISPLAY (object))); return; case PROP_MODE: @@ -309,9 +287,9 @@ mail_display_dispose (GObject *object) priv = E_MAIL_DISPLAY_GET_PRIVATE (object); - if (priv->formatter) { - g_object_unref (priv->formatter); - priv->formatter = NULL; + if (priv->part_list) { + g_object_unref (priv->part_list); + priv->part_list = NULL; } if (priv->settings) { @@ -319,6 +297,11 @@ mail_display_dispose (GObject *object) priv->settings = NULL; } + if (priv->widgets) { + g_hash_table_destroy (priv->widgets); + priv->widgets = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -353,18 +336,14 @@ mail_display_process_mailto (EWebView *web_view, g_return_val_if_fail (mailto_uri != NULL, FALSE); if (g_ascii_strncasecmp (mailto_uri, "mailto:", 7) == 0) { - EMFormat *format; - CamelFolder *folder = NULL; EShell *shell; + EMailPartList *part_list; - format = (EMFormat *) E_MAIL_DISPLAY (web_view)->priv->formatter; - - if (format != NULL && format->folder != NULL) - folder = format->folder; + part_list = E_MAIL_DISPLAY (web_view)->priv->part_list; shell = e_shell_get_default (); em_utils_compose_new_message_with_mailto ( - shell, mailto_uri, folder); + shell, mailto_uri, part_list->folder); return TRUE; } @@ -380,7 +359,6 @@ mail_display_link_clicked (WebKitWebView *web_view, WebKitWebPolicyDecision *policy_decision, gpointer user_data) { - EMailDisplay *display; const gchar *uri = webkit_network_request_get_uri (request); if (g_str_has_prefix (uri, "file://")) { @@ -397,10 +375,6 @@ mail_display_link_clicked (WebKitWebView *web_view, g_free (filename); } - display = E_MAIL_DISPLAY (web_view); - if (display->priv->formatter == NULL) - return FALSE; - if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) { /* do nothing, function handled the "mailto:" uri already */ webkit_web_policy_decision_ignore (policy_decision); @@ -457,10 +431,11 @@ mail_display_resource_requested (WebKitWebView *web_view, gpointer user_data) { EMailDisplay *display = E_MAIL_DISPLAY (web_view); - EMFormat *formatter = EM_FORMAT (display->priv->formatter); + EMailPartList *part_list; const gchar *uri = webkit_network_request_get_uri (request); - if (!formatter) { + part_list = display->priv->part_list; + if (!part_list) { return; } @@ -468,10 +443,10 @@ mail_display_resource_requested (WebKitWebView *web_view, if (g_str_has_prefix (uri, "cid:")) { /* Always write raw content of CID object */ - gchar *new_uri = em_format_build_mail_uri (formatter->folder, - formatter->message_uid, + gchar *new_uri = e_mail_part_build_uri ( + part_list->folder, part_list->message_uid, "part_id", G_TYPE_STRING, uri, - "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, NULL); + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, NULL); webkit_network_request_set_uri (request, new_uri); @@ -499,6 +474,7 @@ mail_display_resource_requested (WebKitWebView *web_view, GHashTable *query; gchar *uri_md5; CamelStream *stream; + EMailImageLoadingPolicy image_policy; /* Open Evolution's cache */ uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); @@ -509,15 +485,17 @@ mail_display_resource_requested (WebKitWebView *web_view, /* If the URI is not cached and we are not allowed to load it * then redirect to invalid URI, so that webkit would display * a native placeholder for it. */ + image_policy = e_mail_formatter_get_image_loading_policy ( + display->priv->formatter); if (!stream && !display->priv->force_image_load && - !em_format_html_can_load_images (display->priv->formatter)) { + (image_policy == E_MAIL_IMAGE_LOADING_POLICY_NEVER)) { webkit_network_request_set_uri (request, "about:blank"); return; } new_uri = g_strconcat ("evo-", uri, NULL); - mail_uri = em_format_build_mail_uri (formatter->folder, - formatter->message_uid, NULL, NULL); + mail_uri = e_mail_part_build_uri (part_list->folder, + part_list->message_uid, NULL, NULL); soup_uri = soup_uri_new (new_uri); if (soup_uri->query) { @@ -557,6 +535,9 @@ find_element_by_id (WebKitDOMDocument *document, WebKitDOMElement *element; gulong i, length; + if (!WEBKIT_DOM_IS_DOCUMENT (document)) + return NULL; + /* Try to look up the element in this DOM document */ element = webkit_dom_document_get_element_by_id (document, id); if (element) @@ -595,7 +576,6 @@ mail_display_plugin_widget_resize (GObject *object, gint height; widget = GTK_WIDGET (object); - gtk_widget_get_preferred_height (widget, &height, NULL); parent_element = g_object_get_data (object, "parent_element"); if (!parent_element || !WEBKIT_DOM_IS_ELEMENT (parent_element)) { @@ -604,6 +584,14 @@ mail_display_plugin_widget_resize (GObject *object, return; } + /* For attachment bar, we need to ask for height it's parent, + * GtkBox, because EAttachmentBar itself lies about it's real height. */ + if (E_IS_ATTACHMENT_BAR (widget)) { + widget = gtk_widget_get_parent (widget); + } + + gtk_widget_get_preferred_height (widget, &height, NULL); + /* Int -> Str */ dim = g_strdup_printf ("%d", height); @@ -618,6 +606,31 @@ static void mail_display_plugin_widget_realize_cb (GtkWidget *widget, gpointer user_data) { + WebKitDOMHTMLElement *el; + + if (GTK_IS_BOX (widget)) { + GList *children; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + if (children && children->data && + E_IS_ATTACHMENT_BAR (children->data)) { + widget = children->data; + } + + g_list_free (children); + } + + /* First check if we are actually supposed to be visible */ + el = g_object_get_data (G_OBJECT (widget), "parent_element"); + if (!el || !WEBKIT_DOM_IS_HTML_ELEMENT (el)) { + g_warning ("UAAAAA"); + } else { + if (webkit_dom_html_element_get_hidden (el)) { + gtk_widget_hide (widget); + return; + } + } + /* Initial resize of the <object> element when the widget * is displayed for the first time. */ mail_display_plugin_widget_resize (G_OBJECT (widget), NULL, user_data); @@ -647,166 +660,184 @@ plugin_widget_set_parent_element (GtkWidget *widget, * and the GtkWidget to "widget" data of the DOM Element */ g_object_set_data (G_OBJECT (widget), "parent_element", element); g_object_set_data (G_OBJECT (element), "widget", widget); + + g_object_bind_property ( + element, "hidden", + widget, "visible", + G_BINDING_SYNC_CREATE | + G_BINDING_INVERT_BOOLEAN); } static void -attachment_button_expanded (GObject *object, - GParamSpec *pspec, - gpointer user_data) +toggle_widget_visibility (EAttachmentButton *button, + EMailDisplay *display, + WebKitDOMElement *element) { - EAttachmentButton *button = E_ATTACHMENT_BUTTON (object); - WebKitDOMElement *attachment = user_data; - WebKitDOMCSSStyleDeclaration *css; - gboolean expanded; + gchar *id; + GtkWidget *widget; - d(printf("Attachment button %s (%p) expansion state toggled!\n", - (gchar *) g_object_get_data (object, "uri"), object)); + id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (element)); + if (!id || !*id) { + return; + } - expanded = e_attachment_button_get_expanded (button) && - gtk_widget_get_visible (GTK_WIDGET (button)); + if (!display->priv->widgets) { + g_free (id); + return; + } - if (!WEBKIT_DOM_IS_ELEMENT (attachment)) { - d(printf("%s: Parent element for button %s does not exist!\n", - G_STRFUNC, (gchar *) g_object_get_data (object, "uri"))); + widget = g_hash_table_lookup (display->priv->widgets, id); + g_free (id); + if (!widget) { return; } - /* Show or hide the DIV which contains the attachment (iframe, image...) */ - css = webkit_dom_element_get_style (attachment); - webkit_dom_css_style_declaration_set_property ( - css, "display", expanded ? "block" : "none", "", NULL); -} + /* If the widget encapsulates EAttachmentBar then check, whether + * the attachment bar is not empty. We want to display it only + * when there's at least one attachment */ + if (GTK_IS_BOX (widget)) { + GList *children; -static void -constraint_widget_visibility (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - GtkWidget *widget = GTK_WIDGET (object); - EAttachmentButton *button = user_data; + children = gtk_container_get_children (GTK_CONTAINER (widget)); + if (children && children->data && E_IS_ATTACHMENT_BAR (children->data)) { + EAttachmentStore *store; - gboolean can_show = e_attachment_button_get_expanded (button); - gboolean is_visible = gtk_widget_get_visible (widget); + store = e_attachment_bar_get_store ( + E_ATTACHMENT_BAR (children->data)); - if (is_visible && !can_show) - gtk_widget_hide (widget); - else if (!is_visible && can_show) - gtk_widget_show (widget); + g_list_free (children); + + /* Don't allow to display such attachment bar, + * but always allow to hide it */ + if (e_attachment_button_get_expanded (button) && + (e_attachment_store_get_num_attachments (store) == 0)) { + return; + } + } + } - /* Otherwise it's OK */ + webkit_dom_html_element_set_hidden ( + WEBKIT_DOM_HTML_ELEMENT (element), + !e_attachment_button_get_expanded (button)); + + if (e_attachment_button_get_expanded (button)) { + gtk_widget_show (widget); + } else { + gtk_widget_hide (widget); + } } +/** + * @button: An #EAttachmentButton + * @iframe: An iframe element containing document with an attachment + * represented by the @button + */ static void -bind_iframe_content_visibility (EAttachmentButton *button, - WebKitDOMElement *iframe) +bind_iframe_content_visibility (WebKitDOMElement *iframe, + EMailDisplay *display, + EAttachmentButton *button) { WebKitDOMDocument *document; WebKitDOMNodeList *nodes; gulong i, length; - if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe)) + if (!iframe || !WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe)) return; document = webkit_dom_html_iframe_element_get_content_document ( WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); + if (!WEBKIT_DOM_IS_DOCUMENT (document)) + return; + nodes = webkit_dom_document_get_elements_by_tag_name (document, "object"); length = webkit_dom_node_list_get_length (nodes); - d(printf("Found %ld objects within iframe %s\n", length, - webkit_dom_html_iframe_element_get_name ( - WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)))); + d ({ + gchar *name = webkit_dom_html_iframe_element_get_name ( + WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe)); + printf("Found %ld objects within iframe %s\n", length, name); + g_free (name); + }); /* Iterate through all <object>s and bind visibility of their widget * with expanded-state of related attachment button */ for (i = 0; i < length; i++) { WebKitDOMNode *node = webkit_dom_node_list_item (nodes, i); - GtkWidget *widget; - - widget = g_object_get_data (G_OBJECT (node), "widget"); - if (!widget) - continue; - - d(printf("Binding visibility of widget %s (%p) with button %s (%p)\n", - (gchar *) g_object_get_data (G_OBJECT (widget), "uri"), widget, - (gchar *) g_object_get_data (G_OBJECT (button), "uri"), button)); - - g_object_bind_property ( - button, "expanded", - widget, "visible", - G_BINDING_SYNC_CREATE); - /* Ensure that someone won't attempt to _show() the widget when - * it is supposed to be hidden and vice versa. */ - g_signal_connect (widget, "notify::visible", - G_CALLBACK (constraint_widget_visibility), button); + /* Initial sync */ + toggle_widget_visibility (button, display, WEBKIT_DOM_ELEMENT (node)); } } static void -bind_attachment_iframe_visibility (GObject *object, - GParamSpec *pspec, - gpointer user_data) +attachment_button_expanded (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - WebKitWebFrame *webframe; - const gchar *frame_name; - gchar *button_uri; + EAttachmentButton *button = E_ATTACHMENT_BUTTON (object); + EMailDisplay *display = user_data; WebKitDOMDocument *document; - WebKitDOMElement *attachment; - WebKitDOMElement *button_element; - WebKitDOMNodeList *nodes; - gulong i, length; - GtkWidget *button; - - /* Whenever an <iframe> is loaded, bind visibility of all GtkWidgets - * the document within the <iframe> contains with "expanded" property - * of the EAttachmentButton */ + WebKitDOMElement *element; + WebKitDOMCSSStyleDeclaration *css; + gboolean expanded; + gchar *id; - webframe = WEBKIT_WEB_FRAME (object); - if (webkit_web_frame_get_load_status (webframe) != WEBKIT_LOAD_FINISHED) - return; + d(printf("Attachment button %s has been %s!\n", + (gchar *) g_object_get_data (object, "uri"), + (e_attachment_button_get_expanded (button) ? "expanded" : "collapsed"))); - frame_name = webkit_web_frame_get_name (webframe); + expanded = e_attachment_button_get_expanded (button) && + gtk_widget_get_visible (GTK_WIDGET (button)); - d(printf("Rebinding visibility of frame %s because it's URL changed\n", - frame_name)); + document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); + element = find_element_by_id (document, g_object_get_data (object, "attachment_id")); - /* Get DOMDocument of the main document */ - document = webkit_web_view_get_dom_document ( - webkit_web_frame_get_web_view (webframe)); - if (!document) + if (!WEBKIT_DOM_IS_ELEMENT (element)) { + d(printf("%s: Content <div> of attachment %s does not exist!!\n", + G_STRFUNC, (gchar *) g_object_get_data (object, "uri"))); return; + } - /* Find the <DIV> containing the <iframe> and related EAttachmentButton - * within the DOM */ - attachment = find_element_by_id (document, frame_name); - if (!attachment) - return; + /* Show or hide the DIV which contains the attachment (iframe, image...) */ + css = webkit_dom_element_get_style (element); + webkit_dom_css_style_declaration_set_property ( + css, "display", expanded ? "block" : "none", "", NULL); + + id = g_strconcat (g_object_get_data (object, "attachment_id"), ".iframe", NULL); + element = find_element_by_id (document, id); + g_free (id); - button_uri = g_strconcat (frame_name, ".attachment_button", NULL); - button_element = find_element_by_id (document, button_uri); - g_free (button_uri); - if (!button_element) + if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) { + d(printf("%s: No <iframe> found\n", + (gchar *) g_object_get_data (object, "attachment_id"))); return; + } + bind_iframe_content_visibility (element, display, button); +} - button = g_object_get_data (G_OBJECT (button_element), "widget"); +static void +mail_display_attachment_count_changed (EAttachmentStore *store, + GParamSpec *pspec, + GtkWidget *box) +{ + WebKitDOMHTMLElement *element; + GList *children; - /* Get <iframe> representing the attachment content */ - nodes = webkit_dom_element_get_elements_by_tag_name (attachment, "iframe"); - length = webkit_dom_node_list_get_length (nodes); - for (i = 0; i < length; i++) { + children = gtk_container_get_children (GTK_CONTAINER (box)); + g_return_if_fail (children && children->data); - WebKitDOMNode *node = - webkit_dom_node_list_item (nodes, i); + element = g_object_get_data (children->data, "parent_element"); + g_list_free (children); - if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node)) - continue; + g_return_if_fail (WEBKIT_DOM_IS_HTML_ELEMENT (element)); - /* Bind visibility of all GtkWidget within the - * iframe with "expanded" property of the button */ - bind_iframe_content_visibility ( - E_ATTACHMENT_BUTTON (button), - WEBKIT_DOM_ELEMENT (node)); + if (e_attachment_store_get_num_attachments (store) == 0) { + gtk_widget_hide (box); + webkit_dom_html_element_set_hidden (element, TRUE); + } else { + gtk_widget_show (box); + webkit_dom_html_element_set_hidden (element, FALSE); } } @@ -817,44 +848,91 @@ mail_display_plugin_widget_requested (WebKitWebView *web_view, GHashTable *param, gpointer user_data) { - EMFormat *emf; EMailDisplay *display; - EMFormatPURI *puri; + EMailExtensionRegistry *reg; + EMailFormatterExtension *extension; + GQueue *extensions; + GList *iter; + EMailPart *part; GtkWidget *widget; - gchar *puri_uri; + gchar *part_id, *type, *object_uri; - puri_uri = g_hash_table_lookup (param, "data"); - if (!puri_uri || !g_str_has_prefix (uri, "mail://")) + part_id = g_hash_table_lookup (param, "data"); + if (!part_id || !g_str_has_prefix (uri, "mail://")) + return NULL; + + type = g_hash_table_lookup (param, "type"); + if (!type) return NULL; display = E_MAIL_DISPLAY (web_view); - emf = (EMFormat *) display->priv->formatter; - puri = em_format_find_puri (emf, puri_uri); - if (!puri) { + if ((widget = g_hash_table_lookup (display->priv->widgets, part_id)) != NULL) { + d(printf("Handeled %s widget request from cache\n", part_id)); + return widget; + } + + /* Findt EMailPart representing requested widget */ + part = e_mail_part_list_find_part (display->priv->part_list, part_id); + if (!part) { return NULL; } - if (puri->widget_func) - widget = puri->widget_func (emf, puri, NULL); - else - widget = NULL; + reg = e_mail_formatter_get_extension_registry (display->priv->formatter); + extensions = e_mail_extension_registry_get_for_mime_type (reg, type); + if (!extensions) + return NULL; + + extension = NULL; + for (iter = g_queue_peek_head_link (extensions); iter; iter = iter->next) { + + extension = iter->data; + if (!extension) + continue; + if (e_mail_formatter_extension_has_widget (extension)) + break; + } + + if (!extension) + return NULL; + + /* Get the widget from formatter */ + widget = e_mail_formatter_extension_get_widget ( + extension, display->priv->part_list, part, param); + d(printf("Created widget %s (%p) for part %s\n", + G_OBJECT_TYPE_NAME (widget), widget, part_id)); + + /* Should not happen! WebKit will display an ugly 'Plug-in not available' + * placeholder instead of hiding the <object> element */ if (!widget) return NULL; + /* Attachment button has URI different then the actual PURI because + * that URI identifies the attachment itself */ if (E_IS_ATTACHMENT_BUTTON (widget)) { - /* Attachment button has URI different then the actual PURI because - * that URI identifies the attachment itself */ - gchar *button_uri = g_strconcat (puri_uri, ".attachment_button", NULL); - g_object_set_data_full (G_OBJECT (widget), "uri", - button_uri, (GDestroyNotify) g_free); + EMailPartAttachment *empa = (EMailPartAttachment *) part; + gchar *attachment_part_id; + + if (empa->attachment_view_part_id) + attachment_part_id = empa->attachment_view_part_id; + else + attachment_part_id = part_id; + + object_uri = g_strconcat (attachment_part_id, ".attachment_button", NULL); + g_object_set_data_full (G_OBJECT (widget), "attachment_id", + g_strdup (attachment_part_id), (GDestroyNotify) g_free); } else { - g_object_set_data_full (G_OBJECT (widget), "uri", - g_strdup (puri_uri), (GDestroyNotify) g_free); + object_uri = g_strdup (part_id); } - /* Set widget's <object> container as GObject data "parent_element" */ + /* Store the uri as data of the widget */ + g_object_set_data_full (G_OBJECT (widget), "uri", + object_uri, (GDestroyNotify) g_free); + + /* Set pointer to the <object> element as GObject data "parent_element" + * and set pointer to the widget as GObject data "widget" to the <object> + * element */ plugin_widget_set_parent_element (widget, display); /* Resizing a GtkWidget requires changing size of parent @@ -864,26 +942,32 @@ mail_display_plugin_widget_requested (WebKitWebView *web_view, g_signal_connect (widget, "size-allocate", G_CALLBACK (mail_display_plugin_widget_resize), display); - /* Embed the attachment bar into the GtkBox before we do anything - * further with the widget. */ - if (E_IS_MAIL_ATTACHMENT_BAR (widget)) { - - /* When EMailAttachmentBar is expanded/collapsed it does not - * emit size-allocate signal despite it changes it's height. */ + if (E_IS_ATTACHMENT_BAR (widget)) { GtkWidget *box = NULL; + EAttachmentStore *store; - /* Only when packed in box, EMailAttachmentBar reports correct - * height */ - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); + /* Only when packed in box (grid does not work), + * EAttachmentBar reports correct height */ + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0); + /* When EAttachmentBar is expanded/collapsed it does not + * emit size-allocate signal despite it changes it's height. */ g_signal_connect (widget, "notify::expanded", G_CALLBACK (mail_display_plugin_widget_resize), display); g_signal_connect (widget, "notify::active-view", G_CALLBACK (mail_display_plugin_widget_resize), display); - /* Show the EAttachmentBar but not the containing layout */ + /* Always hide an attachment bar without attachments */ + store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (widget)); + g_signal_connect (store, "notify::num-attachments", + G_CALLBACK (mail_display_attachment_count_changed), box); + gtk_widget_show (widget); + gtk_widget_show (box); + + /* Initial sync */ + mail_display_attachment_count_changed (store, NULL, box); widget = box; @@ -894,61 +978,63 @@ mail_display_plugin_widget_requested (WebKitWebView *web_view, * attachment button. */ WebKitDOMElement *attachment; WebKitDOMDocument *document; + EMailPartAttachment *empa = (EMailPartAttachment *) part; + gchar *attachment_part_id; + + if (empa->attachment_view_part_id) + attachment_part_id = empa->attachment_view_part_id; + else + attachment_part_id = part_id; + /* Find attachment-wrapper div which contains the content of the + * attachment (iframe) */ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display)); - attachment = find_element_by_id (document, puri_uri); + attachment = find_element_by_id (document, attachment_part_id); + + /* None found? Attachment cannot be expanded */ if (!attachment) { e_attachment_button_set_expandable ( E_ATTACHMENT_BUTTON (widget), FALSE); } else { const CamelContentDisposition *disposition; - WebKitDOMNodeList *nodes; - gulong i, length; - - /* Show/hide the attachment when the EAttachmentButton - * is expanded/collapsed or shown/hidden */ - g_signal_connect_data (widget, "notify::expanded", - G_CALLBACK (attachment_button_expanded), - g_object_ref (attachment), (GClosureNotify) g_object_unref, 0); - g_signal_connect_data (widget, "notify::visible", - G_CALLBACK (attachment_button_expanded), - g_object_ref (attachment), (GClosureNotify) g_object_unref, 0); - /* Initial synchronization */ - attachment_button_expanded (G_OBJECT (widget), - NULL, attachment); - - /* Find all <iframes> within the attachment and bind - * it's visiblity to expanded state of the attachment btn */ - nodes = webkit_dom_element_get_elements_by_tag_name ( - attachment, "iframe"); - length = webkit_dom_node_list_get_length (nodes); - for (i = 0; i < length; i++) { - - WebKitDOMNode *node = - webkit_dom_node_list_item (nodes, i); - - if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node)) - continue; - - bind_iframe_content_visibility ( - E_ATTACHMENT_BUTTON (widget), - WEBKIT_DOM_ELEMENT (node)); - } - /* Expand inlined attachments */ + e_attachment_button_set_expandable ( + E_ATTACHMENT_BUTTON (widget), TRUE); + + /* Show/hide the attachment when the EAttachmentButton + * * is expanded/collapsed or shown/hidden */ + g_signal_connect (widget, "notify::expanded", + G_CALLBACK (attachment_button_expanded), display); + g_signal_connect (widget, "notify::visible", + G_CALLBACK (attachment_button_expanded), display); + + /* Automatically expand attachments that have inline + * disposition or the EMailParts have specific force_inline + * flag set */ disposition = - camel_mime_part_get_content_disposition (puri->part); - if (disposition && - g_ascii_strncasecmp ( - disposition->disposition, "inline", 6) == 0) { + camel_mime_part_get_content_disposition (part->part); + if (!part->force_collapse && + (part->force_inline || + (g_strcmp0 (empa->snoop_mime_type, "message/rfc822") == 0) || + (disposition && disposition->disposition && + g_ascii_strncasecmp ( + disposition->disposition, "inline", 6) == 0))) { e_attachment_button_set_expanded ( E_ATTACHMENT_BUTTON (widget), TRUE); + } else { + e_attachment_button_set_expanded ( + E_ATTACHMENT_BUTTON (widget), FALSE); + attachment_button_expanded ( + G_OBJECT (widget), NULL, display); } } } - d(printf("Created widget %s (%p)\n", puri_uri, widget)); + g_hash_table_insert ( + display->priv->widgets, + g_strdup (object_uri), g_object_ref (widget)); + return widget; } @@ -1123,18 +1209,17 @@ setup_DOM_bindings (GObject *object, } static void -puri_bind_dom (GObject *object, - GParamSpec *pspec, - gpointer user_data) +mail_parts_bind_dom (GObject *object, + GParamSpec *pspec, + gpointer user_data) { WebKitWebFrame *frame; WebKitLoadStatus load_status; WebKitWebView *web_view; WebKitDOMDocument *document; EMailDisplay *display; - GList *iter; - EMFormat *emf; - const gchar *frame_puri; + GSList *iter; + const gchar *frame_name; frame = WEBKIT_WEB_FRAME (object); load_status = webkit_web_frame_get_load_status (frame); @@ -1142,36 +1227,40 @@ puri_bind_dom (GObject *object, if (load_status != WEBKIT_LOAD_FINISHED) return; - frame_puri = webkit_web_frame_get_name (frame); web_view = webkit_web_frame_get_web_view (frame); display = E_MAIL_DISPLAY (web_view); - - emf = EM_FORMAT (display->priv->formatter); - if (!emf) + if (display->priv->part_list == NULL) return; - iter = g_hash_table_lookup ( - emf->mail_part_table, - webkit_web_frame_get_name (frame)); + frame_name = webkit_web_frame_get_name (frame); + for (iter = display->priv->part_list->list; iter; iter = iter->next) { + + EMailPart *part = iter->data; + if (!part) + continue; - document = webkit_web_view_get_dom_document (web_view); + if (g_strcmp0 (part->id, frame_name) == 0) + break; + } + document = webkit_web_view_get_dom_document (web_view); while (iter) { - EMFormatPURI *puri = iter->data; - - if (!puri) + EMailPart *part = iter->data; + if (!part) { + iter = iter->next; continue; + } - /* Iterate only the PURI rendered in the frame and all it's "subPURIs" */ - if (!g_str_has_prefix (puri->uri, frame_puri)) + /* Iterate only the parts rendered in the frame and all it's subparts */ + if (!g_str_has_prefix (part->id, frame_name)) break; - if (puri->bind_func) { - WebKitDOMElement *el = find_element_by_id (document, puri->uri); + if (part->bind_func) { + WebKitDOMElement *el = find_element_by_id (document, part->id); if (el) { - d(printf("bind_func for %s\n", puri->uri)); - puri->bind_func (el, puri); + d(printf("/*bind_func*/ for %s\n", part->id)); + part->bind_func (part, el); } } @@ -1186,15 +1275,25 @@ mail_display_frame_created (WebKitWebView *web_view, { d(printf("Frame %s created!\n", webkit_web_frame_get_name (frame))); - /* Re-bind visibility of this newly created <iframe> with - * related EAttachmentButton whenever content of this <iframe> is - * (re)loaded */ + /* Call bind_func of all parts written in this frame */ g_signal_connect (frame, "notify::load-status", - G_CALLBACK (bind_attachment_iframe_visibility), NULL); + G_CALLBACK (mail_parts_bind_dom), NULL); +} - /* Call bind_func of all PURIs written in this frame */ - g_signal_connect (frame, "notify::load-status", - G_CALLBACK (puri_bind_dom), NULL); +static void +mail_display_uri_changed (EMailDisplay *display, + GParamSpec *pspec, + gpointer dummy) +{ + d(printf("EMailDisplay URI changed, recreating widgets hashtable\n")); + + if (display->priv->widgets) + g_hash_table_destroy (display->priv->widgets); + + display->priv->widgets = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); } static void @@ -1253,12 +1352,11 @@ e_mail_display_class_init (EMailDisplayClass *class) g_object_class_install_property ( object_class, - PROP_FORMATTER, - g_param_spec_object ( - "formatter", - "HTML Formatter", + PROP_PART_LIST, + g_param_spec_pointer ( + "part-list", + "Part List", NULL, - EM_TYPE_FORMAT_HTML, G_PARAM_READWRITE)); g_object_class_install_property ( @@ -1268,9 +1366,9 @@ e_mail_display_class_init (EMailDisplayClass *class) "mode", "Display Mode", NULL, - 0, + E_MAIL_FORMATTER_MODE_INVALID, G_MAXINT, - EM_FORMAT_WRITE_MODE_NORMAL, + E_MAIL_FORMATTER_MODE_NORMAL, G_PARAM_READWRITE)); g_object_class_install_property ( @@ -1305,6 +1403,10 @@ e_mail_display_init (EMailDisplay *display) display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display); + /* Set invalid mode so that MODE property initialization is run + * completely (see e_mail_display_set_mode) */ + display->priv->mode = E_MAIL_FORMATTER_MODE_INVALID; + e_mail_display_set_mode (display, E_MAIL_FORMATTER_MODE_NORMAL); display->priv->force_image_load = FALSE; display->priv->mailto_actions = gtk_action_group_new ("mailto"); gtk_action_group_add_actions (display->priv->mailto_actions, mailto_entries, @@ -1331,6 +1433,8 @@ e_mail_display_init (EMailDisplay *display) G_CALLBACK (mail_display_plugin_widget_requested), NULL); g_signal_connect (display, "frame-created", G_CALLBACK (mail_display_frame_created), NULL); + g_signal_connect (display, "notify::uri", + G_CALLBACK (mail_display_uri_changed), NULL); display->priv->settings = g_settings_new ("org.gnome.evolution.mail"); g_signal_connect_swapped ( @@ -1350,7 +1454,7 @@ e_mail_display_init (EMailDisplay *display) G_CALLBACK (setup_DOM_bindings), NULL); main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display)); g_signal_connect (main_frame, "notify::load-status", - G_CALLBACK (puri_bind_dom), NULL); + G_CALLBACK (mail_parts_bind_dom), NULL); /* Because we are loading from a hard-coded string, there is * no chance of I/O errors. Failure here implies a malformed @@ -1387,75 +1491,99 @@ e_mail_display_init (EMailDisplay *display) } } -EMFormatHTML * -e_mail_display_get_formatter (EMailDisplay *display) +EMailFormatterMode +e_mail_display_get_mode (EMailDisplay *display) { - g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL); + g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), + E_MAIL_FORMATTER_MODE_NORMAL); - return display->priv->formatter; + return display->priv->mode; } void -e_mail_display_set_formatter (EMailDisplay *display, - EMFormatHTML *formatter) +e_mail_display_set_mode (EMailDisplay *display, + EMailFormatterMode mode) { + EMailFormatter *formatter; g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - if (formatter) { - g_return_if_fail (EM_IS_FORMAT_HTML (formatter)); - g_object_ref (formatter); - } - - if (display->priv->formatter != NULL) { - /* The formatter might still exist after unrefing it, so - * we need to stop listening to it's request for redrawing */ - g_signal_handlers_disconnect_by_func ( - display->priv->formatter, e_mail_display_reload, display); - g_object_unref (display->priv->formatter); - } + if (display->priv->mode == mode) + return; - display->priv->formatter = formatter; + display->priv->mode = mode; - if (!formatter) { - e_web_view_clear (E_WEB_VIEW (display)); - return; + if (display->priv->mode == E_MAIL_FORMATTER_MODE_PRINTING) { + formatter = e_mail_formatter_print_new (); + } else { + formatter = e_mail_formatter_new (); } + g_clear_object (&display->priv->formatter); + display->priv->formatter = formatter; mail_display_update_formatter_colors (display); g_signal_connect (formatter, "notify::image-loading-policy", G_CALLBACK (formatter_image_loading_policy_changed_cb), display); - g_signal_connect_swapped (formatter, "redraw-requested", - G_CALLBACK (e_mail_display_reload), display); - g_signal_connect_swapped (formatter, "notify::charset", - G_CALLBACK (e_mail_display_reload), display); - g_object_notify (G_OBJECT (display), "formatter"); + g_object_connect (formatter, + "swapped-signal::notify::charset", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::image-loading-policy", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::mark-citations", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::only-local-photos", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::show-sender-photo", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::show-real-date", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::animate-images", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::text-color", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::body-color", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::citation-color", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::content-color", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::frame-color", + G_CALLBACK (e_mail_display_reload), display, + "swapped-signal::notify::header-color", + G_CALLBACK (e_mail_display_reload), display, + NULL); + + e_mail_display_reload (display); + + g_object_notify (G_OBJECT (display), "mode"); } -EMFormatWriteMode -e_mail_display_get_mode (EMailDisplay *display) +EMailPartList * +e_mail_display_get_parts_list (EMailDisplay *display) { - g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), - EM_FORMAT_WRITE_MODE_NORMAL); + g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL); - return display->priv->mode; + return display->priv->part_list; } void -e_mail_display_set_mode (EMailDisplay *display, - EMFormatWriteMode mode) +e_mail_display_set_parts_list (EMailDisplay *display, + EMailPartList *part_list) { g_return_if_fail (E_IS_MAIL_DISPLAY (display)); - if (display->priv->mode == mode) - return; + if (part_list) { + g_return_if_fail (E_IS_MAIL_PART_LIST (part_list)); + g_object_ref (part_list); + } - display->priv->mode = mode; + if (display->priv->part_list) + g_object_unref (display->priv->part_list); - e_mail_display_reload (display); + display->priv->part_list = part_list; - g_object_notify (G_OBJECT (display), "mode"); + g_object_notify (G_OBJECT (display), "part-list"); } gboolean @@ -1510,16 +1638,21 @@ void e_mail_display_load (EMailDisplay *display, const gchar *msg_uri) { - EMFormat *emf; gchar *uri; + EMailPartList *part_list; g_return_if_fail (E_IS_MAIL_DISPLAY (display)); display->priv->force_image_load = FALSE; - emf = EM_FORMAT (display->priv->formatter); + part_list = display->priv->part_list; + if (!part_list) { + e_web_view_clear (E_WEB_VIEW (display)); + return; + } - uri = em_format_build_mail_uri (emf->folder, emf->message_uid, + uri = e_mail_part_build_uri ( + part_list->folder, part_list->message_uid, "mode", G_TYPE_INT, display->priv->mode, "headers_collapsable", G_TYPE_BOOLEAN, display->priv->headers_collapsable, "headers_collapsed", G_TYPE_BOOLEAN, display->priv->headers_collapsed, diff --git a/mail/e-mail-display.h b/mail/e-mail-display.h index 9865a3ee37..e19895623a 100644 --- a/mail/e-mail-display.h +++ b/mail/e-mail-display.h @@ -24,7 +24,8 @@ #include <misc/e-web-view.h> #include <misc/e-search-bar.h> -#include "em-format-html.h" + +#include <em-format/e-mail-formatter.h> /* Standard GObject macros */ #define E_TYPE_MAIL_DISPLAY \ @@ -62,13 +63,16 @@ struct _EMailDisplayClass { }; GType e_mail_display_get_type (void); -EMFormatHTML * e_mail_display_get_formatter (EMailDisplay *display); -void e_mail_display_set_formatter (EMailDisplay *display, - EMFormatHTML *formatter); void e_mail_display_set_mode (EMailDisplay *display, - EMFormatWriteMode mode); -EMFormatWriteMode e_mail_display_get_mode (EMailDisplay *display); + EMailFormatterMode mode); +EMailFormatterMode e_mail_display_get_mode (EMailDisplay *display); + +EMailPartList * e_mail_display_get_parts_list (EMailDisplay *display); + +void e_mail_display_set_parts_list (EMailDisplay *display, + EMailPartList *parts_list); + void e_mail_display_set_headers_collapsable (EMailDisplay *display, gboolean collapsable); diff --git a/mail/e-mail-folder-pane.c b/mail/e-mail-folder-pane.c index 03168cec18..e6c0e9213b 100644 --- a/mail/e-mail-folder-pane.c +++ b/mail/e-mail-folder-pane.c @@ -43,7 +43,6 @@ #include "mail/e-mail-reader.h" #include "mail/e-mail-reader-utils.h" #include "mail/em-folder-tree-model.h" -#include "mail/em-format-html-display.h" #include "mail/em-composer-utils.h" #include "mail/em-utils.h" #include "mail/message-list.h" diff --git a/mail/e-mail-paned-view.h b/mail/e-mail-paned-view.h index 5e6879ae94..dbea57b99f 100644 --- a/mail/e-mail-paned-view.h +++ b/mail/e-mail-paned-view.h @@ -28,8 +28,6 @@ #include <shell/e-shell-searchbar.h> #include <shell/e-shell-view.h> -#include <mail/em-format-html-display.h> - /* Standard GObject macros */ #define E_TYPE_MAIL_PANED_VIEW \ (e_mail_paned_view_get_type ()) diff --git a/mail/e-mail-printer.c b/mail/e-mail-printer.c index 0a52a0a5b2..f90414f02d 100644 --- a/mail/e-mail-printer.c +++ b/mail/e-mail-printer.c @@ -24,13 +24,15 @@ #include <glib/gi18n.h> #include <gtk/gtk.h> +#include <em-format/e-mail-formatter-print.h> +#include <em-format/e-mail-part-utils.h> + #include <e-util/e-print.h> #include <e-util/e-marshal.h> #include <webkit/webkitdom.h> #include "e-mail-printer.h" -#include "em-format-html-print.h" #include "e-mail-display.h" static gpointer parent_class = NULL; @@ -48,9 +50,11 @@ enum { #define w(x) struct _EMailPrinterPrivate { - EMFormatHTMLPrint *efhp; + EMailFormatterPrint *formatter; + EMailPartList *parts_list; gboolean export_mode; + gchar *export_filename; GtkListStore *headers; @@ -69,7 +73,7 @@ G_DEFINE_TYPE ( enum { PROP_0, - PROP_PRINT_FORMATTER + PROP_PART_LIST }; enum { @@ -88,8 +92,8 @@ enum { static guint signals[LAST_SIGNAL]; static gint -emp_header_name_equal (const EMFormatHeader *h1, - const EMFormatHeader *h2) +emp_header_name_equal (const EMailFormatterHeader *h1, + const EMailFormatterHeader *h2) { if ((h2->value == NULL) || (h1->value == NULL)) { return g_strcmp0 (h1->name, h2->name); @@ -169,8 +173,7 @@ emp_start_printing (GObject *object, if (emp->priv->export_mode) { gtk_print_operation_set_export_filename ( - emp->priv->operation, - emp->priv->efhp->export_filename); + emp->priv->operation, emp->priv->export_filename); webkit_web_frame_print_full ( frame, emp->priv->operation, GTK_PRINT_OPERATION_ACTION_EXPORT, NULL); @@ -185,32 +188,23 @@ emp_start_printing (GObject *object, static void emp_run_print_operation (EMailPrinter *emp) { - EMFormat *emf; - SoupSession *session; - GHashTable *formatters; gchar *mail_uri; - emf = EM_FORMAT (emp->priv->efhp); - mail_uri = em_format_build_mail_uri (emf->folder, emf->message_uid, NULL, NULL); - - /* It's safe to assume that session exists and contains formatters table, - * because at least the message we are about to print now must be already - * there */ - session = webkit_get_default_session (); - formatters = g_object_get_data (G_OBJECT (session), "formatters"); - g_hash_table_insert (formatters, g_strdup (mail_uri), emp->priv->efhp); + mail_uri = e_mail_part_build_uri (emp->priv->parts_list->folder, + emp->priv->parts_list->message_uid, + "__evo-load-image", G_TYPE_BOOLEAN, TRUE, + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_PRINTING, + NULL); /* Print_layout is a special EMPart created by EMFormatHTMLPrint */ - if (emp->priv->uri) - g_free (emp->priv->uri); - - emp->priv->uri = g_strconcat (mail_uri, "?part_id=print_layout&__evo-load-images=1", NULL); - if (emp->priv->webview == NULL) { - emp->priv->webview = g_object_new (E_TYPE_MAIL_DISPLAY, NULL); + emp->priv->webview = g_object_new ( + E_TYPE_MAIL_DISPLAY, + "mode", E_MAIL_FORMATTER_MODE_PRINTING, NULL); e_web_view_set_enable_frame_flattening (E_WEB_VIEW (emp->priv->webview), FALSE); e_mail_display_set_force_load_images ( - E_MAIL_DISPLAY (emp->priv->webview), TRUE); + E_MAIL_DISPLAY (emp->priv->webview), TRUE); + g_object_ref_sink (emp->priv->webview); g_signal_connect (emp->priv->webview, "notify::load-status", G_CALLBACK (emp_start_printing), emp); @@ -224,18 +218,16 @@ emp_run_print_operation (EMailPrinter *emp) gtk_widget_show_all (window); }); } - - e_mail_display_set_formatter (E_MAIL_DISPLAY (emp->priv->webview), - (EMFormatHTML *) emp->priv->efhp); - - webkit_web_view_load_uri (emp->priv->webview, emp->priv->uri); + e_mail_display_set_parts_list ( + E_MAIL_DISPLAY (emp->priv->webview), emp->priv->parts_list); + webkit_web_view_load_uri (emp->priv->webview, mail_uri); g_free (mail_uri); } static void set_header_visible (EMailPrinter *emp, - EMFormatHeader *header, + EMailFormatterHeader *header, gint index, gboolean visible) { @@ -263,7 +255,7 @@ header_active_renderer_toggled_cb (GtkCellRendererToggle *renderer, GtkTreeIter iter; GtkTreePath *p; gboolean active; - EMFormatHeader *header; + EMailFormatterHeader *header; gint *indices; gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (emp->priv->headers), @@ -301,7 +293,7 @@ emp_headers_tab_toggle_selection (GtkWidget *button, return; do { - EMFormatHeader *header; + EMailFormatterHeader *header; GtkTreePath *path; gint *indices; @@ -594,47 +586,44 @@ emp_create_headers_tab (GtkPrintOperation *operation, } static void -emp_set_formatter (EMailPrinter *emp, - EMFormatHTMLPrint *formatter) +emp_set_parts_list (EMailPrinter *emp, + EMailPartList *parts_list) { - EMFormat *emf = (EMFormat *) formatter; CamelMediumHeader *header; GArray *headers; gint i; GtkTreeIter last_known; - g_return_if_fail (EM_IS_FORMAT_HTML_PRINT (formatter)); - - g_object_ref (formatter); + g_return_if_fail (parts_list); - if (emp->priv->efhp) - g_object_unref (emp->priv->efhp); - - emp->priv->efhp = formatter; + emp->priv->parts_list = g_object_ref (parts_list); if (emp->priv->headers) g_object_unref (emp->priv->headers); emp->priv->headers = gtk_list_store_new (5, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT); - headers = camel_medium_get_headers (CAMEL_MEDIUM (emf->message)); + headers = camel_medium_get_headers (CAMEL_MEDIUM (parts_list->message)); if (!headers) return; for (i = 0; i < headers->len; i++) { GtkTreeIter iter; GList *found_header; - EMFormatHeader *emfh; + EMailFormatterHeader *emfh; header = &g_array_index (headers, CamelMediumHeader, i); - emfh = em_format_header_new (header->name, header->value); + emfh = e_mail_formatter_header_new (header->name, header->value); - found_header = g_queue_find_custom (&EM_FORMAT (formatter)->header_list, + found_header = g_queue_find_custom ( + (GQueue *) e_mail_formatter_get_headers ( + E_MAIL_FORMATTER (emp->priv->formatter)), emfh, (GCompareFunc) emp_header_name_equal); if (!found_header) { - emfh->flags |= EM_FORMAT_HTML_HEADER_HIDDEN; - em_format_add_header_struct (EM_FORMAT (formatter), emfh); + emfh->flags |= E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN; + e_mail_formatter_add_header_struct ( + E_MAIL_FORMATTER (emp->priv->formatter), emfh); gtk_list_store_append (emp->priv->headers, &iter); } else { if (gtk_list_store_iter_is_valid (emp->priv->headers, &last_known)) @@ -652,7 +641,7 @@ emp_set_formatter (EMailPrinter *emp, COLUMN_HEADER_STRUCT, emfh, -1); } - camel_medium_free_headers (CAMEL_MEDIUM (emf->message), headers); + camel_medium_free_headers (CAMEL_MEDIUM (parts_list->message), headers); } static void @@ -665,8 +654,8 @@ emp_set_property (GObject *object, switch (property_id) { - case PROP_PRINT_FORMATTER: - emp_set_formatter (emp, g_value_get_object (value)); + case PROP_PART_LIST: + emp_set_parts_list (emp, g_value_get_pointer (value)); return; } @@ -683,9 +672,9 @@ emp_get_property (GObject *object, switch (property_id) { - case PROP_PRINT_FORMATTER: - g_value_set_object (value, - e_mail_printer_get_print_formatter (emp)); + case PROP_PART_LIST: + g_value_set_pointer (value, + emp->priv->parts_list); return; } @@ -697,9 +686,9 @@ emp_finalize (GObject *object) { EMailPrinterPrivate *priv = E_MAIL_PRINTER (object)->priv; - if (priv->efhp) { - g_object_unref (priv->efhp); - priv->efhp = NULL; + if (priv->formatter) { + g_object_unref (priv->formatter); + priv->formatter = NULL; } if (priv->headers) { @@ -707,10 +696,10 @@ emp_finalize (GObject *object) if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->headers), &iter)) { do { - EMFormatHeader *header = NULL; + EMailFormatterHeader *header = NULL; gtk_tree_model_get (GTK_TREE_MODEL (priv->headers), &iter, COLUMN_HEADER_STRUCT, &header, -1); - em_format_header_free (header); + e_mail_formatter_header_free (header); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->headers), &iter)); } g_object_unref (priv->headers); @@ -732,6 +721,11 @@ emp_finalize (GObject *object) priv->operation = NULL; } + if (priv->parts_list) { + g_object_unref (priv->parts_list); + priv->parts_list = NULL; + } + /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -751,13 +745,13 @@ e_mail_printer_class_init (EMailPrinterClass *klass) g_object_class_install_property ( object_class, - PROP_PRINT_FORMATTER, - g_param_spec_object ( - "print-formatter", - NULL, + PROP_PART_LIST, + g_param_spec_pointer ( + "parts-list", + "Parts List", NULL, - EM_TYPE_FORMAT_HTML_PRINT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); signals[SIGNAL_DONE] = g_signal_new ("done", G_TYPE_FROM_CLASS (klass), @@ -775,23 +769,18 @@ e_mail_printer_init (EMailPrinter *emp) emp->priv = G_TYPE_INSTANCE_GET_PRIVATE ( emp, E_TYPE_MAIL_PRINTER, EMailPrinterPrivate); - emp->priv->efhp = NULL; + emp->priv->formatter = (EMailFormatterPrint *) e_mail_formatter_print_new (); emp->priv->headers = NULL; emp->priv->webview = NULL; } EMailPrinter * -e_mail_printer_new (EMFormatHTML *source) +e_mail_printer_new (EMailPartList *source) { EMailPrinter *emp; - EMFormatHTMLPrint *efhp; - - efhp = em_format_html_print_new (source); emp = g_object_new (E_TYPE_MAIL_PRINTER, - "print-formatter", efhp, NULL); - - g_object_unref (efhp); + "parts-list", source, NULL); return emp; } @@ -830,10 +819,7 @@ e_mail_printer_get_export_filename (EMailPrinter *printer) { g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL); - if (!printer->priv->efhp) - return NULL; - - return printer->priv->efhp->export_filename; + return printer->priv->export_filename; } void @@ -841,19 +827,9 @@ e_mail_printer_set_export_filename (EMailPrinter *printer, const gchar *filename) { g_return_if_fail (E_IS_MAIL_PRINTER (printer)); - g_return_if_fail (printer->priv->efhp != NULL); - if (printer->priv->efhp->export_filename && *printer->priv->efhp->export_filename) - g_free (printer->priv->efhp->export_filename); + if (printer->priv->export_filename) + g_free (printer->priv->export_filename); - printer->priv->efhp->export_filename = g_strdup (filename); + printer->priv->export_filename = g_strdup (filename); } - -EMFormatHTMLPrint * -e_mail_printer_get_print_formatter (EMailPrinter *emp) -{ - g_return_val_if_fail (E_IS_MAIL_PRINTER (emp), NULL); - - return emp->priv->efhp; -} - diff --git a/mail/e-mail-printer.h b/mail/e-mail-printer.h index fcd163ec78..a0721ea9d4 100644 --- a/mail/e-mail-printer.h +++ b/mail/e-mail-printer.h @@ -20,7 +20,7 @@ #ifndef E_MAIL_PRINTER_H #define E_MAIL_PRINTER_H -#include "mail/em-format-html-print.h" +#include <em-format/e-mail-part-list.h> /* Standard GObject macros */ #define E_TYPE_MAIL_PRINTER \ @@ -63,7 +63,7 @@ struct _EMailPrinterClass { GType e_mail_printer_get_type (void); -EMailPrinter * e_mail_printer_new (EMFormatHTML *source); +EMailPrinter * e_mail_printer_new (EMailPartList *source); void e_mail_printer_print (EMailPrinter *printer, gboolean export, @@ -76,10 +76,6 @@ void e_mail_printer_set_export_filename const gchar * e_mail_printer_get_export_filename (EMailPrinter *printer); -EMFormatHTMLPrint * - e_mail_printer_get_print_formatter - (EMailPrinter *printer); - G_END_DECLS #endif /* E_MAIL_PRINTER_H */ diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c index 2240523beb..f56b094e14 100644 --- a/mail/e-mail-reader-utils.c +++ b/mail/e-mail-reader-utils.c @@ -46,13 +46,20 @@ #include "mail/e-mail-backend.h" #include "mail/e-mail-browser.h" #include "mail/e-mail-printer.h" +#include "mail/e-mail-display.h" #include "mail/em-composer-utils.h" -#include "mail/em-format-html-print.h" #include "mail/em-utils.h" #include "mail/mail-autofilter.h" #include "mail/mail-vfolder-ui.h" #include "mail/message-list.h" +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> + +#define d(x) + +static GHashTable * mail_reader_get_mail_register (void); + typedef struct _AsyncContext AsyncContext; struct _AsyncContext { @@ -434,7 +441,7 @@ e_mail_reader_open_selected (EMailReader *reader) MessageList *ml; browser = e_mail_browser_new (backend, folder, uid, - EM_FORMAT_WRITE_MODE_NORMAL); + E_MAIL_FORMATTER_MODE_NORMAL); e_mail_reader_set_folder (E_MAIL_READER (browser), folder); e_mail_reader_set_message (E_MAIL_READER (browser), uid); @@ -508,30 +515,109 @@ printing_done_cb (EMailPrinter *printer, (GSourceFunc) destroy_printing_activity, activity, NULL); } -void -e_mail_reader_print (EMailReader *reader, - GtkPrintOperationAction action) -{ - EMailDisplay *display; - EMailPrinter *printer; - EMFormatHTML *formatter; +struct _MessagePrintingContext { + EMailReader *reader; + CamelFolder *folder; + gchar *message_uid; + EActivity *activity; - GCancellable *cancellable; +}; - g_return_if_fail (E_IS_MAIL_READER (reader)); +static void +free_message_printing_context (struct _MessagePrintingContext *context) +{ + g_return_if_fail (context != NULL); - display = e_mail_reader_get_mail_display (reader); - formatter = e_mail_display_get_formatter (display); + g_clear_object (&context->reader); + g_clear_object (&context->folder); + g_clear_object (&context->activity); - activity = e_mail_reader_new_activity (reader); + if (context->message_uid) + g_free (context->message_uid); + + g_free (context); +} + +static void +mail_reader_do_print_message (EMailPartList *part_list, + gpointer user_data) +{ + EActivity *activity; + GCancellable *cancellable; + EMailPrinter *printer; + struct _MessagePrintingContext *context = user_data; + + activity = e_mail_reader_new_activity (context->reader); e_activity_set_text (activity, _("Printing")); e_activity_set_state (activity, E_ACTIVITY_RUNNING); cancellable = e_activity_get_cancellable (activity); - printer = e_mail_printer_new (formatter); + printer = e_mail_printer_new (part_list); g_signal_connect (printer, "done", G_CALLBACK (printing_done_cb), activity); e_mail_printer_print (printer, FALSE, cancellable); + + free_message_printing_context (context); +} + +static void +mail_reader_get_message_to_print_ready_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + CamelMimeMessage *message; + struct _MessagePrintingContext *context = user_data; + + message = camel_folder_get_message_finish (CAMEL_FOLDER (object), result, NULL); + if (!CAMEL_IS_MIME_MESSAGE (message)) { + free_message_printing_context (context); + return; + } + + /* "Retrieving message" activity (or NULL) */ + g_clear_object (&context->activity); + + e_mail_reader_parse_message ( + context->reader, context->folder, context->message_uid, + message, (GFunc) mail_reader_do_print_message, context); +} + +void +e_mail_reader_print (EMailReader *reader, + GtkPrintOperationAction action) +{ + EMailPartList *parts; + struct _MessagePrintingContext *context; + MessageList *message_list; + gchar *uri; + + context = g_new0 (struct _MessagePrintingContext, 1); + + message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader)); + context->reader = g_object_ref (reader); + context->message_uid = g_strdup (message_list->cursor_uid); + context->folder = g_object_ref (e_mail_reader_get_folder (reader)); + + g_return_if_fail (E_IS_MAIL_READER (reader)); + + uri = e_mail_part_build_uri ( + context->folder, context->message_uid, NULL, NULL); + parts = e_mail_reader_lookup_part_list (reader, uri); + if (!parts) { + GCancellable *cancellable; + + context->activity = e_mail_reader_new_activity (reader); + cancellable = e_activity_get_cancellable (context->activity); + + camel_folder_get_message ( + context->folder, context->message_uid, + G_PRIORITY_DEFAULT, cancellable, + (GAsyncReadyCallback) mail_reader_get_message_to_print_ready_cb, + context); + + } else { + mail_reader_do_print_message (parts, context); + } } static void @@ -770,15 +856,31 @@ html_contains_nonwhitespace (const gchar *html, } static void +mail_reader_reply_message_parsed (EMailPartList *part_list, + gpointer user_data) +{ + EShell *shell; + EMailBackend *backend; + AsyncContext *context = user_data; + + backend = e_mail_reader_get_backend (context->reader); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); + + em_utils_reply_to_message ( + shell, part_list->message, + context->folder, context->message_uid, + context->reply_type, context->reply_style, + part_list, context->address); + + async_context_free (context); +} + +static void mail_reader_get_message_ready_cb (CamelFolder *folder, GAsyncResult *result, AsyncContext *context) { - EShell *shell; - EMailBackend *backend; EAlertSink *alert_sink; - EMFormatHTML *formatter; - EMailDisplay *display; CamelMimeMessage *message; GError *error = NULL; @@ -804,22 +906,10 @@ mail_reader_get_message_ready_cb (CamelFolder *folder, g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - backend = e_mail_reader_get_backend (context->reader); - shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); - display = e_mail_reader_get_mail_display (context->reader); - formatter = e_mail_display_get_formatter (display); - - em_utils_reply_to_message ( - shell, message, - context->folder, context->message_uid, - context->reply_type, context->reply_style, - EM_FORMAT (formatter), context->address); - - g_object_unref (message); - - e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED); - - async_context_free (context); + e_mail_reader_parse_message (context->reader, context->folder, + context->message_uid, message, + (GFunc) mail_reader_reply_message_parsed, + context); } void @@ -831,7 +921,7 @@ e_mail_reader_reply_to_message (EMailReader *reader, EMailBackend *backend; EShellBackend *shell_backend; EMailDisplay *display; - EMFormatHTML *formatter; + EMailPartList *part_list; GtkWidget *message_list; CamelMimeMessage *new_message; CamelInternetAddress *address = NULL; @@ -842,6 +932,8 @@ e_mail_reader_reply_to_message (EMailReader *reader, const gchar *uid; gchar *selection = NULL; gint length; + gchar *mail_uri; + GHashTable *mail_register; /* This handles quoting only selected text in the reply. If * nothing is selected or only whitespace is selected, fall @@ -852,7 +944,6 @@ e_mail_reader_reply_to_message (EMailReader *reader, backend = e_mail_reader_get_backend (reader); folder = e_mail_reader_get_folder (reader); display = e_mail_reader_get_mail_display (reader); - formatter = e_mail_display_get_formatter (display); message_list = e_mail_reader_get_message_list (reader); reply_style = e_mail_reader_get_reply_style (reader); @@ -889,11 +980,19 @@ e_mail_reader_reply_to_message (EMailReader *reader, uid = MESSAGE_LIST (message_list)->cursor_uid; g_return_if_fail (uid != NULL); - if (!gtk_widget_get_mapped (GTK_WIDGET (web_view))) + if (!gtk_widget_get_visible (GTK_WIDGET (web_view))) + goto whole_message; + + mail_register = mail_reader_get_mail_register (); + mail_uri = e_mail_part_build_uri (folder, uid, NULL, NULL); + part_list = g_hash_table_lookup (mail_register, mail_uri); + g_free (mail_uri); + + if (!part_list) goto whole_message; if (src_message == NULL) { - src_message = EM_FORMAT (formatter)->message; + src_message = part_list->message; if (src_message != NULL) g_object_ref (src_message); @@ -976,7 +1075,7 @@ whole_message: em_utils_reply_to_message ( shell, src_message, folder, uid, - reply_type, reply_style, EM_FORMAT (formatter), address); + reply_type, reply_style, part_list, address); if (address) g_object_unref (address); @@ -1419,43 +1518,58 @@ e_mail_reader_header_free (EMailReaderHeader *header) g_free (header); } +struct headers_changed_closure { + EMailFormatter *formatter; + EMailDisplay *display; +}; + +static void +free_headers_changed_closure (struct headers_changed_closure *closure) +{ + g_clear_object (&closure->formatter); + g_clear_object (&closure->display); + + g_free (closure); +} + static void headers_changed_cb (GConfClient *client, guint cnxn_id, GConfEntry *entry, - EMFormat *emf) + struct headers_changed_closure *closure) { GSList *header_config_list, *p; g_return_if_fail (client != NULL); - g_return_if_fail (EM_IS_FORMAT (emf)); header_config_list = gconf_client_get_list ( client, "/apps/evolution/mail/display/headers", GCONF_VALUE_STRING, NULL); - em_format_clear_headers (emf); + e_mail_formatter_clear_headers (closure->formatter); for (p = header_config_list; p; p = g_slist_next (p)) { EMailReaderHeader *h; gchar *xml = (gchar *) p->data; h = e_mail_reader_header_from_xml (xml); if (h && h->enabled) - em_format_add_header ( - emf, h->name, NULL, EM_FORMAT_HEADER_BOLD); + e_mail_formatter_add_header ( + closure->formatter, h->name, NULL, + E_MAIL_FORMATTER_HEADER_FLAG_BOLD); e_mail_reader_header_free (h); } if (!header_config_list) - em_format_default_headers (emf); + e_mail_formatter_set_default_headers (closure->formatter); g_slist_foreach (header_config_list, (GFunc) g_free, NULL); g_slist_free (header_config_list); /* force a redraw */ - if (emf->message) - em_format_redraw (emf); + if (closure->display) { + e_mail_display_reload (closure->display); + } } static void @@ -1482,26 +1596,159 @@ remove_header_notify_cb (gpointer data) **/ void e_mail_reader_connect_headers (EMailReader *reader, - EMFormat *emf) + EMailFormatter *formatter) { GConfClient *client; guint notify_id; + struct headers_changed_closure *closure; client = gconf_client_get_default (); + closure = g_new0 (struct headers_changed_closure, 1); + closure->display = g_object_ref (e_mail_reader_get_mail_display (reader)); + closure->formatter = g_object_ref (formatter); + gconf_client_add_dir ( client, "/apps/evolution/mail/display", GCONF_CLIENT_PRELOAD_NONE, NULL); notify_id = gconf_client_notify_add ( client, "/apps/evolution/mail/display/headers", (GConfClientNotifyFunc) headers_changed_cb, - emf, NULL, NULL); + closure, (GFreeFunc) free_headers_changed_closure, NULL); g_object_set_data_full ( - G_OBJECT (emf), "reader-header-notify-id", + G_OBJECT (formatter), "reader-header-notify-id", GINT_TO_POINTER (notify_id), remove_header_notify_cb); - headers_changed_cb (client, 0, NULL, emf); + headers_changed_cb (client, 0, NULL, closure); g_object_unref (client); } + +static GHashTable * +mail_reader_get_mail_register (void) +{ + SoupSession *session; + GHashTable *mails; + + session = webkit_get_default_session (); + mails = g_object_get_data (G_OBJECT (session), "mails"); + if (!mails) { + mails = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, NULL); + g_object_set_data_full ( + G_OBJECT (session), "mails", mails, + (GDestroyNotify) g_hash_table_destroy); + } + + return mails; +} + +EMailPartList * +e_mail_reader_lookup_part_list (EMailReader *reader, + const gchar *uri) +{ + GHashTable *mails; + + g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL); + g_return_val_if_fail (uri && *uri, NULL); + + mails = mail_reader_get_mail_register (); + return g_hash_table_lookup (mails, uri); +} + +struct _formatter_weak_ref_closure { + GHashTable *part_lists; + gchar *mail_uri; +}; + +static void +part_list_weak_ref_cb (gchar *mail_uri, + EMailPartList *part_list) +{ + GHashTable *mails; + + mails = mail_reader_get_mail_register (); + + /* When this callback is called, the partslist is being finalized + * so we only remove it from the parts list table. */ + g_hash_table_remove (mails, mail_uri); + + d(printf("Destroying parts list %p (%s)\n", part_list, mail_uri)); + + g_free (mail_uri); +} + +struct format_parser_async_closure_ { + EActivity *activity; + gchar *mail_uri; + + GFunc user_callback; + gpointer user_data; +}; + +static void +format_parser_async_done_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + EMailParser *parser = E_MAIL_PARSER (source); + EMailPartList *part_list; + GHashTable *mails; + struct format_parser_async_closure_ *closure = user_data; + + part_list = e_mail_parser_parse_finish (parser, result, NULL); + + /* When no EMailDisplay holds reference to the part list, then + * the list can be destroyed. */ + g_object_weak_ref (G_OBJECT (part_list), + (GWeakNotify) part_list_weak_ref_cb, g_strdup (closure->mail_uri)); + + mails = mail_reader_get_mail_register (); + g_hash_table_insert (mails, g_strdup (closure->mail_uri), part_list); + d(printf("Registered EMailPartList %s\n", closure->mail_uri)); + + if (closure->user_callback) + closure->user_callback (part_list, closure->user_data); + + g_object_unref (closure->activity); + g_free (closure->mail_uri); + g_free (closure); + + g_object_unref (result); + g_object_unref (parser); +} + +void +e_mail_reader_parse_message (EMailReader *reader, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GFunc ready_callback, + gpointer user_data) +{ + EMailParser *parser; + EMailBackend *mail_backend; + EMailSession *mail_session; + gchar *mail_uri; + struct format_parser_async_closure_ *closure; + + mail_uri = e_mail_part_build_uri (folder, message_uid, NULL, NULL); + + mail_backend = e_mail_reader_get_backend (reader); + mail_session = e_mail_backend_get_session (mail_backend); + + closure = g_new0 (struct format_parser_async_closure_, 1); + parser = e_mail_parser_new (CAMEL_SESSION (mail_session)); + + closure->activity = e_mail_reader_new_activity (reader); + e_activity_set_text (closure->activity, _("Parsing message")); + closure->mail_uri = mail_uri; + closure->user_callback = ready_callback; + closure->user_data = user_data; + + e_mail_parser_parse (parser, folder, message_uid, + message, format_parser_async_done_cb, + e_activity_get_cancellable (closure->activity), + closure); +} diff --git a/mail/e-mail-reader-utils.h b/mail/e-mail-reader-utils.h index 6913d0964c..887e399451 100644 --- a/mail/e-mail-reader-utils.h +++ b/mail/e-mail-reader-utils.h @@ -68,7 +68,22 @@ gchar * e_mail_reader_header_to_xml (EMailReaderHeader *header); void e_mail_reader_header_free (EMailReaderHeader *header); void e_mail_reader_connect_headers (EMailReader *reader, - EMFormat *emf); + EMailFormatter *formatter); + +EMailPartList * e_mail_reader_lookup_part_list (EMailReader *reader, + const gchar *uri); + +void e_mail_reader_store_part_list (EMailReader *reader, + const gchar *uri, + EMailPartList *part_list); + +void e_mail_reader_parse_message + (EMailReader *reader, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GFunc ready_callback, + gpointer user_data); G_END_DECLS diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c index af8a0eea94..3d0911b1fc 100644 --- a/mail/e-mail-reader.c +++ b/mail/e-mail-reader.c @@ -53,12 +53,15 @@ #include "mail/em-event.h" #include "mail/em-folder-selector.h" #include "mail/em-folder-tree.h" -#include "mail/em-format-html-display.h" #include "mail/em-utils.h" #include "mail/mail-autofilter.h" #include "mail/mail-vfolder-ui.h" #include "mail/message-list.h" +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-part-utils.h> + #if HAVE_CLUTTER #include <clutter/clutter.h> #include <mx/mx.h> @@ -69,7 +72,7 @@ ((EMailReaderPrivate *) g_object_get_qdata \ (G_OBJECT (obj), quark_private)) -#define d(x) +#define d(x) x typedef struct _EMailReaderClosure EMailReaderClosure; typedef struct _EMailReaderPrivate EMailReaderPrivate; @@ -85,6 +88,8 @@ struct _EMailReaderPrivate { EMailForwardStyle forward_style; EMailReplyStyle reply_style; + EMailFormatter *formatter; + /* This timer runs when the user selects a single message. */ guint message_selected_timeout_id; @@ -280,7 +285,7 @@ action_mail_image_save_cb (GtkAction *action, { EMailDisplay *display; EWebView *web_view; - EMFormat *emf; + EMailPartList *parts; const gchar *image_src; CamelMimePart *part; EAttachment *attachment; @@ -296,13 +301,13 @@ action_mail_image_save_cb (GtkAction *action, if (!image_src) return; - emf = EM_FORMAT (e_mail_display_get_formatter (display)); - g_return_if_fail (emf != NULL); - g_return_if_fail (emf->message != NULL); + parts = e_mail_display_get_parts_list (display); + g_return_if_fail (parts != NULL); + g_return_if_fail (parts->message != NULL); if (g_str_has_prefix (image_src, "cid:")) { part = camel_mime_message_get_part_by_content_id ( - emf->message, image_src + 4); + parts->message, image_src + 4); g_return_if_fail (part != NULL); g_object_ref (part); @@ -371,20 +376,18 @@ action_mail_charset_cb (GtkRadioAction *action, GtkRadioAction *current, EMailReader *reader) { - EMailDisplay *display; - EMFormatHTML *formatter; + EMailFormatter *formatter; const gchar *charset; if (action != current) return; - display = e_mail_reader_get_mail_display (reader); - formatter = e_mail_display_get_formatter (display); + formatter = e_mail_reader_get_formatter (reader); charset = g_object_get_data (G_OBJECT (action), "charset"); /* Charset for "Default" action will be NULL. */ if (formatter) - em_format_set_charset (EM_FORMAT (formatter), charset); + e_mail_formatter_set_charset (formatter, charset); } static void @@ -1711,9 +1714,9 @@ action_mail_show_all_headers_cb (GtkToggleAction *action, display = e_mail_reader_get_mail_display (reader); if (gtk_toggle_action_get_active (action)) - e_mail_display_set_mode (display, EM_FORMAT_WRITE_MODE_ALL_HEADERS); + e_mail_display_set_mode (display, E_MAIL_FORMATTER_MODE_ALL_HEADERS); else - e_mail_display_set_mode (display, EM_FORMAT_WRITE_MODE_NORMAL); + e_mail_display_set_mode (display, E_MAIL_FORMATTER_MODE_NORMAL); } struct _source_retrieval_closure { @@ -1782,13 +1785,13 @@ action_mail_show_source_cb (GtkAction *action, g_return_if_fail (uids != NULL && uids->len == 1); message_uid = g_ptr_array_index (uids, 0); - browser = e_mail_browser_new (backend, NULL, NULL, EM_FORMAT_WRITE_MODE_SOURCE); + browser = e_mail_browser_new (backend, NULL, NULL, E_MAIL_FORMATTER_MODE_SOURCE); e_mail_reader_set_folder (E_MAIL_READER (browser), folder); e_mail_reader_set_message (E_MAIL_READER (browser), message_uid); display = e_mail_reader_get_mail_display (E_MAIL_READER (browser)); string = g_strdup_printf (_("Retrieving message '%s'"), message_uid); - e_mail_display_set_formatter (display, NULL); + e_mail_display_set_parts_list (display, NULL); e_mail_display_set_status (display, string); gtk_widget_show (browser); @@ -2664,7 +2667,7 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure) { EMailReader *reader; GtkWidget *message_list; - EMFormatHTML *formatter; + EMailPartList *parts; EMailDisplay *display; CamelMimeMessage *message; const gchar *current_uid; @@ -2675,7 +2678,7 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure) message_uid = closure->message_uid; display = e_mail_reader_get_mail_display (reader); - formatter = e_mail_display_get_formatter (display); + parts = e_mail_display_get_parts_list (display); message_list = e_mail_reader_get_message_list (reader); if (e_tree_is_dragging (E_TREE (message_list))) @@ -2684,8 +2687,8 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure) current_uid = MESSAGE_LIST (message_list)->cursor_uid; uid_is_current &= (g_strcmp0 (current_uid, message_uid) == 0); - if (formatter) - message = EM_FORMAT (formatter)->message; + if (parts) + message = parts->message; else message = NULL; @@ -2846,7 +2849,7 @@ mail_reader_message_selected_timeout_cb (EMailReader *reader) CamelFolder *folder; const gchar *cursor_uid; const gchar *format_uid; - EMFormat *formatter; + EMailPartList *parts; priv = E_MAIL_READER_GET_PRIVATE (reader); @@ -2854,10 +2857,10 @@ mail_reader_message_selected_timeout_cb (EMailReader *reader) message_list = e_mail_reader_get_message_list (reader); display = e_mail_reader_get_mail_display (reader); - formatter = EM_FORMAT (e_mail_display_get_formatter (display)); + parts = e_mail_display_get_parts_list (display); cursor_uid = MESSAGE_LIST (message_list)->cursor_uid; - format_uid = formatter ? formatter->message_uid : NULL; + format_uid = parts ? parts->message_uid : NULL; if (MESSAGE_LIST (message_list)->last_sel_single) { GtkWidget *widget; @@ -2877,7 +2880,7 @@ mail_reader_message_selected_timeout_cb (EMailReader *reader) gchar *string; string = g_strdup_printf (_("Retrieving message '%s'"), cursor_uid); - e_mail_display_set_formatter (display, NULL); + e_mail_display_set_parts_list (display, NULL); e_mail_display_set_status (display, string); g_free (string); @@ -2900,7 +2903,7 @@ mail_reader_message_selected_timeout_cb (EMailReader *reader) priv->retrieving_message = g_object_ref (cancellable); } } else { - e_mail_display_set_formatter (display, NULL); + e_mail_display_set_parts_list (display, NULL); priv->restoring_message_selection = FALSE; } @@ -3100,52 +3103,25 @@ mail_reader_folder_loaded (EMailReader *reader) e_mail_reader_update_actions (reader, state); } -struct _formatter_weak_ref_closure { - GHashTable *formatters; - gchar *mail_uri; -}; - -static void -formatter_weak_ref_cb (struct _formatter_weak_ref_closure *data, - EMFormat *formatter) -{ - /* When this callback is called, the formatter is being finalized - * so we only remove it from the formatters table. */ - g_hash_table_remove (data->formatters, - data->mail_uri); - - d(printf("Destroying formatter %p (%s)\n", formatter, data->mail_uri)); - - /* Destroying the formatter will prevent this callback - * being called, so we can remove the closure data as well */ - g_hash_table_unref (data->formatters); - g_free (data->mail_uri); - g_free (data); -} - struct format_parser_async_closure_ { + struct _formatter_weak_ref_closure *weak_ref_closure; EMailDisplay *display; EActivity *activity; }; static void -format_parser_async_done_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) +set_mail_display_part_list (EMailPartList *part_list, + gpointer user_data) { - EMFormat *emf = EM_FORMAT (source); - struct format_parser_async_closure_ *closure = user_data; - - e_mail_display_set_formatter (closure->display, EM_FORMAT_HTML (emf)); - e_mail_display_load (closure->display, emf->uri_base); + EMailDisplay *display = user_data; - g_object_unref (closure->activity); - g_object_unref (closure->display); - g_free (closure); + e_mail_display_set_parts_list (display, part_list); + e_mail_display_load (display, NULL); - /* Remove the reference added when formatter was created, + g_object_unref (display); + /* Remove the reference added when parts list was created, * so that only owners are EMailDisplays */ - g_object_unref (emf); + g_object_unref (part_list); } static void @@ -3155,67 +3131,23 @@ mail_reader_set_display_formatter_for_message (EMailReader *reader, CamelMimeMessage *message, CamelFolder *folder) { - SoupSession *session; - GHashTable *formatters; - EMFormat *formatter; + EMailPartList *parts; gchar *mail_uri; - mail_uri = em_format_build_mail_uri (folder, message_uid, NULL, NULL); + mail_uri = e_mail_part_build_uri (folder, message_uid, NULL, NULL); + parts = e_mail_reader_lookup_part_list (reader, mail_uri); + g_free (mail_uri); - session = webkit_get_default_session (); - formatters = g_object_get_data (G_OBJECT (session), "formatters"); - if (!formatters) { - formatters = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify) g_free, NULL); - g_object_set_data_full ( - G_OBJECT (session), "formatters", formatters, - (GDestroyNotify) g_hash_table_destroy); - } - - if ((formatter = g_hash_table_lookup (formatters, mail_uri)) == NULL) { - EMailBackend *mail_backend; - EMailSession *mail_session; - struct _formatter_weak_ref_closure *formatter_data = - g_new0 (struct _formatter_weak_ref_closure, 1); - - struct format_parser_async_closure_ *closure; - - formatter_data->formatters = g_hash_table_ref (formatters); - formatter_data->mail_uri = g_strdup (mail_uri); - - mail_backend = e_mail_reader_get_backend (reader); - mail_session = e_mail_backend_get_session (mail_backend); - - formatter = EM_FORMAT ( - em_format_html_display_new ( - CAMEL_SESSION (mail_session))); - - /* When no EMailDisplay holds reference to the formatter, then - * the formatter can be destroyed. */ - g_object_weak_ref (G_OBJECT (formatter), - (GWeakNotify) formatter_weak_ref_cb, formatter_data); - - formatter->message_uid = g_strdup (message_uid); - formatter->uri_base = g_strdup (mail_uri); - - e_mail_reader_connect_headers (reader, formatter); + if (parts == NULL) { - closure = g_new0 (struct format_parser_async_closure_, 1); - closure->activity = e_mail_reader_new_activity (reader); - e_activity_set_text (closure->activity, _("Parsing message")); - closure->display = g_object_ref (display); + e_mail_reader_parse_message ( + reader, folder, message_uid, message, + (GFunc) set_mail_display_part_list, + g_object_ref (display)); - em_format_parse_async (formatter, message, folder, - e_activity_get_cancellable (closure->activity), - format_parser_async_done_cb, closure); - - /* Don't free the mail_uri!! */ - g_hash_table_insert (formatters, mail_uri, formatter); } else { - e_mail_display_set_formatter (display, EM_FORMAT_HTML (formatter)); - e_mail_display_load (display, formatter->uri_base); - - g_free (mail_uri); + e_mail_display_set_parts_list (display, parts); + e_mail_display_load (display, NULL); } } @@ -3895,6 +3827,12 @@ e_mail_reader_init (EMailReader *reader, message_list = e_mail_reader_get_message_list (reader); display = e_mail_reader_get_mail_display (reader); + /* Initialize a private struct. */ + g_object_set_qdata_full ( + G_OBJECT (reader), quark_private, + g_slice_new0 (EMailReaderPrivate), + (GDestroyNotify) mail_reader_private_free); + if (!init_actions) goto connect_signals; @@ -4114,7 +4052,7 @@ e_mail_reader_init (EMailReader *reader, connect_signals: if (!connect_signals) - goto init_private; + return; /* Connect signals. */ g_signal_connect_swapped ( @@ -4158,15 +4096,6 @@ connect_signals: g_signal_connect_swapped ( message_list, "selection-change", G_CALLBACK (e_mail_reader_changed), reader); - -init_private: - - /* Initialize a private struct. */ - - g_object_set_qdata_full ( - G_OBJECT (reader), quark_private, - g_slice_new0 (EMailReaderPrivate), - (GDestroyNotify) mail_reader_private_free); } void @@ -4805,3 +4734,39 @@ e_mail_reader_avoid_next_mark_as_seen (EMailReader *reader) priv->avoid_next_mark_as_seen = TRUE; } + +EMailFormatter * +e_mail_reader_get_formatter (EMailReader *reader) +{ + EMailReaderPrivate *priv; + + g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL); + + priv = E_MAIL_READER_GET_PRIVATE (reader); + g_return_val_if_fail (priv != NULL, NULL); + + return priv->formatter; +} + +void +e_mail_reader_set_formatter (EMailReader *reader, + EMailFormatter *formatter) +{ + EMailReaderPrivate *priv; + + g_return_if_fail (E_IS_MAIL_READER (reader)); + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + priv = E_MAIL_READER_GET_PRIVATE (reader); + g_return_if_fail (priv != NULL); + + g_object_ref (formatter); + + if (priv->formatter) { + g_object_unref (priv->formatter); + } + + priv->formatter = formatter; + + e_mail_reader_connect_headers (reader, formatter); +} diff --git a/mail/e-mail-reader.h b/mail/e-mail-reader.h index 6f03fc0cfe..b33aee7554 100644 --- a/mail/e-mail-reader.h +++ b/mail/e-mail-reader.h @@ -179,6 +179,10 @@ void e_mail_reader_enable_show_folder void e_mail_reader_avoid_next_mark_as_seen (EMailReader *reader); +EMailFormatter * e_mail_reader_get_formatter (EMailReader *reader); +void e_mail_reader_set_formatter (EMailReader *reader, + EMailFormatter *formatter); + G_END_DECLS #endif /* E_MAIL_READER_H */ diff --git a/mail/e-mail-request.c b/mail/e-mail-request.c index 7522bc5e7e..01c114405d 100644 --- a/mail/e-mail-request.c +++ b/mail/e-mail-request.c @@ -24,10 +24,14 @@ #include <libsoup/soup-requester.h> #include <libsoup/soup-request-http.h> +#include <webkit/webkit.h> + #include <glib/gi18n.h> #include <camel/camel.h> -#include "em-format-html.h" +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-formatter-utils.h> +#include <em-format/e-mail-formatter-print.h> #include <e-util/e-icon-factory.h> #include <e-util/e-util.h> @@ -40,15 +44,13 @@ ((obj), E_TYPE_MAIL_REQUEST, EMailRequestPrivate)) struct _EMailRequestPrivate { - EMFormatHTML *efh; - CamelStream *output_stream; - EMFormatPURI *puri; gchar *mime_type; gint content_length; GHashTable *uri_query; + gchar *uri_base; gchar *ret_mime_type; }; @@ -61,14 +63,17 @@ handle_mail_request (GSimpleAsyncResult *res, GCancellable *cancellable) { EMailRequest *request = E_MAIL_REQUEST (object); - EMFormatHTML *efh = request->priv->efh; - EMFormat *emf = EM_FORMAT (efh); GInputStream *stream; + EMailFormatter *formatter; + EMailPartList *part_list; + SoupSession *session; + GHashTable *mails; GByteArray *ba; gchar *part_id; - EMFormatWriterInfo info = {0}; gchar *val; + EMailFormatterContext context = { 0 }; + if (g_cancellable_is_cancelled (cancellable)) return; @@ -76,39 +81,67 @@ handle_mail_request (GSimpleAsyncResult *res, g_object_unref (request->priv->output_stream); } + session = webkit_get_default_session (); + mails = g_object_get_data (G_OBJECT (session), "mails"); + part_list = g_hash_table_lookup (mails, request->priv->uri_base); + g_return_if_fail (part_list != NULL); + request->priv->output_stream = camel_stream_mem_new (); val = g_hash_table_lookup (request->priv->uri_query, "headers_collapsed"); - if (val) - info.headers_collapsed = atoi (val); + if (val && atoi (val) == 1) + context.flags |= E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED; val = g_hash_table_lookup (request->priv->uri_query, "headers_collapsable"); - if (val) - info.headers_collapsable = atoi (val); + if (val && atoi (val) == 1) + context.flags |= E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE; val = g_hash_table_lookup (request->priv->uri_query, "mode"); if (val) - info.mode = atoi (val); + context.mode = atoi (val); + + context.message = part_list->message; + context.message_uid = part_list->message_uid; + context.folder = part_list->folder; + context.parts = part_list->list; + + if (context.mode == E_MAIL_FORMATTER_MODE_PRINTING) + formatter = e_mail_formatter_print_new (); + else + formatter = e_mail_formatter_new (); part_id = g_hash_table_lookup (request->priv->uri_query, "part_id"); if (part_id) { + EMailPart *part; + const gchar *mime_type; /* original part_id is owned by the GHashTable */ part_id = soup_uri_decode (part_id); - request->priv->puri = em_format_find_puri (emf, part_id); + part = e_mail_part_list_find_part (part_list, part_id); - if (request->priv->puri) { - em_format_puri_write (request->priv->puri, - request->priv->output_stream, &info, cancellable); + val = g_hash_table_lookup (request->priv->uri_query, "mime_type"); + if (val) { + mime_type = val; + } else { + mime_type = NULL; + } + + if (context.mode == E_MAIL_FORMATTER_MODE_SOURCE) { + mime_type = "application/vnd.evolution.source"; + } + + if (part) { + e_mail_formatter_format_as ( + formatter, &context, part, request->priv->output_stream, + mime_type ? mime_type : part->mime_type, cancellable); } else { g_warning ("Failed to lookup requested part '%s' - this should not happen!", part_id); } g_free (part_id); } else { - if (info.mode == 0) - info.mode = EM_FORMAT_WRITE_MODE_NORMAL; - - em_format_write (emf, request->priv->output_stream, &info, cancellable); + e_mail_formatter_format_sync ( + formatter, part_list, request->priv->output_stream, + context.flags, context.mode, cancellable); } /* Convert the GString to GInputStream and send it back to WebKit */ @@ -136,10 +169,7 @@ mail_request_finalize (GObject *object) { EMailRequest *request = E_MAIL_REQUEST (object); - if (request->priv->output_stream) { - g_object_unref (request->priv->output_stream); - request->priv->output_stream = NULL; - } + g_clear_object (&request->priv->output_stream); if (request->priv->mime_type) { g_free (request->priv->mime_type); @@ -156,9 +186,9 @@ mail_request_finalize (GObject *object) request->priv->ret_mime_type = NULL; } - if (request->priv->efh) { - g_object_unref (request->priv->efh); - request->priv->efh = NULL; + if (request->priv->uri_base) { + g_free (request->priv->uri_base); + request->priv->uri_base = NULL; } G_OBJECT_CLASS (e_mail_request_parent_class)->finalize (object); @@ -178,14 +208,11 @@ mail_request_send_async (SoupRequest *request, GAsyncReadyCallback callback, gpointer user_data) { - SoupSession *session; EMailRequest *emr = E_MAIL_REQUEST (request); GSimpleAsyncResult *simple; SoupURI *uri; - GHashTable *formatters; gchar *uri_str; - session = soup_request_get_session (request); uri = soup_request_get_uri (request); d(printf("received request for %s\n", soup_uri_to_string (uri, FALSE))); @@ -196,18 +223,9 @@ mail_request_send_async (SoupRequest *request, emr->priv->uri_query = NULL; } - formatters = g_object_get_data (G_OBJECT (session), "formatters"); - g_return_if_fail (formatters != NULL); - uri_str = g_strdup_printf ( "%s://%s%s", uri->scheme, uri->host, uri->path); - emr->priv->efh = g_hash_table_lookup (formatters, uri_str); - g_free (uri_str); - - g_return_if_fail (emr->priv->efh); - - /* Make sure the formatter lives until we are finished here */ - g_object_ref (emr->priv->efh); + emr->priv->uri_base = uri_str; simple = g_simple_async_result_new ( G_OBJECT (request), callback, @@ -229,10 +247,11 @@ mail_request_send_finish (SoupRequest *request, { GInputStream *stream; - stream = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); + stream = g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (result)); /* Reset the stream before passing it back to webkit */ - if (stream && G_IS_SEEKABLE (stream)) + if (G_IS_INPUT_STREAM (stream) && G_IS_SEEKABLE (stream)) g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL); if (!stream) /* We must always return something */ @@ -269,13 +288,8 @@ mail_request_get_content_type (SoupRequest *request) if (emr->priv->mime_type) { mime_type = g_strdup (emr->priv->mime_type); - } else if (!emr->priv->puri) { - mime_type = g_strdup ("text/html"); - } else if (!emr->priv->puri->mime_type) { - CamelContentType *ct = camel_mime_part_get_content_type (emr->priv->puri->part); - mime_type = camel_content_type_simple (ct); } else { - mime_type = g_strdup (emr->priv->puri->mime_type); + mime_type = g_strdup ("text/html"); } if (g_strcmp0 (mime_type, "text/html") == 0) { diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index 4fe141447a..71754a6d0d 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -43,6 +43,9 @@ #include <libemail-engine/mail-ops.h> #include <libemail-engine/mail-tools.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-formatter-quote.h> + #include <shell/e-shell.h> #include <composer/e-msg-composer.h> @@ -54,10 +57,6 @@ #include "em-composer-utils.h" #include "em-folder-selector.h" #include "em-folder-tree.h" -#include "em-format-html.h" -#include "em-format-html-print.h" -#include "em-format-html-display.h" -#include "em-format-quote.h" #include "em-event.h" #include "mail-send-recv.h" @@ -958,8 +957,6 @@ composer_print_done_cb (EMailPrinter *emp, GtkPrintOperationResult result, gpointer user_data) { - EMFormat *emf = user_data; - g_object_unref (emf); g_object_unref (emp); } @@ -971,23 +968,23 @@ em_utils_composer_print_cb (EMsgComposer *composer, EMailSession *session) { EMailPrinter *emp; - EMFormatHTMLDisplay *efhd; + EMailParser *parser; + EMailPartList *parts; const gchar *message_id; - efhd = em_format_html_display_new (CAMEL_SESSION (session)); + parser = e_mail_parser_new (CAMEL_SESSION (session)); message_id = camel_mime_message_get_message_id (message); - ((EMFormat *) efhd)->message_uid = g_strdup (message_id); - - /* Parse the message */ - em_format_parse ((EMFormat *) efhd, message, NULL, NULL); + parts = e_mail_parser_parse_sync (parser, NULL, g_strdup (message_id), message, NULL); /* Use EMailPrinter and WebKit to print the message */ - emp = e_mail_printer_new ((EMFormatHTML *) efhd); + emp = e_mail_printer_new (parts); g_signal_connect (emp, "done", - G_CALLBACK (composer_print_done_cb), efhd); + G_CALLBACK (composer_print_done_cb), NULL); e_mail_printer_print (emp, FALSE, NULL); + + g_object_unref (parts); } /* Composing messages... */ @@ -1543,13 +1540,13 @@ emu_update_composers_security (EMsgComposer *composer, shell_settings = e_shell_get_shell_settings (shell); sign_by_default = - (validity_found & EM_FORMAT_VALIDITY_FOUND_SIGNED) != 0 && + (validity_found & E_MAIL_PART_VALIDITY_SIGNED) != 0 && e_shell_settings_get_boolean ( shell_settings, "composer-sign-reply-if-signed"); /* Pre-set only for encrypted messages, not for signed */ if (sign_by_default) { - if (validity_found & EM_FORMAT_VALIDITY_FOUND_SMIME) + if (validity_found & E_MAIL_PART_VALIDITY_SMIME) action = E_COMPOSER_ACTION_SMIME_SIGN (composer); else action = E_COMPOSER_ACTION_PGP_SIGN (composer); @@ -1558,8 +1555,8 @@ emu_update_composers_security (EMsgComposer *composer, GTK_TOGGLE_ACTION (action), TRUE); } - if (validity_found & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED) { - if (validity_found & EM_FORMAT_VALIDITY_FOUND_SMIME) + if (validity_found & E_MAIL_PART_VALIDITY_ENCRYPTED) { + if (validity_found & E_MAIL_PART_VALIDITY_SMIME) action = E_COMPOSER_ACTION_SMIME_ENCRYPT (composer); else action = E_COMPOSER_ACTION_PGP_ENCRYPT (composer); @@ -1703,9 +1700,9 @@ forward_non_attached (EShell *shell, guint32 validity_found = 0; guint32 flags; - flags = EM_FORMAT_QUOTE_HEADERS | EM_FORMAT_QUOTE_KEEP_SIG; + flags = E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS | E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG; if (style == E_MAIL_FORWARD_STYLE_QUOTED) - flags |= EM_FORMAT_QUOTE_CITE; + flags |= E_MAIL_FORMATTER_QUOTE_FLAG_CITE; forward = quoting_text (QUOTING_FORWARD); text = em_utils_message_to_html ( @@ -2760,7 +2757,7 @@ static void composer_set_body (EMsgComposer *composer, CamelMimeMessage *message, EMailReplyStyle style, - EMFormat *source) + EMailPartList *parts_list) { gchar *text, *credits, *original; CamelMimePart *part; @@ -2788,8 +2785,8 @@ composer_set_body (EMsgComposer *composer, case E_MAIL_REPLY_STYLE_OUTLOOK: original = quoting_text (QUOTING_ORIGINAL); text = em_utils_message_to_html ( - session, message, original, EM_FORMAT_QUOTE_HEADERS, - source, start_bottom ? "<BR>" : NULL, &validity_found); + session, message, original, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS, + parts_list, start_bottom ? "<BR>" : NULL, &validity_found); e_msg_composer_set_body_text (composer, text, TRUE); has_body_text = text && *text; g_free (text); @@ -2802,8 +2799,8 @@ composer_set_body (EMsgComposer *composer, /* do what any sane user would want when replying... */ credits = attribution_format (message); text = em_utils_message_to_html ( - session, message, credits, EM_FORMAT_QUOTE_CITE, - source, start_bottom ? "<BR>" : NULL, &validity_found); + session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE, + parts_list, start_bottom ? "<BR>" : NULL, &validity_found); g_free (credits); e_msg_composer_set_body_text (composer, text, TRUE); has_body_text = text && *text; @@ -2842,7 +2839,7 @@ composer_set_body (EMsgComposer *composer, gchar * em_utils_construct_composer_text (CamelSession *session, CamelMimeMessage *message, - EMFormat *source) + EMailPartList *parts_list) { gchar *text, *credits; gboolean start_bottom = 0; @@ -2851,8 +2848,8 @@ em_utils_construct_composer_text (CamelSession *session, credits = attribution_format (message); text = em_utils_message_to_html ( - session, message, credits, EM_FORMAT_QUOTE_CITE, - source, start_bottom ? "<BR>" : NULL, NULL); + session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE, + parts_list, start_bottom ? "<BR>" : NULL, NULL); g_free (credits); return text; @@ -2881,7 +2878,7 @@ em_utils_reply_to_message (EShell *shell, const gchar *message_uid, EMailReplyType type, EMailReplyStyle style, - EMFormat *source_formatter, + EMailPartList *parts_list, CamelInternetAddress *address) { ESourceRegistry *registry; @@ -2956,7 +2953,7 @@ em_utils_reply_to_message (EShell *shell, g_object_unref (to); g_object_unref (cc); - composer_set_body (composer, message, style, source_formatter); + composer_set_body (composer, message, style, parts_list); if (folder != NULL) { gchar *folder_uri; diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h index a525cec2a2..93d87bfb2e 100644 --- a/mail/em-composer-utils.h +++ b/mail/em-composer-utils.h @@ -24,7 +24,7 @@ #ifndef EM_COMPOSER_UTILS_H #define EM_COMPOSER_UTILS_H -#include <em-format/em-format.h> +#include <em-format/e-mail-part.h> #include <mail/e-mail-backend.h> #include <mail/e-mail-reader.h> #include <composer/e-msg-composer.h> @@ -62,7 +62,7 @@ void em_utils_redirect_message (EShell *shell, gchar * em_utils_construct_composer_text (CamelSession *session, CamelMimeMessage *message, - EMFormat *source_formatter); + EMailPartList *source_formatter); gboolean em_utils_is_munged_list_message (CamelMimeMessage *message); void em_utils_get_reply_sender (CamelMimeMessage *message, CamelInternetAddress *to, @@ -78,7 +78,7 @@ EMsgComposer * em_utils_reply_to_message (EShell *shell, const gchar *message_uid, EMailReplyType type, EMailReplyStyle style, - EMFormat *source, + EMailPartList *source, CamelInternetAddress *address); EDestination ** em_utils_camel_address_to_destination (CamelInternetAddress *iaddr); diff --git a/mail/em-format-hook.c b/mail/em-format-hook.c deleted file mode 100644 index 7ceeb20e4d..0000000000 --- a/mail/em-format-hook.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <stdlib.h> - -#include "em-format-hook.h" - -#include <glib/gi18n.h> - -/* class name -> class map for EMFormat and subclasses */ -static GHashTable *emfh_types; - -/* ********************************************************************** */ - -/* Mail formatter handler plugin */ - -/* - * <hook class="org.gnome.evolution.mail.format:1.0"> - * <group id="EMFormatHTML"> - * <item flags="inline,inline_disposition" - * mime_type="text/vcard" - * format="format_vcard"/> - * </group> - * </hook> - */ - -#define emfh ((EMFormatHook *)eph) - -#define d(x) - -static const EPluginHookTargetKey emfh_flag_map[] = { - { "inline", EM_FORMAT_HANDLER_INLINE }, - { "inline_disposition", EM_FORMAT_HANDLER_INLINE_DISPOSITION }, - { NULL } -}; - -G_DEFINE_TYPE (EMFormatHook, em_format_hook, E_TYPE_PLUGIN_HOOK) - -static void -emfh_parse_part (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - struct _EMFormatHookItem *item = (EMFormatHookItem *) info->handler; - - if (item->hook->hook.plugin->enabled) { - EMFormatHookTarget target = { - emf, part, part_id, info - }; - - e_plugin_invoke (item->hook->hook.plugin, item->format, &target); - } else if (info->handler->old) { - info->handler->old->parse_func ( - emf, part, part_id, info, cancellable); - } -} - -static void -emfh_free_item (struct _EMFormatHookItem *item) -{ - /* FIXME: remove from formatter class */ - - g_free (item->handler.mime_type); - g_free (item->format); - g_free (item); -} - -static void -emfh_free_group (struct _EMFormatHookGroup *group) -{ - g_slist_foreach (group->items, (GFunc) emfh_free_item, NULL); - g_slist_free (group->items); - - g_free (group->id); - g_free (group); -} - -static struct _EMFormatHookItem * -emfh_construct_item (EPluginHook *eph, - EMFormatHookGroup *group, - xmlNodePtr root) -{ - struct _EMFormatHookItem *item; - - d(printf(" loading group item\n")); - item = g_malloc0 (sizeof (*item)); - - item->handler.mime_type = e_plugin_xml_prop(root, "mime_type"); - item->handler.flags = e_plugin_hook_mask(root, emfh_flag_map, "flags"); - item->format = e_plugin_xml_prop(root, "format"); - - item->handler.parse_func = emfh_parse_part; - item->hook = emfh; - - if (item->handler.mime_type == NULL || item->format == NULL) - goto error; - - d(printf(" type='%s' format='%s'\n", item->handler.mime_type, item->format)); - - return item; -error: - d(printf("error!\n")); - emfh_free_item (item); - return NULL; -} - -static struct _EMFormatHookGroup * -emfh_construct_group (EPluginHook *eph, - xmlNodePtr root) -{ - struct _EMFormatHookGroup *group; - xmlNodePtr node; - - d(printf(" loading group\n")); - group = g_malloc0 (sizeof (*group)); - - group->id = e_plugin_xml_prop(root, "id"); - if (group->id == NULL) - goto error; - - node = root->children; - while (node) { - if (0 == strcmp((gchar *)node->name, "item")) { - struct _EMFormatHookItem *item; - - item = emfh_construct_item (eph, group, node); - if (item) - group->items = g_slist_append (group->items, item); - } - node = node->next; - } - - return group; -error: - emfh_free_group (group); - return NULL; -} - -static gint -emfh_construct (EPluginHook *eph, - EPlugin *ep, - xmlNodePtr root) -{ - xmlNodePtr node; - - d(printf("loading format hook\n")); - - if (((EPluginHookClass *) em_format_hook_parent_class)-> - construct (eph, ep, root) == -1) - return -1; - - node = root->children; - while (node) { - if (strcmp((gchar *)node->name, "group") == 0) { - struct _EMFormatHookGroup *group; - - group = emfh_construct_group (eph, node); - if (group) { - EMFormatClass *class; - - if (emfh_types - && (class = g_hash_table_lookup (emfh_types, group->id))) { - GSList *l = group->items; - - for (; l; l = g_slist_next (l)) { - EMFormatHookItem *item = l->data; - /* TODO: only add handlers if enabled? */ - /* Well, disabling is handled by the callback, - * if we leave as is, then we can enable the - * plugin after startup and it will start - * working automagically */ - em_format_class_add_handler (class, &item->handler); - } - } - /* We don't actually need to keep this - * around once its set on the class. */ - emfh->groups = g_slist_append (emfh->groups, group); - } - } - node = node->next; - } - - eph->plugin = ep; - - /* Load the plugin as it does a few thing in the formatter thread. */ - ((EPluginClass *) G_OBJECT_GET_CLASS (ep))->enable (ep, 1); - return 0; -} - -static void -emfh_enable (EPluginHook *eph, - gint state) -{ - GSList *g, *l; - EMFormatClass *class; - - g = emfh->groups; - if (emfh_types == NULL) - return; - - for (; g; g = g_slist_next (g)) { - struct _EMFormatHookGroup *group = g->data; - - class = g_hash_table_lookup (emfh_types, group->id); - for (l = group->items; l; l = g_slist_next (l)) { - EMFormatHookItem *item = l->data; - - if (state) - em_format_class_add_handler (class, &item->handler); - else - em_format_class_remove_handler (class, &item->handler); - } - } -} - -static void -format_hook_finalize (GObject *object) -{ - EPluginHook *eph = (EPluginHook *) object; - - g_slist_foreach (emfh->groups, (GFunc) emfh_free_group, NULL); - g_slist_free (emfh->groups); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (em_format_hook_parent_class)->finalize (object); -} - -static void -em_format_hook_class_init (EMFormatHookClass *class) -{ - GObjectClass *object_class; - EPluginHookClass *hook_class; - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = format_hook_finalize; - - hook_class = E_PLUGIN_HOOK_CLASS (class); - hook_class->construct = emfh_construct; - hook_class->enable = emfh_enable; - hook_class->id = "org.gnome.evolution.mail.format:1.0"; -} - -static void -em_format_hook_init (EMFormatHook *hook) -{ -} - -void -em_format_hook_register_type (GType type) -{ - EMFormatClass *class; - - if (emfh_types == NULL) - emfh_types = g_hash_table_new (g_str_hash, g_str_equal); - - d(printf("registering formatter type '%s'\n", g_type_name(type))); - - class = g_type_class_ref (type); - g_hash_table_insert (emfh_types, (gpointer) g_type_name (type), class); -} diff --git a/mail/em-format-hook.h b/mail/em-format-hook.h deleted file mode 100644 index 09076c2f48..0000000000 --- a/mail/em-format-hook.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michel Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef __EM_FORMAT_HOOK_H__ -#define __EM_FORMAT_HOOK_H__ - -#include <e-util/e-plugin.h> -#include <em-format/em-format.h> - -G_BEGIN_DECLS - -typedef struct _EMFormatHookItem EMFormatHookItem; -typedef struct _EMFormatHookGroup EMFormatHookGroup; -typedef struct _EMFormatHook EMFormatHook; -typedef struct _EMFormatHookClass EMFormatHookClass; - -typedef struct _EMFormatHookTarget EMFormatHookTarget; - -typedef void (*EMFormatHookFunc)(struct _EPlugin *plugin, EMFormatHookTarget *data); - -struct _EMFormatHookTarget { - struct _EMFormat *format; - CamelMimePart *part; - GString *part_id; - EMFormatParserInfo *info; -}; - -struct _EMFormatHookItem { - EMFormatHandler handler; - - struct _EMFormatHook *hook; /* parent pointer */ - gchar *format; /* format handler */ -}; - -struct _EMFormatHookGroup { - struct _EMFormatHook *hook; /* parent pointer */ - gchar *id; /* target formatter id */ - GSList *items; /* items to consider */ -}; - -/** - * struct _EMFormatHook - Mail formatter hook. - * - * @hook: - * @groups: - * - * The Mail formatter hook links all of the plugin formatter hooks - * into the relevent formatter classes. - **/ -struct _EMFormatHook { - EPluginHook hook; - - GSList *groups; -}; - -struct _EMFormatHookClass { - EPluginHookClass hook_class; - - /* which class to add matching items to */ - GHashTable *format_classes; -}; - -GType em_format_hook_get_type (void); - -/* register a type as a possible formatter hook point */ -void em_format_hook_register_type (GType type); - -G_END_DECLS - -#endif /* __EM_FORMAT_HOOK_H__ */ diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c deleted file mode 100644 index efa5881542..0000000000 --- a/mail/em-format-html-display.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> - -#include <gtk/gtk.h> -#include <glib/gstdio.h> -#include <gdk/gdkkeysyms.h> - -#ifdef G_OS_WIN32 -/* Work around 'DATADIR' and 'interface' lossage in <windows.h> */ -#define DATADIR crap_DATADIR -#include <windows.h> -#undef DATADIR -#undef interface -#endif - -#include <glib/gi18n.h> - -#include <e-util/e-util.h> -#include <e-util/e-util-private.h> - -#include "e-util/e-datetime-format.h" -#include <e-util/e-dialog-utils.h> -#include <e-util/e-icon-factory.h> - -#include <shell/e-shell.h> -#include <shell/e-shell-utils.h> - -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) -#include "certificate-viewer.h" -#include "e-cert-db.h" -#endif - -#include "e-mail-display.h" -#include "e-mail-attachment-bar.h" -#include "em-format-html-display.h" -#include "em-utils.h" -#include "widgets/misc/e-attachment.h" -#include "widgets/misc/e-attachment-button.h" -#include "widgets/misc/e-attachment-view.h" -#include "shell/e-shell.h" -#include "shell/e-shell-window.h" - -#define EM_FORMAT_HTML_DISPLAY_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplayPrivate)) - -#define d(x) - -#define EM_FORMAT_HTML_DISPLAY_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplayPrivate)) - -struct _EMFormatHTMLDisplayPrivate { - - EAttachmentView *last_view; - -}; - -/* TODO: move the dialogue elsehwere */ -/* FIXME: also in em-format-html.c */ -static const struct { - const gchar *icon, *shortdesc, *description; -} smime_sign_table[5] = { - { "stock_signature-bad", N_("Unsigned"), N_("This message is not signed. There is no guarantee that this message is authentic.") }, - { "stock_signature-ok", N_("Valid signature"), N_("This message is signed and is valid meaning that it is very likely that this message is authentic.") }, - { "stock_signature-bad", N_("Invalid signature"), N_("The signature of this message cannot be verified, it may have been altered in transit.") }, - { "stock_signature", N_("Valid signature, but cannot verify sender"), N_("This message is signed with a valid signature, but the sender of the message cannot be verified.") }, - { "stock_signature-bad", N_("Signature exists, but need public key"), N_("This message is signed with a signature, but there is no corresponding public key.") }, - -}; - -static const struct { - const gchar *icon, *shortdesc, *description; -} smime_encrypt_table[4] = { - { "stock_lock-broken", N_("Unencrypted"), N_("This message is not encrypted. Its content may be viewed in transit across the Internet.") }, - { "stock_lock-ok", N_("Encrypted, weak"), N_("This message is encrypted, but with a weak encryption algorithm. It would be difficult, but not impossible for an outsider to view the content of this message in a practical amount of time.") }, - { "stock_lock-ok", N_("Encrypted"), N_("This message is encrypted. It would be difficult for an outsider to view the content of this message.") }, - { "stock_lock-ok", N_("Encrypted, strong"), N_("This message is encrypted, with a strong encryption algorithm. It would be very difficult for an outsider to view the content of this message in a practical amount of time.") }, -}; - -static const GdkRGBA smime_sign_colour[5] = { - { 0 }, { 0.53, 0.73, 0.53, 1 }, { 0.73, 0.53, 0.53, 1 }, { 0.91, 0.82, 0.13, 1 }, { 0 }, -}; - -static void efhd_message_prefix (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efhd_message_add_bar (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efhd_parse_attachment (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efhd_parse_secure (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); - -static GtkWidget * efhd_attachment_bar (EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable); -static GtkWidget * efhd_attachment_button (EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable); - -static void efhd_write_attachment_bar (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efhd_write_attachment (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efhd_write_secure_button (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -static void efhd_free_attach_puri_data (EMFormatPURI *puri); - -static void efhd_builtin_init (EMFormatHTMLDisplayClass *efhc); - -static gpointer parent_class; - -static EAttachmentStore * -find_parent_attachment_store (EMFormatHTMLDisplay *efhd, - const gchar *part_id) -{ - EMFormat *emf = (EMFormat *) efhd; - EMFormatAttachmentBarPURI *abp; - gchar *tmp, *pos; - GList *item; - - tmp = g_strdup (part_id); - - do { - gchar *id; - - pos = g_strrstr (tmp, "."); - if (!pos) - break; - - g_free (tmp); - tmp = g_strndup (part_id, pos - tmp); - id = g_strdup_printf ("%s.attachment-bar", tmp); - - item = g_hash_table_lookup (emf->mail_part_table, id); - - g_free (id); - - } while (pos && !item); - - g_free (tmp); - - abp = (EMFormatAttachmentBarPURI *) item->data; - - if (abp) - return abp->store; - else - return NULL; -} - -static void -efhd_attachment_bar_puri_free (EMFormatPURI *puri) -{ - EMFormatAttachmentBarPURI *abp; - - abp = (EMFormatAttachmentBarPURI *) puri; - - if (abp->store) { - g_object_unref (abp->store); - abp->store = NULL; - } -} - -static void -efhd_xpkcs7mime_free (EMFormatPURI *puri) -{ - EMFormatSMIMEPURI *sp = (EMFormatSMIMEPURI *) puri; - - if (sp->widget) - gtk_widget_destroy (sp->widget); - - if (sp->description) - g_free (sp->description); - - if (sp->valid) - camel_cipher_validity_free (sp->valid); -} - -static void -efhd_xpkcs7mime_info_response (GtkWidget *widget, - guint button, - EMFormatSMIMEPURI *po) -{ - gtk_widget_destroy (widget); - po->widget = NULL; -} - -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) -static void -efhd_xpkcs7mime_viewcert_clicked (GtkWidget *button, - EMFormatSMIMEPURI *po) -{ - CamelCipherCertInfo *info = g_object_get_data((GObject *)button, "e-cert-info"); - ECert *ec = NULL; - - if (info->cert_data) - ec = e_cert_new (CERT_DupCertificate (info->cert_data)); - - if (ec != NULL) { - GtkWidget *w = certificate_viewer_show (ec); - - /* oddly enough certificate_viewer_show doesn't ... */ - gtk_widget_show (w); - g_signal_connect ( - w, "response", - G_CALLBACK (gtk_widget_destroy), NULL); - - if (w && po->widget) - gtk_window_set_transient_for ((GtkWindow *) w, (GtkWindow *) po->widget); - - g_object_unref (ec); - } else { - g_warning("can't find certificate for %s <%s>", info->name?info->name:"", info->email?info->email:""); - } -} -#endif - -static void -efhd_xpkcs7mime_add_cert_table (GtkWidget *grid, - GQueue *certlist, - EMFormatSMIMEPURI *po) -{ - GList *head, *link; - GtkTable *table; - gint n = 0; - - table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE); - - head = g_queue_peek_head_link (certlist); - - for (link = head; link != NULL; link = g_list_next (link)) { - CamelCipherCertInfo *info = link->data; - gchar *la = NULL; - const gchar *l = NULL; - - if (info->name) { - if (info->email && strcmp (info->name, info->email) != 0) - l = la = g_strdup_printf("%s <%s>", info->name, info->email); - else - l = info->name; - } else { - if (info->email) - l = info->email; - } - - if (l) { - GtkWidget *w; -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) - ECert *ec = NULL; -#endif - w = gtk_label_new (l); - gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); - g_free (la); - gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3); -#if defined (HAVE_NSS) && defined (ENABLE_SMIME) - w = gtk_button_new_with_mnemonic(_("_View Certificate")); - gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); - g_object_set_data((GObject *)w, "e-cert-info", info); - g_signal_connect ( - w, "clicked", - G_CALLBACK (efhd_xpkcs7mime_viewcert_clicked), po); - - if (info->cert_data) - ec = e_cert_new (CERT_DupCertificate (info->cert_data)); - - if (ec == NULL) - gtk_widget_set_sensitive (w, FALSE); - else - g_object_unref (ec); -#else - w = gtk_label_new (_("This certificate is not viewable")); - gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3); -#endif - n++; - } - } - - gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table)); -} - -static void -efhd_xpkcs7mime_validity_clicked (GtkWidget *button, - EMFormatPURI *puri) -{ - EMFormatSMIMEPURI *po = (EMFormatSMIMEPURI *) puri; - GtkBuilder *builder; - GtkWidget *grid, *w; - - if (po->widget) - /* FIXME: window raise? */ - return; - - builder = gtk_builder_new (); - e_load_ui_builder_definition (builder, "mail-dialogs.ui"); - - po->widget = e_builder_get_widget(builder, "message_security_dialog"); - - grid = e_builder_get_widget(builder, "signature_grid"); - w = gtk_label_new (_(smime_sign_table[po->valid->sign.status].description)); - gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); - gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); - gtk_container_add (GTK_CONTAINER (grid), w); - if (po->valid->sign.description) { - GtkTextBuffer *buffer; - - buffer = gtk_text_buffer_new (NULL); - gtk_text_buffer_set_text (buffer, po->valid->sign.description, strlen (po->valid->sign.description)); - w = g_object_new (gtk_scrolled_window_get_type (), - "hscrollbar_policy", GTK_POLICY_AUTOMATIC, - "vscrollbar_policy", GTK_POLICY_AUTOMATIC, - "shadow_type", GTK_SHADOW_IN, - "expand", TRUE, - "child", g_object_new(gtk_text_view_get_type(), - "buffer", buffer, - "cursor_visible", FALSE, - "editable", FALSE, - "width_request", 500, - "height_request", 160, - NULL), - NULL); - g_object_unref (buffer); - - gtk_container_add (GTK_CONTAINER (grid), w); - } - - if (!g_queue_is_empty (&po->valid->sign.signers)) - efhd_xpkcs7mime_add_cert_table (grid, &po->valid->sign.signers, po); - - gtk_widget_show_all (grid); - - grid = e_builder_get_widget(builder, "encryption_grid"); - w = gtk_label_new (_(smime_encrypt_table[po->valid->encrypt.status].description)); - gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5); - gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); - gtk_container_add (GTK_CONTAINER (grid), w); - if (po->valid->encrypt.description) { - GtkTextBuffer *buffer; - - buffer = gtk_text_buffer_new (NULL); - gtk_text_buffer_set_text (buffer, po->valid->encrypt.description, strlen (po->valid->encrypt.description)); - w = g_object_new (gtk_scrolled_window_get_type (), - "hscrollbar_policy", GTK_POLICY_AUTOMATIC, - "vscrollbar_policy", GTK_POLICY_AUTOMATIC, - "shadow_type", GTK_SHADOW_IN, - "expand", TRUE, - "child", g_object_new(gtk_text_view_get_type(), - "buffer", buffer, - "cursor_visible", FALSE, - "editable", FALSE, - "width_request", 500, - "height_request", 160, - NULL), - NULL); - g_object_unref (buffer); - - gtk_container_add (GTK_CONTAINER (grid), w); - } - - if (!g_queue_is_empty (&po->valid->encrypt.encrypters)) - efhd_xpkcs7mime_add_cert_table (grid, &po->valid->encrypt.encrypters, po); - - gtk_widget_show_all (grid); - - g_object_unref (builder); - - g_signal_connect ( - po->widget, "response", - G_CALLBACK (efhd_xpkcs7mime_info_response), po); - - gtk_widget_show (po->widget); -} - -static GtkWidget * -efhd_xpkcs7mime_button (EMFormat *emf, - EMFormatPURI *puri, - GCancellable *cancellable) -{ - GtkWidget *box, *button, *layout, *widget; - EMFormatSMIMEPURI *po = (EMFormatSMIMEPURI *) puri; - const gchar *icon_name; - - /* FIXME: need to have it based on encryption and signing too */ - if (po->valid->sign.status != 0) - icon_name = smime_sign_table[po->valid->sign.status].icon; - else - icon_name = smime_encrypt_table[po->valid->encrypt.status].icon; - - box = gtk_event_box_new (); - if (po->valid->sign.status != 0) - gtk_widget_override_background_color (box, GTK_STATE_FLAG_NORMAL, - &smime_sign_colour[po->valid->sign.status]); - - layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_container_add (GTK_CONTAINER (box), layout); - - button = gtk_button_new (); - gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0); - g_signal_connect (button, "clicked", - G_CALLBACK (efhd_xpkcs7mime_validity_clicked), puri); - - widget = gtk_image_new_from_icon_name ( - icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); - gtk_button_set_image (GTK_BUTTON (button), widget); - - widget = gtk_label_new (po->description); - gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0); - - gtk_widget_show_all (box); - - return box; -} - -struct attachment_load_data { - EAttachment *attachment; - EFlag *flag; -}; - -static void -attachment_loaded (EAttachment *attachment, - GAsyncResult *res, - gpointer user_data) -{ - struct attachment_load_data *data = user_data; - EShell *shell; - GtkWindow *window; - - shell = e_shell_get_default (); - window = e_shell_get_active_window (shell); - - e_attachment_load_handle_error (data->attachment, res, window); - - e_flag_set (data->flag); -} - -/* Idle callback */ -static gboolean -load_attachment_idle (struct attachment_load_data *data) -{ - e_attachment_load_async (data->attachment, - (GAsyncReadyCallback) attachment_loaded, data); - - return FALSE; -} - -static void -efhd_parse_attachment (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - gchar *text, *html; - EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf; - EMFormatAttachmentPURI *puri; - EAttachmentStore *store; - const EMFormatHandler *handler; - CamelContentType *ct; - gchar *mime_type; - gint len; - const gchar *cid; - guint32 size; - struct attachment_load_data *load_data; - gboolean can_show = FALSE; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - len = part_id->len; - g_string_append (part_id, ".attachment"); - - /* Try to find handler for the mime part */ - ct = camel_mime_part_get_content_type (part); - if (ct) { - mime_type = camel_content_type_simple (ct); - handler = em_format_find_handler (emf, mime_type); - } - - /* FIXME: should we look up mime_type from object again? */ - text = em_format_describe_part (part, mime_type); - html = camel_text_to_html ( - text, EM_FORMAT_HTML (emf)->text_html_flags & - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - g_free (text); - g_free (mime_type); - - puri = (EMFormatAttachmentPURI *) em_format_puri_new ( - emf, sizeof (EMFormatAttachmentPURI), part, part_id->str); - puri->puri.free = efhd_free_attach_puri_data; - puri->puri.write_func = efhd_write_attachment; - puri->puri.widget_func = efhd_attachment_button; - puri->shown = (handler && em_format_is_inline (emf, part_id->str, part, handler)); - puri->snoop_mime_type = em_format_snoop_type (part); - puri->attachment = e_attachment_new (); - puri->attachment_view_part_id = NULL; - puri->description = html; - puri->handle = handler; - if (info->validity) - puri->puri.validity = camel_cipher_validity_clone (info->validity); - - cid = camel_mime_part_get_content_id (part); - if (cid) - puri->puri.cid = g_strdup_printf ("cid:%s", cid); - - if (handler) { - CamelContentType *ct; - - /* This mime_type is important for WebKit to determine content type. - * We have converted text/ * to text/html, other (binary) formats remained - * untouched. */ - ct = camel_content_type_decode (handler->mime_type); - if (g_strcmp0 (ct->type, "text") == 0) - puri->puri.mime_type = g_strdup ("text/html"); - else - puri->puri.mime_type = camel_content_type_simple (ct); - camel_content_type_unref (ct); - } - - em_format_add_puri (emf, (EMFormatPURI *) puri); - - /* Though it is an attachment, we still might be able to parse it and - * so discover some parts that we might be even able to display. */ - if (handler && handler->parse_func && (handler->parse_func != efhd_parse_attachment) && - ((handler->flags & EM_FORMAT_HANDLER_COMPOUND_TYPE) || - (handler->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION))) { - GList *i; - EMFormatParserInfo attachment_info = { .handler = handler, - .is_attachment = TRUE }; - handler->parse_func (emf, puri->puri.part, part_id, &attachment_info, cancellable); - - i = g_hash_table_lookup (emf->mail_part_table, part_id->str); - if (i->next && i->next->data) { - EMFormatPURI *p = i->next->data; - puri->attachment_view_part_id = g_strdup (p->uri); - can_show = TRUE; - } - } - - e_attachment_set_mime_part (puri->attachment, part); - e_attachment_set_shown (puri->attachment, puri->shown); - if (puri->puri.validity) { - e_attachment_set_signed (puri->attachment, puri->puri.validity->sign.status); - e_attachment_set_encrypted (puri->attachment, puri->puri.validity->encrypt.status); - } - e_attachment_set_can_show (puri->attachment, - can_show || (puri->handle && puri->handle->write_func)); - - store = find_parent_attachment_store (efhd, part_id->str); - e_attachment_store_add_attachment (store, puri->attachment); - - if (emf->folder && emf->folder->summary && emf->message_uid) { - CamelDataWrapper *dw = camel_medium_get_content (CAMEL_MEDIUM (puri->puri.part)); - GByteArray *ba; - ba = camel_data_wrapper_get_byte_array (dw); - if (ba) { - size = ba->len; - - if (camel_mime_part_get_encoding (puri->puri.part) == CAMEL_TRANSFER_ENCODING_BASE64) - size = size / 1.37; - } - } - - load_data = g_new0 (struct attachment_load_data, 1); - load_data->attachment = g_object_ref (puri->attachment); - load_data->flag = e_flag_new (); - - e_flag_clear (load_data->flag); - - /* e_attachment_load_async must be called from main thread */ - g_idle_add ((GSourceFunc) load_attachment_idle, load_data); - - e_flag_wait (load_data->flag); - - e_flag_free (load_data->flag); - g_object_unref (load_data->attachment); - g_free (load_data); - - if (size != 0) { - GFileInfo *fileinfo; - - fileinfo = e_attachment_get_file_info (puri->attachment); - g_file_info_set_size (fileinfo, size); - e_attachment_set_file_info (puri->attachment, fileinfo); - } - - g_string_truncate (part_id, len); -} - -static void -efhd_parse_secure (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - if (info->validity - && (info->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE - || info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) { - GString *buffer; - EMFormatSMIMEPURI *pobj; - - pobj = (EMFormatSMIMEPURI *) em_format_puri_new ( - emf, sizeof (EMFormatSMIMEPURI), part, part_id->str); - pobj->puri.free = efhd_xpkcs7mime_free; - pobj->valid = camel_cipher_validity_clone (info->validity); - pobj->puri.widget_func = efhd_xpkcs7mime_button; - pobj->puri.write_func = efhd_write_secure_button; - - em_format_add_puri (emf, (EMFormatPURI *) pobj); - - buffer = g_string_new (""); - - if (info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) { - const gchar *desc; - gint status; - - status = info->validity->sign.status; - desc = smime_sign_table[status].shortdesc; - - g_string_append (buffer, gettext (desc)); - - em_format_html_format_cert_infos ( - &info->validity->sign.signers, buffer); - } - - if (info->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) { - const gchar *desc; - gint status; - - if (info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) - g_string_append (buffer, "\n"); - - status = info->validity->encrypt.status; - desc = smime_encrypt_table[status].shortdesc; - g_string_append (buffer, gettext (desc)); - } - - pobj->description = g_string_free (buffer, FALSE); - } -} - -/******************************************************************************/ -static void -efhd_write_attachment_bar (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatAttachmentBarPURI *efab = (EMFormatAttachmentBarPURI *) puri; - gchar *str; - - if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) - return; - - if (e_attachment_store_get_num_attachments (efab->store) == 0) - return; - - str = g_strdup_printf ( - "<object type=\"application/x-attachment-bar\" " - "height=\"20\" width=\"100%%\" " - "id=\"%s\"data=\"%s\"></object>", puri->uri, puri->uri); - - camel_stream_write_string (stream, str, cancellable, NULL); - - g_free (str); -} - -static void -efhd_write_attachment (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *str, *desc; - const gchar *mime_type; - gchar *button_id; - - EMFormatAttachmentPURI *efa = (EMFormatAttachmentPURI *) puri; - - /* If the attachment is requested as RAW, then call the handler directly - * and do not append any other code. */ - if ((info->mode == EM_FORMAT_WRITE_MODE_RAW) && - efa->handle && efa->handle->write_func) { - - efa->handle->write_func (emf, puri, stream, info, cancellable); - return; - } - - if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { - - if (efa->handle && efa->handle->write_func) - efa->handle->write_func (emf, puri, stream, info, cancellable); - - return; - } - - if (efa->handle) - mime_type = efa->handle->mime_type; - else - mime_type = efa->snoop_mime_type; - - button_id = g_strconcat (puri->uri, ".attachment_button", NULL); - - desc = em_format_describe_part (puri->part, mime_type); - str = g_strdup_printf ( - "<div class=\"attachment\">" - "<table width=\"100%%\" border=\"0\">" - "<tr valign=\"middle\">" - "<td align=\"left\" width=\"100\">" - "<object type=\"application/x-attachment-button\" " - "height=\"20\" width=\"100\" data=\"%s\" id=\"%s\"></object>" - "</td>" - "<td align=\"left\">%s</td>" - "</tr>", puri->uri, button_id, desc); - - camel_stream_write_string (stream, str, cancellable, NULL); - g_free (desc); - g_free (button_id); - g_free (str); - - /* If we know how to write the attachment, then do it */ - if ((efa->handle && efa->handle->write_func) || - (efa->attachment_view_part_id)) { - - str = g_strdup_printf ( - "<tr><td colspan=\"2\">" - "<div class=\"attachment-wrapper\" id=\"%s\">", - puri->uri); - - camel_stream_write_string (stream, str, cancellable, NULL); - g_free (str); - - if (efa->handle->write_func) { - efa->handle->write_func ( - emf, puri, stream, info, cancellable); - } else if (efa->attachment_view_part_id) { - EMFormatPURI *p; - - p = em_format_find_puri ( - emf, efa->attachment_view_part_id); - if (p && p->write_func) - p->write_func (emf, p, stream, info, cancellable); - } - - camel_stream_write_string (stream, "</div></td></tr>", cancellable, NULL); - } - - camel_stream_write_string (stream, "</table></div>", cancellable, NULL); -} - -static void -efhd_write_secure_button (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *str; - - if ((info->mode != EM_FORMAT_WRITE_MODE_NORMAL) && - (info->mode != EM_FORMAT_WRITE_MODE_RAW)) - return; - - str = g_strdup_printf ( - "<object type=\"application/x-secure-button\" " - "height=\"20\" width=\"100%%\" " - "data=\"%s\" id=\"%s\"></object>", puri->uri, puri->uri); - - camel_stream_write_string (stream, str, cancellable, NULL); - - g_free (str); -} - -static void -efhd_finalize (GObject *object) -{ - EMFormatHTMLDisplay *efhd; - - efhd = EM_FORMAT_HTML_DISPLAY (object); - g_return_if_fail (efhd != NULL); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -efhd_preparse (EMFormat *emf) -{ - EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf; - - efhd->priv->last_view = NULL; -} - -static void -efhd_class_init (EMFormatHTMLDisplayClass *class) -{ - GObjectClass *object_class; - EMFormatHTMLClass *format_html_class; - EMFormatClass *format_class; - - parent_class = g_type_class_peek_parent (class); - g_type_class_add_private (class, sizeof (EMFormatHTMLDisplayPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = efhd_finalize; - - format_html_class = EM_FORMAT_HTML_CLASS (class); - format_html_class->html_widget_type = E_TYPE_MAIL_DISPLAY; - - format_class = EM_FORMAT_CLASS (class); - format_class->preparse = efhd_preparse; - - efhd_builtin_init (class); -} - -static void -efhd_init (EMFormatHTMLDisplay *efhd) -{ - efhd->priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efhd); - - /* we want to convert url's etc */ - EM_FORMAT_HTML (efhd)->text_html_flags |= - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | - CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; - -} - -GType -em_format_html_display_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatHTMLDisplayClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) efhd_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormatHTMLDisplay), - 0, /* n_preallocs */ - (GInstanceInitFunc) efhd_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - EM_TYPE_FORMAT_HTML, "EMFormatHTMLDisplay", - &type_info, 0); - } - - return type; -} - -EMFormatHTMLDisplay * -em_format_html_display_new (CamelSession *session) -{ - g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); - - return g_object_new ( - EM_TYPE_FORMAT_HTML_DISPLAY, - "session", session, NULL); -} - -/* ********************************************************************** */ - -static EMFormatHandler type_builtin_table[] = { - { (gchar *) "x-evolution/message/prefix", efhd_message_prefix, }, - { (gchar *) "x-evolution/message/attachment-bar", (EMFormatParseFunc) efhd_message_add_bar, efhd_write_attachment_bar, }, - { (gchar *) "x-evolution/message/attachment", efhd_parse_attachment, efhd_write_attachment, }, - { (gchar *) "x-evolution/message/x-secure-button", efhd_parse_secure, efhd_write_secure_button, }, -}; - -static void -efhd_builtin_init (EMFormatHTMLDisplayClass *efhc) -{ - gint i; - - EMFormatClass *emfc = (EMFormatClass *) efhc; - - for (i = 0; i < G_N_ELEMENTS (type_builtin_table); i++) - em_format_class_add_handler (emfc, &type_builtin_table[i]); -} - -static void -efhd_message_prefix (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - const gchar *flag, *comp, *due; - time_t date; - gchar *iconpath, *due_date_str; - GString *buffer; - EMFormatAttachmentPURI *puri; - - if (emf->folder == NULL || emf->message_uid == NULL - || (flag = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "follow-up")) == NULL - || flag[0] == 0) - return; - - puri = (EMFormatAttachmentPURI *) em_format_puri_new ( - emf, sizeof (EMFormatAttachmentPURI), part, ".message_prefix"); - - puri->attachment_view_part_id = g_strdup (part_id->str); - - comp = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "completed-on"); - iconpath = e_icon_factory_get_icon_filename (comp && comp[0] ? "stock_mail-flag-for-followup-done" : "stock_mail-flag-for-followup", GTK_ICON_SIZE_MENU); - if (iconpath) { - gchar *classid; - - classid = g_strdup_printf ( - "icon:///em-format-html-display/%s/%s", - part_id->str, - comp && comp[0] ? "comp" : "uncomp"); - - puri->puri.uri = classid; - - g_free (classid); - } - - buffer = g_string_new (""); - - if (comp && comp[0]) { - date = camel_header_decode_date (comp, NULL); - due_date_str = e_datetime_format_format ( - "mail", "header", DTFormatKindDateTime, date); - g_string_append_printf ( - buffer, "%s, %s %s", - flag, _("Completed on"), - due_date_str ? due_date_str : "???"); - g_free (due_date_str); - } else if ((due = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "due-by")) != NULL && due[0]) { - time_t now; - - date = camel_header_decode_date (due, NULL); - now = time (NULL); - if (now > date) - g_string_append_printf ( - buffer, - "<b>%s</b> ", - _("Overdue:")); - - due_date_str = e_datetime_format_format ( - "mail", "header", DTFormatKindDateTime, date); - /* Translators: the "by" is part of the string, - * like "Follow-up by Tuesday, January 13, 2009" */ - g_string_append_printf ( - buffer, "%s %s %s", - flag, _("by"), - due_date_str ? due_date_str : "???"); - g_free (due_date_str); - } else { - g_string_append (buffer, flag); - } - - puri->description = g_string_free (buffer, FALSE); -} - -/* ********************************************************************** */ - -/* attachment button callback */ -static GtkWidget * -efhd_attachment_button (EMFormat *emf, - EMFormatPURI *puri, - GCancellable *cancellable) -{ - EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri; - GtkWidget *widget; - - /* FIXME: handle default shown case */ - d(printf("adding attachment button/content\n")); - - if (g_cancellable_is_cancelled (cancellable)) - return NULL; - - if (!info || info->forward) { - g_warning ("unable to expand the attachment\n"); - return NULL; - } - - widget = e_attachment_button_new (); - g_object_set_data (G_OBJECT (widget), "uri", puri->uri); - e_attachment_button_set_attachment ( - E_ATTACHMENT_BUTTON (widget), info->attachment); - e_attachment_button_set_view ( - E_ATTACHMENT_BUTTON (widget), - EM_FORMAT_HTML_DISPLAY (emf)->priv->last_view); - - gtk_widget_set_can_focus (widget, TRUE); - gtk_widget_show (widget); - - return widget; -} - -static GtkWidget * -efhd_attachment_bar (EMFormat *emf, - EMFormatPURI *puri, - GCancellable *cancellable) -{ - EMFormatAttachmentBarPURI *abp = (EMFormatAttachmentBarPURI *) puri; - GtkWidget *widget; - - widget = e_mail_attachment_bar_new (abp->store); - EM_FORMAT_HTML_DISPLAY (emf)->priv->last_view = (EAttachmentView *) widget; - - return widget; -} - -static void -efhd_message_add_bar (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatAttachmentBarPURI *puri; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - len = part_id->len; - g_string_append (part_id, ".attachment-bar"); - puri = (EMFormatAttachmentBarPURI *) em_format_puri_new ( - emf, sizeof (EMFormatAttachmentBarPURI), part, part_id->str); - puri->puri.write_func = efhd_write_attachment_bar; - puri->puri.widget_func = efhd_attachment_bar; - puri->puri.free = efhd_attachment_bar_puri_free; - puri->store = E_ATTACHMENT_STORE (e_attachment_store_new ()); - - em_format_add_puri (emf, (EMFormatPURI *) puri); - - g_string_truncate (part_id, len); -} - -static void -efhd_free_attach_puri_data (EMFormatPURI *puri) -{ - EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri; - - g_return_if_fail (puri != NULL); - - if (info->attachment) { - g_object_unref (info->attachment); - info->attachment = NULL; - } - - if (info->description) { - g_free (info->description); - info->description = NULL; - } - - if (info->attachment_view_part_id) { - g_free (info->attachment_view_part_id); - info->attachment_view_part_id = NULL; - } - - if (info->mstream) { - g_object_unref (info->mstream); - info->mstream = NULL; - } -} diff --git a/mail/em-format-html-display.h b/mail/em-format-html-display.h deleted file mode 100644 index 7c0d7a9fd5..0000000000 --- a/mail/em-format-html-display.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -/* - Concrete class for formatting mails to displayed html -*/ - -#ifndef EM_FORMAT_HTML_DISPLAY_H -#define EM_FORMAT_HTML_DISPLAY_H - -#include <mail/em-format-html.h> -#include <misc/e-attachment-view.h> - -/* Standard GObject macros */ -#define EM_TYPE_FORMAT_HTML_DISPLAY \ - (em_format_html_display_get_type ()) -#define EM_FORMAT_HTML_DISPLAY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplay)) -#define EM_FORMAT_HTML_DISPLAY_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplayClass)) -#define EM_IS_FORMAT_HTML_DISPLAY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_FORMAT_HTML_DISPLAY)) -#define EM_IS_FORMAT_HTML_DISPLAY_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_FORMAT_HTML_DISPLAY)) -#define EM_FORMAT_HTML_DISPLAY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplayClass)) - -G_BEGIN_DECLS - -typedef struct _EMFormatHTMLDisplay EMFormatHTMLDisplay; -typedef struct _EMFormatHTMLDisplayClass EMFormatHTMLDisplayClass; -typedef struct _EMFormatHTMLDisplayPrivate EMFormatHTMLDisplayPrivate; -typedef struct _EMFormatAttachmentBarPURI EMFormatAttachmentBarPURI; -typedef struct _EMFormatAttachmentPURI EMFormatAttachmentPURI; -typedef struct _EMFormatSMIMEPURI EMFormatSMIMEPURI; - -struct _EMFormatAttachmentBarPURI { - EMFormatPURI puri; - - EAttachmentStore *store; -}; - -struct _EMFormatAttachmentPURI { - EMFormatPURI puri; - - const EMFormatHandler *handle; - - const gchar *snoop_mime_type; - - /* for the > and V buttons */ - GtkWidget *forward, *down; - guint shown : 1; - - /* Attachment */ - EAttachment *attachment; - gchar *attachment_view_part_id; - gchar *description; - - /* image stuff */ - gint fit_width; - gint fit_height; - GtkImage *image; - GtkWidget *event_box; - - /* Optional Text Mem Stream */ - CamelStreamMem *mstream; -}; - -struct _EMFormatSMIMEPURI { - EMFormatPURI puri; - - gchar *description; - - gint signature; - CamelCipherValidity *valid; - GtkWidget *widget; -}; - -struct _EMFormatHTMLDisplay { - EMFormatHTML parent; - EMFormatHTMLDisplayPrivate *priv; - - struct _ESearchingTokenizer *search_tok; -}; - -struct _EMFormatHTMLDisplayClass { - EMFormatHTMLClass parent_class; -}; - -GType em_format_html_display_get_type (void); -EMFormatHTMLDisplay * - em_format_html_display_new (CamelSession *session); -G_END_DECLS - -#endif /* EM_FORMAT_HTML_DISPLAY_H */ diff --git a/mail/em-format-html-print.c b/mail/em-format-html-print.c deleted file mode 100644 index 150c16dbf5..0000000000 --- a/mail/em-format-html-print.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Jeffrey Stedfast <fejj@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <glib/gi18n.h> -#include <gtk/gtk.h> - -#include "em-format-html-print.h" -#include "em-format-html-display.h" -#include "e-mail-attachment-bar.h" -#include <e-util/e-print.h> -#include <e-util/e-util.h> -#include <widgets/misc/e-attachment-store.h> -#include <libemail-engine/mail-ops.h> - -#include "em-format-html-print.h" - -#define d(x) - -static gpointer parent_class = NULL; - -struct _EMFormatHTMLPrintPrivate { - - EMFormatHTML *original_formatter; - EMFormatPURI *top_level_puri; - - /* List of attachment PURIs */ - GList *attachments; - -}; - -enum { - PROP_0, - PROP_ORIGINAL_FORMATTER -}; - -static void efhp_write_print_layout (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efhp_write_headers (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efhp_write_inline_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -static void -efhp_write_attachments_list (EMFormatHTMLPrint *efhp, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GString *str; - GList *iter; - - if (!efhp->priv->attachments) - return; - - str = g_string_new ( - "<table border=\"0\" cellspacing=\"5\" cellpadding=\"0\" " - "class=\"attachments-list\" >\n"); - g_string_append_printf (str, - "<tr><th colspan=\"2\"><h1>%s</h1></td></tr>\n" - "<tr><th>%s</th><th>%s</th></tr>\n", - _("Attachments"), _("Name"), _("Size")); - - for (iter = efhp->priv->attachments; iter; iter = iter->next) { - EMFormatPURI *puri = iter->data; - EAttachment *attachment; - GFileInfo *fi; - gchar *name, *size; - GByteArray *ba; - CamelDataWrapper *dw; - - attachment = ((EMFormatAttachmentPURI *) puri)->attachment; - fi = e_attachment_get_file_info (attachment); - if (!fi) - continue; - - if (e_attachment_get_description (attachment) && - *e_attachment_get_description (attachment)) { - name = g_strdup_printf ("%s (%s)", - e_attachment_get_description (attachment), - g_file_info_get_display_name (fi)); - } else { - name = g_strdup (g_file_info_get_display_name (fi)); - } - - dw = camel_medium_get_content ((CamelMedium *) puri->part); - ba = camel_data_wrapper_get_byte_array (dw); - size = g_format_size (ba->len); - - g_string_append_printf (str, "<tr><td>%s</td><td>%s</td></tr>\n", - name, size); - - g_free (name); - g_free (size); - } - - g_string_append (str, "</table>\n"); - - camel_stream_write_string (stream, str->str, cancellable, NULL); - g_string_free (str, TRUE); -} - -static void -efhp_write_headers (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - struct _camel_header_raw raw_header; - GString *str, *tmp; - gchar *subject; - const gchar *buf; - EMFormatPURI *p; - GList *iter; - gint attachments_count; - gchar *puri_prefix; - - buf = camel_medium_get_header (CAMEL_MEDIUM (puri->part), "subject"); - subject = camel_header_decode_string (buf, "UTF-8"); - str = g_string_new ("<table border=\"0\" cellspacing=\"5\" " \ - "cellpadding=\"0\" class=\"printing-header\">\n"); - g_string_append_printf ( - str, - "<tr class=\"header-item\">" - "<td colspan=\"2\"><h1>%s</h1></td>" - "</tr>\n", - subject); - g_free (subject); - - for (iter = g_queue_peek_head_link (&emf->header_list); iter; iter = iter->next) { - - EMFormatHeader *header = iter->data; - raw_header.name = header->name; - - /* Skip 'Subject' header, it's already displayed. */ - if (g_ascii_strncasecmp (header->name, "Subject", 7) == 0) - continue; - - if (header->value && *header->value) { - raw_header.value = header->value; - em_format_html_format_header (emf, str, - CAMEL_MEDIUM (puri->part), &raw_header, - header->flags | EM_FORMAT_HTML_HEADER_NOLINKS, - "UTF-8"); - } else { - raw_header.value = g_strdup (camel_medium_get_header ( - CAMEL_MEDIUM (emf->message), header->name)); - - if (raw_header.value && *raw_header.value) { - em_format_html_format_header (emf, str, - CAMEL_MEDIUM (puri->part), &raw_header, - header->flags | EM_FORMAT_HTML_HEADER_NOLINKS, - "UTF-8"); - } - - if (raw_header.value) - g_free (raw_header.value); - } - } - - /* Get prefix of this PURI */ - puri_prefix = g_strndup (puri->uri, g_strrstr (puri->uri, ".") - puri->uri); - - /* Add encryption/signature header */ - raw_header.name = _("Security"); - tmp = g_string_new (""); - /* Find first secured part. */ - for (iter = emf->mail_part_list, puri; iter; iter = iter->next) { - - p = iter->data; - - if (p->validity_type == 0) - continue; - - if (!g_str_has_prefix (p->uri, puri_prefix)) - continue; - - if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) && - (p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) { - g_string_append (tmp, _("GPG signed")); - } - if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) && - (p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) { - if (tmp->len > 0) g_string_append (tmp, ", "); - g_string_append (tmp, _("GPG encrpyted")); - } - if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) && - (p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) { - - if (tmp->len > 0) g_string_append (tmp, ", "); - g_string_append (tmp, _("S/MIME signed")); - } - if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) && - (p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) { - - if (tmp->len > 0) g_string_append (tmp, ", "); - g_string_append (tmp, _("S/MIME encrpyted")); - } - - break; - } - - if (tmp->len > 0) { - raw_header.value = tmp->str; - em_format_html_format_header (emf, str, CAMEL_MEDIUM (p->part), - &raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8"); - } - g_string_free (tmp, TRUE); - - /* Count attachments and display the number as a header */ - attachments_count = 0; - - for (iter = emf->mail_part_list; iter; iter = iter ? iter->next : iter) { - - p = iter->data; - - if (!g_str_has_prefix (p->uri, puri_prefix)) - continue; - - if ((p->is_attachment || g_str_has_suffix(p->uri, ".attachment")) && - (!p->cid)) { - attachments_count++; - /* EFHD sometimes creates two PURIs per attachment! */ - if (iter->next && iter->next->data) { - EMFormatPURI *p2 = iter->next->data; - if (g_str_has_prefix (p2->uri, p->uri)) - iter = iter->next; - } - } - } - if (attachments_count > 0) { - raw_header.name = _("Attachments"); - raw_header.value = g_strdup_printf ("%d", attachments_count); - em_format_html_format_header (emf, str, CAMEL_MEDIUM (puri->part), - &raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8"); - g_free (raw_header.value); - } - - g_string_append (str, "</table>"); - - camel_stream_write_string (stream, str->str, cancellable, NULL); - g_string_free (str, TRUE); - g_free (puri_prefix); -} - -static void -efhp_write_inline_attachment (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *name; - EMFormatAttachmentPURI *att_puri = (EMFormatAttachmentPURI *) puri; - EAttachment *attachment; - GFileInfo *fi; - - attachment = att_puri->attachment; - fi = e_attachment_get_file_info (attachment); - - if (e_attachment_get_description (attachment) && - *e_attachment_get_description (attachment)) { - name = g_strdup_printf ("<h2>Attachment: %s (%s)</h2>\n", - e_attachment_get_description (attachment), - g_file_info_get_display_name (fi)); - } else { - name = g_strdup_printf ("<h2>Attachment: %s</h2>\n", - g_file_info_get_display_name (fi)); - } - - camel_stream_write_string (stream, name, cancellable, NULL); - g_free (name); -} - -static void -efhp_write_print_layout (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GList *iter; - EMFormatWriterInfo print_info = { - EM_FORMAT_WRITE_MODE_PRINTING, FALSE, FALSE }; - EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (emf); - - g_list_free (efhp->priv->attachments); - efhp->priv->attachments = NULL; - - camel_stream_write_string (stream, - "<!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\" media=\"print\" " - "href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview-print.css\" />\n" - "</head>\n" - "<body style=\"background: #FFF; color: #000;\">", - cancellable, NULL); - - for (iter = emf->mail_part_list; iter != NULL; iter = iter ? iter->next : iter) { - - EMFormatPURI *puri = iter->data; - - if (g_str_has_suffix (puri->uri, "print_layout")) - continue; - - /* To late to change .headers writer_func, do it manually. */ - if (g_str_has_suffix (puri->uri, ".headers")) { - efhp_write_headers (emf, puri, stream, &print_info, cancellable); - continue; - } - - if (g_str_has_suffix (puri->uri, ".rfc822")) { - - puri->write_func (emf, puri, stream, &print_info, cancellable); - - while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) { - - iter = iter->next; - if (iter) - puri = iter->data; - } - - if (!iter) - break; - - continue; - - } - - if (puri->is_attachment || g_str_has_suffix (puri->uri, ".attachment")) { - const EMFormatHandler *handler; - CamelContentType *ct; - gchar *mime_type; - - if (puri->cid && g_ascii_strncasecmp (puri->cid, "cid:", 4) == 0) - continue; - - ct = camel_mime_part_get_content_type (puri->part); - mime_type = camel_content_type_simple (ct); - - handler = em_format_find_handler (puri->emf, mime_type); - d(printf("Handler for PURI %s (%s): %s\n", puri->uri, mime_type, - handler ? handler->mime_type : "(null)")); - g_free (mime_type); - - efhp->priv->attachments = - g_list_append (efhp->priv->attachments, puri); - - /* If we can't inline this attachment, skip it */ - if (handler && puri->write_func) { - efhp_write_inline_attachment (puri->emf, puri, - stream, &print_info, cancellable); - - if (iter->next && iter->next->data) { - EMFormatPURI *p; - p = iter->next->data; - - /* Has the next PURI the same prefix? */ - if (p->write_func && - g_str_has_prefix (p->uri, puri->uri)) { - p->write_func (emf, p, stream, - &print_info, cancellable); - iter = iter->next; - } else { - if (puri->write_func) { - puri->write_func (emf, puri, - stream, &print_info, - cancellable); - } - } - } - } - - continue; - } - - /* Ignore widget parts and unwritable non-attachment parts */ - if (puri->write_func == NULL) - continue; - - /* Passed all tests, probably a regular part - display it */ - puri->write_func (puri->emf, puri, stream, &print_info, cancellable); - - } - - efhp_write_attachments_list (efhp, stream, &print_info, cancellable); - - camel_stream_write_string (stream, "</body></html>", cancellable, NULL); -} - -static void -efhp_finalize (GObject *object) -{ - EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (object); - - if (efhp->priv->original_formatter) { - g_object_unref (efhp->priv->original_formatter); - efhp->priv->original_formatter = NULL; - } - - if (efhp->priv->top_level_puri) { - em_format_puri_free (efhp->priv->top_level_puri); - efhp->priv->top_level_puri = NULL; - } - - if (efhp->priv->attachments) { - g_list_free (efhp->priv->attachments); - efhp->priv->attachments = NULL; - } - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -efhp_is_inline (EMFormat *emf, - const gchar *part_id, - CamelMimePart *mime_part, - const EMFormatHandler *handle) -{ - /* When printing, inline any part that has a handler. */ - return (handle != NULL); -} - -static void -efhp_set_orig_formatter (EMFormatHTMLPrint *efhp, - EMFormat *formatter) -{ - EMFormat *emfp, *emfs; - EMFormatPURI *puri; - GHashTableIter iter; - gpointer key, value; - - efhp->priv->original_formatter = g_object_ref (formatter); - - emfp = EM_FORMAT (efhp); - emfs = EM_FORMAT (formatter); - - emfp->mail_part_list = g_list_copy (emfs->mail_part_list); - - /* Make a shallow copy of the table. This table will NOT destroy - * the PURIs when free'd! */ - if (emfp->mail_part_table) - g_hash_table_unref (emfp->mail_part_table); - - emfp->mail_part_table = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_iter_init (&iter, emfs->mail_part_table); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_hash_table_insert (emfp->mail_part_table, key, value); - - if (emfs->folder) - emfp->folder = g_object_ref (emfs->folder); - emfp->message_uid = g_strdup (emfs->message_uid); - emfp->message = g_object_ref (emfs->message); - - /* Add a generic PURI that will write a HTML layout - * for all the parts */ - puri = em_format_puri_new (EM_FORMAT (efhp), - sizeof (EMFormatPURI), NULL, "print_layout"); - puri->write_func = efhp_write_print_layout; - puri->mime_type = g_strdup ("text/html"); - em_format_add_puri (EM_FORMAT (efhp), puri); - efhp->priv->top_level_puri = puri; -} - -static EMFormatHandler type_builtin_table[] = { - { (gchar *) "x-evolution/message/headers", 0, efhp_write_headers, }, -}; - -static void -efhp_builtin_init (EMFormatHTMLPrintClass *efhc) -{ - EMFormatClass *emfc; - gint ii; - - emfc = (EMFormatClass *) efhc; - - for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++) - em_format_class_add_handler ( - emfc, &type_builtin_table[ii]); -} - -static void -efhp_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) { - - case PROP_ORIGINAL_FORMATTER: - efhp_set_orig_formatter ( - EM_FORMAT_HTML_PRINT (object), - (EMFormat *) g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -} - -static void -efhp_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - EMFormatHTMLPrintPrivate *priv; - - priv = EM_FORMAT_HTML_PRINT (object)->priv; - - switch (prop_id) { - - case PROP_ORIGINAL_FORMATTER: - g_value_set_pointer (value, - priv->original_formatter); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -} - -static void -em_format_html_print_base_init (EMFormatHTMLPrintClass *klass) -{ - efhp_builtin_init (klass); -} - -static void -em_format_html_print_class_init (EMFormatHTMLPrintClass *klass) -{ - GObjectClass *object_class; - EMFormatClass *format_class; - - parent_class = g_type_class_peek_parent (klass); - g_type_class_add_private (klass, sizeof (EMFormatHTMLPrintPrivate)); - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = efhp_finalize; - object_class->set_property = efhp_set_property; - object_class->get_property = efhp_get_property; - - format_class = EM_FORMAT_CLASS (klass); - format_class->is_inline = efhp_is_inline; - - g_object_class_install_property ( - object_class, - PROP_ORIGINAL_FORMATTER, - g_param_spec_object ( - "original-formatter", - NULL, - NULL, - EM_TYPE_FORMAT, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY)); -} - -static void -em_format_html_print_init (EMFormatHTMLPrint *efhp) -{ - efhp->priv = G_TYPE_INSTANCE_GET_PRIVATE ( - efhp, EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrintPrivate); - - efhp->priv->attachments = NULL; - efhp->export_filename = NULL; -} - -GType -em_format_html_print_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatHTMLPrintClass), - (GBaseInitFunc) em_format_html_print_base_init, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) em_format_html_print_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormatHTMLPrint), - 0, /* n_preallocs */ - (GInstanceInitFunc) em_format_html_print_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - em_format_html_get_type(), "EMFormatHTMLPrint", - &type_info, 0); - } - - return type; -} - -EMFormatHTMLPrint * -em_format_html_print_new (EMFormatHTML *source) -{ - EMFormatHTMLPrint *efhp; - CamelSession *session; - - g_return_val_if_fail (EM_IS_FORMAT_HTML (source), NULL); - - session = em_format_get_session (EM_FORMAT (source)); - - efhp = g_object_new ( - EM_TYPE_FORMAT_HTML_PRINT, - "session", session, - "original-formatter", source, - NULL); - - return efhp; -} diff --git a/mail/em-format-html-print.h b/mail/em-format-html-print.h deleted file mode 100644 index d9fe1ff6d4..0000000000 --- a/mail/em-format-html-print.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Concrete class for formatting mails to displayed html - * - * 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/> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - */ - -#ifndef EM_FORMAT_HTML_PRINT_H -#define EM_FORMAT_HTML_PRINT_H - -#include "mail/em-format-html.h" - -/* Standard GObject macros */ -#define EM_TYPE_FORMAT_HTML_PRINT \ - (em_format_html_print_get_type ()) -#define EM_FORMAT_HTML_PRINT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrint)) -#define EM_FORMAT_HTML_PRINT_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrintClass)) -#define EM_IS_FORMAT_HTML_PRINT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_FORMAT_HTML_PRINT)) -#define EM_IS_FORMAT_HTML_PRINT_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_FORMAT_HTML_PRINT)) -#define EM_FORMAT_HTML_PRINT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrintClass)) - -G_BEGIN_DECLS - -typedef struct _EMFormatHTMLPrint EMFormatHTMLPrint; -typedef struct _EMFormatHTMLPrintClass EMFormatHTMLPrintClass; -typedef struct _EMFormatHTMLPrintPrivate EMFormatHTMLPrintPrivate; - -struct _EMFormatHTMLPrint { - EMFormatHTML parent; - EMFormatHTMLPrintPrivate *priv; - gchar *export_filename; -}; - -struct _EMFormatHTMLPrintClass { - EMFormatHTMLClass parent_class; -}; - -GType em_format_html_print_get_type (void); -EMFormatHTMLPrint * - em_format_html_print_new (EMFormatHTML *source); - -G_END_DECLS - -#endif /* EM_FORMAT_HTML_PRINT_H */ diff --git a/mail/em-format-html.c b/mail/em-format-html.c deleted file mode 100644 index bb80d3957b..0000000000 --- a/mail/em-format-html.c +++ /dev/null @@ -1,3011 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#define _GNU_SOURCE /* Enable strcasestr in string.h */ - -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#include <gtk/gtk.h> -#ifdef G_OS_WIN32 -/* Work around 'DATADIR' and 'interface' lossage in <windows.h> */ -#define DATADIR crap_DATADIR -#include <windows.h> -#undef DATADIR -#undef interface -#endif - -#include <libebackend/libebackend.h> - -#include "e-util/e-datetime-format.h" -#include "e-util/e-icon-factory.h" -#include "e-util/e-util-private.h" -#include "e-util/e-util.h" -#include "misc/e-web-view.h" - -#include <shell/e-shell.h> - -#include <glib/gi18n.h> - -#include <JavaScriptCore/JavaScript.h> -#include <webkit/webkit.h> - -#include <libemail-utils/mail-mt.h> -#include <libemail-engine/e-mail-enumtypes.h> -#include <libemail-engine/e-mail-utils.h> -#include <libemail-engine/mail-config.h> - -#include "em-format-html.h" -#include "em-utils.h" -#include "e-mail-display.h" -#include <em-format/em-inline-filter.h> - -#define EM_FORMAT_HTML_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), EM_TYPE_FORMAT_HTML, EMFormatHTMLPrivate)) - -#define d(x) - -struct _EMFormatHTMLPrivate { - GdkColor colors[EM_FORMAT_HTML_NUM_COLOR_TYPES]; - EMailImageLoadingPolicy image_loading_policy; - - guint can_load_images : 1; - guint only_local_photos : 1; - guint show_sender_photo : 1; - guint show_real_date : 1; - guint animate_images : 1; -}; - -static gpointer parent_class; - -enum { - PROP_0, - PROP_BODY_COLOR, - PROP_CITATION_COLOR, - PROP_CONTENT_COLOR, - PROP_FRAME_COLOR, - PROP_HEADER_COLOR, - PROP_IMAGE_LOADING_POLICY, - PROP_MARK_CITATIONS, - PROP_ONLY_LOCAL_PHOTOS, - PROP_SHOW_SENDER_PHOTO, - PROP_SHOW_REAL_DATE, - PROP_TEXT_COLOR, - PROP_ANIMATE_IMAGES -}; - -#define EFM_MESSAGE_START_ANAME "evolution_message_start" -#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>" - -static void efh_parse_image (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_text_enriched (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_text_plain (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_text_html (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_message_external (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_message_deliverystatus (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_message_rfc822 (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); - -static void efh_write_image (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_text_enriched (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_text_plain (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_text_html (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_source (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_headers (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_error (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_message_rfc822 (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -static void efh_format_full_headers (EMFormatHTML *efh, GString *buffer, CamelMedium *part, gboolean all_headers, gboolean visible, GCancellable *cancellable); -static void efh_format_short_headers (EMFormatHTML *efh, GString *buffer, CamelMedium *part, gboolean visible, GCancellable *cancellable); - -static void efh_write_message (EMFormat *emf, GList *puris, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -/*****************************************************************************/ -static void -efh_parse_image (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - const gchar *tmp; - gchar *cid; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - tmp = camel_mime_part_get_content_id (part); - if (!tmp) { - em_format_parse_part_as (emf, part, part_id, info, - "x-evolution/message/attachment", cancellable); - return; - } - - cid = g_strdup_printf ("cid:%s", tmp); - len = part_id->len; - g_string_append (part_id, ".image"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->cid = cid; - puri->write_func = efh_write_image; - puri->mime_type = g_strdup (info->handler->mime_type); - puri->is_attachment = TRUE; - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_text_enriched (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - const gchar *tmp; - gchar *cid; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - tmp = camel_mime_part_get_content_id (part); - if (!tmp) { - cid = g_strdup_printf ("em-no-cid:%s", part_id->str); - } else { - cid = g_strdup_printf ("cid:%s", tmp); - } - - len = part_id->len; - g_string_append (part_id, ".text_enriched"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->cid = cid; - puri->mime_type = g_strdup (info->handler->mime_type); - puri->write_func = efh_write_text_enriched; - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_text_plain (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - CamelStream *filtered_stream, *null; - CamelMultipart *mp; - CamelDataWrapper *dw; - CamelContentType *type; - gint i, count, len; - EMInlineFilter *inline_filter; - gboolean charset_added = FALSE; - const gchar *snoop_type = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - dw = camel_medium_get_content ((CamelMedium *) part); - if (!dw) - return; - - /* This scans the text part for inline-encoded data, creates - * a multipart of all the parts inside it. */ - - /* FIXME: We should discard this multipart if it only contains - * the original text, but it makes this hash lookup more complex */ - - /* TODO: We could probably put this in the superclass, since - * no knowledge of html is required - but this messes with - * filters a bit. Perhaps the superclass should just deal with - * html anyway and be done with it ... */ - - if (!dw->mime_type) - snoop_type = em_format_snoop_type (part); - - /* if we had to snoop the part type to get here, then - * use that as the base type, yuck */ - if (snoop_type == NULL - || (type = camel_content_type_decode (snoop_type)) == NULL) { - type = dw->mime_type; - camel_content_type_ref (type); - } - - if (dw->mime_type && type != dw->mime_type && camel_content_type_param (dw->mime_type, "charset")) { - camel_content_type_set_param (type, "charset", camel_content_type_param (dw->mime_type, "charset")); - charset_added = TRUE; - } - - null = camel_stream_null_new (); - filtered_stream = camel_stream_filter_new (null); - g_object_unref (null); - inline_filter = em_inline_filter_new (camel_mime_part_get_encoding (part), type); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), - CAMEL_MIME_FILTER (inline_filter)); - camel_data_wrapper_decode_to_stream_sync ( - dw, (CamelStream *) filtered_stream, cancellable, NULL); - camel_stream_close ((CamelStream *) filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); - - mp = em_inline_filter_get_multipart (inline_filter); - - if (charset_added) { - camel_content_type_set_param (type, "charset", NULL); - } - - g_object_unref (inline_filter); - camel_content_type_unref (type); - - /* We handle our made-up multipart here, so we don't recursively call ourselves */ - len = part_id->len; - count = camel_multipart_get_number (mp); - for (i = 0; i < count; i++) { - CamelMimePart *newpart = camel_multipart_get_part (mp, i); - - if (!newpart) - continue; - - type = camel_mime_part_get_content_type (newpart); - if (camel_content_type_is (type, "text", "*") && (!camel_content_type_is (type, "text", "calendar"))) { - gint s_len = part_id->len; - - g_string_append (part_id, ".plain_text"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), newpart, part_id->str); - puri->write_func = efh_write_text_plain; - puri->mime_type = g_strdup ("text/html"); - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - g_string_truncate (part_id, s_len); - em_format_add_puri (emf, puri); - } else { - g_string_append_printf (part_id, ".inline.%d", i); - em_format_parse_part (emf, CAMEL_MIME_PART (newpart), part_id, info, cancellable); - g_string_truncate (part_id, len); - } - } - - g_object_unref (mp); -} - -static void -efh_parse_text_html (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - const gchar *location; - gchar *cid = NULL; - CamelURL *base; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - base = em_format_get_base_url (emf); - location = camel_mime_part_get_content_location (part); - if (location == NULL) { - if (base) - cid = camel_url_to_string (base, 0); - else - cid = g_strdup (part_id->str); - } else { - if (strchr (location, ':') == NULL && base != NULL) { - CamelURL *uri; - - uri = camel_url_new_with_base (base, location); - cid = camel_url_to_string (uri, 0); - camel_url_free (uri); - } else { - cid = g_strdup (location); - } - } - - len = part_id->len; - g_string_append (part_id, ".text_html"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_text_html; - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); - - if (cid) - g_free (cid); -} - -static void -efh_parse_message_external (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - CamelMimePart *newpart; - CamelContentType *type; - const gchar *access_type; - gchar *url = NULL, *desc = NULL; - gchar *content; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - newpart = camel_mime_part_new (); - - /* needs to be cleaner */ - type = camel_mime_part_get_content_type (part); - access_type = camel_content_type_param (type, "access-type"); - if (!access_type) { - const gchar *msg = _("Malformed external-body part"); - camel_mime_part_set_content (newpart, msg, strlen (msg), - "text/plain"); - goto addPart; - } - - if (!g_ascii_strcasecmp(access_type, "ftp") || - !g_ascii_strcasecmp(access_type, "anon-ftp")) { - const gchar *name, *site, *dir, *mode; - gchar *path; - gchar ftype[16]; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - dir = camel_content_type_param (type, "directory"); - mode = camel_content_type_param (type, "mode"); - if (name == NULL || site == NULL) - goto fail; - - /* Generate the path. */ - if (dir) - path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name); - else - path = g_strdup_printf("/%s", *name=='/'?name+1:name); - - if (mode && *mode) - sprintf(ftype, ";type=%c", *mode); - else - ftype[0] = 0; - - url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype); - g_free (path); - desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url); - } else if (!g_ascii_strcasecmp (access_type, "local-file")) { - const gchar *name, *site; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - if (name == NULL) - goto fail; - - url = g_filename_to_uri (name, NULL, NULL); - if (site) - desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site); - else - desc = g_strdup_printf(_("Pointer to local file (%s)"), name); - } else if (!g_ascii_strcasecmp (access_type, "URL")) { - const gchar *urlparam; - gchar *s, *d; - - /* RFC 2017 */ - urlparam = camel_content_type_param (type, "url"); - if (urlparam == NULL) - goto fail; - - /* For obscure MIMEy reasons, the URL may be split into words */ - url = g_strdup (urlparam); - s = d = url; - while (*s) { - if (!isspace ((guchar) * s)) - *d++ = *s; - s++; - } - *d = 0; - desc = g_strdup_printf (_("Pointer to remote data (%s)"), url); - } else - goto fail; - - content = g_strdup_printf ("<a href=\"%s\">%s</a>", url, desc); - camel_mime_part_set_content (newpart, content, strlen (content), "text/html"); - g_free (content); - - g_free (url); - g_free (desc); - -fail: - content = g_strdup_printf ( - _("Pointer to unknown external data (\"%s\" type)"), - access_type); - camel_mime_part_set_content (newpart, content, strlen (content), "text/plain"); - g_free (content); - -addPart: - len = part_id->len; - g_string_append (part_id, ".msg_external"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_text_html; - puri->mime_type = g_strdup ("text/html"); - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_message_deliverystatus (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - len = part_id->len; - g_string_append (part_id, ".deliverystatus"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_source; - puri->mime_type = g_strdup ("text/html"); - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_message_rfc822 (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelDataWrapper *dw; - CamelMimePart *opart; - CamelStream *stream; - CamelMimeParser *parser; - gint len; - EMFormatParserInfo oinfo = *info; - EMFormatPURI *puri; - - len = part_id->len; - g_string_append (part_id, ".rfc822"); - - /* Create an empty PURI that will represent start of the RFC message */ - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_message_rfc822; - puri->mime_type = g_strdup ("text/html"); - puri->is_attachment = info->is_attachment; - em_format_add_puri (emf, puri); - - /* Now parse the message, creating multiple sub-PURIs */ - stream = camel_stream_mem_new (); - dw = camel_medium_get_content ((CamelMedium *) part); - camel_data_wrapper_write_to_stream_sync (dw, stream, cancellable, NULL); - g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, cancellable, NULL); - - parser = camel_mime_parser_new (); - camel_mime_parser_init_with_stream (parser, stream, NULL); - - opart = camel_mime_part_new (); - camel_mime_part_construct_from_parser_sync (opart, parser, cancellable, NULL); - - em_format_parse_part_as (emf, opart, part_id, &oinfo, - "x-evolution/message", cancellable); - - /* Add another generic PURI that represents end of the RFC message. - * The em_format_write() function will skip all PURIs between the ".rfc822" - * PURI and ".rfc822.end" PURI as they will be rendered in an <iframe> */ - g_string_append (part_id, ".end"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), NULL, part_id->str); - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); - - g_object_unref (opart); - g_object_unref (parser); - g_object_unref (stream); -} - -/*****************************************************************************/ - -static void -efh_write_image (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *content; - EMFormatHTML *efh; - CamelDataWrapper *dw; - GByteArray *ba; - CamelStream *raw_content; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - efh = (EMFormatHTML *) emf; - - dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part)); - g_return_if_fail (dw); - - raw_content = camel_stream_mem_new (); - camel_data_wrapper_decode_to_stream_sync (dw, raw_content, cancellable, NULL); - ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (raw_content)); - - if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - - if (!efh->priv->animate_images) { - - gchar *buff; - gsize len; - - em_format_html_animation_extract_frame (ba, &buff, &len); - - camel_stream_write (stream, buff, len, cancellable, NULL); - - g_free (buff); - - } else { - - camel_stream_write_to_stream (raw_content, stream, cancellable, NULL); - } - - } else { - - gchar *buffer; - - if (!efh->priv->animate_images) { - - gchar *buff; - gsize len; - - em_format_html_animation_extract_frame (ba, &buff, &len); - - content = g_base64_encode ((guchar *) buff, len); - g_free (buff); - - } else { - content = g_base64_encode ((guchar *) ba->data, ba->len); - } - - /* The image is already base64-encrypted so we can directly - * paste it to the output */ - buffer = g_strdup_printf ( - "<img src=\"data:%s;base64,%s\" style=\"max-width: 100%%;\" />", - puri->mime_type ? puri->mime_type : "image/*", content); - - camel_stream_write_string (stream, buffer, cancellable, NULL); - g_free (buffer); - g_free (content); - } - - g_object_unref (raw_content); -} - -static void -efh_write_text_enriched (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - CamelStream *filtered_stream; - CamelMimeFilter *enriched; - guint32 flags = 0; - GString *buffer; - CamelContentType *ct; - gchar *mime_type = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - ct = camel_mime_part_get_content_type (puri->part); - if (ct) { - mime_type = camel_content_type_simple (ct); - } - - if (!g_strcmp0(mime_type, "text/richtext")) { - flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; - camel_stream_write_string ( - stream, "\n<!-- text/richtext -->\n", - cancellable, NULL); - } else { - camel_stream_write_string ( - stream, "\n<!-- text/enriched -->\n", - cancellable, NULL); - } - - if (mime_type) - g_free (mime_type); - - enriched = camel_mime_filter_enriched_new (flags); - filtered_stream = camel_stream_filter_new (stream); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), enriched); - g_object_unref (enriched); - - buffer = g_string_new (""); - - g_string_append_printf (buffer, - "<div class=\"part-container\" style=\"border-color: #%06x; " - "background-color: #%06x; color: #%06x;\">" - "<div class=\"part-container-inner-margin\">\n", - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_CONTENT]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - - camel_stream_write_string (stream, buffer->str, cancellable, NULL); - g_string_free (buffer, TRUE); - - em_format_format_text ( - emf, (CamelStream *) filtered_stream, - (CamelDataWrapper *) puri->part, cancellable); - - g_object_unref (filtered_stream); - camel_stream_write_string (stream, "</div></div>", cancellable, NULL); -} - -static void -efh_write_text_plain (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - CamelDataWrapper *dw; - CamelStream *filtered_stream; - CamelMimeFilter *html_filter; - EMFormatHTML *efh = (EMFormatHTML *) emf; - gchar *content; - const gchar *format; - guint32 flags; - guint32 rgb; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - flags = efh->text_html_flags; - - dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part)); - - /* Check for RFC 2646 flowed text. */ - if (camel_content_type_is(dw->mime_type, "text", "plain") - && (format = camel_content_type_param(dw->mime_type, "format")) - && !g_ascii_strcasecmp(format, "flowed")) - flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; - - rgb = e_color_to_value ( - &efh->priv->colors[EM_FORMAT_HTML_COLOR_CITATION]); - filtered_stream = camel_stream_filter_new (stream); - html_filter = camel_mime_filter_tohtml_new (flags, rgb); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), html_filter); - g_object_unref (html_filter); - - content = g_strdup_printf ( - "<div class=\"part-container\" style=\"border-color: #%06x; " - "background-color: #%06x; color: #%06x;\">" - "<div class=\"part-container-inner-margin pre\">\n", - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_CONTENT]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - - camel_stream_write_string (stream, content, cancellable, NULL); - em_format_format_text (emf, filtered_stream, (CamelDataWrapper *) puri->part, cancellable); - camel_stream_flush (filtered_stream, cancellable, NULL); - - g_object_unref (filtered_stream); - g_free (content); - - camel_stream_write_string (stream, "</div></div>\n", cancellable, NULL); -} - -static gchar * -get_tag (const gchar *tag_name, - gchar *opening, - gchar *closing) -{ - gchar *t; - gboolean has_end; - - for (t = closing - 1; t != opening; t--) { - if (*t != ' ') - break; - } - - /* Not a pair tag */ - if (*t == '/') - return g_strndup (opening, closing - opening + 1); - - for (t = closing; t && *t; t++) { - if (*t == '<') - break; - } - - do { - if (*t == '/') { - has_end = TRUE; - break; - } - - if (*t == '>') { - has_end = FALSE; - break; - } - - t++; - - } while (t && *t); - - /* Broken HTML? */ - if (!has_end) - return g_strndup (opening, closing - opening + 1); - - do { - if ((*t != ' ') && (*t != '/')) - break; - - t++; - } while (t && *t); - - if (g_strncasecmp (t, tag_name, strlen (tag_name)) == 0) { - - closing = strstr (t, ">"); - - return g_strndup (opening, closing - opening + strlen (tag_name)); - } - - /* Broken HTML? */ - return g_strndup (opening, closing - opening + 1); -} - -static void -efh_write_text_html (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatHTML *efh = (EMFormatHTML *) emf; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - em_format_format_text (emf, stream, - (CamelDataWrapper *) puri->part, cancellable); - - } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { - GString *string; - GByteArray *ba; - gchar *pos; - GList *tags, *iter; - gboolean valid; - gchar *tag; - const gchar *document_end; - gint length; - gint i; - CamelStream *decoded_stream; - - decoded_stream = camel_stream_mem_new (); - em_format_format_text (emf, decoded_stream, - (CamelDataWrapper *) puri->part, cancellable); - g_seekable_seek (G_SEEKABLE (decoded_stream), 0, G_SEEK_SET, cancellable, NULL); - - ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (decoded_stream)); - string = g_string_new_len ((gchar *) ba->data, ba->len); - - g_object_unref (decoded_stream); - - tags = NULL; - pos = string->str; - valid = FALSE; - do { - gchar *closing; - gchar *opening; - - pos = strstr (pos + 1, "<"); - if (!pos) - break; - - opening = pos; - closing = strstr (pos, ">"); - - /* Find where the actual tag name begins */ - for (tag = pos + 1; tag && *tag; tag++) { - if (*tag != ' ') - break; - } - - if (g_ascii_strncasecmp (tag, "style", 5) == 0) { - tags = g_list_append ( - tags, - get_tag ("style", opening, closing)); - } else if (g_ascii_strncasecmp (tag, "script", 6) == 0) { - tags = g_list_append ( - tags, - get_tag ("script", opening, closing)); - } else if (g_ascii_strncasecmp (tag, "link", 4) == 0) { - tags = g_list_append ( - tags, - get_tag ("link", opening, closing)); - } else if (g_ascii_strncasecmp (tag, "body", 4) == 0) { - valid = TRUE; - break; - } - - } while (TRUE); - - /* Something's wrong, let's write the entire HTML and hope - * that WebKit can handle it */ - if (!valid) { - EMFormatWriterInfo i = *info; - i.mode = EM_FORMAT_WRITE_MODE_RAW; - efh_write_text_html (emf, puri, stream, &i, cancellable); - return; - } - - /* include the "body" as well -----v */ - g_string_erase (string, 0, tag - string->str + 4); - g_string_prepend (string, "<div "); - - for (iter = tags; iter; iter = iter->next) { - g_string_prepend (string, iter->data); - } - - g_list_free_full (tags, g_free); - - /* that's reversed </body></html>... */ - document_end = ">lmth/<>ydob/<"; - length = strlen (document_end); - tag = string->str + string->len - 1; - i = 0; - valid = FALSE; - while (i < length - 1) { - gchar c; - - if (g_ascii_isspace (*tag)) { - tag--; - continue; - } - - if ((*tag >= 'A') && (*tag <= 'Z')) - c = *tag + 32; - else - c = *tag; - - if (c == document_end[i]) { - tag--; - i++; - valid = TRUE; - continue; - } - - valid = FALSE; - } - - if (valid) - g_string_truncate (string, tag - string->str); - - camel_stream_write_string (stream, string->str, cancellable, NULL); - - g_string_free (string, TRUE); - } else { - gchar *str; - gchar *uri; - - uri = em_format_build_mail_uri ( - emf->folder, emf->message_uid, - "part_id", G_TYPE_STRING, puri->uri, - "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, - NULL); - - str = g_strdup_printf ( - "<div class=\"part-container-nostyle\">" - "<iframe width=\"100%%\" height=\"auto\"" - " frameborder=\"0\" src=\"%s\" " - " style=\"border: 1px solid #%06x; background-color: #%06x;\">" - "</iframe>" - "</div>", - uri, - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT])); - - camel_stream_write_string (stream, str, cancellable, NULL); - - g_free (str); - g_free (uri); - } -} - -static void -efh_write_source (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatHTML *efh = (EMFormatHTML *) emf; - GString *buffer; - CamelStream *filtered_stream; - CamelMimeFilter *filter; - CamelDataWrapper *dw = (CamelDataWrapper *) puri->part; - - filtered_stream = camel_stream_filter_new (stream); - - filter = camel_mime_filter_tohtml_new ( - CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | - CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), filter); - g_object_unref (filter); - - buffer = g_string_new (""); - - g_string_append_printf ( - buffer, "<div class=\"part-container\" style=\"border: 0; background: #%06x; color: #%06x;\" >", - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_BODY]), - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - - camel_stream_write_string ( - stream, buffer->str, cancellable, NULL); - camel_stream_write_string ( - stream, "<code class=\"pre\">", cancellable, NULL); - camel_data_wrapper_write_to_stream_sync (dw, filtered_stream, - cancellable, NULL); - camel_stream_write_string ( - stream, "</code>", cancellable, NULL); - - g_object_unref (filtered_stream); - g_string_free (buffer, TRUE); -} - -static void -efh_write_headers (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GString *buffer; - EMFormatHTML *efh = (EMFormatHTML *) emf; - gint bg_color; - - if (!puri->part) - return; - - buffer = g_string_new (""); - - if (info->mode & EM_FORMAT_WRITE_MODE_PRINTING) { - GdkColor white = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 }; - bg_color = e_color_to_value (&white); - } else { - bg_color = e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]); - } - - g_string_append_printf ( - buffer, - "<div class=\"headers\" style=\"background: #%06x;\">" - "<table border=\"0\" width=\"100%%\" style=\"color: #%06x;\">\n" - "<tr><td valign=\"top\" width=\"16\">\n", - bg_color, - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER])); - - if (info->headers_collapsable) { - g_string_append_printf (buffer, - "<img src=\"evo-file://%s/%s\" class=\"navigable\" " - "id=\"__evo-collapse-headers-img\" />" - "</td><td>", - EVOLUTION_IMAGESDIR, - (info->headers_collapsed) ? "plus.png" : "minus.png"); - - efh_format_short_headers (efh, buffer, (CamelMedium *) puri->part, - info->headers_collapsed, - cancellable); - } - - efh_format_full_headers (efh, buffer, (CamelMedium *) puri->part, - (info->mode == EM_FORMAT_WRITE_MODE_ALL_HEADERS), - !info->headers_collapsed, - cancellable); - - g_string_append (buffer, "</td></tr></table></div>"); - - camel_stream_write_string (stream, buffer->str, cancellable, NULL); - - g_string_free (buffer, true); -} - -static void -efh_write_error (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - CamelStream *filtered_stream; - CamelMimeFilter *filter; - CamelDataWrapper *dw; - - dw = camel_medium_get_content ((CamelMedium *) puri->part); - - camel_stream_write_string (stream, "<em><font color=\"red\">", cancellable, NULL); - - filtered_stream = camel_stream_filter_new (stream); - filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), filter); - g_object_unref (filter); - - camel_data_wrapper_decode_to_stream_sync (dw, filtered_stream, cancellable, NULL); - - g_object_unref (filtered_stream); - - camel_stream_write_string (stream, "</font></em><br>", cancellable, NULL); -} - -static void -efh_write_message_rfc822 (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - - GList *puris; - GList *iter; - EMFormatWriterInfo msgInfo = *info; - msgInfo.mode = EM_FORMAT_WRITE_MODE_NORMAL; - - /* Create a new fake list of PURIs which will contain only - * PURIs from this message. */ - iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); - if (!iter || !iter->next) - return; - - iter = iter->next; - puris = NULL; - while (iter) { - - EMFormatPURI *p; - p = iter->data; - - if (g_str_has_suffix (p->uri, ".rfc822.end")) - break; - - puris = g_list_append (puris, p); - iter = iter->next; - - }; - - efh_write_message (emf, puris, stream, &msgInfo, cancellable); - - g_list_free (puris); - - } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { - - GList *iter; - gboolean can_write = FALSE; - - iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); - if (!iter || !iter->next) - return; - - /* Skip everything before attachment bar, inclusive */\ - iter = iter->next; - while (iter) { - - EMFormatPURI *p = iter->data; - - /* EMFormatHTMLPrint has registered a special writer - * for headers, try to find it and use it. */ - if (g_str_has_suffix (p->uri, ".headers")) { - - const EMFormatHandler *handler; - - handler = em_format_find_handler ( - emf, "x-evolution/message/headers"); - if (handler && handler->write_func) - handler->write_func (emf, p, stream, info, cancellable); - - iter = iter->next; - continue; - } - - if (g_str_has_suffix (p->uri, ".rfc822.end")) - break; - - if (g_str_has_suffix (p->uri, ".attachment-bar")) - can_write = TRUE; - - if (can_write && p->write_func) { - p->write_func ( - emf, p, stream, info, cancellable); - } - - iter = iter->next; - } - - } else { - gchar *str; - gchar *uri; - - EMFormatHTML *efh = (EMFormatHTML *) emf; - EMFormatPURI *p; - GList *iter; - - iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); - if (!iter || !iter->next) - return; - - iter = iter->next; - p = iter->data; - - uri = em_format_build_mail_uri (emf->folder, emf->message_uid, - "part_id", G_TYPE_STRING, p->uri, - "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, - NULL); - - str = g_strdup_printf ( - "<div class=\"part-container\" style=\"border-color: #%06x; " - "background-color: #%06x;\">" - "<div class=\"part-container-inner-margin\">\n" - "<iframe width=\"100%%\" height=\"auto\"" - " frameborder=\"0\" src=\"%s\" name=\"%s\"></iframe>" - "</div></div>", - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]), - uri, puri->uri); - - camel_stream_write_string (stream, str, cancellable, NULL); - - g_free (str); - g_free (uri); - } - -} - -/*****************************************************************************/ - -/* Notes: - * - * image/tiff is omitted because it's a multi-page image format, but - * gdk-pixbuf unconditionally renders the first page only, and doesn't - * even indicate through meta-data whether multiple pages are present - * (see bug 335959). Therefore, make no attempt to render TIFF images - * inline and defer to an application that can handle multi-page TIFF - * files properly like Evince or Gimp. Once the referenced bug is - * fixed we can reevaluate this policy. - */ -static EMFormatHandler type_builtin_table[] = { - { (gchar *) "image/gif", efh_parse_image, efh_write_image, }, - { (gchar *) "image/jpeg", efh_parse_image, efh_write_image, }, - { (gchar *) "image/png", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-png", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-bmp", efh_parse_image, efh_write_image, }, - { (gchar *) "image/bmp", efh_parse_image, efh_write_image, }, - { (gchar *) "image/svg", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-cmu-raster", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-ico", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-anymap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-bitmap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-graymap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-pixmap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-xpixmap", efh_parse_image, efh_write_image, }, - { (gchar *) "text/enriched", efh_parse_text_enriched, efh_write_text_enriched, }, - { (gchar *) "text/plain", efh_parse_text_plain, efh_write_text_plain, }, - { (gchar *) "text/html", efh_parse_text_html, efh_write_text_html, }, - { (gchar *) "text/richtext", efh_parse_text_enriched, efh_write_text_enriched, }, - { (gchar *) "text/*", efh_parse_text_plain, efh_write_text_plain, }, - { (gchar *) "message/rfc822", efh_parse_message_rfc822, efh_write_message_rfc822, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "message/news", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "message/delivery-status", efh_parse_message_deliverystatus, efh_write_text_plain, }, - { (gchar *) "message/external-body", efh_parse_message_external, efh_write_text_plain, }, - { (gchar *) "message/*", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE }, - - /* This is where one adds those busted, non-registered types, - * that some idiot mailer writers out there decide to pull out - * of their proverbials at random. */ - { (gchar *) "image/jpg", efh_parse_image, efh_write_image, }, - { (gchar *) "image/pjpeg", efh_parse_image, efh_write_image, }, - - /* special internal types */ - { (gchar *) "x-evolution/message/rfc822", 0, efh_write_text_plain, }, - { (gchar *) "x-evolution/message/headers", 0, efh_write_headers, }, - { (gchar *) "x-evolution/message/source", 0, efh_write_source, }, - { (gchar *) "x-evolution/message/attachment", 0, efh_write_attachment, }, - { (gchar *) "x-evolution/message/error", 0, efh_write_error, }, -}; - -static void -efh_builtin_init (EMFormatHTMLClass *efhc) -{ - EMFormatClass *emfc; - gint ii; - - emfc = (EMFormatClass *) efhc; - - for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++) - em_format_class_add_handler ( - emfc, &type_builtin_table[ii]); -} - -static void -efh_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_BODY_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_BODY, - g_value_get_boxed (value)); - return; - - case PROP_CITATION_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CITATION, - g_value_get_boxed (value)); - return; - - case PROP_CONTENT_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CONTENT, - g_value_get_boxed (value)); - return; - - case PROP_FRAME_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_FRAME, - g_value_get_boxed (value)); - return; - - case PROP_HEADER_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_HEADER, - g_value_get_boxed (value)); - return; - - case PROP_IMAGE_LOADING_POLICY: - em_format_html_set_image_loading_policy ( - EM_FORMAT_HTML (object), - g_value_get_enum (value)); - return; - - case PROP_MARK_CITATIONS: - em_format_html_set_mark_citations ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_ONLY_LOCAL_PHOTOS: - em_format_html_set_only_local_photos ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_SHOW_SENDER_PHOTO: - em_format_html_set_show_sender_photo ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_SHOW_REAL_DATE: - em_format_html_set_show_real_date ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_TEXT_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_TEXT, - g_value_get_boxed (value)); - return; - - case PROP_ANIMATE_IMAGES: - em_format_html_set_animate_images ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -efh_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GdkColor color; - - switch (property_id) { - case PROP_BODY_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_BODY, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_CITATION_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CITATION, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_CONTENT_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CONTENT, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_FRAME_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_FRAME, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_HEADER_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_HEADER, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_IMAGE_LOADING_POLICY: - g_value_set_enum ( - value, - em_format_html_get_image_loading_policy ( - EM_FORMAT_HTML (object))); - return; - - case PROP_MARK_CITATIONS: - g_value_set_boolean ( - value, em_format_html_get_mark_citations ( - EM_FORMAT_HTML (object))); - return; - - case PROP_ONLY_LOCAL_PHOTOS: - g_value_set_boolean ( - value, em_format_html_get_only_local_photos ( - EM_FORMAT_HTML (object))); - return; - - case PROP_SHOW_SENDER_PHOTO: - g_value_set_boolean ( - value, em_format_html_get_show_sender_photo ( - EM_FORMAT_HTML (object))); - return; - - case PROP_SHOW_REAL_DATE: - g_value_set_boolean ( - value, em_format_html_get_show_real_date ( - EM_FORMAT_HTML (object))); - return; - - case PROP_TEXT_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_TEXT, - &color); - g_value_set_boxed (value, &color); - return; - case PROP_ANIMATE_IMAGES: - g_value_set_boolean ( - value, em_format_html_get_animate_images ( - EM_FORMAT_HTML (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -efh_finalize (GObject *object) -{ - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -efh_constructed (GObject *object) -{ - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (parent_class)->constructed (object); - - e_extensible_load_extensions (E_EXTENSIBLE (object)); -} - -static void -efh_write_attachment (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *text, *html; - CamelContentType *ct; - gchar *mime_type; - const EMFormatHandler *handler; - - /* we display all inlined attachments only */ - - /* this could probably be cleaned up ... */ - camel_stream_write_string ( - stream, - "<table border=1 cellspacing=0 cellpadding=0><tr><td>" - "<table width=10 cellspacing=0 cellpadding=0>" - "<tr><td></td></tr></table></td>" - "<td><table width=3 cellspacing=0 cellpadding=0>" - "<tr><td></td></tr></table></td><td><font size=-1>\n", - cancellable, NULL); - - ct = camel_mime_part_get_content_type (puri->part); - mime_type = camel_content_type_simple (ct); - - /* output some info about it */ - text = em_format_describe_part (puri->part, mime_type); - html = camel_text_to_html ( - text, ((EMFormatHTML *) emf)->text_html_flags & - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_write_string (stream, html, cancellable, NULL); - g_free (html); - g_free (text); - - camel_stream_write_string ( - stream, "</font></td></tr><tr></table>", cancellable, NULL); - - handler = em_format_find_handler (emf, mime_type); - if (handler && handler->write_func && handler->write_func != efh_write_attachment) { - if (em_format_is_inline (emf, puri->uri, puri->part, handler)) - handler->write_func (emf, puri, stream, info, cancellable); - } - - g_free (mime_type); -} - -static void -efh_preparse (EMFormat *emf) -{ - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - CamelInternetAddress *addr; - CamelSession *session; - ESourceRegistry *registry; - - if (!emf->message) { - efh->priv->can_load_images = FALSE; - return; - } - - session = em_format_get_session (emf); - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - addr = camel_mime_message_get_from (emf->message); - efh->priv->can_load_images = em_utils_in_addressbook ( - registry, addr, FALSE); -} - -static void -efh_write_message (EMFormat *emf, - GList *puris, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GList *iter; - EMFormatHTML *efh; - gchar *header; - - efh = (EMFormatHTML *) emf; - - header = 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: #000; font-weight: bold; }\n" - "</style>\n" - "</head><body bgcolor=\"#%06x\">", - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_BODY])); - - camel_stream_write_string (stream, header, cancellable, NULL); - g_free (header); - - if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) { - - efh_write_source (emf, emf->mail_part_list->data, - stream, info, cancellable); - - camel_stream_write_string (stream, "</body></html>", cancellable, NULL); - return; - } - - for (iter = puris; iter; iter = iter->next) { - - EMFormatPURI *puri = iter->data; - - if (!puri) - continue; - - /* If current PURI has suffix .rfc822 then iterate through all - * subsequent PURIs until PURI with suffix .rfc822.end is found. - * These skipped PURIs contain entire RFC message which will - * be written in <iframe> as attachment. - */ - if (g_str_has_suffix (puri->uri, ".rfc822")) { - - /* If the PURI is not an attachment, then we must - * inline it here otherwise it would not be displayed. */ - if (!puri->is_attachment && puri->write_func) { - /* efh_write_message_rfc822 starts parsing _after_ - * the passed PURI, so we must give it previous PURI here */ - EMFormatPURI *p; - if (!iter->prev) - continue; - - p = iter->prev->data; - puri->write_func (emf, p, stream, info, cancellable); - } - - while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) { - - iter = iter->next; - if (iter) - puri = iter->data; - - d(printf(".rfc822 - skipping %s\n", puri->uri)); - } - - /* Skip the .rfc822.end PURI as well. */ - if (!iter) - break; - - continue; - } - - if (puri->write_func && !puri->is_attachment) { - puri->write_func (emf, puri, stream, info, cancellable); - d(printf("Writing PURI %s\n", puri->uri)); - } else { - d(printf("Skipping PURI %s\n", puri->uri)); - } - } - - camel_stream_write_string (stream, "</body></html>", cancellable, NULL); -} - -static void -efh_write (EMFormat *emf, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - efh_write_message (emf, emf->mail_part_list, stream, info, cancellable); -} - -static void -efh_base_init (EMFormatHTMLClass *klass) -{ - efh_builtin_init (klass); -} - -static void -efh_class_init (EMFormatHTMLClass *klass) -{ - GObjectClass *object_class; - EMFormatClass *emf_class; - - parent_class = g_type_class_peek_parent (klass); - g_type_class_add_private (klass, sizeof (EMFormatHTMLPrivate)); - - emf_class = EM_FORMAT_CLASS (klass); - emf_class->preparse = efh_preparse; - emf_class->write = efh_write; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = efh_constructed; - object_class->set_property = efh_set_property; - object_class->get_property = efh_get_property; - object_class->finalize = efh_finalize; - - 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_enum ( - "image-loading-policy", - "Image Loading Policy", - NULL, - E_TYPE_MAIL_IMAGE_LOADING_POLICY, - E_MAIL_IMAGE_LOADING_POLICY_ALWAYS, - 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)); -} - -static void -efh_init (EMFormatHTML *efh, - EMFormatHTMLClass *klass) -{ - GdkColor *color; - - efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh); - - g_queue_init (&efh->pending_object_list); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]; - gdk_color_parse ("#eeeeee", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]; - gdk_color_parse ("#ffffff", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]; - gdk_color_parse ("#3f3f3f", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER]; - gdk_color_parse ("#eeeeee", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_TEXT]; - gdk_color_parse ("#000000", color); - - efh->text_html_flags = - CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - efh->show_icon = TRUE; -} - -GType -em_format_html_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatHTMLClass), - (GBaseInitFunc) efh_base_init, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) efh_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormatHTML), - 0, /* n_preallocs */ - (GInstanceInitFunc) efh_init, - NULL /* value_table */ - }; - - static const GInterfaceInfo extensible_info = { - (GInterfaceInitFunc) NULL, - (GInterfaceFinalizeFunc) NULL, - NULL /* interface_data */ - }; - - type = g_type_register_static ( - em_format_get_type(), "EMFormatHTML", - &type_info, G_TYPE_FLAG_ABSTRACT); - - g_type_add_interface_static ( - type, E_TYPE_EXTENSIBLE, &extensible_info); - } - - return type; -} - -/*****************************************************************************/ -void -em_format_html_get_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - GdkColor *color) -{ - GdkColor *format_color; - - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES); - g_return_if_fail (color != NULL); - - format_color = &efh->priv->colors[type]; - - color->red = format_color->red; - color->green = format_color->green; - color->blue = format_color->blue; -} - -void -em_format_html_set_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - const GdkColor *color) -{ - GdkColor *format_color; - const gchar *property_name; - - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES); - g_return_if_fail (color != NULL); - - format_color = &efh->priv->colors[type]; - - if (gdk_color_equal (color, format_color)) - return; - - format_color->red = color->red; - format_color->green = color->green; - format_color->blue = color->blue; - - switch (type) { - case EM_FORMAT_HTML_COLOR_BODY: - property_name = "body-color"; - break; - case EM_FORMAT_HTML_COLOR_CITATION: - property_name = "citation-color"; - break; - case EM_FORMAT_HTML_COLOR_CONTENT: - property_name = "content-color"; - break; - case EM_FORMAT_HTML_COLOR_FRAME: - property_name = "frame-color"; - break; - case EM_FORMAT_HTML_COLOR_HEADER: - property_name = "header-color"; - break; - case EM_FORMAT_HTML_COLOR_TEXT: - property_name = "text-color"; - break; - default: - g_return_if_reached (); - } - - g_object_notify (G_OBJECT (efh), property_name); -} - -EMailImageLoadingPolicy -em_format_html_get_image_loading_policy (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0); - - return efh->priv->image_loading_policy; -} - -void -em_format_html_set_image_loading_policy (EMFormatHTML *efh, - EMailImageLoadingPolicy policy) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (policy == efh->priv->image_loading_policy) - return; - - efh->priv->image_loading_policy = policy; - - g_object_notify (G_OBJECT (efh), "image-loading-policy"); -} - -gboolean -em_format_html_get_mark_citations (EMFormatHTML *efh) -{ - guint32 flags; - - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - flags = efh->text_html_flags; - - return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0); -} - -void -em_format_html_set_mark_citations (EMFormatHTML *efh, - gboolean mark_citations) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (mark_citations) - efh->text_html_flags |= - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - else - efh->text_html_flags &= - ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - - g_object_notify (G_OBJECT (efh), "mark-citations"); -} - -gboolean -em_format_html_get_only_local_photos (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->only_local_photos; -} - -void -em_format_html_set_only_local_photos (EMFormatHTML *efh, - gboolean only_local_photos) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->only_local_photos = only_local_photos; - - g_object_notify (G_OBJECT (efh), "only-local-photos"); -} - -gboolean -em_format_html_get_show_sender_photo (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->show_sender_photo; -} - -void -em_format_html_set_show_sender_photo (EMFormatHTML *efh, - gboolean show_sender_photo) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->show_sender_photo = show_sender_photo; - - g_object_notify (G_OBJECT (efh), "show-sender-photo"); -} - -gboolean -em_format_html_get_show_real_date (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->show_real_date; -} - -void -em_format_html_set_show_real_date (EMFormatHTML *efh, - gboolean show_real_date) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->show_real_date = show_real_date; - - g_object_notify (G_OBJECT (efh), "show-real-date"); -} - -gboolean -em_format_html_get_animate_images (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->animate_images; -} - -void -em_format_html_set_animate_images (EMFormatHTML *efh, - gboolean animate_images) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->animate_images = animate_images; - - g_object_notify (G_OBJECT (efh), "animate-images"); -} - -CamelMimePart * -em_format_html_file_part (EMFormatHTML *efh, - const gchar *mime_type, - const gchar *filename, - GCancellable *cancellable) -{ - CamelMimePart *part; - CamelStream *stream; - CamelDataWrapper *dw; - gchar *basename; - - stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL); - if (stream == NULL) - return NULL; - - dw = camel_data_wrapper_new (); - camel_data_wrapper_construct_from_stream_sync ( - dw, stream, cancellable, NULL); - g_object_unref (stream); - if (mime_type) - camel_data_wrapper_set_mime_type (dw, mime_type); - part = camel_mime_part_new (); - camel_medium_set_content ((CamelMedium *) part, dw); - g_object_unref (dw); - basename = g_path_get_basename (filename); - camel_mime_part_set_filename (part, basename); - g_free (basename); - - return part; -} - -void -em_format_html_format_cert_infos (GQueue *cert_infos, - GString *output_buffer) -{ - GQueue valid = G_QUEUE_INIT; - GList *head, *link; - - g_return_if_fail (cert_infos != NULL); - g_return_if_fail (output_buffer != NULL); - - head = g_queue_peek_head_link (cert_infos); - - /* Make sure we have a valid CamelCipherCertInfo before - * appending anything to the output buffer, so we don't - * end up with "()". */ - for (link = head; link != NULL; link = g_list_next (link)) { - CamelCipherCertInfo *cinfo = link->data; - - if ((cinfo->name != NULL && *cinfo->name != '\0') || - (cinfo->email != NULL && *cinfo->email != '\0')) { - g_queue_push_tail (&valid, cinfo); - } - } - - if (g_queue_is_empty (&valid)) - return; - - g_string_append (output_buffer, " ("); - - while (!g_queue_is_empty (&valid)) { - CamelCipherCertInfo *cinfo; - - cinfo = g_queue_pop_head (&valid); - - if (cinfo->name != NULL && *cinfo->name != '\0') { - g_string_append (output_buffer, cinfo->name); - - if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, " <"); - g_string_append (output_buffer, cinfo->email); - g_string_append (output_buffer, ">"); - } - - } else if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, cinfo->email); - } - - if (!g_queue_is_empty (&valid)) - g_string_append (output_buffer, ", "); - } - - g_string_append_c (output_buffer, ')'); -} - -static void -efh_format_text_header (EMFormatHTML *emfh, - GString *buffer, - const gchar *label, - const gchar *value, - guint32 flags) -{ - const gchar *fmt, *html; - gchar *mhtml = NULL; - gboolean is_rtl; - - if (value == NULL) - return; - - while (*value == ' ') - value++; - - if (!(flags & EM_FORMAT_HTML_HEADER_HTML)) - html = mhtml = camel_text_to_html (value, emfh->text_html_flags, 0); - else - html = value; - - is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; - - if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) { - if (flags & EM_FORMAT_HEADER_BOLD) { - fmt = "<tr class=\"header-item\" style=\"display: %s\"><td><b>%s:</b> %s</td></tr>"; - } else { - fmt = "<tr class=\"header-item\" style=\"display: %s\"><td>%s: %s</td></tr>"; - } - } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) { - if (is_rtl) - fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b> </b></th></tr>"; - else - fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s<b> </b></th><td valign=top>%s</td></tr>"; - } else { - if (flags & EM_FORMAT_HEADER_BOLD) { - if (is_rtl) - fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b> </b></th></tr>"; - else - fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s:<b> </b></th><td>%s</td></tr>"; - } else { - if (is_rtl) - fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b> </b></td></tr>"; - else - fmt = "<tr class=\"header-item\" style=\"display: %s\"><td align=\"right\" valign=\"top\" nowrap>%s:<b> </b></td><td>%s</td></tr>"; - } - } - - g_string_append_printf (buffer, fmt, - (flags & EM_FORMAT_HTML_HEADER_HIDDEN ? "none" : "table-row"), label, html); - - g_free (mhtml); -} - -static const gchar *addrspec_hdrs[] = { - "Sender", "From", "Reply-To", "To", "Cc", "Bcc", - "Resent-Sender", "Resent-From", "Resent-Reply-To", - "Resent-To", "Resent-Cc", "Resent-Bcc", NULL -}; - -static gchar * -efh_format_address (EMFormatHTML *efh, - GString *out, - struct _camel_header_address *a, - gchar *field, - gboolean no_links) -{ - guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; - gchar *name, *mailto, *addr; - gint i = 0; - gchar *str = NULL; - gint limit = mail_config_get_address_count (); - - while (a) { - if (a->name) - name = camel_text_to_html (a->name, flags, 0); - else - name = NULL; - - switch (a->type) { - case CAMEL_HEADER_ADDRESS_NAME: - if (name && *name) { - gchar *real, *mailaddr; - - if (strchr (a->name, ',') || strchr (a->name, ';')) - g_string_append_printf (out, ""%s"", name); - else - g_string_append (out, name); - - g_string_append (out, " <"); - - /* rfc2368 for mailto syntax and url encoding extras */ - if ((real = camel_header_encode_phrase ((guchar *) a->name))) { - mailaddr = g_strdup_printf("%s <%s>", real, a->v.addr); - g_free (real); - mailto = camel_url_encode (mailaddr, "?=&()"); - g_free (mailaddr); - } else { - mailto = camel_url_encode (a->v.addr, "?=&()"); - } - } else { - mailto = camel_url_encode (a->v.addr, "?=&()"); - } - addr = camel_text_to_html (a->v.addr, flags, 0); - if (no_links) - g_string_append_printf (out, "%s", addr); - else - g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr); - g_free (mailto); - g_free (addr); - - if (name && *name) - g_string_append (out, ">"); - break; - case CAMEL_HEADER_ADDRESS_GROUP: - g_string_append_printf (out, "%s: ", name); - efh_format_address (efh, out, a->v.members, field, no_links); - g_string_append_printf (out, ";"); - break; - default: - g_warning ("Invalid address type"); - break; - } - - g_free (name); - - i++; - a = a->next; - if (a) - g_string_append (out, ", "); - - /* Let us add a '...' if we have more addresses */ - if (limit > 0 && (i == limit - 1)) { - const gchar *id = NULL; - - if (strcmp (field, _("To")) == 0) { - id = "to"; - } else if (strcmp (field, _("Cc")) == 0) { - id = "cc"; - } else if (strcmp (field, _("Bcc")) == 0) { - id = "bcc"; - } - - if (id) { - g_string_append_printf (out, - "<span id=\"__evo-moreaddr-%s\" " - "style=\"display: none;\">", id); - str = g_strdup_printf ( - "<img src=\"evo-file://%s/plus.png\" " - "id=\"__evo-moreaddr-img-%s\" class=\"navigable\">", - EVOLUTION_IMAGESDIR, id); - } - } - } - - if (str) { - const gchar *id = NULL; - - if (strcmp (field, _("To")) == 0) { - id = "to"; - } else if (strcmp (field, _("Cc")) == 0) { - id = "cc"; - } else if (strcmp (field, _("Bcc")) == 0) { - id = "bcc"; - } - - if (id) { - g_string_append_printf (out, - "</span>" - "<span class=\"navigable\" " - "id=\"__evo-moreaddr-ellipsis-%s\" " - "style=\"display: inline;\">...</span>", - id); - } - } - - return str; -} - -static void -canon_header_name (gchar *name) -{ - gchar *inptr = name; - - /* canonicalise the header name... first letter is - * capitalised and any letter following a '-' also gets - * capitalised */ - - if (*inptr >= 'a' && *inptr <= 'z') - *inptr -= 0x20; - - inptr++; - - while (*inptr) { - if (inptr[-1] == '-' && *inptr >= 'a' && *inptr <= 'z') - *inptr -= 0x20; - else if (*inptr >= 'A' && *inptr <= 'Z') - *inptr += 0x20; - - inptr++; - } -} - -void -em_format_html_format_header (EMFormat *emf, - GString *buffer, - CamelMedium *part, - struct _camel_header_raw *header, - guint32 flags, - const gchar *charset) -{ - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - gchar *name, *buf, *value = NULL; - const gchar *label, *txt; - gboolean addrspec = FALSE; - gchar *str_field = NULL; - gint i; - - name = g_alloca (strlen (header->name) + 1); - strcpy (name, header->name); - canon_header_name (name); - - for (i = 0; addrspec_hdrs[i]; i++) { - if (!strcmp (name, addrspec_hdrs[i])) { - addrspec = TRUE; - break; - } - } - - label = _(name); - - if (addrspec) { - struct _camel_header_address *addrs; - GString *html; - gchar *img; - const gchar *charset = em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf); - - buf = camel_header_unfold (header->value); - if (!(addrs = camel_header_address_decode (buf, charset))) { - g_free (buf); - return; - } - - g_free (buf); - - html = g_string_new(""); - img = efh_format_address (efh, html, addrs, (gchar *) label, - (flags & EM_FORMAT_HTML_HEADER_NOLINKS)); - - if (img) { - str_field = g_strdup_printf ("%s%s:", img, label); - label = str_field; - flags |= EM_FORMAT_HTML_HEADER_NODEC; - g_free (img); - } - - camel_header_address_list_clear (&addrs); - txt = value = html->str; - g_string_free (html, FALSE); - - flags |= EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_HTML; - } else if (!strcmp (name, "Subject")) { - buf = camel_header_unfold (header->value); - txt = value = camel_header_decode_string (buf, charset); - g_free (buf); - - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp(name, "X-evolution-mailer")) { - /* pseudo-header */ - label = _("Mailer"); - txt = value = camel_header_format_ctext (header->value, charset); - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) { - gint msg_offset, local_tz; - time_t msg_date; - struct tm local; - gchar *html; - gboolean hide_real_date; - - hide_real_date = !em_format_html_get_show_real_date (efh); - - txt = header->value; - while (*txt == ' ' || *txt == '\t') - txt++; - - html = camel_text_to_html (txt, efh->text_html_flags, 0); - - msg_date = camel_header_decode_date (txt, &msg_offset); - e_localtime_with_offset (msg_date, &local, &local_tz); - - /* Convert message offset to minutes (e.g. -0400 --> -240) */ - msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100); - /* Turn into offset from localtime, not UTC */ - msg_offset -= local_tz / 60; - - /* value will be freed at the end */ - if (!hide_real_date && !msg_offset) { - /* No timezone difference; just show the real Date: header */ - txt = value = html; - } else { - gchar *date_str; - - date_str = e_datetime_format_format ("mail", "header", - DTFormatKindDateTime, msg_date); - - if (hide_real_date) { - /* Show only the local-formatted date, losing all timezone - * information like Outlook does. Should we attempt to show - * it somehow? */ - txt = value = date_str; - } else { - txt = value = g_strdup_printf ("%s (<I>%s</I>)", html, date_str); - g_free (date_str); - } - g_free (html); - } - flags |= EM_FORMAT_HTML_HEADER_HTML | EM_FORMAT_HEADER_BOLD; - } else if (!strcmp(name, "Newsgroups")) { - struct _camel_header_newsgroup *ng, *scan; - GString *html; - - buf = camel_header_unfold (header->value); - - if (!(ng = camel_header_newsgroups_decode (buf))) { - g_free (buf); - return; - } - - g_free (buf); - - html = g_string_new(""); - scan = ng; - while (scan) { - if (flags & EM_FORMAT_HTML_HEADER_NOLINKS) - g_string_append_printf (html, "%s", scan->newsgroup); - else - g_string_append_printf(html, "<a href=\"news:%s\">%s</a>", - scan->newsgroup, scan->newsgroup); - scan = scan->next; - if (scan) - g_string_append_printf(html, ", "); - } - - camel_header_newsgroups_free (ng); - - txt = html->str; - g_string_free (html, FALSE); - flags |= EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_HTML; - } else if (!strcmp (name, "Received") || !strncmp (name, "X-", 2)) { - /* don't unfold Received nor extension headers */ - txt = value = camel_header_decode_string (header->value, charset); - } else { - /* don't unfold Received nor extension headers */ - buf = camel_header_unfold (header->value); - txt = value = camel_header_decode_string (buf, charset); - g_free (buf); - } - - efh_format_text_header (efh, buffer, label, txt, flags); - - g_free (value); - g_free (str_field); -} - -static void -efh_format_short_headers (EMFormatHTML *efh, - GString *buffer, - CamelMedium *part, - gboolean visible, - GCancellable *cancellable) -{ - EMFormat *emf = EM_FORMAT (efh); - const gchar *charset; - CamelContentType *ct; - const gchar *hdr_charset; - gchar *evolution_imagesdir; - gchar *subject = NULL; - struct _camel_header_address *addrs = NULL; - struct _camel_header_raw *header; - GString *from; - gboolean is_rtl; - - if (cancellable && g_cancellable_is_cancelled (cancellable)) - return; - - ct = camel_mime_part_get_content_type ((CamelMimePart *) part); - charset = camel_content_type_param (ct, "charset"); - charset = camel_iconv_charset_name (charset); - hdr_charset = em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf); - - evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); - from = g_string_new (""); - - g_string_append_printf (buffer, - "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" " - "id=\"__evo-short-headers\" style=\"display: %s\">", - visible ? "block" : "none"); - - header = ((CamelMimePart *) part)->headers; - while (header) { - if (!g_ascii_strcasecmp (header->name, "From")) { - GString *tmp; - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) { - header = header->next; - continue; - } - tmp = g_string_new (""); - efh_format_address (efh, tmp, addrs, header->name, FALSE); - - if (tmp->len) - g_string_printf (from, _("From: %s"), tmp->str); - g_string_free (tmp, TRUE); - - } else if (!g_ascii_strcasecmp (header->name, "Subject")) { - gchar *buf = NULL; - subject = camel_header_unfold (header->value); - buf = camel_header_decode_string (subject, hdr_charset); - g_free (subject); - subject = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); - g_free (buf); - } - header = header->next; - } - - is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; - if (is_rtl) { - g_string_append_printf ( - buffer, - "<tr><td width=\"100%%\" align=\"right\">%s%s%s <strong>%s</strong></td></tr>", - from->len ? "(" : "", from->str, from->len ? ")" : "", - subject ? subject : _("(no subject)")); - } else { - g_string_append_printf ( - buffer, - "<tr><td><strong>%s</strong> %s%s%s</td></tr>", - subject ? subject : _("(no subject)"), - from->len ? "(" : "", from->str, from->len ? ")" : ""); - } - - g_string_append (buffer, "</table>"); - - g_free (subject); - if (addrs) - camel_header_address_list_clear (&addrs); - - g_string_free (from, TRUE); - g_free (evolution_imagesdir); -} - -static void -write_contact_picture (CamelMimePart *part, - gint size, - GString *buffer) -{ - gchar *b64, *content_type; - CamelDataWrapper *dw; - CamelContentType *ct; - GByteArray *ba; - - ba = NULL; - dw = camel_medium_get_content (CAMEL_MEDIUM (part)); - if (dw) { - ba = camel_data_wrapper_get_byte_array (dw); - } - - if (!ba || ba->len == 0) { - - if (camel_mime_part_get_filename (part)) { - - if (size >= 0) { - g_string_append_printf ( - buffer, - "<img width=\"%d\" src=\"evo-file://%s\" />", - size, camel_mime_part_get_filename (part)); - } else { - g_string_append_printf ( - buffer, - "<img src=\"evo-file://%s\" />", - camel_mime_part_get_filename (part)); - } - } - - return; - } - - b64 = g_base64_encode (ba->data, ba->len); - ct = camel_mime_part_get_content_type (part); - content_type = camel_content_type_simple (ct); - - if (size >= 0) { - g_string_append_printf ( - buffer, - "<img width=\"%d\" src=\"data:%s;base64,%s\">", - size, content_type, b64); - } else { - g_string_append_printf ( - buffer, - "<img src=\"data:%s;base64,%s\">", - content_type, b64); - } - - g_free (b64); - g_free (content_type); -} - -static void -efh_format_full_headers (EMFormatHTML *efh, - GString *buffer, - CamelMedium *part, - gboolean all_headers, - gboolean visible, - GCancellable *cancellable) -{ - EMFormat *emf = EM_FORMAT (efh); - const gchar *charset; - CamelContentType *ct; - struct _camel_header_raw *header; - gboolean have_icon = FALSE; - const gchar *photo_name = NULL; - CamelInternetAddress *cia = NULL; - CamelSession *session; - ESourceRegistry *registry; - gboolean face_decoded = FALSE, contact_has_photo = FALSE; - guchar *face_header_value = NULL; - gsize face_header_len = 0; - gchar *header_sender = NULL, *header_from = NULL, *name; - gboolean mail_from_delegate = FALSE; - const gchar *hdr_charset; - gchar *evolution_imagesdir; - - if (cancellable && g_cancellable_is_cancelled (cancellable)) - return; - - session = em_format_get_session (emf); - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - ct = camel_mime_part_get_content_type ((CamelMimePart *) part); - charset = camel_content_type_param (ct, "charset"); - charset = camel_iconv_charset_name (charset); - hdr_charset = em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf); - - evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); - - g_string_append_printf (buffer, - "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" " - "id=\"__evo-full-headers\" style=\"display: %s\" width=\"100%%\">", - visible ? "block" : "none"); - - header = ((CamelMimePart *) part)->headers; - while (header) { - if (!g_ascii_strcasecmp (header->name, "Sender")) { - struct _camel_header_address *addrs; - GString *html; - - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) - break; - - html = g_string_new(""); - name = efh_format_address (efh, html, addrs, header->name, FALSE); - - header_sender = html->str; - camel_header_address_list_clear (&addrs); - - g_string_free (html, FALSE); - g_free (name); - } else if (!g_ascii_strcasecmp (header->name, "From")) { - struct _camel_header_address *addrs; - GString *html; - - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) - break; - - html = g_string_new(""); - name = efh_format_address (efh, html, addrs, header->name, FALSE); - - header_from = html->str; - camel_header_address_list_clear (&addrs); - - g_string_free (html, FALSE); - g_free (name); - } else if (!g_ascii_strcasecmp (header->name, "X-Evolution-Mail-From-Delegate")) { - mail_from_delegate = TRUE; - } - - header = header->next; - } - - if (header_sender && header_from && mail_from_delegate) { - gchar *bold_sender, *bold_from; - - g_string_append ( - buffer, - "<tr><td><table border=1 width=\"100%%\" " - "cellspacing=2 cellpadding=2><tr>"); - if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) - g_string_append ( - buffer, "<td align=\"right\" width=\"100%%\">"); - else - g_string_append ( - buffer, "<td align=\"left\" width=\"100%%\">"); - bold_sender = g_strconcat ("<b>", header_sender, "</b>", NULL); - bold_from = g_strconcat ("<b>", header_from, "</b>", NULL); - /* Translators: This message suggests to the receipients - * that the sender of the mail is different from the one - * listed in From field. */ - g_string_append_printf ( - buffer, - _("This message was sent by %s on behalf of %s"), - bold_sender, bold_from); - g_string_append (buffer, "</td></tr></table></td></tr>"); - g_free (bold_sender); - g_free (bold_from); - } - - g_free (header_sender); - g_free (header_from); - - g_string_append (buffer, "<tr><td width=\"100%%\"><table border=0 cellpadding=\"0\">\n"); - - g_free (evolution_imagesdir); - - /* dump selected headers */ - if (all_headers) { - header = ((CamelMimePart *) part)->headers; - while (header) { - em_format_html_format_header ( - emf, buffer, part, header, - EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset); - header = header->next; - } - } else { - GList *link; - gint mailer_shown = FALSE; - - link = g_queue_peek_head_link (&emf->header_list); - - while (link != NULL) { - EMFormatHeader *h = link->data; - gint mailer, face; - - header = ((CamelMimePart *) part)->headers; - mailer = !g_ascii_strcasecmp (h->name, "X-Evolution-Mailer"); - face = !g_ascii_strcasecmp (h->name, "Face"); - - while (header) { - if (em_format_html_get_show_sender_photo (efh) && - !photo_name && !g_ascii_strcasecmp (header->name, "From")) - photo_name = header->value; - - if (!mailer_shown && mailer && ( - !g_ascii_strcasecmp (header->name, "X-Mailer") || - !g_ascii_strcasecmp (header->name, "User-Agent") || - !g_ascii_strcasecmp (header->name, "X-Newsreader") || - !g_ascii_strcasecmp (header->name, "X-MimeOLE"))) { - struct _camel_header_raw xmailer, *use_header = NULL; - - if (!g_ascii_strcasecmp (header->name, "X-MimeOLE")) { - for (use_header = header->next; use_header; use_header = use_header->next) { - if (!g_ascii_strcasecmp (use_header->name, "X-Mailer") || - !g_ascii_strcasecmp (use_header->name, "User-Agent") || - !g_ascii_strcasecmp (use_header->name, "X-Newsreader")) { - /* even we have X-MimeOLE, then use rather the standard one, when available */ - break; - } - } - } - - if (!use_header) - use_header = header; - - xmailer.name = (gchar *) "X-Evolution-Mailer"; - xmailer.value = use_header->value; - mailer_shown = TRUE; - - em_format_html_format_header ( - emf, buffer, part, - &xmailer, h->flags, charset); - if (strstr(use_header->value, "Evolution")) - have_icon = TRUE; - } else if (!face_decoded && face && !g_ascii_strcasecmp (header->name, "Face")) { - gchar *cp = header->value; - - /* Skip over spaces */ - while (*cp == ' ') - cp++; - - face_header_value = g_base64_decode ( - cp, &face_header_len); - face_header_value = g_realloc ( - face_header_value, - face_header_len + 1); - face_header_value[face_header_len] = 0; - face_decoded = TRUE; - /* Showing an encoded "Face" header makes little sense */ - } else if (!g_ascii_strcasecmp (header->name, h->name) && !face) { - em_format_html_format_header ( - emf, buffer, part, - header, h->flags, charset); - } - - header = header->next; - } - - link = g_list_next (link); - } - } - - g_string_append (buffer, "</table></td>"); - - if (photo_name) { - CamelMimePart *photopart; - gboolean only_local_photo; - - cia = camel_internet_address_new (); - camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name); - only_local_photo = - em_format_html_get_only_local_photos (efh); - photopart = em_utils_contact_photo ( - registry, cia, only_local_photo); - - if (photopart) { - g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); - write_contact_picture (photopart, -1, buffer); - g_string_append (buffer, "</td>"); - g_object_unref (photopart); - } - g_object_unref (cia); - } - - if (!contact_has_photo && face_decoded) { - CamelMimePart *part; - - part = camel_mime_part_new (); - camel_mime_part_set_content ( - (CamelMimePart *) part, - (const gchar *) face_header_value, - face_header_len, "image/png"); - - g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); - write_contact_picture (part, 48, buffer); - g_string_append (buffer, "</td>"); - - g_object_unref (part); - g_free (face_header_value); - } - - if (have_icon && efh->show_icon) { - GtkIconInfo *icon_info; - CamelMimePart *iconpart = NULL; - - icon_info = gtk_icon_theme_lookup_icon ( - gtk_icon_theme_get_default (), - "evolution", 16, GTK_ICON_LOOKUP_NO_SVG); - if (icon_info != NULL) { - iconpart = em_format_html_file_part ( - (EMFormatHTML *) emf, "image/png", - gtk_icon_info_get_filename (icon_info), - cancellable); - gtk_icon_info_free (icon_info); - } - if (iconpart) { - g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); - write_contact_picture (iconpart, 16, buffer); - g_string_append (buffer, "</td>"); - - g_object_unref (iconpart); - } - } - - g_string_append (buffer, "</tr></table>"); -} - -gboolean -em_format_html_can_load_images (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) || - ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES) && - efh->priv->can_load_images)); -} - -void -em_format_html_animation_extract_frame (const GByteArray *anim, - gchar **frame, - gsize *len) -{ - GdkPixbufLoader *loader; - GdkPixbufAnimation *animation; - GdkPixbuf *frame_buf; - - /* GIF89a (GIF image signature) */ - const gchar GIF_HEADER[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; - const gint GIF_HEADER_LEN = sizeof (GIF_HEADER); - - /* NETSCAPE2.0 (extension describing animated GIF, starts on 0x310) */ - const gchar GIF_APPEXT[] = { 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, - 0x50, 0x45, 0x32, 0x2E, 0x30 }; - const gint GIF_APPEXT_LEN = sizeof (GIF_APPEXT); - - if ((anim == NULL) || (anim->data == NULL)) { - *frame = NULL; - *len = 0; - return; - } - - /* Check if the image is an animated GIF. We don't care about any - * other animated formats (APNG or MNG) as WebKit does not support them - * and displays only the first frame. */ - if ((anim->len < 0x331) - || (memcmp (anim->data, GIF_HEADER, GIF_HEADER_LEN) != 0) - || (memcmp (&anim->data[0x310], GIF_APPEXT, GIF_APPEXT_LEN) != 0)) { - - *frame = g_memdup (anim->data, anim->len); - *len = anim->len; - return; - } - - loader = gdk_pixbuf_loader_new (); - gdk_pixbuf_loader_write (loader, (guchar *) anim->data, anim->len, NULL); - gdk_pixbuf_loader_close (loader, NULL); - animation = gdk_pixbuf_loader_get_animation (loader); - if (!animation) { - - *frame = g_memdup (anim->data, anim->len); - *len = anim->len; - g_object_unref (loader); - return; - } - - /* Extract first frame */ - frame_buf = gdk_pixbuf_animation_get_static_image (animation); - if (!frame_buf) { - *frame = g_memdup (anim->data, anim->len); - *len = anim->len; - g_object_unref (loader); - g_object_unref (animation); - return; - } - - /* Unforunatelly, GdkPixbuf cannot save to GIF, but WebKit does not - * have any trouble displaying PNG image despite the part having - * image/gif mime-type */ - gdk_pixbuf_save_to_buffer (frame_buf, frame, len, "png", NULL, NULL); - - g_object_unref (loader); -} diff --git a/mail/em-format-html.h b/mail/em-format-html.h deleted file mode 100644 index 9749c37f31..0000000000 --- a/mail/em-format-html.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -/* - Abstract class for formatting mails to html -*/ - -#ifndef EM_FORMAT_HTML_H -#define EM_FORMAT_HTML_H - -#include <em-format/em-format.h> -#include <misc/e-web-view.h> -#include <libemail-engine/e-mail-enums.h> - -/* Standard GObject macros */ -#define EM_TYPE_FORMAT_HTML \ - (em_format_html_get_type ()) -#define EM_FORMAT_HTML(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), EM_TYPE_FORMAT_HTML, EMFormatHTML)) -#define EM_FORMAT_HTML_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), EM_TYPE_FORMAT_HTML, EMFormatHTMLClass)) -#define EM_IS_FORMAT_HTML(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), EM_TYPE_FORMAT_HTML)) -#define EM_IS_FORMAT_HTML_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), EM_TYPE_FORMAT_HTML)) -#define EM_FORMAT_HTML_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), EM_TYPE_FORMAT_HTML, EMFormatHTMLClass)) - -G_BEGIN_DECLS - -typedef struct _EMFormatHTML EMFormatHTML; -typedef struct _EMFormatHTMLClass EMFormatHTMLClass; -typedef struct _EMFormatHTMLPrivate EMFormatHTMLPrivate; -typedef struct _EMFormatWidgetPURI EMFormatWidgetPURI; - -enum _em_format_html_header_flags { - EM_FORMAT_HTML_HEADER_TO = 1 << 0, - EM_FORMAT_HTML_HEADER_CC = 1 << 1, - EM_FORMAT_HTML_HEADER_BCC = 1 << 2 -}; - -typedef enum { - EM_FORMAT_HTML_COLOR_BODY, /* header area background */ - EM_FORMAT_HTML_COLOR_CITATION, /* citation font color */ - EM_FORMAT_HTML_COLOR_CONTENT, /* message area background */ - EM_FORMAT_HTML_COLOR_FRAME, /* frame around message area */ - EM_FORMAT_HTML_COLOR_HEADER, /* header font color */ - EM_FORMAT_HTML_COLOR_TEXT, /* message font color */ - EM_FORMAT_HTML_NUM_COLOR_TYPES -} EMFormatHTMLColorType; - -#define EM_FORMAT_HTML_HEADER_NOCOLUMNS (EM_FORMAT_HEADER_LAST) - -/* header already in html format */ -#define EM_FORMAT_HTML_HEADER_HTML (EM_FORMAT_HEADER_LAST<<1) -#define EM_FORMAT_HTML_HEADER_NODEC (EM_FORMAT_HEADER_LAST<<2) -#define EM_FORMAT_HTML_HEADER_NOLINKS (EM_FORMAT_HEADER_LAST<<3) -#define EM_FORMAT_HTML_HEADER_HIDDEN (EM_FORMAT_HEADER_LAST<<4) - -#define EM_FORMAT_HTML_HEADER_LAST (EM_FORMAT_HEADER_LAST<<8) - -#define EM_FORMAT_HTML_VPAD \ - "<table cellspacing=0 cellpadding=3><tr><td>" \ - "<a name=\"padding\"></a></td></tr></table>\n" - -/** - * struct _EMFormatHTML - HTML formatter object. - * - * @format: - * @priv: - * @html: - * @pending_object_list: - * @headers: - * @text_html_flags: - * @body_colour: - * @header_colour: - * @text_colour: - * @frame_colour: - * @content_colour: - * @citation_color: - * @load_http:2: - * @load_http_now:1: - * @mark_citations:1: - * @hide_headers:1: - * @show_icon:1: - * - * Most of these fields are private or read-only. - * - * The base HTML formatter object. This object drives HTML generation - * into a WebKit parser. It also handles text to HTML conversion, - * multipart/related objects and inline images. - **/ -struct _EMFormatHTML { - EMFormat parent; - EMFormatHTMLPrivate *priv; - - GQueue pending_object_list; - - GSList *headers; - - guint32 text_html_flags; /* default flags for text to html conversion */ - guint hide_headers:1; /* no headers at all */ - guint show_icon:1; /* show an icon when the sender used Evo */ - guint32 header_wrap_flags; -}; - -struct _EMFormatHTMLClass { - EMFormatClass parent_class; - - GType html_widget_type; -}; - -GType em_format_html_get_type (void); -void em_format_html_get_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - GdkColor *color); -void em_format_html_set_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - const GdkColor *color); -EMailImageLoadingPolicy - em_format_html_get_image_loading_policy - (EMFormatHTML *efh); -void em_format_html_set_image_loading_policy - (EMFormatHTML *efh, - EMailImageLoadingPolicy policy); -gboolean em_format_html_get_mark_citations - (EMFormatHTML *efh); -void em_format_html_set_mark_citations - (EMFormatHTML *efh, - gboolean mark_citations); -gboolean em_format_html_get_only_local_photos - (EMFormatHTML *efh); -void em_format_html_set_only_local_photos - (EMFormatHTML *efh, - gboolean only_local_photos); -gboolean em_format_html_get_show_sender_photo - (EMFormatHTML *efh); -void em_format_html_set_show_sender_photo - (EMFormatHTML *efh, - gboolean show_sender_photo); -gboolean em_format_html_get_animate_images - (EMFormatHTML *efh); -void em_format_html_set_animate_images - (EMFormatHTML *efh, - gboolean animate_images); -void em_format_html_clone_sync (CamelFolder *folder, - const gchar *message_uid, - CamelMimeMessage *message, - EMFormatHTML *efh, - EMFormat *source); -gboolean em_format_html_get_show_real_date - (EMFormatHTML *efh); -void em_format_html_set_show_real_date - (EMFormatHTML *efh, - gboolean show_real_date); - -/* retrieves a pseudo-part icon wrapper for a file */ -CamelMimePart * em_format_html_file_part (EMFormatHTML *efh, - const gchar *mime_type, - const gchar *filename, - GCancellable *cancellable); - -void em_format_html_format_cert_infos - (GQueue *cert_infos, - GString *output_buffer); - -void em_format_html_format_message (EMFormatHTML *efh, - CamelStream *stream, - GCancellable *cancellable); - -void em_format_html_format_message_part - (EMFormatHTML *efh, - const gchar *part_id, - CamelStream *stream, - GCancellable *cancellable); - -void em_format_html_format_headers (EMFormatHTML *efh, - CamelStream *stream, - CamelMedium *part, - gboolean all_headers, - GCancellable *cancellable); -void em_format_html_format_header (EMFormat *emf, - GString *buffer, - CamelMedium *part, - struct _camel_header_raw *header, - guint32 flags, - const gchar *charset); - -gboolean em_format_html_can_load_images (EMFormatHTML *efh); - -void em_format_html_animation_extract_frame - (const GByteArray *anim, - gchar **frame, - gsize *len); - -G_END_DECLS - -#endif /* EM_FORMAT_HTML_H */ diff --git a/mail/em-utils.c b/mail/em-utils.c index bf75253d34..9be80fbf79 100644 --- a/mail/em-utils.c +++ b/mail/em-utils.c @@ -56,6 +56,9 @@ #include <shell/e-shell.h> #include <widgets/misc/e-attachment.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-formatter-quote.h> + #include <libemail-utils/mail-mt.h> #include <libemail-engine/e-mail-folder-utils.h> @@ -65,13 +68,11 @@ #include "e-mail-tag-editor.h" #include "em-composer-utils.h" -#include "em-format-html-display.h" -#include "em-format-html-print.h" #include "em-utils.h" #include "e-mail-printer.h" -#include "em-format/em-format-quote.h" /* XXX This is a dirty hack on a dirty hack. We really need +#include <em-format/e-mail-print-formatter.h> * to rework or get rid of the functions that use this. */ extern const gchar *shell_builtin_backend; @@ -616,20 +617,22 @@ do_print_msg_to_file (GObject *source, GAsyncResult *result, gpointer user_data) { - - EMFormatHTML *efh = EM_FORMAT_HTML (source); + EMailParser *parser; + EMailPartList *parts_list; gchar *filename = user_data; - EMailPrinter *printer; - printer = e_mail_printer_new (efh); + parser = E_MAIL_PARSER (source); + parts_list = e_mail_parser_parse_finish (parser, result, NULL); + + printer = e_mail_printer_new (parts_list); e_mail_printer_set_export_filename (printer, filename); g_signal_connect_swapped (printer, "done", G_CALLBACK (g_object_unref), printer); e_mail_printer_print (printer, TRUE, NULL); - g_object_unref (efh); + g_object_unref (parser); } static gboolean @@ -637,7 +640,7 @@ em_utils_print_messages_to_file (CamelFolder *folder, const gchar *uid, const gchar *filename) { - EMFormatHTMLDisplay *efhd; + EMailParser *parser; CamelMimeMessage *message; CamelStore *parent_store; CamelSession *session; @@ -649,11 +652,10 @@ em_utils_print_messages_to_file (CamelFolder *folder, parent_store = camel_folder_get_parent_store (folder); session = camel_service_get_session (CAMEL_SERVICE (parent_store)); - efhd = em_format_html_display_new (session); - ((EMFormat *) efhd)->message_uid = g_strdup (uid); + parser = e_mail_parser_new (session); - em_format_parse_async ((EMFormat *) efhd, message, folder, NULL, - (GAsyncReadyCallback) do_print_msg_to_file, g_strdup (filename)); + e_mail_parser_parse (parser, folder, uid, message, + (GAsyncReadyCallback) do_print_msg_to_file, NULL, g_strdup (filename)); return TRUE; } @@ -1180,13 +1182,19 @@ em_utils_message_to_html (CamelSession *session, CamelMimeMessage *message, const gchar *credits, guint32 flags, - EMFormat *source, + EMailPartList *parts_list, const gchar *append, guint32 *validity_found) { - EMFormatQuote *emfq; + EMailFormatter *formatter; + EMailParser *parser; CamelStream *mem; GByteArray *buf; + EShell *shell; + GtkWindow *window; + + shell = e_shell_get_default (); + window = e_shell_get_active_window (shell); g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); @@ -1194,10 +1202,12 @@ em_utils_message_to_html (CamelSession *session, mem = camel_stream_mem_new (); camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (mem), buf); - emfq = em_format_quote_new (session, credits, mem, flags); - em_format_set_composer ((EMFormat *) emfq, TRUE); + formatter = e_mail_formatter_quote_new (credits, flags); + e_mail_formatter_set_style (formatter, + gtk_widget_get_style (GTK_WIDGET (window)), + gtk_widget_get_state (GTK_WIDGET (window))); - if (!source) { + if (!parts_list) { GSettings *settings; gchar *charset; @@ -1205,37 +1215,38 @@ em_utils_message_to_html (CamelSession *session, * current view, not the global setting. */ settings = g_settings_new ("org.gnome.evolution.mail"); charset = g_settings_get_string (settings, "charset"); - em_format_set_default_charset ((EMFormat *) emfq, charset); + if (charset && *charset) + e_mail_formatter_set_default_charset (formatter, charset); g_object_unref (settings); g_free (charset); - } - /* FIXME Not passing a GCancellable here. */ - em_format_parse (EM_FORMAT (emfq), message, NULL, NULL); + parser = e_mail_parser_new (session); + parts_list = e_mail_parser_parse_sync (parser, NULL, NULL, message, NULL); + } if (validity_found) { - GList *iter; - EMFormat *emf = (EMFormat *) emfq; + GSList *iter; if (validity_found) *validity_found = 0; /* Return all found validities */ - for (iter = emf->mail_part_list; iter; iter = iter->next) { + for (iter = parts_list->list; iter; iter = iter->next) { - EMFormatPURI *puri = iter->data; - if (!puri) + EMailPart *part = iter->data; + if (!part) continue; - if (*validity_found && puri->validity_type) - *validity_found |= puri->validity_type; + if (*validity_found && part->validity_type) + *validity_found |= part->validity_type; } } - em_format_quote_write (emfq, mem, NULL); - - g_object_unref (emfq); + e_mail_formatter_format_sync ( + formatter, parts_list, mem, 0, + E_MAIL_FORMATTER_MODE_PRINTING, NULL); + g_object_unref (formatter); if (append && *append) camel_stream_write_string (mem, append, NULL, NULL); diff --git a/mail/em-utils.h b/mail/em-utils.h index 092b75933c..db286eb55b 100644 --- a/mail/em-utils.h +++ b/mail/em-utils.h @@ -35,7 +35,7 @@ G_BEGIN_DECLS -struct _EMFormat; +struct _EMailPartList; struct _EShell; gboolean em_utils_ask_open_many (GtkWindow *parent, gint how_many); @@ -66,7 +66,7 @@ void em_utils_selection_get_urilist (GtkSelectionData *data, CamelFolder *folder EProxy * em_utils_get_proxy (void); /* FIXME: should this have an override charset? */ -gchar *em_utils_message_to_html (CamelSession *session, CamelMimeMessage *msg, const gchar *credits, guint32 flags, struct _EMFormat *source, const gchar *append, guint32 *validity_found); +gchar *em_utils_message_to_html (CamelSession *session, CamelMimeMessage *msg, const gchar *credits, guint32 flags, struct _EMailPartList *parts_list, const gchar *append, guint32 *validity_found); void em_utils_empty_trash (GtkWidget *parent, EMailSession *session); diff --git a/modules/mail/e-mail-config-format-html.c b/modules/mail/e-mail-config-format-html.c index 527f720159..cbedecf914 100644 --- a/modules/mail/e-mail-config-format-html.c +++ b/modules/mail/e-mail-config-format-html.c @@ -26,7 +26,7 @@ #include <shell/e-shell.h> #include <e-util/e-util.h> -#include <mail/em-format-html.h> +#include <em-format/e-mail-formatter.h> static gpointer parent_class; @@ -95,7 +95,7 @@ mail_config_format_html_class_init (EExtensionClass *class) object_class = G_OBJECT_CLASS (class); object_class->constructed = mail_config_format_html_constructed; - class->extensible_type = EM_TYPE_FORMAT_HTML; + class->extensible_type = E_TYPE_MAIL_FORMATTER; } void diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c index f5bb100c39..3aecfd91d8 100644 --- a/modules/mail/e-mail-shell-backend.c +++ b/modules/mail/e-mail-shell-backend.c @@ -53,14 +53,16 @@ #include <mail/e-mail-reader.h> #include <mail/em-composer-utils.h> #include <mail/em-folder-utils.h> -#include <mail/em-format-hook.h> -#include <mail/em-format-html-display.h> #include <mail/em-utils.h> #include <mail/mail-send-recv.h> #include <mail/mail-vfolder-ui.h> #include <mail/importers/mail-importer.h> #include <mail/e-mail-ui-session.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-formatter.h> +#include <em-format/e-mail-part-utils.h> + #include "e-mail-shell-settings.h" #include "e-mail-shell-sidebar.h" #include "e-mail-shell-view.h" @@ -447,14 +449,6 @@ mail_shell_backend_constructed (GObject *object) /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_mail_shell_backend_parent_class)->constructed (object); - /* Register format types for EMFormatHook. */ - em_format_hook_register_type (em_format_get_type ()); - em_format_hook_register_type (em_format_html_get_type ()); - em_format_hook_register_type (em_format_html_display_get_type ()); - - /* Register plugin hook types. */ - em_format_hook_get_type (); - mail_shell_backend_init_importers (); g_signal_connect ( @@ -851,13 +845,35 @@ message_parsed_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - EMFormatHTML *formatter = EM_FORMAT_HTML (source_object); + EMailParser *parser = E_MAIL_PARSER (source_object); + EMailPartList *parts_list; GObject *preview = user_data; EMailDisplay *display; + SoupSession *soup_session; + GHashTable *mails; + gchar *mail_uri; display = g_object_get_data (preview, "mbox-imp-display"); - e_mail_display_set_formatter (display, formatter); - e_mail_display_load (display, EM_FORMAT (formatter)->uri_base); + + parts_list = e_mail_parser_parse_finish (parser, res, NULL); + + soup_session = webkit_get_default_session (); + mails = g_object_get_data (G_OBJECT (soup_session), "mails"); + if (!mails) { + mails = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, NULL); + g_object_set_data ( + G_OBJECT (soup_session), "mails", mails); + } + mail_uri = e_mail_part_build_uri ( + parts_list->folder, parts_list->message_uid, NULL, NULL); + + g_hash_table_insert (mails, mail_uri, parts_list); + + e_mail_display_set_parts_list (display, parts_list); + e_mail_display_load (display, NULL); + + g_object_unref (parts_list); } /* utility functions for mbox importer */ @@ -883,12 +899,9 @@ mbox_fill_preview_cb (GObject *preview, { EShell *shell; EMailDisplay *display; - EMFormat *formatter; - GHashTable *formatters; - SoupSession *soup_session; + EMailParser *parser; EMailSession *mail_session; ESourceRegistry *registry; - gchar *mail_uri; g_return_if_fail (preview != NULL); g_return_if_fail (msg != NULL); @@ -896,33 +909,13 @@ mbox_fill_preview_cb (GObject *preview, display = g_object_get_data (preview, "mbox-imp-display"); g_return_if_fail (display != NULL); - soup_session = webkit_get_default_session (); - formatters = g_object_get_data (G_OBJECT (soup_session), "formatters"); - if (!formatters) { - formatters = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify) g_free, NULL); - g_object_set_data ( - G_OBJECT (soup_session), "formatters", formatters); - } - - mail_uri = em_format_build_mail_uri (NULL, msg->message_id, NULL, NULL); - shell = e_shell_get_default (); registry = e_shell_get_registry (shell); mail_session = e_mail_session_new (registry); - formatter = EM_FORMAT ( - em_format_html_display_new ( - CAMEL_SESSION (mail_session))); - formatter->message_uid = g_strdup (msg->message_id); - formatter->uri_base = g_strdup (mail_uri); - - /* Don't free the mail_uri!! */ - g_hash_table_insert (formatters, mail_uri, formatter); - - em_format_parse_async ( - formatter, msg, NULL, NULL, - message_parsed_cb, preview); + parser = e_mail_parser_new (CAMEL_SESSION (mail_session)); + e_mail_parser_parse (parser, NULL, msg->message_id, msg, + message_parsed_cb, NULL, preview); g_object_unref (mail_session); } diff --git a/modules/mail/e-mail-shell-content.h b/modules/mail/e-mail-shell-content.h index b3eecbef63..ff8fb94bf8 100644 --- a/modules/mail/e-mail-shell-content.h +++ b/modules/mail/e-mail-shell-content.h @@ -26,7 +26,6 @@ #include <shell/e-shell-searchbar.h> #include <shell/e-shell-view.h> #include <mail/e-mail-view.h> -#include <mail/em-format-html-display.h> /* Standard GObject macros */ #define E_TYPE_MAIL_SHELL_CONTENT \ diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c index afe347cae6..3468339200 100644 --- a/modules/mail/e-mail-shell-view-actions.c +++ b/modules/mail/e-mail-shell-view-actions.c @@ -145,7 +145,7 @@ action_mail_account_properties_cb (GtkAction *action, registry = e_shell_get_registry (shell); source = e_source_registry_ref_source (registry, uid); g_return_if_fail (source != NULL); - + e_mail_shell_backend_edit_account ( E_MAIL_SHELL_BACKEND (shell_backend), GTK_WINDOW (shell_window), source); diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c index 00334d7559..9262ff8bb0 100644 --- a/modules/mail/em-mailer-prefs.c +++ b/modules/mail/em-mailer-prefs.c @@ -28,7 +28,6 @@ #include <glib/gi18n-lib.h> #include "em-mailer-prefs.h" -#include "em-format/em-format.h" #include <gtkhtml/gtkhtml-properties.h> #include <libxml/tree.h> diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am index 68b37b9693..6b81d16089 100644 --- a/widgets/misc/Makefile.am +++ b/widgets/misc/Makefile.am @@ -11,6 +11,7 @@ widgetsinclude_HEADERS = \ e-alarm-selector.h \ e-alert-bar.h \ e-attachment.h \ + e-attachment-bar.h \ e-attachment-button.h \ e-attachment-dialog.h \ e-attachment-handler.h \ @@ -100,6 +101,7 @@ libemiscwidgets_la_SOURCES = \ e-alarm-selector.c \ e-alert-bar.c \ e-attachment.c \ + e-attachment-bar.c \ e-attachment-button.c \ e-attachment-dialog.c \ e-attachment-handler.c \ diff --git a/mail/e-mail-attachment-bar.c b/widgets/misc/e-attachment-bar.c index 495e327a3e..d7c90df330 100644 --- a/mail/e-mail-attachment-bar.c +++ b/widgets/misc/e-attachment-bar.c @@ -1,5 +1,5 @@ /* - * e-mail-attachment-bar.c + * e-attachment-bar.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,7 +23,7 @@ #include <config.h> #endif -#include "e-mail-attachment-bar.h" +#include "e-attachment-bar.h" #include <glib/gi18n.h> @@ -31,13 +31,13 @@ #include "e-attachment-icon-view.h" #include "e-attachment-tree-view.h" -#define E_MAIL_ATTACHMENT_BAR_GET_PRIVATE(obj) \ +#define E_ATTACHMENT_BAR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MAIL_ATTACHMENT_BAR, EMailAttachmentBarPrivate)) + ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBarPrivate)) #define NUM_VIEWS 2 -struct _EMailAttachmentBarPrivate { +struct _EAttachmentBarPrivate { GtkTreeModel *model; GtkWidget *vbox; GtkWidget *expander; @@ -65,19 +65,19 @@ enum { }; /* Forward Declarations */ -static void e_mail_attachment_bar_interface_init +static void e_attachment_bar_interface_init (EAttachmentViewInterface *interface); G_DEFINE_TYPE_WITH_CODE ( - EMailAttachmentBar, - e_mail_attachment_bar, + EAttachmentBar, + e_attachment_bar, GTK_TYPE_VBOX, G_IMPLEMENT_INTERFACE ( E_TYPE_ATTACHMENT_VIEW, - e_mail_attachment_bar_interface_init)) + e_attachment_bar_interface_init)) static void -mail_attachment_bar_update_status (EMailAttachmentBar *bar) +attachment_bar_update_status (EAttachmentBar *bar) { EAttachmentStore *store; GtkActivatable *activatable; @@ -119,8 +119,8 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar) } static void -mail_attachment_bar_set_store (EMailAttachmentBar *bar, - EAttachmentStore *store) +attachment_bar_set_store (EAttachmentBar *bar, + EAttachmentStore *store) { g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); @@ -131,28 +131,30 @@ mail_attachment_bar_set_store (EMailAttachmentBar *bar, gtk_tree_view_set_model (GTK_TREE_VIEW (bar->priv->tree_view), bar->priv->model); - g_signal_connect_swapped ( + g_signal_connect_object ( bar->priv->model, "notify::num-attachments", - G_CALLBACK (mail_attachment_bar_update_status), bar); + G_CALLBACK (attachment_bar_update_status), bar, + G_CONNECT_SWAPPED); - g_signal_connect_swapped ( + g_signal_connect_object ( bar->priv->model, "notify::total-size", - G_CALLBACK (mail_attachment_bar_update_status), bar); + G_CALLBACK (attachment_bar_update_status), bar, + G_CONNECT_SWAPPED); /* Initialize */ - mail_attachment_bar_update_status (bar); + attachment_bar_update_status (bar); } static void -mail_attachment_bar_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) +attachment_bar_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { switch (property_id) { case PROP_ACTIVE_VIEW: - e_mail_attachment_bar_set_active_view ( - E_MAIL_ATTACHMENT_BAR (object), + e_attachment_bar_set_active_view ( + E_ATTACHMENT_BAR (object), g_value_get_int (value)); return; @@ -169,13 +171,13 @@ mail_attachment_bar_set_property (GObject *object, return; case PROP_EXPANDED: - e_mail_attachment_bar_set_expanded ( - E_MAIL_ATTACHMENT_BAR (object), + e_attachment_bar_set_expanded ( + E_ATTACHMENT_BAR (object), g_value_get_boolean (value)); return; case PROP_STORE: - mail_attachment_bar_set_store ( - E_MAIL_ATTACHMENT_BAR (object), + attachment_bar_set_store ( + E_ATTACHMENT_BAR (object), g_value_get_object (value)); return; } @@ -184,17 +186,17 @@ mail_attachment_bar_set_property (GObject *object, } static void -mail_attachment_bar_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) +attachment_bar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { switch (property_id) { case PROP_ACTIVE_VIEW: g_value_set_int ( value, - e_mail_attachment_bar_get_active_view ( - E_MAIL_ATTACHMENT_BAR (object))); + e_attachment_bar_get_active_view ( + E_ATTACHMENT_BAR (object))); return; case PROP_DRAGGING: @@ -214,25 +216,25 @@ mail_attachment_bar_get_property (GObject *object, case PROP_EXPANDED: g_value_set_boolean ( value, - e_mail_attachment_bar_get_expanded ( - E_MAIL_ATTACHMENT_BAR (object))); + e_attachment_bar_get_expanded ( + E_ATTACHMENT_BAR (object))); return; case PROP_STORE: g_value_set_object ( value, - e_mail_attachment_bar_get_store ( - E_MAIL_ATTACHMENT_BAR (object))); + e_attachment_bar_get_store ( + E_ATTACHMENT_BAR (object))); } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -mail_attachment_bar_dispose (GObject *object) +attachment_bar_dispose (GObject *object) { - EMailAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; - priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object); + priv = E_ATTACHMENT_BAR_GET_PRIVATE (object); if (priv->model != NULL) { g_object_unref (priv->model); @@ -295,16 +297,16 @@ mail_attachment_bar_dispose (GObject *object) } /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_mail_attachment_bar_parent_class)->dispose (object); + G_OBJECT_CLASS (e_attachment_bar_parent_class)->dispose (object); } static void -mail_attachment_bar_constructed (GObject *object) +attachment_bar_constructed (GObject *object) { - EMailAttachmentBarPrivate *priv; + EAttachmentBarPrivate *priv; GSettings *settings; - priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object); + priv = E_ATTACHMENT_BAR_GET_PRIVATE (object); /* Set up property-to-property bindings. */ @@ -365,131 +367,131 @@ mail_attachment_bar_constructed (GObject *object) g_object_unref (settings); /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (e_mail_attachment_bar_parent_class)->constructed (object); + G_OBJECT_CLASS (e_attachment_bar_parent_class)->constructed (object); } static EAttachmentViewPrivate * -mail_attachment_bar_get_private (EAttachmentView *view) +attachment_bar_get_private (EAttachmentView *view) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); return e_attachment_view_get_private (view); } static GtkTreePath * -mail_attachment_bar_get_path_at_pos (EAttachmentView *view, - gint x, - gint y) +attachment_bar_get_path_at_pos (EAttachmentView *view, + gint x, + gint y) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); return e_attachment_view_get_path_at_pos (view, x, y); } static EAttachmentStore * -mail_attachment_bar_get_store (EAttachmentView *view) +attachment_bar_get_store (EAttachmentView *view) { - return e_mail_attachment_bar_get_store (E_MAIL_ATTACHMENT_BAR (view)); + return e_attachment_bar_get_store (E_ATTACHMENT_BAR (view)); } static GList * -mail_attachment_bar_get_selected_paths (EAttachmentView *view) +attachment_bar_get_selected_paths (EAttachmentView *view) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); return e_attachment_view_get_selected_paths (view); } static gboolean -mail_attachment_bar_path_is_selected (EAttachmentView *view, - GtkTreePath *path) +attachment_bar_path_is_selected (EAttachmentView *view, + GtkTreePath *path) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); return e_attachment_view_path_is_selected (view, path); } static void -mail_attachment_bar_select_path (EAttachmentView *view, - GtkTreePath *path) +attachment_bar_select_path (EAttachmentView *view, + GtkTreePath *path) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); e_attachment_view_select_path (view, path); } static void -mail_attachment_bar_unselect_path (EAttachmentView *view, - GtkTreePath *path) +attachment_bar_unselect_path (EAttachmentView *view, + GtkTreePath *path) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); e_attachment_view_unselect_path (view, path); } static void -mail_attachment_bar_select_all (EAttachmentView *view) +attachment_bar_select_all (EAttachmentView *view) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); e_attachment_view_select_all (view); } static void -mail_attachment_bar_unselect_all (EAttachmentView *view) +attachment_bar_unselect_all (EAttachmentView *view) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); e_attachment_view_unselect_all (view); } static void -mail_attachment_bar_update_actions (EAttachmentView *view) +attachment_bar_update_actions (EAttachmentView *view) { - EMailAttachmentBar *bar; + EAttachmentBar *bar; - bar = E_MAIL_ATTACHMENT_BAR (view); + bar = E_ATTACHMENT_BAR (view); view = E_ATTACHMENT_VIEW (bar->priv->icon_view); e_attachment_view_update_actions (view); } static void -e_mail_attachment_bar_class_init (EMailAttachmentBarClass *class) +e_attachment_bar_class_init (EAttachmentBarClass *class) { GObjectClass *object_class; - g_type_class_add_private (class, sizeof (EMailAttachmentBarPrivate)); + g_type_class_add_private (class, sizeof (EAttachmentBarPrivate)); object_class = G_OBJECT_CLASS (class); - object_class->set_property = mail_attachment_bar_set_property; - object_class->get_property = mail_attachment_bar_get_property; - object_class->dispose = mail_attachment_bar_dispose; - object_class->constructed = mail_attachment_bar_constructed; + object_class->set_property = attachment_bar_set_property; + object_class->get_property = attachment_bar_get_property; + object_class->dispose = attachment_bar_dispose; + object_class->constructed = attachment_bar_constructed; g_object_class_install_property ( object_class, @@ -534,22 +536,22 @@ e_mail_attachment_bar_class_init (EMailAttachmentBarClass *class) } static void -e_mail_attachment_bar_interface_init (EAttachmentViewInterface *interface) +e_attachment_bar_interface_init (EAttachmentViewInterface *interface) { - interface->get_private = mail_attachment_bar_get_private; - interface->get_store = mail_attachment_bar_get_store; - interface->get_path_at_pos = mail_attachment_bar_get_path_at_pos; - interface->get_selected_paths = mail_attachment_bar_get_selected_paths; - interface->path_is_selected = mail_attachment_bar_path_is_selected; - interface->select_path = mail_attachment_bar_select_path; - interface->unselect_path = mail_attachment_bar_unselect_path; - interface->select_all = mail_attachment_bar_select_all; - interface->unselect_all = mail_attachment_bar_unselect_all; - interface->update_actions = mail_attachment_bar_update_actions; + interface->get_private = attachment_bar_get_private; + interface->get_store = attachment_bar_get_store; + interface->get_path_at_pos = attachment_bar_get_path_at_pos; + interface->get_selected_paths = attachment_bar_get_selected_paths; + interface->path_is_selected = attachment_bar_path_is_selected; + interface->select_path = attachment_bar_select_path; + interface->unselect_path = attachment_bar_unselect_path; + interface->select_all = attachment_bar_select_all; + interface->unselect_all = attachment_bar_unselect_all; + interface->update_actions = attachment_bar_update_actions; } static void -e_mail_attachment_bar_init (EMailAttachmentBar *bar) +e_attachment_bar_init (EAttachmentBar *bar) { EAttachmentView *view; GtkSizeGroup *size_group; @@ -557,7 +559,7 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar) GtkWidget *widget; GtkAction *action; - bar->priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (bar); + bar->priv = E_ATTACHMENT_BAR_GET_PRIVATE (bar); gtk_box_set_spacing (GTK_BOX (bar), 6); @@ -685,32 +687,32 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar) } GtkWidget * -e_mail_attachment_bar_new (EAttachmentStore *store) +e_attachment_bar_new (EAttachmentStore *store) { g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL); return g_object_new ( - E_TYPE_MAIL_ATTACHMENT_BAR, + E_TYPE_ATTACHMENT_BAR, "editable", FALSE, "store", store, NULL); } gint -e_mail_attachment_bar_get_active_view (EMailAttachmentBar *bar) +e_attachment_bar_get_active_view (EAttachmentBar *bar) { - g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), 0); + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), 0); return bar->priv->active_view; } void -e_mail_attachment_bar_set_active_view (EMailAttachmentBar *bar, - gint active_view) +e_attachment_bar_set_active_view (EAttachmentBar *bar, + gint active_view) { EAttachmentView *source; EAttachmentView *target; - g_return_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar)); + g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS); if (active_view == bar->priv->active_view) @@ -744,18 +746,18 @@ e_mail_attachment_bar_set_active_view (EMailAttachmentBar *bar, } gboolean -e_mail_attachment_bar_get_expanded (EMailAttachmentBar *bar) +e_attachment_bar_get_expanded (EAttachmentBar *bar) { - g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), FALSE); + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), FALSE); return bar->priv->expanded; } void -e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar, +e_attachment_bar_set_expanded (EAttachmentBar *bar, gboolean expanded) { - g_return_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar)); + g_return_if_fail (E_IS_ATTACHMENT_BAR (bar)); bar->priv->expanded = expanded; @@ -763,9 +765,9 @@ e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar, } EAttachmentStore * -e_mail_attachment_bar_get_store (EMailAttachmentBar *bar) +e_attachment_bar_get_store (EAttachmentBar *bar) { - g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), NULL); + g_return_val_if_fail (E_IS_ATTACHMENT_BAR (bar), NULL); return E_ATTACHMENT_STORE (bar->priv->model); } diff --git a/widgets/misc/e-attachment-bar.h b/widgets/misc/e-attachment-bar.h new file mode 100644 index 0000000000..9f40973fe9 --- /dev/null +++ b/widgets/misc/e-attachment-bar.h @@ -0,0 +1,79 @@ +/* + * e-attachment-bar.h + * + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_ATTACHMENT_BAR_H +#define E_ATTACHMENT_BAR_H + +#include <gtk/gtk.h> +#include <misc/e-attachment-view.h> + +/* Standard GObject macros */ +#define E_TYPE_ATTACHMENT_BAR \ + (e_attachment_bar_get_type ()) +#define E_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBar)) +#define E_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass)) +#define E_IS_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ATTACHMENT_BAR)) +#define E_IS_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ATTACHMENT_BAR)) +#define E_ATTACHMENT_BAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ATTACHMENT_BAR, EAttachmentBarClass)) + +G_BEGIN_DECLS + +typedef struct _EAttachmentBar EAttachmentBar; +typedef struct _EAttachmentBarClass EAttachmentBarClass; +typedef struct _EAttachmentBarPrivate EAttachmentBarPrivate; + +struct _EAttachmentBar { + GtkVBox parent; + EAttachmentBarPrivate *priv; +}; + +struct _EAttachmentBarClass { + GtkVBoxClass parent_class; +}; + +GType e_attachment_bar_get_type (void); +GtkWidget * e_attachment_bar_new (EAttachmentStore *store); +gint e_attachment_bar_get_active_view + (EAttachmentBar *bar); +void e_attachment_bar_set_active_view + (EAttachmentBar *bar, + gint active_view); +gboolean e_attachment_bar_get_expanded + (EAttachmentBar *bar); +void e_attachment_bar_set_expanded + (EAttachmentBar *bar, + gboolean expanded); +EAttachmentStore * + e_attachment_bar_get_store (EAttachmentBar *bar); + +G_END_DECLS + +#endif /* E_ATTACHMENT_BAR_H */ diff --git a/widgets/misc/e-attachment-button.c b/widgets/misc/e-attachment-button.c index 0d46807307..d3aa4a6350 100644 --- a/widgets/misc/e-attachment-button.c +++ b/widgets/misc/e-attachment-button.c @@ -701,9 +701,10 @@ e_attachment_button_set_view (EAttachmentButton *button, g_return_if_fail (button->priv->view == NULL); + g_object_ref (view); if (button->priv->view) g_object_unref (button->priv->view); - button->priv->view = g_object_ref (view); + button->priv->view = view; popup_menu = e_attachment_view_get_popup_menu (view); |