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 /em-format | |
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.
Diffstat (limited to 'em-format')
79 files changed, 14640 insertions, 4014 deletions
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 */ - |