diff options
Diffstat (limited to 'em-format')
77 files changed, 16046 insertions, 0 deletions
diff --git a/em-format/Makefile.am b/em-format/Makefile.am new file mode 100644 index 0000000000..2983eb0ecf --- /dev/null +++ b/em-format/Makefile.am @@ -0,0 +1,147 @@ +privsolib_LTLIBRARIES = libevolution-mail-formatter.la + +evolution_mail_formatter_includedir = $(privincludedir)/em-format + +ENUM_TYPES = e-mail-formatter-enums.h + +e-mail-formatter-enumtypes.h: $(top_srcdir)/enumtypes.h.template $(ENUM_TYPES) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $(top_srcdir)/enumtypes.h.template \ + --fhead "#ifndef E_MAIL_FORMATTER_ENUMTYPES_H\n#define E_MAIL_FORMATTER_ENUMTYPES_H\n" \ + --ftail "#endif /* E_MAIL_FORMATTER_ENUMTYPES_H */\n" \ + $(addprefix $(srcdir)/,$(ENUM_TYPES)) > $@ + +e-mail-formatter-enumtypes.c: $(top_srcdir)/enumtypes.c.template $(ENUM_TYPES) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $(top_srcdir)/enumtypes.c.template \ + --fhead "#include \"e-mail-formatter-enumtypes.h\"" \ + $(addprefix $(srcdir)/,$(ENUM_TYPES)) > $@ + +ENUM_GENERATED = \ + e-mail-formatter-enumtypes.c \ + e-mail-formatter-enumtypes.h \ + $(NULL) + +evolution_mail_formatter_include_HEADERS = \ + e-mail-extension-registry.h \ + e-mail-formatter-extension.h \ + e-mail-formatter.h \ + e-mail-formatter-enums.h \ + e-mail-formatter-enumtypes.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-headers.h \ + e-mail-part-image.h \ + e-mail-part-list.h \ + e-mail-part-utils.h \ + e-mail-stripsig-filter.h + +libevolution_mail_formatter_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/em-format \ + -I$(top_srcdir)/smime/lib \ + -I$(top_srcdir)/smime/gui \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(GTKHTML_CFLAGS) \ + $(LIBSOUP_CFLAGS) + +if ENABLE_SMIME +SMIME_EXTENSIONS = e-mail-parser-application-smime.c +endif + +libevolution_mail_formatter_la_SOURCES = \ + $(evolution_mail_formatter_include_HEADERS) \ + e-mail-extension-registry.c \ + e-mail-inline-filter.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-enumtypes.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-attachment-bar.c \ + e-mail-part-headers.c \ + e-mail-part-image.c \ + e-mail-part-list.c \ + e-mail-part-utils.c \ + e-mail-stripsig-filter.c \ + $(SMIME_EXTENSIONS) + +libevolution_mail_formatter_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 + +libevolution_mail_formatter_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(top_builddir)/shell/libevolution-shell.la \ + $(top_builddir)/libemail-engine/libemail-engine.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(GTKHTML_LIBS) \ + $(LIBSOUP_LIBS) \ + $(SMIME_LIBS) + +BUILT_SOURCES = \ + $(ENUM_GENERATED) \ + $(NULL) + +CLEANFILES = $(BUILT_SOURCES) + +-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..d5beb2f64b --- /dev/null +++ b/em-format/e-mail-extension-registry.c @@ -0,0 +1,335 @@ +/* + * 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 "e-mail-extension-registry.h" + +#include <string.h> + +#include <libebackend/libebackend.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-parser-extension.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; +}; + +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 +destroy_queue (GQueue *queue) +{ + g_queue_free_full (queue, g_object_unref); +} + +static void +mail_extension_registry_add_extension (EMailExtensionRegistry *registry, + const gchar **mime_types, + GType extension_type, + GCompareDataFunc compare_func) +{ + GObject *extension; + gint ii; + + if (mime_types == NULL) { + g_critical ( + "%s does not define any MIME types", + g_type_name (extension_type)); + return; + } + + extension = g_object_new (extension_type, NULL); + + for (ii = 0; mime_types[ii] != NULL; ii++) { + GQueue *queue; + + queue = g_hash_table_lookup ( + registry->priv->table, mime_types[ii]); + if (queue == NULL) { + queue = g_queue_new (); + g_hash_table_insert ( + registry->priv->table, + (gpointer) mime_types[ii], + queue); + } + + g_queue_insert_sorted ( + queue, g_object_ref (extension), + compare_func, NULL); + + if (camel_debug ("emformat:registry")) { + printf ( + "Added extension '%s' for type '%s'\n", + g_type_name (extension_type), + mime_types[ii]); + } + } + + g_object_unref (extension); +} + +static void +mail_extension_registry_finalize (GObject *object) +{ + EMailExtensionRegistryPrivate *priv; + + priv = E_MAIL_EXTENSION_REGISTRY_GET_PRIVATE (object); + + g_hash_table_destroy (priv->table); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_extension_registry_parent_class)-> + finalize (object); +} + +void +e_mail_extension_registry_class_init (EMailExtensionRegistryClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private ( + class, sizeof (EMailExtensionRegistryPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = mail_extension_registry_finalize; +} + +void +e_mail_extension_registry_init (EMailExtensionRegistry *registry) +{ + registry->priv = E_MAIL_EXTENSION_REGISTRY_GET_PRIVATE (registry); + + registry->priv->table = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) destroy_queue); +} + +/** + * e_mail_extension_registry_get_for_mime_type: + * @registry: 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 *registry, + const gchar *mime_type) +{ + g_return_val_if_fail (E_IS_MAIL_EXTENSION_REGISTRY (registry), NULL); + g_return_val_if_fail (mime_type && *mime_type, NULL); + + return g_hash_table_lookup (registry->priv->table, mime_type); +} + +/** + * e_mail_extension_registry_get_fallback: + * @registry: 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 *registry, + const gchar *mime_type) +{ + gchar *s, *type; + gsize len; + GQueue *parsers; + + g_return_val_if_fail (E_IS_MAIL_EXTENSION_REGISTRY (registry), NULL); + g_return_val_if_fail (mime_type && *mime_type, NULL); + + s = strchr (mime_type, '/'); + if (!s) + return NULL; + + 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 (registry->priv->table, s); + + g_free (type); + g_free (s); + + return parsers; +} + +/******************************************************************************/ + +G_DEFINE_TYPE_WITH_CODE ( + EMailParserExtensionRegistry, + e_mail_parser_extension_registry, + E_TYPE_MAIL_EXTENSION_REGISTRY, + G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)) + +static void +e_mail_parser_extension_registry_class_init (EMailParserExtensionRegistryClass *class) +{ +} + +static void +e_mail_parser_extension_registry_init (EMailParserExtensionRegistry *registry) +{ +} + +static gint +mail_parser_extension_registry_compare (gconstpointer extension1, + gconstpointer extension2, + gpointer user_data) +{ + EMailParserExtensionClass *class1; + EMailParserExtensionClass *class2; + + class1 = E_MAIL_PARSER_EXTENSION_GET_CLASS (extension1); + class2 = E_MAIL_PARSER_EXTENSION_GET_CLASS (extension2); + + if (class1->priority == class2->priority) + return 0; + + return (class1->priority < class2->priority) ? -1 : 1; +} + +void +e_mail_parser_extension_registry_load (EMailParserExtensionRegistry *registry) +{ + GType *children; + GType base_extension_type; + guint ii, n_children; + + g_return_if_fail (E_IS_MAIL_PARSER_EXTENSION_REGISTRY (registry)); + + base_extension_type = E_TYPE_MAIL_PARSER_EXTENSION; + children = g_type_children (base_extension_type, &n_children); + + for (ii = 0; ii < n_children; ii++) { + EMailParserExtensionClass *class; + + if (G_TYPE_IS_ABSTRACT (children[ii])) + continue; + + class = g_type_class_ref (children[ii]); + + mail_extension_registry_add_extension ( + E_MAIL_EXTENSION_REGISTRY (registry), + class->mime_types, children[ii], + mail_parser_extension_registry_compare); + + g_type_class_unref (class); + } + + g_free (children); +} + +/******************************************************************************/ + +G_DEFINE_TYPE_WITH_CODE ( + EMailFormatterExtensionRegistry, + e_mail_formatter_extension_registry, + E_TYPE_MAIL_EXTENSION_REGISTRY, + G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)) + +static void +e_mail_formatter_extension_registry_class_init (EMailFormatterExtensionRegistryClass *class) +{ +} + +static void +e_mail_formatter_extension_registry_init (EMailFormatterExtensionRegistry *registry) +{ +} + +static gint +mail_formatter_extension_registry_compare (gconstpointer extension1, + gconstpointer extension2, + gpointer user_data) +{ + EMailFormatterExtensionClass *class1; + EMailFormatterExtensionClass *class2; + + class1 = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension1); + class2 = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension2); + + if (class1->priority == class2->priority) + return 0; + + return (class1->priority < class2->priority) ? -1 : 1; +} + +void +e_mail_formatter_extension_registry_load (EMailFormatterExtensionRegistry *registry, + GType base_extension_type) +{ + GType *children; + guint ii, n_children; + + g_return_if_fail (E_IS_MAIL_FORMATTER_EXTENSION_REGISTRY (registry)); + + children = g_type_children (base_extension_type, &n_children); + + for (ii = 0; ii < n_children; ii++) { + EMailFormatterExtensionClass *class; + + if (G_TYPE_IS_ABSTRACT (children[ii])) + continue; + + class = g_type_class_ref (children[ii]); + + mail_extension_registry_add_extension ( + E_MAIL_EXTENSION_REGISTRY (registry), + class->mime_types, children[ii], + mail_formatter_extension_registry_compare); + + g_type_class_unref (class); + } + + g_free (children); +} + diff --git a/em-format/e-mail-extension-registry.h b/em-format/e-mail-extension-registry.h new file mode 100644 index 0000000000..7002eb0385 --- /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 <glib-object.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 _EMailExtensionRegistry { + GObject parent; + EMailExtensionRegistryPrivate *priv; +}; + +struct _EMailExtensionRegistryClass { + GObjectClass parent_class; +}; + +GType e_mail_extension_registry_get_type + (void) G_GNUC_CONST; +GQueue * e_mail_extension_registry_get_for_mime_type + (EMailExtensionRegistry *registry, + const gchar *mime_type); +GQueue * e_mail_extension_registry_get_fallback + (EMailExtensionRegistry *registry, + 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 _EMailParserExtensionRegistry { + EMailExtensionRegistry parent; + EMailParserExtensionRegistryPrivate *priv; +}; + +struct _EMailParserExtensionRegistryClass { + EMailExtensionRegistryClass parent_class; +}; + +GType e_mail_parser_extension_registry_get_type + (void) G_GNUC_CONST; +void e_mail_parser_extension_registry_load + (EMailParserExtensionRegistry *registry); + +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 _EMailFormatterExtensionRegistry { + EMailExtensionRegistry parent; + EMailFormatterExtensionRegistryPrivate *priv; +}; + +struct _EMailFormatterExtensionRegistryClass { + EMailExtensionRegistryClass parent_class; +}; + +GType e_mail_formatter_extension_registry_get_type + (void) G_GNUC_CONST; +void e_mail_formatter_extension_registry_load + (EMailFormatterExtensionRegistry *registry, + GType base_extension_type); + +G_END_DECLS + +#endif /* E_MAIL_EXTENSION_REGISTRY_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..89378b8b16 --- /dev/null +++ b/em-format/e-mail-formatter-attachment-bar.c @@ -0,0 +1,104 @@ +/* + * 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-part-attachment-bar.h" + +#include <glib/gi18n-lib.h> + +#include "e-mail-formatter-extension.h" + +typedef EMailFormatterExtension EMailFormatterAttachmentBar; +typedef EMailFormatterExtensionClass EMailFormatterAttachmentBarClass; + +GType e_mail_formatter_attachment_bar_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterAttachmentBar, + e_mail_formatter_attachment_bar, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +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) && + (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS)) + return FALSE; + + str = g_strdup_printf ( + "<object type=\"application/vnd.evolution.widget.attachment-bar\" " + "height=\"0\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>", + e_mail_part_get_id (part), + e_mail_part_get_id (part)); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + + return TRUE; +} + +static GtkWidget * +emfe_attachment_bar_get_widget (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params) +{ + EAttachmentStore *store; + GtkWidget *widget; + + g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT_BAR (part), NULL); + + store = e_mail_part_attachment_bar_get_store ( + E_MAIL_PART_ATTACHMENT_BAR (part)); + + widget = e_attachment_bar_new (store); + g_object_set_data (G_OBJECT (store), "attachment-bar", widget); + + return widget; +} + +static void +e_mail_formatter_attachment_bar_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_attachment_bar_format; + class->get_widget = emfe_attachment_bar_get_widget; +} + +static void +e_mail_formatter_attachment_bar_init (EMailFormatterExtension *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..9e44901fce --- /dev/null +++ b/em-format/e-mail-formatter-attachment.c @@ -0,0 +1,408 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <shell/e-shell.h> +#include <shell/e-shell-window.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-attachment-bar.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-utils.h" + +#define d(x) + +typedef EMailFormatterExtension EMailFormatterAttachment; +typedef EMailFormatterExtensionClass EMailFormatterAttachmentClass; + +GType e_mail_formatter_attachment_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterAttachment, + e_mail_formatter_attachment, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + E_MAIL_PART_ATTACHMENT_MIME_TYPE, + "application/vnd.evolution.widget.attachment-button", + NULL +}; + +static EAttachmentStore * +find_attachment_store (EMailPartList *part_list, + EMailPart *start) +{ + EAttachmentStore *store = NULL; + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + const gchar *start_id; + gchar *tmp, *pos; + EMailPart *part; + gchar *id; + + start_id = e_mail_part_get_id (start); + + e_mail_part_list_queue_parts (part_list, NULL, &queue); + + head = g_queue_peek_head_link (&queue); + + id = g_strconcat (start_id, ".attachment-bar", NULL); + tmp = g_strdup (id); + part = NULL; + do { + d (printf ("Looking up attachment bar as %s\n", id)); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *p = link->data; + const gchar *p_id; + + p_id = e_mail_part_get_id (p); + + 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 != NULL) + store = e_mail_part_attachment_bar_get_store ( + E_MAIL_PART_ATTACHMENT_BAR (part)); + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + return store; +} + +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 *registry; + GQueue *extensions; + EMailPartAttachment *empa; + CamelMimePart *mime_part; + CamelMimeFilterToHTMLFlags flags; + const gchar *attachment_part_id; + const gchar *part_id; + + g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT (part), FALSE); + + empa = (EMailPartAttachment *) part; + part_id = e_mail_part_get_id (part); + + if ((context->mode == E_MAIL_FORMATTER_MODE_NORMAL) || + (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) || + (context->mode == E_MAIL_FORMATTER_MODE_ALL_HEADERS)) { + EAttachment *attachment; + GList *head, *link; + + attachment = e_mail_part_attachment_ref_attachment ( + E_MAIL_PART_ATTACHMENT (part)); + + head = g_queue_peek_head_link (&part->validities); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (pair == NULL) + continue; + + if ((pair->validity_type & E_MAIL_PART_VALIDITY_SIGNED) != 0) + e_attachment_set_signed ( + attachment, + pair->validity->sign.status); + + if ((pair->validity_type & E_MAIL_PART_VALIDITY_ENCRYPTED) != 0) + e_attachment_set_encrypted ( + attachment, + pair->validity->encrypt.status); + } + + store = find_attachment_store (context->part_list, part); + if (store) { + GList *attachments = e_attachment_store_get_attachments (store); + if (!g_list_find (attachments, attachment)) { + e_attachment_store_add_attachment ( + store, attachment); + } + g_list_free (attachments); + } else { + g_warning ("Failed to locate attachment-bar for %s", part_id); + } + + g_object_unref (attachment); + } + + registry = e_mail_formatter_get_extension_registry (formatter); + + extensions = e_mail_extension_registry_get_for_mime_type ( + registry, empa->snoop_mime_type); + if (extensions == NULL) + extensions = e_mail_extension_registry_get_fallback ( + registry, empa->snoop_mime_type); + + /* 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)) { + GList *head, *link; + gboolean success = FALSE; + + if (extensions == NULL) + return FALSE; + + if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) { + gchar *name; + EAttachment *attachment; + GFileInfo *file_info; + const gchar *display_name; + gchar *description; + + attachment = e_mail_part_attachment_ref_attachment ( + E_MAIL_PART_ATTACHMENT (part)); + + file_info = e_attachment_ref_file_info (attachment); + display_name = g_file_info_get_display_name (file_info); + + description = e_attachment_dup_description (attachment); + if (description != NULL && *description != '\0') { + name = g_strdup_printf ( + "<h2>Attachment: %s (%s)</h2>\n", + description, display_name); + } else { + name = g_strdup_printf ( + "<h2>Attachment: %s</h2>\n", + display_name); + } + + camel_stream_write_string ( + stream, name, cancellable, NULL); + + g_free (description); + g_free (name); + + g_object_unref (attachment); + g_object_unref (file_info); + } + + head = g_queue_peek_head_link (extensions); + + for (link = head; link != NULL; link = g_list_next (link)) { + success = e_mail_formatter_extension_format ( + E_MAIL_FORMATTER_EXTENSION (link->data), + formatter, context, part, stream, cancellable); + if (success) + break; + } + + return success; + } + + /* E_MAIL_FORMATTER_MODE_NORMAL: */ + + mime_part = e_mail_part_ref_mime_part (part); + text = e_mail_part_describe (mime_part, empa->snoop_mime_type); + flags = e_mail_formatter_get_text_format_flags (formatter); + html = camel_text_to_html ( + text, flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); + g_free (text); + g_object_unref (mime_part); + + 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 != NULL) { + CamelStream *content_stream; + gboolean success = FALSE; + + content_stream = camel_stream_mem_new (); + if (empa->attachment_view_part_id != NULL) { + EMailPart *attachment_view_part; + + attachment_view_part = e_mail_part_list_ref_part ( + context->part_list, + empa->attachment_view_part_id); + + /* Avoid recursion. */ + if (attachment_view_part == part) + g_clear_object (&attachment_view_part); + + if (attachment_view_part != NULL) { + success = e_mail_formatter_format_as ( + formatter, context, + attachment_view_part, + content_stream, NULL, + cancellable); + g_object_unref (attachment_view_part); + } + + } else { + GList *head, *link; + + head = g_queue_peek_head_link (extensions); + + for (link = head; link != NULL; link = g_list_next (link)) { + success = e_mail_formatter_extension_format ( + E_MAIL_FORMATTER_EXTENSION (link->data), + formatter, context, + part, content_stream, + cancellable); + if (success) + break; + } + } + + if (success) { + gchar *wrapper_element_id; + + wrapper_element_id = g_strconcat ( + attachment_part_id, ".wrapper", NULL); + + str = g_strdup_printf ( + "<tr><td colspan=\"2\">" + "<div class=\"attachment-wrapper\" id=\"%s\">", + wrapper_element_id); + + camel_stream_write_string ( + stream, str, cancellable, NULL); + + 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_free (wrapper_element_id); + g_free (str); + } + + 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) +{ + EAttachment *attachment; + EAttachmentStore *store; + EAttachmentView *view; + GtkWidget *widget; + const gchar *part_id; + + g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT (part), NULL); + + attachment = e_mail_part_attachment_ref_attachment ( + E_MAIL_PART_ATTACHMENT (part)); + + part_id = e_mail_part_get_id (part); + + store = find_attachment_store (context, part); + widget = e_attachment_button_new (); + g_object_set_data_full ( + G_OBJECT (widget), + "uri", g_strdup (part_id), + (GDestroyNotify) g_free); + e_attachment_button_set_attachment ( + E_ATTACHMENT_BUTTON (widget), attachment); + + view = g_object_get_data (G_OBJECT (store), "attachment-bar"); + if (view != NULL) + e_attachment_button_set_view ( + E_ATTACHMENT_BUTTON (widget), view); + + gtk_widget_set_can_focus (widget, TRUE); + gtk_widget_show (widget); + + g_object_unref (attachment); + + return widget; +} + +static void +e_mail_formatter_attachment_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Attachment"); + class->description = _("Display as attachment"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_attachment_format; + class->get_widget = emfe_attachment_get_widget; +} + +static void +e_mail_formatter_attachment_init (EMailFormatterExtension *extension) +{ +} diff --git a/em-format/e-mail-formatter-enums.h b/em-format/e-mail-formatter-enums.h new file mode 100644 index 0000000000..e1b41d6cf6 --- /dev/null +++ b/em-format/e-mail-formatter-enums.h @@ -0,0 +1,90 @@ +/* + * e-mail-formatter-enums.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_ENUMS_H +#define E_MAIL_FORMATTER_ENUMS_H + +#include <glib.h> + +G_BEGIN_DECLS + +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 /*< skip >*/ +} EMailFormatterColor; + +typedef enum { /*< flags >*/ + 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_MODE_INVALID = -1, + E_MAIL_FORMATTER_MODE_NORMAL = 0, + E_MAIL_FORMATTER_MODE_SOURCE, + E_MAIL_FORMATTER_MODE_RAW, + E_MAIL_FORMATTER_MODE_CID, + E_MAIL_FORMATTER_MODE_PRINTING, + E_MAIL_FORMATTER_MODE_ALL_HEADERS +} EMailFormatterMode; + +typedef enum { /*< flags >*/ + 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; + +/** + * EMailParserExtensionFlags: + * @E_MAIL_PARSER_EXTENSION_INLINE: + * Don't parse as attachment. + * @E_MAIL_PARSER_EXTENSION_INLINE_DISPOSITION: + * Always expand. + * @E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE: + * Always check what's inside. + **/ +typedef enum { /*< flags >*/ + E_MAIL_PARSER_EXTENSION_INLINE = 1 << 0, + E_MAIL_PARSER_EXTENSION_INLINE_DISPOSITION = 1 << 1, + E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE = 1 << 2 +} EMailParserExtensionFlags; + +typedef enum { /*< flags >*/ + 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; + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_ENUMS_H */ + diff --git a/em-format/e-mail-formatter-error.c b/em-format/e-mail-formatter-error.c new file mode 100644 index 0000000000..8669c11ced --- /dev/null +++ b/em-format/e-mail-formatter-error.c @@ -0,0 +1,121 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" + +typedef EMailFormatterExtension EMailFormatterError; +typedef EMailFormatterExtensionClass EMailFormatterErrorClass; + +GType e_mail_formatter_error_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterError, + e_mail_formatter_error, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +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; + CamelMimePart *mime_part; + CamelDataWrapper *dw; + gchar *html; + + mime_part = e_mail_part_ref_mime_part (part); + dw = camel_medium_get_content (CAMEL_MEDIUM (mime_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_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT)), + 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); + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_error_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_error_format; +} + +static void +e_mail_formatter_error_init (EMailFormatterExtension *extension) +{ +} diff --git a/em-format/e-mail-formatter-extension.c b/em-format/e-mail-formatter-extension.c new file mode 100644 index 0000000000..bbac4069c8 --- /dev/null +++ b/em-format/e-mail-formatter-extension.c @@ -0,0 +1,141 @@ +/* + * 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_ABSTRACT_TYPE ( + EMailFormatterExtension, + e_mail_formatter_extension, + G_TYPE_OBJECT) + +static void +e_mail_formatter_extension_class_init (EMailFormatterExtensionClass *class) +{ + class->priority = G_PRIORITY_DEFAULT; +} + +static void +e_mail_formatter_extension_init (EMailFormatterExtension *extension) +{ +} + +/** + * 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. + * + * Returns: 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) +{ + EMailFormatterExtensionClass *class; + + 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); + + class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension); + g_return_val_if_fail (class->format != NULL, FALSE); + + return class->format (extension, formatter, context, part, stream, cancellable); +} + +/** + * e_mail_formatter_extension_has_widget: + * @extension: an #EMailFormatterExtension + * + * Returns whether the extension can provide a GtkWidget. + * + * Returns: Returns %TRUE when @extension reimplements get_widget(), %FALSE + * otherwise. + */ +gboolean +e_mail_formatter_extension_has_widget (EMailFormatterExtension *extension) +{ + EMailFormatterExtensionClass *class; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER_EXTENSION (extension), FALSE); + + class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension); + + return (class->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. + * + * Returns: 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) +{ + EMailFormatterExtensionClass *class; + GtkWidget *widget = NULL; + + 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); + + class = E_MAIL_FORMATTER_EXTENSION_GET_CLASS (extension); + + if (class->get_widget != NULL) + widget = class->get_widget (extension, context, part, params); + + return widget; +} + diff --git a/em-format/e-mail-formatter-extension.h b/em-format/e-mail-formatter-extension.h new file mode 100644 index 0000000000..1da4069ec8 --- /dev/null +++ b/em-format/e-mail-formatter-extension.h @@ -0,0 +1,112 @@ +/* + * 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 <gtk/gtk.h> +#include <camel/camel.h> +#include <em-format/e-mail-part.h> +#include <em-format/e-mail-formatter.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_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_FORMATTER_EXTENSION, EMailFormatterExtensionClass)) +#define E_IS_MAIL_FORMATTER_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION)) +#define E_IS_MAIL_FORMATTER_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_FORMATTER_EXTENSION)) +#define E_MAIL_FORMATTER_EXTENSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_FORMATTER_EXTENSION, EMailFormatterExtensionClass)) + +G_BEGIN_DECLS + +/** + * EMailFormatterExtension: + * + * The #EMailFormatterExtension is an abstract class for all extensions for + * #EMailFormatter. + */ +typedef struct _EMailFormatterExtension EMailFormatterExtension; +typedef struct _EMailFormatterExtensionClass EMailFormatterExtensionClass; +typedef struct _EMailFormatterExtensionPrivate EMailFormatterExtensionPrivate; + +struct _EMailFormatterExtension { + GObject parent; + EMailFormatterExtensionPrivate *priv; +}; + +struct _EMailFormatterExtensionClass { + GObjectClass parent_class; + + /* This is a short name for the extension (optional). */ + const gchar *display_name; + + /* This is a longer description of the extension (optional). */ + const gchar *description; + + /* This is a NULL-terminated array of supported MIME types. + * The MIME types can be exact (e.g. "text/plain") or use a + * wildcard (e.g. "text/ *"). */ + const gchar **mime_types; + + /* This is used to prioritize extensions with identical MIME + * types. Lower values win. Defaults to G_PRIORITY_DEFAULT. */ + gint priority; + + 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); +}; + +GType e_mail_formatter_extension_get_type + (void) G_GNUC_CONST; +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); + +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..e9f88704c8 --- /dev/null +++ b/em-format/e-mail-formatter-headers.c @@ -0,0 +1,595 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <libemail-engine/e-mail-utils.h> +#include <libedataserver/libedataserver.h> +#include <e-util/e-util.h> +#include <shell/e-shell.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-formatter-utils.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-headers.h" + +typedef EMailFormatterExtension EMailFormatterHeaders; +typedef EMailFormatterExtensionClass EMailFormatterHeadersClass; + +GType e_mail_formatter_headers_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterHeaders, + e_mail_formatter_headers, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "application/vnd.evolution.headers", + NULL +}; + +static void +format_short_headers (EMailFormatter *formatter, + GString *buffer, + EMailPart *part, + guint32 flags, + GCancellable *cancellable) +{ + CamelMimePart *mime_part; + GtkTextDirection direction; + const gchar *charset; + CamelContentType *ct; + gchar *hdr_charset; + gchar *evolution_imagesdir; + gchar *subject = NULL; + struct _camel_header_address *addrs = NULL; + struct _camel_header_raw *header; + GString *from; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + mime_part = e_mail_part_ref_mime_part (part); + direction = gtk_widget_get_default_direction (); + + ct = camel_mime_part_get_content_type (mime_part); + charset = camel_content_type_param (ct, "charset"); + charset = camel_iconv_charset_name (charset); + hdr_charset = e_mail_formatter_dup_charset (formatter); + if (!hdr_charset) + hdr_charset = e_mail_formatter_dup_default_charset (formatter); + + evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); + from = g_string_new (""); + + g_string_append_printf ( + buffer, + "<table class=\"header\" " + "id=\"__evo-short-headers\" style=\"display: %s\">", + flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED ? "table" : "none"); + + header = mime_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 > 0) + g_string_printf ( + from, "%s: %s", + _("From"), 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; + } + + g_free (hdr_charset); + + g_string_append (buffer, "<tr class=\"header\">"); + if (direction == GTK_TEXT_DIR_RTL) + g_string_append (buffer, "<td class=\"header rtl\">"); + else + g_string_append (buffer, "<td class=\"header ltr\">"); + g_string_append (buffer, "<strong>"); + if (subject != NULL && *subject != '\0') + g_string_append (buffer, subject); + else + g_string_append (buffer, _("(no subject)")); + g_string_append (buffer, "</strong>"); + if (from->len > 0) + g_string_append_printf (buffer, " (%s)", from->str); + g_string_append (buffer, "</td></tr>"); + + 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); + + g_object_unref (mime_part); +} + +static void +write_contact_picture (CamelMimePart *mime_part, + gint size, + GString *buffer) +{ + gchar *b64, *content_type; + CamelDataWrapper *dw; + CamelContentType *ct; + GByteArray *ba = NULL; + + dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); + if (dw != NULL) + ba = camel_data_wrapper_get_byte_array (dw); + + if (ba == NULL || ba->len == 0) { + const gchar *filename; + + filename = camel_mime_part_get_filename (mime_part); + + if (filename != NULL) { + if (size >= 0) { + g_string_append_printf ( + buffer, + "<img width=\"%d\" src=\"evo-file://%s\" />", + size, filename); + } else { + g_string_append_printf ( + buffer, + "<img src=\"evo-file://%s\" />", + filename); + } + } + + return; + } + + b64 = g_base64_encode (ba->data, ba->len); + ct = camel_mime_part_get_content_type (mime_part); + content_type = camel_content_type_simple (ct); + + if (size >= 0) { + g_string_append_printf ( + buffer, + "<img width=\"%d\" src=\"data:%s;base64,%s\">", + size, content_type, b64); + } else { + g_string_append_printf ( + buffer, + "<img src=\"data:%s;base64,%s\">", + content_type, b64); + } + + g_free (b64); + g_free (content_type); +} + +static void +format_full_headers (EMailFormatter *formatter, + GString *buffer, + EMailPart *part, + guint32 mode, + guint32 flags, + GCancellable *cancellable) +{ + CamelMimePart *mime_part; + const gchar *charset; + CamelContentType *ct; + struct _camel_header_raw *header; + const gchar *photo_name = NULL; + guchar *face_header_value = NULL; + gsize face_header_len = 0; + gchar *header_sender = NULL, *header_from = NULL, *name; + gboolean mail_from_delegate = FALSE; + gboolean show_sender_photo; + gchar *hdr_charset; + gchar *evolution_imagesdir; + const gchar *direction; + + g_return_if_fail (E_IS_MAIL_PART_HEADERS (part)); + + if (g_cancellable_is_cancelled (cancellable)) + return; + + mime_part = e_mail_part_ref_mime_part (part); + + switch (gtk_widget_get_default_direction ()) { + case GTK_TEXT_DIR_RTL: + direction = "rtl"; + break; + case GTK_TEXT_DIR_LTR: + direction = "ltr"; + break; + default: + direction = "inherit"; + break; + } + + ct = camel_mime_part_get_content_type (mime_part); + charset = camel_content_type_param (ct, "charset"); + charset = camel_iconv_charset_name (charset); + hdr_charset = e_mail_formatter_dup_charset (formatter); + if (!hdr_charset) + hdr_charset = e_mail_formatter_dup_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\" width=\"100%%\" " + "id=\"__evo-full-headers\" " + "style=\"display: %s; direction: %s;\">", + flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED ? "none" : "table", + direction); + + header = mime_part->headers; + while (header != NULL) { + 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; + } + + g_free (hdr_charset); + + if (header_sender && header_from && mail_from_delegate) { + gchar *bold_sender, *bold_from; + + g_string_append ( + buffer, + "<tr valign=\"top\"><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 valign=\"top\"><td width=\"100%\">" + "<table class=\"header\">\n"); + + g_free (evolution_imagesdir); + + /* dump selected headers */ + if (mode & E_MAIL_FORMATTER_MODE_ALL_HEADERS) { + header = mime_part->headers; + while (header != NULL) { + e_mail_formatter_format_header ( + formatter, buffer, + header->name, + header->value, + E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS, charset); + header = header->next; + } + } else { + CamelMedium *medium; + gchar **default_headers; + guint ii, length = 0; + + medium = CAMEL_MEDIUM (mime_part); + + default_headers = + e_mail_part_headers_dup_default_headers ( + E_MAIL_PART_HEADERS (part)); + if (default_headers != NULL) + length = g_strv_length (default_headers); + + for (ii = 0; ii < length; ii++) { + const gchar *header_name; + const gchar *header_value = NULL; + + header_name = default_headers[ii]; + + /* X-Evolution-Mailer is a pseudo-header and + * requires special treatment to extract the + * real header value. */ + if (g_ascii_strcasecmp (header_name, "X-Evolution-Mailer") == 0) { + /* Check for "X-MimeOLE" last, + * as it's the least preferred. */ + if (header_value == NULL) + header_value = camel_medium_get_header ( + medium, "X-Mailer"); + if (header_value == NULL) + header_value = camel_medium_get_header ( + medium, "User-Agent"); + if (header_value == NULL) + header_value = camel_medium_get_header ( + medium, "X-Newsreader"); + if (header_value == NULL) + header_value = camel_medium_get_header ( + medium, "X-MimeOLE"); + } else { + header_value = camel_medium_get_header ( + medium, header_name); + } + + if (header_value == NULL) + continue; + + if (g_ascii_strcasecmp (header_name, "From") == 0) + photo_name = header_value; + + if (g_ascii_strcasecmp (header_name, "Face") == 0) { + if (face_header_value == NULL) { + const 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; + } + continue; + } + + e_mail_formatter_format_header ( + formatter, buffer, + header_name, + header_value, + 0, charset); + } + + g_strfreev (default_headers); + } + + g_string_append (buffer, "</table></td>"); + + show_sender_photo = + e_mail_formatter_get_show_sender_photo (formatter); + + /* Prefer contact photos over archaic "Face" headers. */ + if (show_sender_photo && photo_name != NULL) { + gchar *name; + + name = g_uri_escape_string (photo_name, NULL, FALSE); + g_string_append ( + buffer, + "<td align=\"right\" valign=\"top\">"); + g_string_append_printf ( + buffer, + "<img src=\"mail://contact-photo?mailaddr=\" " + "data-mailaddr=\"%s\" id=\"__evo-contact-photo\"/>", + name); + g_string_append (buffer, "</td>"); + + g_free (name); + + } else if (face_header_value != NULL) { + CamelMimePart *image_part; + + image_part = camel_mime_part_new (); + camel_mime_part_set_content ( + image_part, + (const gchar *) face_header_value, + face_header_len, "image/png"); + + g_string_append ( + buffer, + "<td align=\"right\" valign=\"top\">"); + write_contact_picture (image_part, 48, buffer); + g_string_append (buffer, "</td>"); + + g_object_unref (image_part); + g_free (face_header_value); + } + + g_string_append (buffer, "</tr></table>"); + + g_object_unref (mime_part); +} + +static gboolean +emfe_headers_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelMimePart *mime_part; + GString *buffer; + const GdkRGBA white = { 1.0, 1.0, 1.0, 1.0 }; + const GdkRGBA *body_rgba = &white; + const GdkRGBA *header_rgba; + const gchar *direction; + gboolean is_collapsable; + gboolean is_collapsed; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + mime_part = e_mail_part_ref_mime_part (part); + if (mime_part == NULL) + return FALSE; + + switch (gtk_widget_get_default_direction ()) { + case GTK_TEXT_DIR_RTL: + direction = "rtl"; + break; + case GTK_TEXT_DIR_LTR: + direction = "ltr"; + break; + default: + direction = "inherit"; + break; + } + + is_collapsable = + (context->flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE); + + is_collapsed = + (context->flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED); + + buffer = g_string_new (""); + + if (context->mode != E_MAIL_FORMATTER_MODE_PRINTING) + body_rgba = e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY); + + header_rgba = e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_HEADER); + + g_string_append_printf ( + buffer, + "<div class=\"headers\" style=\"background: #%06x;\" id=\"%s\">" + "<table border=\"0\" width=\"100%%\" " + "style=\"color: #%06x; direction: %s\">" + "<tr>", + e_rgba_to_value (body_rgba), + e_mail_part_get_id (part), + e_rgba_to_value (header_rgba), + direction); + + if (is_collapsable) + g_string_append_printf ( + buffer, + "<td valign=\"top\" width=\"16\">" + "<img src=\"evo-file://%s/%s\" class=\"navigable\" " + " id=\"__evo-collapse-headers-img\" />" + "</td>", + EVOLUTION_IMAGESDIR, + is_collapsed ? "plus.png" : "minus.png"); + + g_string_append (buffer, "<td>"); + + if (is_collapsable) + format_short_headers ( + formatter, + buffer, part, + context->flags, + cancellable); + + format_full_headers ( + formatter, + buffer, part, + context->mode, + context->flags, + cancellable); + + g_string_append (buffer, "</td>"); + + g_string_append (buffer, "</tr></table></div>"); + + camel_stream_write_string (stream, buffer->str, cancellable, NULL); + + g_string_free (buffer, TRUE); + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_headers_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_headers_format; +} + +static void +e_mail_formatter_headers_init (EMailFormatterExtension *extension) +{ +} diff --git a/em-format/e-mail-formatter-image.c b/em-format/e-mail-formatter-image.c new file mode 100644 index 0000000000..c3330e0db3 --- /dev/null +++ b/em-format/e-mail-formatter-image.c @@ -0,0 +1,162 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailFormatterExtension EMailFormatterImage; +typedef EMailFormatterExtensionClass EMailFormatterImageClass; + +GType e_mail_formatter_image_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterImage, + e_mail_formatter_image, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +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 +}; + +static gboolean +emfe_image_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *content; + CamelMimePart *mime_part; + CamelDataWrapper *dw; + GByteArray *ba; + CamelStream *raw_content; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + mime_part = e_mail_part_ref_mime_part (part); + dw = camel_medium_get_content (CAMEL_MEDIUM (mime_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; + const gchar *mime_type; + + 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); + } + + mime_type = e_mail_part_get_mime_type (part); + if (mime_type == NULL) + mime_type = "image/*"; + + /* 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%%;\" />", + mime_type, content); + + camel_stream_write_string (stream, buffer, cancellable, NULL); + g_free (buffer); + g_free (content); + } + + g_object_unref (raw_content); + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_image_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Regular Image"); + class->description = _("Display part as an image"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_image_format; +} + +static void +e_mail_formatter_image_init (EMailFormatterExtension *extension) +{ +} 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..0477e61517 --- /dev/null +++ b/em-format/e-mail-formatter-message-rfc822.c @@ -0,0 +1,262 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-part-list.h" +#include "e-mail-part-utils.h" + +typedef EMailFormatterExtension EMailFormatterMessageRFC822; +typedef EMailFormatterExtensionClass EMailFormatterMessageRFC822Class; + +GType e_mail_formatter_message_rfc822_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterMessageRFC822, + e_mail_formatter_message_rfc822, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "message/rfc822", + "application/vnd.evolution.rfc822.end", + NULL +}; + +static gboolean +emfe_message_rfc822_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + const gchar *part_id; + + part_id = e_mail_part_get_id (part); + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + 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; + + e_mail_part_list_queue_parts ( + context->part_list, part_id, &queue); + + /* Discard the first EMailPart. */ + if (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + head = g_queue_peek_head_link (&queue); + + end = g_strconcat (part_id, ".end", NULL); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *p = link->data; + const gchar *p_id; + + p_id = e_mail_part_get_id (p); + + /* Check for nested rfc822 messages */ + if (e_mail_part_id_has_suffix (p, ".rfc822")) { + gchar *sub_end = g_strconcat (p_id, ".end", NULL); + + while (link != NULL) { + p = link->data; + + if (g_strcmp0 (p_id, sub_end) == 0) + break; + + link = g_list_next (link); + } + 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); + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + 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) { + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + gchar *end; + + /* Part is EMailPartAttachment */ + e_mail_part_list_queue_parts ( + context->part_list, part_id, &queue); + + /* Discard the first EMailPart. */ + if (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + if (g_queue_is_empty (&queue)) + return FALSE; + + part = g_queue_pop_head (&queue); + end = g_strconcat (part_id, ".end", NULL); + g_object_unref (part); + + head = g_queue_peek_head_link (&queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *p = link->data; + const gchar *p_id; + + /* Skip attachment bar */ + if (e_mail_part_id_has_suffix (part, ".attachment-bar")) + continue; + + p_id = e_mail_part_get_id (p); + + /* Check for nested rfc822 messages */ + if (e_mail_part_id_has_suffix (p, ".rfc822")) { + gchar *sub_end = g_strconcat (p_id, ".end", NULL); + + while (link != NULL) { + p = link->data; + + if (g_strcmp0 (p_id, sub_end) == 0) + break; + + link = g_list_next (link); + } + 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); + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + } else { + EMailPart *p; + CamelFolder *folder; + const gchar *message_uid; + const gchar *default_charset, *charset; + gchar *str; + gchar *uri; + + p = e_mail_part_list_ref_part (context->part_list, part_id); + if (p == NULL) + return FALSE; + + folder = e_mail_part_list_get_folder (context->part_list); + message_uid = e_mail_part_list_get_message_uid (context->part_list); + default_charset = e_mail_formatter_get_default_charset (formatter); + charset = e_mail_formatter_get_charset (formatter); + + if (!default_charset) + default_charset = ""; + if (!charset) + charset = ""; + + uri = e_mail_part_build_uri ( + folder, message_uid, + "part_id", G_TYPE_STRING, e_mail_part_get_id (p), + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, + "headers_collapsable", G_TYPE_INT, 0, + "formatter_default_charset", G_TYPE_STRING, default_charset, + "formatter_charset", G_TYPE_STRING, charset, + 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_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_rgba_to_value ( + 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); + + g_object_unref (p); + } + + return TRUE; +} + +static void +e_mail_formatter_message_rfc822_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("RFC822 message"); + class->description = _("Format part as an RFC822 message"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_message_rfc822_format; +} + +static void +e_mail_formatter_message_rfc822_init (EMailFormatterExtension *extension) +{ +} 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..29aa93a278 --- /dev/null +++ b/em-format/e-mail-formatter-print-headers.c @@ -0,0 +1,235 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> +#include <libemail-engine/e-mail-utils.h> + +#include "e-mail-formatter-print.h" +#include "e-mail-formatter-utils.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-headers.h" + +typedef EMailFormatterExtension EMailFormatterPrintHeaders; +typedef EMailFormatterExtensionClass EMailFormatterPrintHeadersClass; + +GType e_mail_formatter_print_headers_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterPrintHeaders, + e_mail_formatter_print_headers, + E_TYPE_MAIL_FORMATTER_PRINT_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "application/vnd.evolution.headers", + NULL +}; + +static gboolean +emfpe_headers_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + EMailPartHeaders *headers_part; + GtkTreeModel *tree_model; + GtkTreeIter iter; + gboolean iter_valid; + GString *str, *tmp; + gchar *subject; + const gchar *buf; + gint attachments_count; + gchar *part_id_prefix; + CamelMimePart *mime_part; + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + const gchar *part_id; + + g_return_val_if_fail (E_IS_MAIL_PART_HEADERS (part), FALSE); + + mime_part = e_mail_part_ref_mime_part (part); + + buf = camel_medium_get_header (CAMEL_MEDIUM (mime_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_part = E_MAIL_PART_HEADERS (part); + tree_model = e_mail_part_headers_ref_print_model (headers_part); + iter_valid = gtk_tree_model_get_iter_first (tree_model, &iter); + + while (iter_valid) { + gchar *header_name = NULL; + gchar *header_value = NULL; + gboolean include = FALSE; + + gtk_tree_model_get ( + tree_model, &iter, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_INCLUDE, + &include, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_NAME, + &header_name, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_VALUE, + &header_value, + -1); + + if (include) + e_mail_formatter_format_header ( + formatter, str, + header_name, header_value, + E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, + "UTF-8"); + + g_free (header_name); + g_free (header_value); + + iter_valid = gtk_tree_model_iter_next (tree_model, &iter); + } + + g_object_unref (tree_model); + + /* Get prefix of this PURI */ + part_id = e_mail_part_get_id (part); + part_id_prefix = g_strndup (part_id, g_strrstr (part_id, ".") - part_id); + + /* Add encryption/signature header */ + tmp = g_string_new (""); + + e_mail_part_list_queue_parts (context->part_list, NULL, &queue); + + head = g_queue_peek_head_link (&queue); + + /* Find first secured part. */ + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + if (!e_mail_part_has_validity (mail_part)) + continue; + + if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix)) + continue; + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SIGNED)) { + g_string_append (tmp, _("GPG signed")); + } + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_ENCRYPTED)) { + if (tmp->len > 0) + g_string_append (tmp, ", "); + g_string_append (tmp, _("GPG encrpyted")); + } + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_SIGNED)) { + if (tmp->len > 0) + g_string_append (tmp, ", "); + g_string_append (tmp, _("S/MIME signed")); + } + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | 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) { + e_mail_formatter_format_header ( + formatter, str, + _("Security"), tmp->str, + 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 (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = E_MAIL_PART (link->data); + + if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix)) + continue; + + if (!e_mail_part_get_is_attachment (mail_part)) + continue; + + if (mail_part->is_hidden) + continue; + + if (e_mail_part_get_cid (mail_part) != NULL) + continue; + + attachments_count++; + } + + if (attachments_count > 0) { + gchar *header_value; + + header_value = g_strdup_printf ("%d", attachments_count); + e_mail_formatter_format_header ( + formatter, str, + _("Attachments"), header_value, + E_MAIL_FORMATTER_HEADER_FLAG_BOLD | + E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS, "UTF-8"); + g_free (header_value); + } + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + g_string_append (str, "</table>"); + + camel_stream_write_string (stream, str->str, cancellable, NULL); + g_string_free (str, TRUE); + g_free (part_id_prefix); + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_print_headers_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfpe_headers_format; +} + +static void +e_mail_formatter_print_headers_init (EMailFormatterExtension *extension) +{ +} diff --git a/em-format/e-mail-formatter-print.c b/em-format/e-mail-formatter-print.c new file mode 100644 index 0000000000..4d5c303d03 --- /dev/null +++ b/em-format/e-mail-formatter-print.c @@ -0,0 +1,295 @@ +/* + * 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 "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> + +#define STYLESHEET_URI \ + "evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview-print.css" + +/* internal formatter extensions */ +GType e_mail_formatter_print_headers_get_type (void); + +void e_mail_formatter_print_internal_extensions_load (EMailExtensionRegistry *ereg); + +static gpointer e_mail_formatter_print_parent_class = 0; + +static void +mail_formatter_print_write_attachments (EMailFormatter *formatter, + GQueue *attachments, + CamelStream *stream, + GCancellable *cancellable) +{ + GString *str; + + 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")); + + while (!g_queue_is_empty (attachments)) { + EMailPartAttachment *part; + EAttachment *attachment; + GFileInfo *file_info; + const gchar *display_name; + gchar *description; + gchar *name; + gchar *size; + + part = g_queue_pop_head (attachments); + attachment = e_mail_part_attachment_ref_attachment (part); + + file_info = e_attachment_ref_file_info (attachment); + if (file_info == NULL) { + g_object_unref (attachment); + continue; + } + + description = e_attachment_dup_description (attachment); + display_name = g_file_info_get_display_name (file_info); + + if (description != NULL && *description != '\0') { + name = g_strdup_printf ( + "%s (%s)", description, display_name); + } else { + name = g_strdup (display_name); + } + + size = g_format_size (g_file_info_get_size (file_info)); + + g_string_append_printf ( + str, "<tr><td>%s</td><td>%s</td></tr>\n", + name, size); + + g_free (description); + g_free (name); + g_free (size); + + g_object_unref (attachment); + g_object_unref (file_info); + } + + 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) +{ + GQueue queue = G_QUEUE_INIT; + GQueue attachments = G_QUEUE_INIT; + GList *head, *link; + + 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\" />\n" + "<title>Evolution Mail Display</title>\n" + "<link type=\"text/css\" rel=\"stylesheet\" " + " media=\"print\" href=\"" STYLESHEET_URI "/>\n" + "</head>\n" + "<body style=\"background: #FFF; color: #000;\">", + cancellable, NULL); + + e_mail_part_list_queue_parts (context->part_list, NULL, &queue); + + head = g_queue_peek_head_link (&queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *part = E_MAIL_PART (link->data); + const gchar *mime_type; + gboolean ok; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + if (part->is_hidden && !part->is_error) { + if (e_mail_part_id_has_suffix (part, ".rfc822")) { + link = e_mail_formatter_find_rfc822_end_iter (link); + } + + continue; + } + + mime_type = e_mail_part_get_mime_type (part); + if (mime_type == NULL) + continue; + + if (e_mail_part_get_is_attachment (part)) { + if (e_mail_part_get_cid (part) != NULL) + continue; + + g_queue_push_tail (&attachments, part); + } + + ok = e_mail_formatter_format_as ( + formatter, context, part, stream, + mime_type, cancellable); + + /* If the written part was message/rfc822 then + * jump to the end of the message, because content + * of the whole message has been formatted by + * message_rfc822 formatter */ + if (ok && e_mail_part_id_has_suffix (part, ".rfc822")) { + link = e_mail_formatter_find_rfc822_end_iter (link); + + continue; + } + } + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + /* This consumes the attachments queue. */ + if (!g_queue_is_empty (&attachments)) + mail_formatter_print_write_attachments ( + formatter, &attachments, + stream, cancellable); + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); +} + +static void +mail_formatter_update_style (EMailFormatter *formatter, + GtkStateFlags state) +{ + /* White background */ + GdkRGBA body_color = { 1.0, 1.0, 1.0, 1.0 }; + /* Black text */ + GdkRGBA text_color = { 0.0, 0.0, 0.0, 0.0 }; + + g_object_freeze_notify (G_OBJECT (formatter)); + + /* Chain up to parent's update_style() method. */ + E_MAIL_FORMATTER_CLASS (e_mail_formatter_print_parent_class)-> + update_style (formatter, 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_class_init (EMailFormatterPrintClass *class) +{ + EMailFormatterClass *formatter_class; + + e_mail_formatter_print_parent_class = g_type_class_peek_parent (class); + + formatter_class = E_MAIL_FORMATTER_CLASS (class); + formatter_class->run = mail_formatter_print_run; + formatter_class->update_style = mail_formatter_update_style; +} + +static void +e_mail_formatter_print_base_init (EMailFormatterPrintClass *class) +{ + /* Register internal extensions. */ + g_type_ensure (e_mail_formatter_print_headers_get_type ()); + + e_mail_formatter_extension_registry_load ( + E_MAIL_FORMATTER_CLASS (class)->extension_registry, + E_TYPE_MAIL_FORMATTER_PRINT_EXTENSION); + + E_MAIL_FORMATTER_CLASS (class)->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; +} + +/* ------------------------------------------------------------------------- */ + +G_DEFINE_ABSTRACT_TYPE ( + EMailFormatterPrintExtension, + e_mail_formatter_print_extension, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static void +e_mail_formatter_print_extension_class_init (EMailFormatterPrintExtensionClass *class) +{ +} + +static void +e_mail_formatter_print_extension_init (EMailFormatterPrintExtension *extension) +{ +} + diff --git a/em-format/e-mail-formatter-print.h b/em-format/e-mail-formatter-print.h new file mode 100644 index 0000000000..e94ca75b7c --- /dev/null +++ b/em-format/e-mail-formatter-print.h @@ -0,0 +1,93 @@ +/* + * 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> +#include <em-format/e-mail-formatter-extension.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; + +struct _EMailFormatterPrint { + EMailFormatter parent; +}; + +struct _EMailFormatterPrintClass { + EMailFormatterClass parent_class; +}; + +GType e_mail_formatter_print_get_type (void) G_GNUC_CONST; +EMailFormatter * + e_mail_formatter_print_new (void); + +G_END_DECLS + +/* ------------------------------------------------------------------------- */ + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER_PRINT_EXTENSION \ + (e_mail_formatter_print_extension_get_type ()) + +G_BEGIN_DECLS + +/** + * EMailFormatterPrintExtension: + * + * This is an abstract base type for formatter extensions which are + * intended only for use by #EMailFormatterPrint. + **/ +typedef struct _EMailFormatterPrintExtension EMailFormatterPrintExtension; +typedef struct _EMailFormatterPrintExtensionClass EMailFormatterPrintExtensionClass; + +struct _EMailFormatterPrintExtension { + EMailFormatterExtension parent; +}; + +struct _EMailFormatterPrintExtensionClass { + EMailFormatterExtensionClass parent_class; +}; + +GType e_mail_formatter_print_extension_get_type + (void) G_GNUC_CONST; + +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..1a6b19408c --- /dev/null +++ b/em-format/e-mail-formatter-quote-attachment.c @@ -0,0 +1,126 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-quote.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-utils.h" + +#define d(x) + +typedef EMailFormatterExtension EMailFormatterQuoteAttachment; +typedef EMailFormatterExtensionClass EMailFormatterQuoteAttachmentClass; + +GType e_mail_formatter_quote_attachment_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterQuoteAttachment, + e_mail_formatter_quote_attachment, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) + +static const gchar *formatter_mime_types[] = { + E_MAIL_PART_ATTACHMENT_MIME_TYPE, + NULL +}; + +static gboolean +emfqe_attachment_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + gchar *text, *html; + EMailPartAttachment *empa; + EMailPart *attachment_view_part; + CamelMimeFilterToHTMLFlags text_format_flags; + CamelMimePart *mime_part; + + empa = E_MAIL_PART_ATTACHMENT (part); + + if (!empa->attachment_view_part_id) + return FALSE; + + attachment_view_part = e_mail_part_list_ref_part ( + context->part_list, empa->attachment_view_part_id); + if (attachment_view_part == NULL) + return FALSE; + + camel_stream_write_string (stream, "<br><br>", cancellable, NULL); + + text_format_flags = + e_mail_formatter_get_text_format_flags (formatter); + mime_part = e_mail_part_ref_mime_part (part); + text = e_mail_part_describe ( + mime_part, + empa ? empa->snoop_mime_type : + e_mail_part_get_mime_type (part)); + g_object_unref (mime_part); + + 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, attachment_view_part, + stream, NULL, cancellable); + + camel_stream_write_string ( + stream, + "</blockquote><!--+GtkHTML:" + "<DATA class=\"ClueFlow\" clear=\"orig\">-->", + cancellable, NULL); + + g_object_unref (attachment_view_part); + + return TRUE; +} + +static void +e_mail_formatter_quote_attachment_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_HIGH; + class->format = emfqe_attachment_format; +} + +static void +e_mail_formatter_quote_attachment_init (EMailFormatterExtension *extension) +{ +} 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..e95e0c9489 --- /dev/null +++ b/em-format/e-mail-formatter-quote-headers.c @@ -0,0 +1,279 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> +#include <libemail-engine/e-mail-utils.h> + +#include "e-mail-formatter-quote.h" +#include "e-mail-formatter-utils.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-headers.h" + +typedef EMailFormatterExtension EMailFormatterQuoteHeaders; +typedef EMailFormatterExtensionClass EMailFormatterQuoteHeadersClass; + +GType e_mail_formatter_quote_headers_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterQuoteHeaders, + e_mail_formatter_quote_headers, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "application/vnd.evolution.headers", + NULL +}; + +static void +emfqe_format_text_header (EMailFormatter *emf, + 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 & E_MAIL_FORMATTER_HEADER_FLAG_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); +} + +/* XXX: This is copied in e-mail-formatter-utils.c */ +static const gchar *addrspec_hdrs[] = { + "Sender", "From", "Reply-To", "To", "Cc", "Bcc", + "Resent-Sender", "Resent-From", "Resent-Reply-To", + "Resent-To", "Resent-Cc", "Resent-Bcc", NULL +}; + +static void +emfqe_format_header (EMailFormatter *formatter, + GString *buffer, + EMailPart *part, + const gchar *header_name, + const gchar *charset) +{ + CamelMimePart *mime_part; + EMailFormatterHeaderFlags flags; + gchar *canon_name, *buf, *value = NULL; + const gchar *txt, *label; + gboolean addrspec = FALSE; + gint is_html = FALSE; + gint i; + + flags = E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE; + + canon_name = g_alloca (strlen (header_name) + 1); + strcpy (canon_name, header_name); + e_mail_formatter_canon_header_name (canon_name); + + /* Never quote Bcc/Resent-Bcc headers. */ + if (g_str_equal (canon_name, "Bcc")) + return; + if (g_str_equal (canon_name, "Resent-Bcc")) + return; + + mime_part = e_mail_part_ref_mime_part (part); + + for (i = 0; addrspec_hdrs[i]; i++) { + if (g_str_equal (canon_name, addrspec_hdrs[i])) { + addrspec = TRUE; + break; + } + } + + label = _(canon_name); + + if (addrspec) { + CamelMedium *medium; + struct _camel_header_address *addrs; + GString *html; + gchar *charset; + + medium = CAMEL_MEDIUM (mime_part); + txt = camel_medium_get_header (medium, canon_name); + if (txt == NULL) + return; + + charset = e_mail_formatter_dup_charset (formatter); + if (!charset) + charset = e_mail_formatter_dup_default_charset (formatter); + + buf = camel_header_unfold (txt); + addrs = camel_header_address_decode (txt, charset); + g_free (charset); + + if (addrs == NULL) { + g_free (buf); + return; + } + + g_free (buf); + + html = g_string_new (""); + e_mail_formatter_format_address (formatter, html, + addrs, canon_name, FALSE, FALSE); + camel_header_address_unref (addrs); + txt = value = html->str; + g_string_free (html, FALSE); + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + is_html = TRUE; + + } else if (g_str_equal (canon_name, "Subject")) { + CamelMimeMessage *message; + + message = CAMEL_MIME_MESSAGE (mime_part); + txt = camel_mime_message_get_subject (message); + label = _("Subject"); + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "X-Evolution-Mailer")) { + CamelMedium *medium; + + medium = CAMEL_MEDIUM (mime_part); + txt = camel_medium_get_header (medium, "x-mailer"); + if (txt == NULL) + txt = camel_medium_get_header (medium, "user-agent"); + if (txt == NULL) + txt = camel_medium_get_header (medium, "x-newsreader"); + if (txt == NULL) + txt = camel_medium_get_header (medium, "x-mimeole"); + if (txt == NULL) + return; + + txt = value = camel_header_format_ctext (txt, charset); + + label = _("Mailer"); + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "Date") || + g_str_equal (canon_name, "Resent-Date")) { + CamelMedium *medium; + + medium = CAMEL_MEDIUM (mime_part); + txt = camel_medium_get_header (medium, canon_name); + if (txt == NULL) + return; + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else { + CamelMedium *medium; + + medium = CAMEL_MEDIUM (mime_part); + txt = camel_medium_get_header (medium, canon_name); + buf = camel_header_unfold (txt); + txt = value = camel_header_decode_string (txt, charset); + g_free (buf); + } + + emfqe_format_text_header (formatter, buffer, label, txt, flags, is_html); + + g_free (value); + + g_object_unref (mime_part); +} + +static gboolean +emqfe_headers_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelContentType *ct; + CamelMimePart *mime_part; + const gchar *charset; + GString *buffer; + gchar **default_headers; + guint ii, length = 0; + + g_return_val_if_fail (E_IS_MAIL_PART_HEADERS (part), FALSE); + + mime_part = e_mail_part_ref_mime_part (part); + + ct = camel_mime_part_get_content_type (mime_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_part_headers_dup_default_headers ( + E_MAIL_PART_HEADERS (part)); + if (default_headers != NULL) + length = g_strv_length (default_headers); + + for (ii = 0; ii < length; ii++) + emfqe_format_header ( + formatter, buffer, part, + default_headers[ii], charset); + + g_strfreev (default_headers); + + g_string_append (buffer, "<br>\n"); + + camel_stream_write_string (stream, buffer->str, cancellable, NULL); + + g_string_free (buffer, TRUE); + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_quote_headers_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_HIGH; + class->format = emqfe_headers_format; +} + +static void +e_mail_formatter_quote_headers_init (EMailFormatterExtension *extension) +{ +} 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..15a9056062 --- /dev/null +++ b/em-format/e-mail-formatter-quote-message-rfc822.c @@ -0,0 +1,154 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-quote.h" +#include "e-mail-part-list.h" +#include "e-mail-part-utils.h" + +typedef EMailFormatterExtension EMailFormatterQuoteMessageRFC822; +typedef EMailFormatterExtensionClass EMailFormatterQuoteMessageRFC822Class; + +GType e_mail_formatter_quote_message_rfc822_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterQuoteMessageRFC822, + e_mail_formatter_quote_message_rfc822, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "message/rfc822", + "application/vnd.evolution.rfc822.end", + NULL +}; + +static gboolean +emfqe_message_rfc822_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + gchar *header, *end; + EMailFormatterQuoteContext *qc = (EMailFormatterQuoteContext *) context; + const gchar *part_id; + + part_id = e_mail_part_get_id (part); + + 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); + + e_mail_part_list_queue_parts (context->part_list, part_id, &queue); + + if (g_queue_is_empty (&queue)) + return FALSE; + + /* Discard the first EMailPart. */ + g_object_unref (g_queue_pop_head (&queue)); + + head = g_queue_peek_head (&queue); + + end = g_strconcat (part_id, ".end", NULL); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *p = link->data; + const gchar *p_id; + + p_id = e_mail_part_get_id (p); + + /* Skip attachment bar */ + if (e_mail_part_id_has_suffix (p, ".attachment-bar")) + continue; + + if (e_mail_part_id_has_suffix (p, ".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 (e_mail_part_id_has_suffix (p, ".rfc822")) { + gchar *sub_end = g_strconcat (p_id, ".end", NULL); + + while (link != NULL) { + p = link->data; + + if (g_strcmp0 (p_id, sub_end) == 0) + break; + + link = g_list_next (link); + } + 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); + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); + + return TRUE; +} + +static void +e_mail_formatter_quote_message_rfc822_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_HIGH; + class->format = emfqe_message_rfc822_format; +} + +static void +e_mail_formatter_quote_message_rfc822_init (EMailFormatterExtension *extension) +{ +} 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..06a67592a7 --- /dev/null +++ b/em-format/e-mail-formatter-quote-text-enriched.c @@ -0,0 +1,101 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-quote.h" +#include "e-mail-inline-filter.h" + +typedef EMailFormatterExtension EMailFormatterQuoteTextEnriched; +typedef EMailFormatterExtensionClass EMailFormatterQuoteTextEnrichedClass; + +GType e_mail_formatter_quote_text_enriched_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterQuoteTextEnriched, + e_mail_formatter_quote_text_enriched, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/enriched", + "text/richtext", + NULL +}; + +static gboolean +emqfe_text_enriched_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *enriched; + const gchar *mime_type; + guint32 camel_flags = 0; + + mime_type = e_mail_part_get_mime_type (part); + + if (g_strcmp0 (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 void +e_mail_formatter_quote_text_enriched_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Richtext"); + class->description = _("Display part as enriched text"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_HIGH; + class->format = emqfe_text_enriched_format; +} + +static void +e_mail_formatter_quote_text_enriched_init (EMailFormatterExtension *extension) +{ +} 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..1d4f7c5794 --- /dev/null +++ b/em-format/e-mail-formatter-quote-text-html.c @@ -0,0 +1,100 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-quote.h" +#include "e-mail-part-utils.h" +#include "e-mail-stripsig-filter.h" + +typedef EMailFormatterExtension EMailFormatterQuoteTextHTML; +typedef EMailFormatterExtensionClass EMailFormatterQuoteTextHTMLClass; + +GType e_mail_formatter_quote_text_html_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterQuoteTextHTML, + e_mail_formatter_quote_text_html, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/html", + NULL +}; + +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 void +e_mail_formatter_quote_text_html_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("HTML"); + class->description = _("Format part as HTML"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_HIGH; + class->format = emqfe_text_html_format; +} + +static void +e_mail_formatter_quote_text_html_init (EMailFormatterExtension *extension) +{ +} 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..efe0638e44 --- /dev/null +++ b/em-format/e-mail-formatter-quote-text-plain.c @@ -0,0 +1,125 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <camel/camel.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-quote.h" +#include "e-mail-part-utils.h" +#include "e-mail-stripsig-filter.h" + +typedef EMailFormatterExtension EMailFormatterQuoteTextPlain; +typedef EMailFormatterExtensionClass EMailFormatterQuoteTextPlainClass; + +GType e_mail_formatter_quote_text_plain_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterQuoteTextPlain, + e_mail_formatter_quote_text_plain, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/plain", + NULL +}; + +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; + CamelMimePart *mime_part; + CamelContentType *type; + EMailFormatterQuoteContext *qf_context; + CamelMimeFilterToHTMLFlags text_flags; + const gchar *format; + guint32 rgb = 0x737373; + + mime_part = e_mail_part_ref_mime_part (part); + if (mime_part == NULL) + 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 (mime_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); + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_quote_text_plain_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Plain Text"); + class->description = _("Format part as plain text"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_HIGH; + class->format = emqfe_text_plain_format; +} + +static void +e_mail_formatter_quote_text_plain_init (EMailFormatterExtension *extension) +{ +} diff --git a/em-format/e-mail-formatter-quote.c b/em-format/e-mail-formatter-quote.c new file mode 100644 index 0000000000..99c4de9520 --- /dev/null +++ b/em-format/e-mail-formatter-quote.c @@ -0,0 +1,250 @@ +/* + * 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-quote.h" +#include "e-mail-formatter-utils.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> + +#define E_MAIL_FORMATTER_QUOTE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_FORMATTER_QUOTE, EMailFormatterQuotePrivate)) + +struct _EMailFormatterQuotePrivate { + gchar *credits; + EMailFormatterQuoteFlags flags; +}; + +/* internal formatter extensions */ +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); + +void e_mail_formatter_quote_internal_extensions_load (EMailExtensionRegistry *ereg); + +static gpointer e_mail_formatter_quote_parent_class = 0; + +static void +mail_formatter_quote_run (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable) +{ + EMailFormatterQuote *qf; + EMailFormatterQuoteContext *qf_context; + GSettings *settings; + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + + 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); + } + + e_mail_part_list_queue_parts (context->part_list, NULL, &queue); + + head = g_queue_peek_head_link (&queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *part = E_MAIL_PART (link->data); + const gchar *mime_type; + + if (e_mail_part_id_has_suffix (part, ".headers") && + !(qf_context->qf_flags & E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS)) { + continue; + } + + if (e_mail_part_id_has_suffix (part, ".rfc822")) { + link = e_mail_formatter_find_rfc822_end_iter (link); + continue; + } + + if (part->is_hidden) + continue; + + if (e_mail_part_get_is_attachment (part)) + continue; + + mime_type = e_mail_part_get_mime_type (part); + + e_mail_formatter_format_as ( + formatter, context, part, stream, + mime_type, cancellable); + } + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + 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 *class) +{ + /* Register internal extensions. */ + g_type_ensure (e_mail_formatter_quote_attachment_get_type ()); + g_type_ensure (e_mail_formatter_quote_headers_get_type ()); + g_type_ensure (e_mail_formatter_quote_message_rfc822_get_type ()); + g_type_ensure (e_mail_formatter_quote_text_enriched_get_type ()); + g_type_ensure (e_mail_formatter_quote_text_html_get_type ()); + g_type_ensure (e_mail_formatter_quote_text_plain_get_type ()); + + e_mail_formatter_extension_registry_load ( + E_MAIL_FORMATTER_CLASS (class)->extension_registry, + E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION); + + E_MAIL_FORMATTER_CLASS (class)->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 *class) +{ + GObjectClass *object_class; + EMailFormatterClass *formatter_class; + + e_mail_formatter_quote_parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailFormatterQuotePrivate)); + + formatter_class = E_MAIL_FORMATTER_CLASS (class); + formatter_class->context_size = sizeof (EMailFormatterQuoteContext); + formatter_class->run = mail_formatter_quote_run; + + object_class = G_OBJECT_CLASS (class); + 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; +} + +/* ------------------------------------------------------------------------- */ + +G_DEFINE_ABSTRACT_TYPE ( + EMailFormatterQuoteExtension, + e_mail_formatter_quote_extension, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static void +e_mail_formatter_quote_extension_class_init (EMailFormatterQuoteExtensionClass *class) +{ +} + +static void +e_mail_formatter_quote_extension_init (EMailFormatterQuoteExtension *extension) +{ +} + diff --git a/em-format/e-mail-formatter-quote.h b/em-format/e-mail-formatter-quote.h new file mode 100644 index 0000000000..85e549acab --- /dev/null +++ b/em-format/e-mail-formatter-quote.h @@ -0,0 +1,103 @@ +/* + * 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> +#include <em-format/e-mail-formatter-extension.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; + +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) G_GNUC_CONST; +EMailFormatter * + e_mail_formatter_quote_new (const gchar *credits, + EMailFormatterQuoteFlags flags); + +G_END_DECLS + +/* ------------------------------------------------------------------------- */ + +/* Standard GObject macros */ +#define E_TYPE_MAIL_FORMATTER_QUOTE_EXTENSION \ + (e_mail_formatter_quote_extension_get_type ()) + +G_BEGIN_DECLS + +/** + * EMailFormatterQuoteExtension: + * + * This is an abstract base type for formatter extensions which are + * intended only for use by #EMailFormatterQuote. + **/ +typedef struct _EMailFormatterQuoteExtension EMailFormatterQuoteExtension; +typedef struct _EMailFormatterQuoteExtensionClass EMailFormatterQuoteExtensionClass; + +struct _EMailFormatterQuoteExtension { + EMailFormatterExtension parent; +}; + +struct _EMailFormatterQuoteExtensionClass { + EMailFormatterExtensionClass parent_class; +}; + +GType e_mail_formatter_quote_extension_get_type + (void) G_GNUC_CONST; + +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..91a330348f --- /dev/null +++ b/em-format/e-mail-formatter-secure-button.c @@ -0,0 +1,479 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#if defined (HAVE_NSS) && defined (ENABLE_SMIME) +#include "certificate-manager.h" +#include "e-cert-db.h" +#endif + +#include "e-mail-formatter-extension.h" + +typedef EMailFormatterExtension EMailFormatterSecureButton; +typedef EMailFormatterExtensionClass EMailFormatterSecureButtonClass; + +GType e_mail_formatter_secure_button_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterSecureButton, + e_mail_formatter_secure_button, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +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) && + (context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS)) + return FALSE; + + str = g_strdup_printf ( + "<object type=\"application/vnd.evolution.widget.secure-button\" " + "height=\"20\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>", + e_mail_part_get_id (part), + e_mail_part_get_id (part)); + + 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 *grid) +{ + 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 *dialog, *parent; + + parent = gtk_widget_get_toplevel (grid); + if (!parent || !GTK_IS_WINDOW (parent)) + parent = NULL; + + dialog = e_cert_manager_new_certificate_viewer ((GtkWindow *) parent, ec); + + gtk_widget_show (dialog); + g_signal_connect ( + dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + 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, + CamelCipherValidity *validity) +{ + GtkBuilder *builder; + GtkWidget *grid, *w; + GtkWidget *dialog; + + g_return_if_fail (validity != NULL); + + 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[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 (validity->sign.description) { + GtkTextBuffer *buffer; + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text ( + buffer, validity->sign.description, + strlen (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 (&validity->sign.signers)) + add_cert_table ( + grid, &validity->sign.signers, NULL); + + gtk_widget_show_all (grid); + + grid = e_builder_get_widget (builder, "encryption_grid"); + w = gtk_label_new (_(smime_encrypt_table[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 (validity->encrypt.description) { + GtkTextBuffer *buffer; + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text ( + buffer, validity->encrypt.description, + strlen (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 (&validity->encrypt.encrypters)) + add_cert_table (grid, &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 * +secure_button_get_widget_for_validity (CamelCipherValidity *validity) +{ + GtkWidget *box, *button, *layout, *widget; + const gchar *icon_name; + gchar *description; + GString *buffer; + + g_return_val_if_fail (validity != NULL, NULL); + + buffer = g_string_new (""); + + if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) { + const gchar *desc; + gint status; + + status = validity->sign.status; + desc = smime_sign_table[status].shortdesc; + + g_string_append (buffer, gettext (desc)); + + format_cert_infos (&validity->sign.signers, buffer); + } + + if (validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) { + const gchar *desc; + gint status; + + if (validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) + g_string_append (buffer, "\n"); + + status = 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 (validity->sign.status != 0) + icon_name = smime_sign_table[validity->sign.status].icon; + else + icon_name = smime_encrypt_table[validity->encrypt.status].icon; + + box = gtk_event_box_new (); + if (validity->sign.status != 0) + gtk_widget_override_background_color ( + box, GTK_STATE_FLAG_NORMAL, + &smime_sign_colour[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), validity); + + 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); + + g_free (description); + + return box; +} + +static GtkWidget * +emfe_secure_button_get_widget (EMailFormatterExtension *extension, + EMailPartList *context, + EMailPart *part, + GHashTable *params) +{ + GtkWidget *grid; + GList *head, *link; + + g_return_val_if_fail (part != NULL, NULL); + + grid = g_object_new ( + GTK_TYPE_GRID, + "orientation", GTK_ORIENTATION_VERTICAL, + "row-spacing", 2, + "halign", GTK_ALIGN_FILL, + "hexpand", TRUE, + NULL); + + head = g_queue_peek_head_link (&part->validities); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + GtkWidget *widget; + + if (pair == NULL) + continue; + + widget = secure_button_get_widget_for_validity (pair->validity); + if (widget != NULL) { + gtk_widget_set_halign (widget, GTK_ALIGN_FILL); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (grid), widget); + } + } + + gtk_widget_show_all (grid); + + return grid; +} + +static void +e_mail_formatter_secure_button_class_init (EMailFormatterExtensionClass *class) +{ + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_secure_button_format; + class->get_widget = emfe_secure_button_get_widget; +} + +static void +e_mail_formatter_secure_button_init (EMailFormatterExtension *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..944f9a4c14 --- /dev/null +++ b/em-format/e-mail-formatter-source.c @@ -0,0 +1,140 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" + +typedef EMailFormatterExtension EMailFormatterSource; +typedef EMailFormatterExtensionClass EMailFormatterSourceClass; + +GType e_mail_formatter_source_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterSource, + e_mail_formatter_source, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +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; + CamelMimePart *mime_part; + + mime_part = e_mail_part_ref_mime_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 (mime_part)) { + g_string_append_printf ( + buffer, + "<div class=\"part-container\" " + "style=\"border: 0; background: #%06x; color: #%06x;\" >", + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT))); + } 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_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT))); + } + + 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 ( + CAMEL_DATA_WRAPPER (mime_part), + 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 (mime_part)) { + camel_stream_write_string (stream, "</div>", cancellable, NULL); + } else { + camel_stream_write_string (stream, "</div></div>", cancellable, NULL); + } + + g_object_unref (mime_part); + + return TRUE; +} + +static void +e_mail_formatter_source_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Source"); + class->description = _("Display source of a MIME part"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_source_format; +} + +static void +e_mail_formatter_source_init (EMailFormatterExtension *extension) +{ +} 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..6c3b033171 --- /dev/null +++ b/em-format/e-mail-formatter-text-enriched.c @@ -0,0 +1,114 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" + +typedef EMailFormatterExtension EMailFormatterTextEnriched; +typedef EMailFormatterExtensionClass EMailFormatterTextEnrichedClass; + +GType e_mail_formatter_text_enriched_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterTextEnriched, + e_mail_formatter_text_enriched, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/enriched", + "text/richtext", + NULL +}; + +static gboolean +emfe_text_enriched_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *enriched; + const gchar *mime_type; + guint32 filter_flags = 0; + GString *buffer; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + mime_type = e_mail_part_get_mime_type (part); + + if (g_strcmp0 (mime_type, "text/richtext") == 0) + 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_rgba_to_value ( + e_mail_formatter_get_color (formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_rgba_to_value ( + e_mail_formatter_get_color (formatter, E_MAIL_FORMATTER_COLOR_CONTENT)), + e_rgba_to_value ( + 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 void +e_mail_formatter_text_enriched_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Richtext"); + class->description = _("Display part as enriched text"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_text_enriched_format; +} + +static void +e_mail_formatter_text_enriched_init (EMailFormatterExtension *extension) +{ +} 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..0a581117a6 --- /dev/null +++ b/em-format/e-mail-formatter-text-html.c @@ -0,0 +1,363 @@ +/* + * 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 <ctype.h> +#include <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-utils.h" + +typedef EMailFormatterExtension EMailFormatterTextHTML; +typedef EMailFormatterExtensionClass EMailFormatterTextHTMLClass; + +GType e_mail_formatter_text_html_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterTextHTML, + e_mail_formatter_text_html, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/html", + NULL +}; + +static gchar * +get_tag (const gchar *utf8_string, + const gchar *tag_name, + gchar *opening, + gchar *closing) +{ + gchar *t; + gunichar c; + gboolean has_end; + + c = '\0'; + 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 == '<') { + if (t[1] == '!' && t[2] == '-' && t[3] == '-') { + /* it's a comment start, read until the end of "-->" */ + gchar *end = strstr (t + 4, "-->"); + if (end) { + t = end + 2; + } else + break; + } else + break; + } + + t = g_utf8_find_next_char (t, NULL); + } + + has_end = FALSE; + 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 NULL; + + 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 NULL; +} + +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) { + 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); + + if (!g_utf8_validate (string->str, -1, NULL)) { + gchar *valid_utf8; + + valid_utf8 = e_util_utf8_make_valid (string->str); + g_string_free (string, TRUE); + string = g_string_new (valid_utf8); + g_free (valid_utf8); + } + + tags = NULL; + pos = string->str; + valid = FALSE; + + 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); + + /* Something's wrong, let's write the entire HTML and hope + * that WebKit can handle it */ + if (!valid) { + EMailFormatterContext c = { + .part_list = context->part_list, + .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) { + if (iter->data) + 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; + } + } else { + /* do not cut, if there is no end 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 { + CamelFolder *folder; + const gchar *message_uid; + const gchar *default_charset, *charset; + gchar *uri, *str; + + folder = e_mail_part_list_get_folder (context->part_list); + message_uid = e_mail_part_list_get_message_uid (context->part_list); + default_charset = e_mail_formatter_get_default_charset (formatter); + charset = e_mail_formatter_get_charset (formatter); + + if (!default_charset) + default_charset = ""; + if (!charset) + charset = ""; + + uri = e_mail_part_build_uri ( + folder, message_uid, + "part_id", G_TYPE_STRING, e_mail_part_get_id (part), + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, + "formatter_default_charset", G_TYPE_STRING, default_charset, + "formatter_charset", G_TYPE_STRING, charset, + NULL); + + /* HTML messages expect white background and black color for text. + * If Evolution uses a dark theme, then the dark background with + * a black text is hard to read, thus force white background color. + * The HTML content can still overwrite both colors. + */ + str = g_strdup_printf ( + "<div class=\"part-container-nostyle\">" + "<iframe width=\"100%%\" height=\"10\" " + " frameborder=\"0\" src=\"%s\" " + " id=\"%s.iframe\" name=\"%s\" " + " style=\"border: 1px solid #%06x; background-color: #ffffff;\">" + "</iframe>" + "</div>", + uri, + e_mail_part_get_id (part), + e_mail_part_get_id (part), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME))); + + camel_stream_write_string (stream, str, cancellable, NULL); + + g_free (str); + g_free (uri); + } + + return TRUE; +} + +static void +e_mail_formatter_text_html_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("HTML"); + class->description = _("Format part as HTML"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_text_html_format; +} + +static void +e_mail_formatter_text_html_init (EMailFormatterExtension *extension) +{ +} 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..638f0119a1 --- /dev/null +++ b/em-format/e-mail-formatter-text-plain.c @@ -0,0 +1,204 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-utils.h" + +typedef EMailFormatterExtension EMailFormatterTextPlain; +typedef EMailFormatterExtensionClass EMailFormatterTextPlainClass; + +GType e_mail_formatter_text_plain_get_type (void); + +G_DEFINE_TYPE ( + EMailFormatterTextPlain, + e_mail_formatter_text_plain, + E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/plain", + "text/*", + "message/*", + "application/vnd.evolution.plaintext", + NULL +}; + +static gboolean +emfe_text_plain_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filtered_stream; + CamelMimeFilter *html_filter; + gchar *content; + const gchar *format; + 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)) { + CamelMimeFilterToHTMLFlags flags; + CamelMimePart *mime_part; + CamelDataWrapper *dw; + + 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); + } + + flags = e_mail_formatter_get_text_format_flags (formatter); + + mime_part = e_mail_part_ref_mime_part (part); + dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); + if (dw == NULL) { + g_object_unref (mime_part); + 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")) + flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; + + rgb = e_rgba_to_value ( + 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 (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 pre\" style=\"" + "border: none; padding: 8px; margin: 0; " + "background-color: #%06x; color: #%06x;\">\n", + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_CONTENT)), + e_rgba_to_value ( + 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>\n", cancellable, NULL); + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + camel_stream_write_string ( + stream, "</body></html>", + cancellable, NULL); + } + + g_object_unref (mime_part); + + return TRUE; + + } else { + CamelFolder *folder; + const gchar *message_uid; + gchar *uri, *str; + const gchar *default_charset, *charset; + + folder = e_mail_part_list_get_folder (context->part_list); + message_uid = e_mail_part_list_get_message_uid (context->part_list); + default_charset = e_mail_formatter_get_default_charset (formatter); + charset = e_mail_formatter_get_charset (formatter); + + if (!default_charset) + default_charset = ""; + if (!charset) + charset = ""; + + uri = e_mail_part_build_uri ( + folder, message_uid, + "part_id", G_TYPE_STRING, e_mail_part_get_id (part), + "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW, + "formatter_default_charset", G_TYPE_STRING, default_charset, + "formatter_charset", G_TYPE_STRING, charset, + NULL); + + str = g_strdup_printf ( + "<div class=\"part-container-nostyle\" >" + "<iframe width=\"100%%\" height=\"10\"" + " id=\"%s.iframe\" name=\"%s\" " + " frameborder=\"0\" src=\"%s\" " + " style=\"border: 1px solid #%06x; background-color: #%06x;\">" + "</iframe>" + "</div>", + e_mail_part_get_id (part), + e_mail_part_get_id (part), + uri, + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME)), + e_rgba_to_value ( + 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 void +e_mail_formatter_text_plain_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Plain Text"); + class->description = _("Format part as plain text"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_text_plain_format; +} + +static void +e_mail_formatter_text_plain_init (EMailFormatterExtension *extension) +{ +} diff --git a/em-format/e-mail-formatter-utils.c b/em-format/e-mail-formatter-utils.c new file mode 100644 index 0000000000..1967a61ccd --- /dev/null +++ b/em-format/e-mail-formatter-utils.c @@ -0,0 +1,541 @@ +/* + * 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 <string.h> +#include <glib/gi18n.h> + +#include <camel/camel.h> +#include <libedataserver/libedataserver.h> + +#include <e-util/e-util.h> +#include <libemail-engine/e-mail-utils.h> +#include <libemail-engine/mail-config.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) +{ + GtkTextDirection direction; + const gchar *fmt, *html; + const gchar *display; + gchar *mhtml = NULL; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (buffer != NULL); + g_return_if_fail (label != NULL); + + if (value == NULL) + return; + + while (*value == ' ') + value++; + + if (!(flags & E_MAIL_FORMATTER_HEADER_FLAG_HTML)) { + CamelMimeFilterToHTMLFlags text_format_flags; + + text_format_flags = + e_mail_formatter_get_text_format_flags (formatter); + html = mhtml = camel_text_to_html ( + value, text_format_flags, 0); + } else { + html = value; + } + + direction = gtk_widget_get_default_direction (); + + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS) { + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) { + fmt = "<tr style=\"display: %s\">" + "<td><b>%s:</b> %s</td></tr>"; + } else { + fmt = "<tr style=\"display: %s\">" + "<td>%s: %s</td></tr>"; + } + } else if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NODEC) { + if (direction == GTK_TEXT_DIR_RTL) + fmt = "<tr class=\"header\" style=\"display: %s\">" + "<th class=\"header rtl\">%s</th>" + "<td class=\"header rtl\">%s</td>" + "</tr>"; + else + fmt = "<tr class=\"header\" style=\"display: %s\">" + "<th class=\"header ltr\">%s</th>" + "<td class=\"header ltr\">%s</td>" + "</tr>"; + } else { + if (direction == GTK_TEXT_DIR_RTL) + fmt = "<tr class=\"header\" style=\"display: %s\">" + "<th class=\"header rtl\">%s:</th>" + "<td class=\"header rtl\">%s</td>" + "</tr>"; + else + fmt = "<tr class=\"header\" style=\"display: %s\">" + "<th class=\"header ltr\">%s:</th>" + "<td class=\"header ltr\">%s</td>" + "</tr>"; + } + + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN) + display = "none"; + else + display = "table-row"; + + g_string_append_printf (buffer, fmt, display, label, html); + + g_free (mhtml); +} + +gchar * +e_mail_formatter_format_address (EMailFormatter *formatter, + GString *out, + struct _camel_header_address *a, + const gchar *field, + gboolean no_links, + gboolean elipsize) +{ + CamelMimeFilterToHTMLFlags flags; + gchar *name, *mailto, *addr; + gint i = 0; + gchar *str = NULL; + gint limit = mail_config_get_address_count (); + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + g_return_val_if_fail (out != NULL, NULL); + g_return_val_if_fail (field != NULL, NULL); + + flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; + + while (a != NULL) { + 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 != NULL && *name != '\0') { + 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 != NULL && *name != '\0') + 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 != NULL) + g_string_append (out, ", "); + + if (!elipsize) + continue; + + /* Let us add a '...' if we have more addresses */ + if (limit > 0 && i == limit && a != NULL) { + 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 != NULL) { + 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 != NULL) { + 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; + + g_return_if_fail (name != NULL); + + /* 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, + const gchar *header_name, + const gchar *header_value, + guint32 flags, + const gchar *charset) +{ + gchar *canon_name, *buf, *value = NULL; + const gchar *label, *txt; + gboolean addrspec = FALSE; + gchar *str_field = NULL; + gint i; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (buffer != NULL); + g_return_if_fail (header_name != NULL); + g_return_if_fail (header_value != NULL); + + canon_name = g_alloca (strlen (header_name) + 1); + strcpy (canon_name, header_name); + e_mail_formatter_canon_header_name (canon_name); + + for (i = 0; addrspec_hdrs[i]; i++) { + if (g_str_equal (canon_name, addrspec_hdrs[i])) { + addrspec = TRUE; + break; + } + } + + label = _(canon_name); + + if (addrspec) { + struct _camel_header_address *addrs; + GString *html; + gchar *img; + gchar *charset; + + charset = e_mail_formatter_dup_charset (formatter); + if (charset == NULL) + charset = e_mail_formatter_dup_default_charset (formatter); + + buf = camel_header_unfold (header_value); + addrs = camel_header_address_decode (buf, charset); + if (addrs == NULL) { + g_free (charset); + g_free (buf); + return; + } + + g_free (charset); + g_free (buf); + + html = g_string_new (""); + img = e_mail_formatter_format_address ( + formatter, html, addrs, label, + (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS), + !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE)); + + if (img != NULL) { + str_field = g_strdup_printf ("%s: %s", label, img); + 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; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_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 (g_str_equal (canon_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 (g_str_equal (canon_name, "Date") || + g_str_equal (canon_name, "Resent-Date")) { + CamelMimeFilterToHTMLFlags text_format_flags; + 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++; + + text_format_flags = + e_mail_formatter_get_text_format_flags (formatter); + + html = camel_text_to_html (txt, text_format_flags, 0); + + msg_date = camel_header_decode_date (txt, &msg_offset); + e_localtime_with_offset (msg_date, &local, &local_tz); + + /* Convert message offset to minutes (e.g. -0400 --> -240) */ + msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100); + /* Turn into offset from localtime, not UTC */ + msg_offset -= local_tz / 60; + + /* value will be freed at the end */ + if (!hide_real_date && !msg_offset) { + /* No timezone difference; just + * show the real Date: header. */ + txt = value = html; + } else { + gchar *date_str; + + date_str = e_datetime_format_format ( + "mail", "header", + DTFormatKindDateTime, msg_date); + + if (hide_real_date) { + /* Show only the local-formatted date, losing + * all timezone information like Outlook does. + * Should we attempt to show it somehow? */ + txt = value = date_str; + } else { + txt = value = g_strdup_printf ( + "%s (<I>%s</I>)", html, date_str); + g_free (date_str); + } + g_free (html); + } + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_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; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "Received") || + g_str_has_prefix (canon_name, "X-")) { + /* 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); +} + +GList * +e_mail_formatter_find_rfc822_end_iter (GList *rfc822_start_iter) +{ + GList *link = rfc822_start_iter; + EMailPart *part; + const gchar *part_id; + gchar *end; + + g_return_val_if_fail (rfc822_start_iter != NULL, NULL); + + part = E_MAIL_PART (link->data); + + part_id = e_mail_part_get_id (part); + g_return_val_if_fail (part_id != NULL, NULL); + + end = g_strconcat (part_id, ".end", NULL); + + while (link != NULL) { + part = E_MAIL_PART (link->data); + + part_id = e_mail_part_get_id (part); + g_return_val_if_fail (part_id != NULL, NULL); + + if (g_strcmp0 (part_id, end) == 0) + break; + + link = g_list_next (link); + } + + g_free (end); + + return link; +} + +gchar * +e_mail_formatter_parse_html_mnemonics (const gchar *label, + gchar **out_access_key) +{ + const gchar *pos = NULL; + GString *html_label = NULL; + + g_return_val_if_fail (label != NULL, NULL); + + if (out_access_key != NULL) + *out_access_key = NULL; + + pos = strstr (label, "_"); + if (pos != NULL) { + gchar ak = pos[1]; + + /* Convert to uppercase */ + if (ak >= 'a') + ak = ak - 32; + + html_label = g_string_new (""); + g_string_append_len (html_label, label, pos - label); + g_string_append_printf (html_label, "<u>%c</u>", pos[1]); + g_string_append (html_label, &pos[2]); + + if (out_access_key != NULL && ak != '\0') + *out_access_key = g_strdup_printf ("%c", ak); + + } else { + html_label = g_string_new (label); + } + + return g_string_free (html_label, FALSE); +} diff --git a/em-format/e-mail-formatter-utils.h b/em-format/e-mail-formatter-utils.h new file mode 100644 index 0000000000..0312d545e0 --- /dev/null +++ b/em-format/e-mail-formatter-utils.h @@ -0,0 +1,60 @@ +/* + * 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, + const gchar *header_name, + const gchar *header_value, + 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, + const gchar *field, + gboolean no_links, + gboolean elipsize); + +void e_mail_formatter_canon_header_name + (gchar *name); + +GList * e_mail_formatter_find_rfc822_end_iter + (GList *rfc822_start_iter); + +gchar * e_mail_formatter_parse_html_mnemonics + (const gchar *label, + gchar **out_access_key); + +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..f3f09670d4 --- /dev/null +++ b/em-format/e-mail-formatter.c @@ -0,0 +1,1435 @@ +/* + * e-mail-formatter.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + */ + +#include "e-mail-formatter.h" + +#include "e-mail-formatter-extension.h" +#include "e-mail-formatter-utils.h" +#include "e-mail-part.h" + +#include <e-util/e-util.h> +#include <libebackend/libebackend.h> +#include <gdk/gdk.h> +#include <glib/gi18n.h> + +#include "libemail-engine/e-mail-enumtypes.h" + +#define d(x) + +#define E_MAIL_FORMATTER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_FORMATTER, EMailFormatterPrivate))\ + +#define STYLESHEET_URI \ + "evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css" + +typedef struct _AsyncContext AsyncContext; + +struct _EMailFormatterPrivate { + EMailImageLoadingPolicy image_loading_policy; + + gboolean show_sender_photo; + gboolean show_real_date; + gboolean animate_images; + + GMutex property_lock; + + gchar *charset; + gchar *default_charset; +}; + +struct _AsyncContext { + CamelStream *stream; + EMailPartList *part_list; + EMailFormatterHeaderFlags flags; + EMailFormatterMode mode; +}; + +/* internal formatter extensions */ +GType e_mail_formatter_attachment_get_type (void); +GType e_mail_formatter_attachment_bar_get_type (void); +GType e_mail_formatter_error_get_type (void); +GType e_mail_formatter_headers_get_type (void); +GType e_mail_formatter_image_get_type (void); +GType e_mail_formatter_message_rfc822_get_type (void); +GType e_mail_formatter_secure_button_get_type (void); +GType e_mail_formatter_source_get_type (void); +GType e_mail_formatter_text_enriched_get_type (void); +GType e_mail_formatter_text_html_get_type (void); +GType e_mail_formatter_text_plain_get_type (void); + +void e_mail_formatter_internal_extensions_load (EMailExtensionRegistry *ereg); + +static gpointer e_mail_formatter_parent_class = 0; + +enum { + PROP_0, + PROP_ANIMATE_IMAGES, + PROP_BODY_COLOR, + PROP_CHARSET, + PROP_CITATION_COLOR, + PROP_CONTENT_COLOR, + PROP_DEFAULT_CHARSET, + PROP_FRAME_COLOR, + PROP_HEADER_COLOR, + PROP_IMAGE_LOADING_POLICY, + PROP_MARK_CITATIONS, + PROP_SHOW_REAL_DATE, + PROP_SHOW_SENDER_PHOTO, + PROP_TEXT_COLOR +}; + +enum { + NEED_REDRAW, + LAST_SIGNAL +}; + +static gint signals[LAST_SIGNAL]; + +static void +async_context_free (AsyncContext *async_context) +{ + g_clear_object (&async_context->part_list); + g_clear_object (&async_context->stream); + + g_slice_free (AsyncContext, async_context); +} + +static EMailFormatterContext * +mail_formatter_create_context (EMailFormatter *formatter, + EMailPartList *part_list, + EMailFormatterMode mode, + EMailFormatterHeaderFlags flags) +{ + EMailFormatterClass *class; + EMailFormatterContext *context; + + class = E_MAIL_FORMATTER_GET_CLASS (formatter); + + g_warn_if_fail (class->context_size >= sizeof (EMailFormatterContext)); + + context = g_malloc0 (class->context_size); + context->part_list = g_object_ref (part_list); + context->mode = mode; + context->flags = flags; + + return context; +} + +static void +mail_formatter_free_context (EMailFormatterContext *context) +{ + if (context->part_list != NULL) + g_object_unref (context->part_list); + + g_free (context); +} + +static void +e_mail_formatter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ANIMATE_IMAGES: + e_mail_formatter_set_animate_images ( + E_MAIL_FORMATTER (object), + g_value_get_boolean (value)); + return; + + case PROP_BODY_COLOR: + e_mail_formatter_set_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_BODY, + g_value_get_boxed (value)); + return; + + case PROP_CHARSET: + e_mail_formatter_set_charset ( + E_MAIL_FORMATTER (object), + g_value_get_string (value)); + return; + + case PROP_CITATION_COLOR: + e_mail_formatter_set_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_CITATION, + g_value_get_boxed (value)); + return; + + case PROP_CONTENT_COLOR: + e_mail_formatter_set_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_CONTENT, + g_value_get_boxed (value)); + return; + + case PROP_DEFAULT_CHARSET: + e_mail_formatter_set_default_charset ( + E_MAIL_FORMATTER (object), + g_value_get_string (value)); + return; + + case PROP_FRAME_COLOR: + e_mail_formatter_set_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_FRAME, + g_value_get_boxed (value)); + return; + + case PROP_HEADER_COLOR: + e_mail_formatter_set_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_HEADER, + g_value_get_boxed (value)); + return; + + case PROP_IMAGE_LOADING_POLICY: + e_mail_formatter_set_image_loading_policy ( + E_MAIL_FORMATTER (object), + g_value_get_enum (value)); + return; + + case PROP_MARK_CITATIONS: + e_mail_formatter_set_mark_citations ( + E_MAIL_FORMATTER (object), + g_value_get_boolean (value)); + return; + + case PROP_SHOW_REAL_DATE: + e_mail_formatter_set_show_real_date ( + E_MAIL_FORMATTER (object), + g_value_get_boolean (value)); + return; + + case PROP_SHOW_SENDER_PHOTO: + e_mail_formatter_set_show_sender_photo ( + E_MAIL_FORMATTER (object), + g_value_get_boolean (value)); + return; + + case PROP_TEXT_COLOR: + e_mail_formatter_set_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_TEXT, + g_value_get_boxed (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_mail_formatter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ANIMATE_IMAGES: + g_value_set_boolean ( + value, + e_mail_formatter_get_animate_images ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_BODY_COLOR: + g_value_set_boxed ( + value, + e_mail_formatter_get_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_BODY)); + return; + + case PROP_CHARSET: + g_value_take_string ( + value, + e_mail_formatter_dup_charset ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_CITATION_COLOR: + g_value_set_boxed ( + value, + e_mail_formatter_get_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_CITATION)); + return; + + case PROP_CONTENT_COLOR: + g_value_set_boxed ( + value, + e_mail_formatter_get_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_CONTENT)); + return; + + case PROP_DEFAULT_CHARSET: + g_value_take_string ( + value, + e_mail_formatter_dup_default_charset ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_FRAME_COLOR: + g_value_set_boxed ( + value, + e_mail_formatter_get_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_FRAME)); + return; + + case PROP_HEADER_COLOR: + g_value_set_boxed ( + value, + e_mail_formatter_get_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_HEADER)); + return; + + case PROP_IMAGE_LOADING_POLICY: + g_value_set_enum ( + value, + e_mail_formatter_get_image_loading_policy ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_MARK_CITATIONS: + g_value_set_boolean ( + value, + e_mail_formatter_get_mark_citations ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_SHOW_REAL_DATE: + g_value_set_boolean ( + value, + e_mail_formatter_get_show_real_date ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_SHOW_SENDER_PHOTO: + g_value_set_boolean ( + value, + e_mail_formatter_get_show_sender_photo ( + E_MAIL_FORMATTER (object))); + return; + + case PROP_TEXT_COLOR: + g_value_set_boxed ( + value, + e_mail_formatter_get_color ( + E_MAIL_FORMATTER (object), + E_MAIL_FORMATTER_COLOR_TEXT)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_mail_formatter_finalize (GObject *object) +{ + EMailFormatterPrivate *priv; + + priv = E_MAIL_FORMATTER_GET_PRIVATE (object); + + g_free (priv->charset); + g_free (priv->default_charset); + + g_mutex_clear (&priv->property_lock); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_formatter_parent_class)->finalize (object); +} + +static void +e_mail_formatter_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_formatter_parent_class)->constructed (object); + + e_extensible_load_extensions (E_EXTENSIBLE (object)); +} + +static void +mail_formatter_run (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable) +{ + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + gchar *hdr; + + hdr = e_mail_formatter_get_html_header (formatter); + camel_stream_write_string (stream, hdr, cancellable, NULL); + g_free (hdr); + + e_mail_part_list_queue_parts (context->part_list, NULL, &queue); + + head = g_queue_peek_head_link (&queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *part = link->data; + const gchar *part_id; + gboolean ok; + + part_id = e_mail_part_get_id (part); + + if (g_cancellable_is_cancelled (cancellable)) + break; + + if (part->is_hidden && !part->is_error) { + if (e_mail_part_id_has_suffix (part, ".rfc822")) { + link = e_mail_formatter_find_rfc822_end_iter (link); + } + + if (link == NULL) + break; + + continue; + } + + /* Force formatting as source if needed */ + if (context->mode != E_MAIL_FORMATTER_MODE_SOURCE) { + const gchar *mime_type; + + mime_type = e_mail_part_get_mime_type (part); + if (mime_type == NULL) + continue; + + ok = e_mail_formatter_format_as ( + formatter, context, part, stream, + mime_type, cancellable); + + /* If the written part was message/rfc822 then + * jump to the end of the message, because content + * of the whole message has been formatted by + * message_rfc822 formatter */ + if (ok && e_mail_part_id_has_suffix (part, ".rfc822")) { + link = e_mail_formatter_find_rfc822_end_iter (link); + + if (link == NULL) + break; + + continue; + } + + } else { + ok = FALSE; + } + + if (!ok) { + /* We don't want to source these */ + if (e_mail_part_id_has_suffix (part, ".headers") || + e_mail_part_id_has_suffix (part, "attachment-bar")) + continue; + + e_mail_formatter_format_as ( + formatter, context, part, stream, + "application/vnd.evolution.source", cancellable); + + /* .message is the entire message. There's nothing more + * to be written. */ + if (g_strcmp0 (part_id, ".message") == 0) + break; + + /* If we just wrote source of a rfc822 message, then jump + * behind the message (otherwise source of all parts + * would be rendered twice) */ + if (e_mail_part_id_has_suffix (part, ".rfc822")) { + + do { + part = link->data; + if (e_mail_part_id_has_suffix (part, ".rfc822.end")) + break; + + link = g_list_next (link); + } while (link != NULL); + + if (link == NULL) + break; + } + } + } + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + camel_stream_write_string (stream, "</body></html>", cancellable, NULL); +} + +static void +mail_formatter_update_style (EMailFormatter *formatter, + GtkStateFlags state) +{ + GtkStyleContext *style_context; + GtkWidgetPath *widget_path; + GdkRGBA rgba; + + g_object_freeze_notify (G_OBJECT (formatter)); + + /* derive colors from top-level window */ + style_context = gtk_style_context_new (); + widget_path = gtk_widget_path_new (); + gtk_widget_path_append_type (widget_path, GTK_TYPE_WINDOW); + gtk_style_context_set_path (style_context, widget_path); + gtk_style_context_invalidate (style_context); + + gtk_style_context_save (style_context); + gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_TOOLBAR); + + gtk_style_context_get_background_color (style_context, state, &rgba); + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY, &rgba); + + rgba.red *= 0.8; + rgba.green *= 0.8; + rgba.blue *= 0.8; + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_FRAME, &rgba); + + gtk_style_context_restore (style_context); + gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ENTRY); + + gtk_style_context_get_color (style_context, state, &rgba); + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_HEADER, &rgba); + + gtk_style_context_get_background_color ( + style_context, state | GTK_STATE_FLAG_FOCUSED, &rgba); + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_CONTENT, &rgba); + + gtk_style_context_get_color ( + style_context, state | GTK_STATE_FLAG_FOCUSED, &rgba); + e_mail_formatter_set_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT, &rgba); + + gtk_widget_path_free (widget_path); + g_object_unref (style_context); + + g_object_thaw_notify (G_OBJECT (formatter)); +} + +static void +e_mail_formatter_base_init (EMailFormatterClass *class) +{ + /* Register internal extensions. */ + g_type_ensure (e_mail_formatter_attachment_get_type ()); + g_type_ensure (e_mail_formatter_attachment_bar_get_type ()); + g_type_ensure (e_mail_formatter_error_get_type ()); + g_type_ensure (e_mail_formatter_headers_get_type ()); + g_type_ensure (e_mail_formatter_image_get_type ()); + g_type_ensure (e_mail_formatter_message_rfc822_get_type ()); + g_type_ensure (e_mail_formatter_secure_button_get_type ()); + g_type_ensure (e_mail_formatter_source_get_type ()); + g_type_ensure (e_mail_formatter_text_enriched_get_type ()); + g_type_ensure (e_mail_formatter_text_html_get_type ()); + g_type_ensure (e_mail_formatter_text_plain_get_type ()); + + class->extension_registry = g_object_new ( + E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, NULL); + + e_mail_formatter_extension_registry_load ( + class->extension_registry, + E_TYPE_MAIL_FORMATTER_EXTENSION); + + e_extensible_load_extensions ( + E_EXTENSIBLE (class->extension_registry)); + + class->text_html_flags = + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | + CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | + CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | + CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES | + CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; +} + +static void +e_mail_formatter_base_finalize (EMailFormatterClass *class) +{ + g_object_unref (class->extension_registry); +} + +static void +e_mail_formatter_class_init (EMailFormatterClass *class) +{ + GObjectClass *object_class; + GdkRGBA *rgba; + + e_mail_formatter_parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailFormatterPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = e_mail_formatter_set_property; + object_class->get_property = e_mail_formatter_get_property; + object_class->finalize = e_mail_formatter_finalize; + object_class->constructed = e_mail_formatter_constructed; + + class->context_size = sizeof (EMailFormatterContext); + class->run = mail_formatter_run; + class->update_style = mail_formatter_update_style; + + rgba = &class->colors[E_MAIL_FORMATTER_COLOR_BODY]; + gdk_rgba_parse (rgba, "#eeeeee"); + + rgba = &class->colors[E_MAIL_FORMATTER_COLOR_CONTENT]; + gdk_rgba_parse (rgba, "#ffffff"); + + rgba = &class->colors[E_MAIL_FORMATTER_COLOR_FRAME]; + gdk_rgba_parse (rgba, "#3f3f3f"); + + rgba = &class->colors[E_MAIL_FORMATTER_COLOR_HEADER]; + gdk_rgba_parse (rgba, "#eeeeee"); + + rgba = &class->colors[E_MAIL_FORMATTER_COLOR_TEXT]; + gdk_rgba_parse (rgba, "#000000"); + + g_object_class_install_property ( + object_class, + PROP_ANIMATE_IMAGES, + g_param_spec_boolean ( + "animate-images", + "Animate images", + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_BODY_COLOR, + g_param_spec_boxed ( + "body-color", + "Body Color", + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_CHARSET, + g_param_spec_string ( + "charset", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_CITATION_COLOR, + g_param_spec_boxed ( + "citation-color", + "Citation Color", + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_CONTENT_COLOR, + g_param_spec_boxed ( + "content-color", + "Content Color", + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_DEFAULT_CHARSET, + g_param_spec_string ( + "default-charset", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_FRAME_COLOR, + g_param_spec_boxed ( + "frame-color", + "Frame Color", + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_HEADER_COLOR, + g_param_spec_boxed ( + "header-color", + "Header Color", + NULL, + GDK_TYPE_RGBA, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_IMAGE_LOADING_POLICY, + g_param_spec_enum ( + "image-loading-policy", + "Image Loading Policy", + NULL, + E_TYPE_MAIL_IMAGE_LOADING_POLICY, + E_MAIL_IMAGE_LOADING_POLICY_NEVER, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MARK_CITATIONS, + g_param_spec_boolean ( + "mark-citations", + "Mark Citations", + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_SHOW_REAL_DATE, + g_param_spec_boolean ( + "show-real-date", + "Show real Date header value", + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_SHOW_SENDER_PHOTO, + g_param_spec_boolean ( + "show-sender-photo", + "Show Sender Photo", + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_TEXT_COLOR, + g_param_spec_boxed ( + "text-color", + "Text Color", + NULL, + GDK_TYPE_COLOR, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + signals[NEED_REDRAW] = g_signal_new ( + "need-redraw", + E_TYPE_MAIL_FORMATTER, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMailFormatterClass, need_redraw), + NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static void +e_mail_formatter_init (EMailFormatter *formatter) +{ + formatter->priv = E_MAIL_FORMATTER_GET_PRIVATE (formatter); + + g_mutex_init (&formatter->priv->property_lock); +} + +static void +e_mail_formatter_extensible_interface_init (EExtensibleInterface *interface) +{ + +} + +EMailFormatter * +e_mail_formatter_new (void) +{ + return g_object_new (E_TYPE_MAIL_FORMATTER, NULL); +} + +GType +e_mail_formatter_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + const GTypeInfo type_info = { + sizeof (EMailFormatterClass), + (GBaseInitFunc) e_mail_formatter_base_init, + (GBaseFinalizeFunc) e_mail_formatter_base_finalize, + (GClassInitFunc) e_mail_formatter_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailFormatter), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_mail_formatter_init, + NULL /* value_table */ + }; + + const GInterfaceInfo e_extensible_interface_info = { + (GInterfaceInitFunc) e_mail_formatter_extensible_interface_init + }; + + type = g_type_register_static ( + G_TYPE_OBJECT, + "EMailFormatter", &type_info, 0); + + g_type_add_interface_static ( + type, E_TYPE_EXTENSIBLE, &e_extensible_interface_info); + } + + return type; +} + +void +e_mail_formatter_format_sync (EMailFormatter *formatter, + EMailPartList *part_list, + CamelStream *stream, + EMailFormatterHeaderFlags flags, + EMailFormatterMode mode, + GCancellable *cancellable) +{ + EMailFormatterContext *context; + EMailFormatterClass *class; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + /* EMailPartList can be NULL. */ + g_return_if_fail (CAMEL_IS_STREAM (stream)); + + class = E_MAIL_FORMATTER_GET_CLASS (formatter); + g_return_if_fail (class->run != NULL); + + context = mail_formatter_create_context ( + formatter, part_list, mode, flags); + + class->run (formatter, context, stream, cancellable); + + mail_formatter_free_context (context); +} + +static void +mail_formatter_format_thread (GSimpleAsyncResult *simple, + GObject *source_object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_mail_formatter_format_sync ( + E_MAIL_FORMATTER (source_object), + async_context->part_list, + async_context->stream, + async_context->flags, + async_context->mode, + cancellable); +} + +void +e_mail_formatter_format (EMailFormatter *formatter, + EMailPartList *part_list, + CamelStream *stream, + EMailFormatterHeaderFlags flags, + EMailFormatterMode mode, + GAsyncReadyCallback callback, + GCancellable *cancellable, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + EMailFormatterClass *class; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + /* EMailPartList can be NULL. */ + g_return_if_fail (CAMEL_IS_STREAM (stream)); + + class = E_MAIL_FORMATTER_GET_CLASS (formatter); + g_return_if_fail (class->run != NULL); + + async_context = g_slice_new0 (AsyncContext); + async_context->stream = g_object_ref (stream); + async_context->flags = flags; + async_context->mode = mode; + + simple = g_simple_async_result_new ( + G_OBJECT (formatter), callback, + user_data, e_mail_formatter_format); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + if (part_list != NULL) { + async_context->part_list = g_object_ref (part_list); + + g_simple_async_result_run_in_thread ( + simple, mail_formatter_format_thread, + G_PRIORITY_DEFAULT, cancellable); + } else { + g_simple_async_result_complete_in_idle (simple); + } + + g_object_unref (simple); +} + +gboolean +e_mail_formatter_format_finish (EMailFormatter *formatter, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (formatter), + e_mail_formatter_format), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +/** + * e_mail_formatter_format_as: + * @formatter: an #EMailFormatter + * @context: an #EMailFormatterContext + * @part: an #EMailPart + * @stream: a #CamelStream + * @as_mime_type: (allow-none) mime-type to use for formatting, or %NULL + * @cancellable: (allow-none) an optional #GCancellable + * + * Formats given @part using a @formatter extension for given mime type. When + * the mime type is %NULL, the function will try to lookup the best formatter + * for given @part by it's default mime type. + * + * Return Value: %TRUE on success, %FALSE when no suitable formatter is found or + * when it fails to format the part. + */ +gboolean +e_mail_formatter_format_as (EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + CamelStream *stream, + const gchar *as_mime_type, + GCancellable *cancellable) +{ + EMailExtensionRegistry *extension_registry; + GQueue *formatters; + gboolean ok; + d ( + gint _call_i; + static gint _call = 0; + G_LOCK_DEFINE_STATIC (_call); + G_LOCK (_call); + _call++; + _call_i = _call; + G_UNLOCK (_call) + ); + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE); + g_return_val_if_fail (part != NULL, FALSE); + g_return_val_if_fail (CAMEL_IS_STREAM (stream), FALSE); + + if (as_mime_type == NULL || *as_mime_type == '\0') + as_mime_type = e_mail_part_get_mime_type (part); + + if (as_mime_type == NULL || *as_mime_type == '\0') + return FALSE; + + extension_registry = + e_mail_formatter_get_extension_registry (formatter); + formatters = e_mail_extension_registry_get_for_mime_type ( + extension_registry, as_mime_type); + if (formatters == NULL) + formatters = e_mail_extension_registry_get_fallback ( + extension_registry, as_mime_type); + + ok = FALSE; + + d ( + printf ("(%d) Formatting for part %s of type %s (found %d formatters)\n", + _call_i, part->id, as_mime_type, + formatters ? g_queue_get_length (formatters) : 0)); + + if (formatters != NULL) { + GList *head, *link; + + head = g_queue_peek_head_link (formatters); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailFormatterExtension *extension; + + extension = link->data; + if (extension == NULL) + continue; + + ok = e_mail_formatter_extension_format ( + extension, formatter, context, + part, stream, cancellable); + + d ( + printf ( + "\t(%d) trying %s...%s\n", _call_i, + G_OBJECT_TYPE_NAME (extension), + ok ? "OK" : "failed")); + + if (ok) + break; + } + } + + return ok; +} + +/** + * em_format_format_text: + * @part: an #EMailPart to decode + * @formatter: an #EMailFormatter + * @stream: Where to write the converted text + * @cancellable: optional #GCancellable object, or %NULL + * + * Decode/output a part's content to @stream. + **/ +void +e_mail_formatter_format_text (EMailFormatter *formatter, + EMailPart *part, + CamelStream *stream, + GCancellable *cancellable) +{ + CamelStream *filter_stream; + CamelMimeFilter *filter; + const gchar *charset = NULL; + CamelMimeFilter *windows = NULL; + CamelStream *mem_stream = NULL; + CamelMimePart *mime_part; + CamelContentType *mime_type; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + mime_part = e_mail_part_ref_mime_part (part); + mime_type = CAMEL_DATA_WRAPPER (mime_part)->mime_type; + + if (formatter->priv->charset != NULL) { + charset = formatter->priv->charset; + } else if (mime_type != NULL + && (charset = camel_content_type_param (mime_type, "charset")) + && g_ascii_strncasecmp (charset, "iso-8859-", 9) == 0) { + CamelStream *null; + + /* Since a few Windows mailers like to claim they sent + * out iso-8859-# encoded text when they really sent + * out windows-cp125#, do some simple sanity checking + * before we move on... */ + + null = camel_stream_null_new (); + filter_stream = camel_stream_filter_new (null); + g_object_unref (null); + + windows = camel_mime_filter_windows_new (charset); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filter_stream), windows); + + camel_data_wrapper_decode_to_stream_sync ( + CAMEL_DATA_WRAPPER (mime_part), + filter_stream, cancellable, NULL); + camel_stream_flush (filter_stream, cancellable, NULL); + g_object_unref (filter_stream); + + charset = camel_mime_filter_windows_real_charset ( + CAMEL_MIME_FILTER_WINDOWS (windows)); + } else if (charset == NULL) { + charset = formatter->priv->default_charset; + } + + mem_stream = (CamelStream *) camel_stream_mem_new (); + filter_stream = camel_stream_filter_new (mem_stream); + + filter = camel_mime_filter_charset_new (charset, "UTF-8"); + if (filter != NULL) { + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filter_stream), filter); + g_object_unref (filter); + } + + camel_data_wrapper_decode_to_stream_sync ( + camel_medium_get_content (CAMEL_MEDIUM (mime_part)), + filter_stream, cancellable, NULL); + camel_stream_flush (filter_stream, cancellable, NULL); + g_object_unref (filter_stream); + + g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL); + + camel_stream_write_to_stream ( + mem_stream, stream, cancellable, NULL); + camel_stream_flush (mem_stream, cancellable, NULL); + + if (windows != NULL) + g_object_unref (windows); + + g_object_unref (mem_stream); + + g_object_unref (mime_part); +} + +gchar * +e_mail_formatter_get_html_header (EMailFormatter *formatter) +{ + return g_strdup_printf ( + "<!DOCTYPE HTML>\n" + "<html>\n" + "<head>\n" + "<meta name=\"generator\" content=\"Evolution Mail\"/>\n" + "<title>Evolution Mail Display</title>\n" + "<link type=\"text/css\" rel=\"stylesheet\" " + " href=\"" STYLESHEET_URI "\"/>\n" + "<style type=\"text/css\">\n" + " table th { color: #%06x; font-weight: bold; }\n" + "</style>\n" + "</head>" + "<body bgcolor=\"#%06x\" text=\"#%06x\">", + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_HEADER)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_BODY)), + e_rgba_to_value ( + e_mail_formatter_get_color ( + formatter, E_MAIL_FORMATTER_COLOR_TEXT))); +} + +EMailExtensionRegistry * +e_mail_formatter_get_extension_registry (EMailFormatter *formatter) +{ + EMailFormatterClass * class; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + + class = E_MAIL_FORMATTER_GET_CLASS (formatter); + return E_MAIL_EXTENSION_REGISTRY (class->extension_registry); +} + +CamelMimeFilterToHTMLFlags +e_mail_formatter_get_text_format_flags (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0); + + return E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags; +} + +const GdkRGBA * +e_mail_formatter_get_color (EMailFormatter *formatter, + EMailFormatterColor type) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + g_return_val_if_fail (type < E_MAIL_FORMATTER_NUM_COLOR_TYPES, NULL); + + return &E_MAIL_FORMATTER_GET_CLASS (formatter)->colors[type]; +} + +void +e_mail_formatter_set_color (EMailFormatter *formatter, + EMailFormatterColor type, + const GdkRGBA *color) +{ + GdkRGBA *format_color; + const gchar *property_name; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (type < E_MAIL_FORMATTER_NUM_COLOR_TYPES); + g_return_if_fail (color != NULL); + + format_color = &E_MAIL_FORMATTER_GET_CLASS (formatter)->colors[type]; + + if (gdk_rgba_equal (color, format_color)) + return; + + format_color->red = color->red; + format_color->green = color->green; + format_color->blue = color->blue; + + switch (type) { + case E_MAIL_FORMATTER_COLOR_BODY: + property_name = "body-color"; + break; + case E_MAIL_FORMATTER_COLOR_CITATION: + property_name = "citation-color"; + break; + case E_MAIL_FORMATTER_COLOR_CONTENT: + property_name = "content-color"; + break; + case E_MAIL_FORMATTER_COLOR_FRAME: + property_name = "frame-color"; + break; + case E_MAIL_FORMATTER_COLOR_HEADER: + property_name = "header-color"; + break; + case E_MAIL_FORMATTER_COLOR_TEXT: + property_name = "text-color"; + break; + default: + g_return_if_reached (); + } + + g_object_notify (G_OBJECT (formatter), property_name); +} + +void +e_mail_formatter_update_style (EMailFormatter *formatter, + GtkStateFlags state) +{ + EMailFormatterClass *class; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + class = E_MAIL_FORMATTER_GET_CLASS (formatter); + g_return_if_fail (class->update_style != NULL); + + class->update_style (formatter, state); +} + +EMailImageLoadingPolicy +e_mail_formatter_get_image_loading_policy (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), 0); + + return formatter->priv->image_loading_policy; +} + +void +e_mail_formatter_set_image_loading_policy (EMailFormatter *formatter, + EMailImageLoadingPolicy policy) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + if (policy == formatter->priv->image_loading_policy) + return; + + formatter->priv->image_loading_policy = policy; + + g_object_notify (G_OBJECT (formatter), "image-loading-policy"); +} + +gboolean +e_mail_formatter_get_mark_citations (EMailFormatter *formatter) +{ + guint32 flags; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE); + + flags = E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags; + + return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0); +} + +void +e_mail_formatter_set_mark_citations (EMailFormatter *formatter, + gboolean mark_citations) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + if (mark_citations) + E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags |= + CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; + else + E_MAIL_FORMATTER_GET_CLASS (formatter)->text_html_flags &= + ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; + + g_object_notify (G_OBJECT (formatter), "mark-citations"); +} + +gboolean +e_mail_formatter_get_show_sender_photo (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE); + + return formatter->priv->show_sender_photo; +} + +void +e_mail_formatter_set_show_sender_photo (EMailFormatter *formatter, + gboolean show_sender_photo) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + if (formatter->priv->show_sender_photo == show_sender_photo) + return; + + formatter->priv->show_sender_photo = show_sender_photo; + + g_object_notify (G_OBJECT (formatter), "show-sender-photo"); +} + +gboolean +e_mail_formatter_get_show_real_date (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE); + + return formatter->priv->show_real_date; +} + +void +e_mail_formatter_set_show_real_date (EMailFormatter *formatter, + gboolean show_real_date) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + if (formatter->priv->show_real_date == show_real_date) + return; + + formatter->priv->show_real_date = show_real_date; + + g_object_notify (G_OBJECT (formatter), "show-real-date"); +} + +gboolean +e_mail_formatter_get_animate_images (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), FALSE); + + return formatter->priv->animate_images; +} + +void +e_mail_formatter_set_animate_images (EMailFormatter *formatter, + gboolean animate_images) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + if (formatter->priv->animate_images == animate_images) + return; + + formatter->priv->animate_images = animate_images; + + g_object_notify (G_OBJECT (formatter), "animate-images"); +} + +const gchar * +e_mail_formatter_get_charset (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + + return formatter->priv->charset; +} + +gchar * +e_mail_formatter_dup_charset (EMailFormatter *formatter) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + + g_mutex_lock (&formatter->priv->property_lock); + + protected = e_mail_formatter_get_charset (formatter); + duplicate = g_strdup (protected); + + g_mutex_unlock (&formatter->priv->property_lock); + + return duplicate; +} + +void +e_mail_formatter_set_charset (EMailFormatter *formatter, + const gchar *charset) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + + g_mutex_lock (&formatter->priv->property_lock); + + if (g_strcmp0 (formatter->priv->charset, charset) == 0) { + g_mutex_unlock (&formatter->priv->property_lock); + return; + } + + g_free (formatter->priv->charset); + formatter->priv->charset = g_strdup (charset); + + g_mutex_unlock (&formatter->priv->property_lock); + + g_object_notify (G_OBJECT (formatter), "charset"); +} + +const gchar * +e_mail_formatter_get_default_charset (EMailFormatter *formatter) +{ + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + + return formatter->priv->default_charset; +} + +gchar * +e_mail_formatter_dup_default_charset (EMailFormatter *formatter) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + + g_mutex_lock (&formatter->priv->property_lock); + + protected = e_mail_formatter_get_default_charset (formatter); + duplicate = g_strdup (protected); + + g_mutex_unlock (&formatter->priv->property_lock); + + return duplicate; +} + +void +e_mail_formatter_set_default_charset (EMailFormatter *formatter, + const gchar *default_charset) +{ + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (default_charset && *default_charset); + + g_mutex_lock (&formatter->priv->property_lock); + + if (g_strcmp0 (formatter->priv->default_charset, default_charset) == 0) { + g_mutex_unlock (&formatter->priv->property_lock); + return; + } + + g_free (formatter->priv->default_charset); + formatter->priv->default_charset = g_strdup (default_charset); + + g_mutex_unlock (&formatter->priv->property_lock); + + g_object_notify (G_OBJECT (formatter), "default-charset"); +} + diff --git a/em-format/e-mail-formatter.h b/em-format/e-mail-formatter.h new file mode 100644 index 0000000000..9405789b72 --- /dev/null +++ b/em-format/e-mail-formatter.h @@ -0,0 +1,192 @@ +/* + * 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 <gdk/gdk.h> +#include <libemail-engine/e-mail-enums.h> + +#include <em-format/e-mail-extension-registry.h> +#include <em-format/e-mail-formatter-enums.h> +#include <em-format/e-mail-part-list.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 struct _EMailFormatter EMailFormatter; +typedef struct _EMailFormatterClass EMailFormatterClass; +typedef struct _EMailFormatterPrivate EMailFormatterPrivate; +typedef struct _EMailFormatterContext EMailFormatterContext; + +struct _EMailFormatterContext { + EMailPartList *part_list; + EMailFormatterMode mode; + EMailFormatterHeaderFlags flags; + + gchar *uri; +}; + +struct _EMailFormatter { + GObject parent; + EMailFormatterPrivate *priv; +}; + +struct _EMailFormatterClass { + GObjectClass parent_class; + + EMailFormatterExtensionRegistry *extension_registry; + CamelMimeFilterToHTMLFlags text_html_flags; + + /* Colors should apply globally */ + GdkRGBA colors[E_MAIL_FORMATTER_NUM_COLOR_TYPES]; + + /* sizeof(EMailFormatterContext) or some derivative struct */ + gsize context_size; + + void (*run) (EMailFormatter *formatter, + EMailFormatterContext *context, + CamelStream *stream, + GCancellable *cancellable); + + void (*update_style) (EMailFormatter *formatter, + GtkStateFlags state); + + /* Signals */ + void (*need_redraw) (EMailFormatter *formatter); +}; + +GType e_mail_formatter_get_type (void); + +EMailFormatter * + e_mail_formatter_new (void); + +void e_mail_formatter_format_sync (EMailFormatter *formatter, + EMailPartList *part_list, + CamelStream *stream, + EMailFormatterHeaderFlags flags, + EMailFormatterMode mode, + GCancellable *cancellable); + +void e_mail_formatter_format (EMailFormatter *formatter, + EMailPartList *part_list, + CamelStream *stream, + EMailFormatterHeaderFlags flags, + EMailFormatterMode mode, + GAsyncReadyCallback callback, + GCancellable *cancellable, + gpointer user_data); + +gboolean e_mail_formatter_format_finish (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); + +CamelMimeFilterToHTMLFlags + e_mail_formatter_get_text_format_flags + (EMailFormatter *formatter); + +const GdkRGBA * e_mail_formatter_get_color (EMailFormatter *formatter, + EMailFormatterColor type); +void e_mail_formatter_set_color (EMailFormatter *formatter, + EMailFormatterColor type, + const GdkRGBA *color); +void e_mail_formatter_update_style (EMailFormatter *formatter, + GtkStateFlags 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_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); +gchar * e_mail_formatter_dup_charset (EMailFormatter *formatter); +void e_mail_formatter_set_charset (EMailFormatter *formatter, + const gchar *charset); + +const gchar * e_mail_formatter_get_default_charset + (EMailFormatter *formatter); +gchar * e_mail_formatter_dup_default_charset + (EMailFormatter *formatter); +void e_mail_formatter_set_default_charset + (EMailFormatter *formatter, + const gchar *charset); + +G_END_DECLS + +#endif /* E_MAIL_FORMATTER_H_ */ diff --git a/em-format/e-mail-inline-filter.c b/em-format/e-mail-inline-filter.c new file mode 100644 index 0000000000..538e884e8e --- /dev/null +++ b/em-format/e-mail-inline-filter.c @@ -0,0 +1,496 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Michael Zucchi <notzed@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "e-mail-inline-filter.h" +#include "e-mail-part-utils.h" + +#define d(x) + +G_DEFINE_TYPE (EMailInlineFilter, e_mail_inline_filter, CAMEL_TYPE_MIME_FILTER) + +enum { + EMIF_PLAIN, + EMIF_BINHEX, + EMIF_POSTSCRIPT, + EMIF_PGPSIGNED, + EMIF_PGPENCRYPTED +}; + +static const struct { + const gchar *type; + const gchar *subtype; + CamelTransferEncoding encoding; + guint plain : 1; +} emif_types[] = { + { "text", "plain", + CAMEL_TRANSFER_ENCODING_DEFAULT, 1 }, + + { "application", "mac-binhex40", + CAMEL_TRANSFER_ENCODING_7BIT, 0 }, + + { "application", "postscript", + CAMEL_TRANSFER_ENCODING_7BIT, 0 }, + + { "application", "x-inlinepgp-signed", + CAMEL_TRANSFER_ENCODING_DEFAULT, 0 }, + + { "application", "x-inlinepgp-encrypted", + CAMEL_TRANSFER_ENCODING_DEFAULT, 0 } +}; + +static CamelMimePart * +construct_part_from_stream (CamelStream *mem, + const GByteArray *data) +{ + CamelMimePart *part = NULL; + CamelMimeParser *parser; + + g_return_val_if_fail (mem != NULL, NULL); + g_return_val_if_fail (data != NULL, NULL); + + if (data->len <= 13 || g_ascii_strncasecmp ((const gchar *) data->data, "Content-Type:", 13) != 0) + return NULL; + + parser = camel_mime_parser_new (); + camel_mime_parser_scan_from (parser, FALSE); + camel_mime_parser_scan_pre_from (parser, FALSE); + + if (camel_mime_parser_init_with_stream (parser, mem, NULL) != -1) { + part = camel_mime_part_new (); + if (!camel_mime_part_construct_from_parser_sync (part, parser, NULL, NULL)) { + g_object_unref (part); + part = NULL; + } + } + + g_object_unref (parser); + + return part; +} + +static void +inline_filter_add_part (EMailInlineFilter *emif, + const gchar *data, + gint len) +{ + CamelTransferEncoding encoding; + CamelContentType *content_type; + CamelDataWrapper *dw; + const gchar *mimetype; + CamelMimePart *part; + CamelStream *mem; + gchar *type; + + if (emif->state == EMIF_PLAIN || emif->state == EMIF_PGPSIGNED || emif->state == EMIF_PGPENCRYPTED) + encoding = emif->base_encoding; + else + encoding = emif_types[emif->state].encoding; + + g_byte_array_append (emif->data, (guchar *) data, len); + /* check the part will actually have content */ + if (emif->data->len <= 0) { + return; + } + + mem = camel_stream_mem_new_with_byte_array (emif->data); + part = construct_part_from_stream (mem, emif->data); + if (part) { + g_object_unref (mem); + emif->data = g_byte_array_new (); + g_free (emif->filename); + emif->filename = NULL; + + emif->parts = g_slist_append (emif->parts, part); + emif->found_any = TRUE; + + return; + } + + emif->data = g_byte_array_new (); + g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL); + + dw = camel_data_wrapper_new (); + if (encoding == emif->base_encoding && (encoding == CAMEL_TRANSFER_ENCODING_BASE64 || encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)) { + CamelMimeFilter *enc_filter = camel_mime_filter_basic_new (encoding == CAMEL_TRANSFER_ENCODING_BASE64 ? CAMEL_MIME_FILTER_BASIC_BASE64_ENC : CAMEL_MIME_FILTER_BASIC_QP_ENC); + CamelStream *filter_stream; + + filter_stream = camel_stream_filter_new (mem); + camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), enc_filter); + + /* properly encode content */ + camel_data_wrapper_construct_from_stream_sync ( + dw, filter_stream, NULL, NULL); + + g_object_unref (enc_filter); + g_object_unref (filter_stream); + } else { + camel_data_wrapper_construct_from_stream_sync ( + dw, mem, NULL, NULL); + } + g_object_unref (mem); + + if (emif_types[emif->state].plain && emif->base_type) { + /* create a copy */ + type = camel_content_type_format (emif->base_type); + content_type = camel_content_type_decode (type); + g_free (type); + } else { + /* we want to preserve all params */ + type = camel_content_type_format (emif->base_type); + content_type = camel_content_type_decode (type); + g_free (type); + + g_free (content_type->type); + g_free (content_type->subtype); + content_type->type = g_strdup (emif_types[emif->state].type); + content_type->subtype = g_strdup (emif_types[emif->state].subtype); + } + + camel_data_wrapper_set_mime_type_field (dw, content_type); + camel_content_type_unref (content_type); + dw->encoding = encoding; + + part = camel_mime_part_new (); + camel_medium_set_content ((CamelMedium *) part, dw); + camel_mime_part_set_encoding (part, encoding); + g_object_unref (dw); + + if (emif->filename) + camel_mime_part_set_filename (part, emif->filename); + + /* 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 = 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); + if (emif->filename) + camel_mime_part_set_filename (part, emif->filename); + } + + g_free (emif->filename); + emif->filename = NULL; + + emif->parts = g_slist_append (emif->parts, part); +} + +static gboolean +newline_or_whitespace_follows (const gchar *str, + guint len, + guint skip_first) +{ + if (len <= skip_first) + return len == skip_first; + + str += skip_first; + len -= skip_first; + + while (len > 0 && *str != '\n') { + if (!*str) + return TRUE; + + if (!camel_mime_is_lwsp (*str)) + return FALSE; + + len--; + str++; + } + + return len == 0 || *str == '\n'; +} + +static gint +inline_filter_scan (CamelMimeFilter *f, + gchar *in, + gsize len, + gint final) +{ + EMailInlineFilter *emif = (EMailInlineFilter *) f; + gchar *inptr = in, *inend = in + len; + gchar *data_start = in; + gchar *start = in; + + while (inptr < inend) { + gint rest_len; + gboolean set_null_byte = FALSE; + + start = inptr; + + while (inptr < inend && *inptr != '\n') + inptr++; + + if (inptr == inend && start == inptr) { + if (!final) { + camel_mime_filter_backup (f, start, inend - start); + inend = start; + } + break; + } + + rest_len = inend - start; + if (inptr < inend) { + *inptr++ = 0; + set_null_byte = TRUE; + } + + #define restore_inptr() G_STMT_START { if (set_null_byte) inptr[-1] = '\n'; } G_STMT_END + + switch (emif->state) { + case EMIF_PLAIN: + if (rest_len >= 45 && strncmp (start, "(This file must be converted with BinHex 4.0)", 45) == 0) { + restore_inptr (); + inline_filter_add_part (emif, data_start, start - data_start); + data_start = start; + emif->state = EMIF_BINHEX; + } else if (rest_len >= 11 && strncmp (start, "%!PS-Adobe-", 11) == 0) { + restore_inptr (); + inline_filter_add_part (emif, data_start, start - data_start); + data_start = start; + emif->state = EMIF_POSTSCRIPT; + } else if (rest_len >= 34 && strncmp (start, "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0 && + newline_or_whitespace_follows (start, rest_len, 34)) { + restore_inptr (); + inline_filter_add_part (emif, data_start, start - data_start); + data_start = start; + emif->state = EMIF_PGPSIGNED; + } else if (rest_len >= 27 && strncmp (start, "-----BEGIN PGP MESSAGE-----", 27) == 0 && + newline_or_whitespace_follows (start, rest_len, 27)) { + restore_inptr (); + inline_filter_add_part (emif, data_start, start - data_start); + data_start = start; + emif->state = EMIF_PGPENCRYPTED; + } + + break; + case EMIF_BINHEX: + if (inptr > (start + 1) && inptr[-2] == ':') { + restore_inptr (); + inline_filter_add_part (emif, data_start, inptr - data_start); + data_start = inptr; + emif->state = EMIF_PLAIN; + emif->found_any = TRUE; + } + break; + case EMIF_POSTSCRIPT: + if (rest_len >= 5 && strncmp (start, "%%EOF", 5) == 0) { + restore_inptr (); + inline_filter_add_part (emif, data_start, inptr - data_start); + data_start = inptr; + emif->state = EMIF_PLAIN; + emif->found_any = TRUE; + } + break; + case EMIF_PGPSIGNED: + if (rest_len >= 27 && strncmp (start, "-----END PGP SIGNATURE-----", 27) == 0 && + newline_or_whitespace_follows (start, rest_len, 27)) { + restore_inptr (); + inline_filter_add_part (emif, data_start, inptr - data_start); + data_start = inptr; + emif->state = EMIF_PLAIN; + emif->found_any = TRUE; + } + break; + case EMIF_PGPENCRYPTED: + if (rest_len >= 25 && strncmp (start, "-----END PGP MESSAGE-----", 25) == 0 && + newline_or_whitespace_follows (start, rest_len, 25)) { + restore_inptr (); + inline_filter_add_part (emif, data_start, inptr - data_start); + data_start = inptr; + emif->state = EMIF_PLAIN; + emif->found_any = TRUE; + } + break; + } + + restore_inptr (); + + #undef restore_inptr + } + + if (final) { + /* always stop as plain, especially when not read those tags fully */ + emif->state = EMIF_PLAIN; + + inline_filter_add_part (emif, data_start, inend - data_start); + } else if (start > data_start) { + /* backup the last line, in case the tag is divided within buffers */ + camel_mime_filter_backup (f, start, inend - start); + g_byte_array_append (emif->data, (guchar *) data_start, start - data_start); + } else { + g_byte_array_append (emif->data, (guchar *) data_start, inend - data_start); + } + + return 0; +} + +static void +inline_filter_finalize (GObject *object) +{ + EMailInlineFilter *emif = E_MAIL_INLINE_FILTER (object); + + if (emif->base_type) + camel_content_type_unref (emif->base_type); + + camel_mime_filter_reset (CAMEL_MIME_FILTER (object)); + g_byte_array_free (emif->data, TRUE); + g_free (emif->filename); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_inline_filter_parent_class)->finalize (object); +} + +static void +inline_filter_filter (CamelMimeFilter *filter, + const gchar *in, + gsize len, + gsize prespace, + gchar **out, + gsize *outlen, + gsize *outprespace) +{ + inline_filter_scan (filter, (gchar *) in, len, FALSE); + + *out = (gchar *)in; + *outlen = len; + *outprespace = prespace; +} + +static void +inline_filter_complete (CamelMimeFilter *filter, + const gchar *in, + gsize len, + gsize prespace, + gchar **out, + gsize *outlen, + gsize *outprespace) +{ + inline_filter_scan (filter, (gchar *) in, len, TRUE); + + *out = (gchar *)in; + *outlen = len; + *outprespace = prespace; +} + +static void +inline_filter_reset (CamelMimeFilter *filter) +{ + EMailInlineFilter *emif = E_MAIL_INLINE_FILTER (filter); + GSList *l; + + l = emif->parts; + while (l) { + GSList *n = l->next; + + g_object_unref (l->data); + g_slist_free_1 (l); + + l = n; + } + emif->parts = NULL; + g_byte_array_set_size (emif->data, 0); + emif->found_any = FALSE; +} + +static void +e_mail_inline_filter_class_init (EMailInlineFilterClass *class) +{ + GObjectClass *object_class; + CamelMimeFilterClass *mime_filter_class; + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = inline_filter_finalize; + + mime_filter_class = CAMEL_MIME_FILTER_CLASS (class); + mime_filter_class->filter = inline_filter_filter; + mime_filter_class->complete = inline_filter_complete; + mime_filter_class->reset = inline_filter_reset; +} + +static void +e_mail_inline_filter_init (EMailInlineFilter *emif) +{ + emif->data = g_byte_array_new (); + emif->found_any = FALSE; +} + +/** + * em_inline_filter_new: + * @base_encoding: The base transfer-encoding of the + * raw data being processed. + * @base_type: The base content-type of the raw data, should always be + * text/plain. + * @filename: Filename of the part, or NULL + * + * Create a filter which will scan a (text) stream for + * embedded parts. You can then retrieve the contents + * as a CamelMultipart object. + * + * Return value: + **/ +EMailInlineFilter * +e_mail_inline_filter_new (CamelTransferEncoding base_encoding, + CamelContentType *base_type, + const gchar *filename) +{ + EMailInlineFilter *emif; + + emif = g_object_new (E_TYPE_MAIL_INLINE_FILTER, NULL); + emif->base_encoding = base_encoding; + if (base_type) { + emif->base_type = base_type; + camel_content_type_ref (emif->base_type); + } + + if (filename && *filename) + emif->filename = g_strdup (filename); + + return emif; +} + +CamelMultipart * +e_mail_inline_filter_get_multipart (EMailInlineFilter *emif) +{ + GSList *l = emif->parts; + CamelMultipart *mp; + + mp = camel_multipart_new (); + while (l) { + camel_multipart_add_part (mp, l->data); + l = l->next; + } + + return mp; +} + +gboolean +e_mail_inline_filter_found_any (EMailInlineFilter *emif) +{ + g_return_val_if_fail (emif != NULL, FALSE); + + return emif->found_any; +} diff --git a/em-format/e-mail-inline-filter.h b/em-format/e-mail-inline-filter.h new file mode 100644 index 0000000000..df4c2bc0f9 --- /dev/null +++ b/em-format/e-mail-inline-filter.h @@ -0,0 +1,83 @@ +/* + * + * 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 E_MAIL_INLINE_FILTER_H +#define E_MAIL_INLINE_FILTER_H + +#include <camel/camel.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_INLINE_FILTER \ + (e_mail_inline_filter_get_type ()) +#define E_MAIL_INLINE_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_INLINE_FILTER, EMailInlineFilter)) +#define E_MAIL_INLINE_FILTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_INLINE_FILTER, EMailInlineFilterClass)) +#define E_IS_MAIL_INLINE_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_INLINE_FILTER)) +#define E_IS_MAIL_INLINE_FILTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_INLINE_FILTER)) +#define E_MAIL_INLINE_FILTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_INLINE_FILTER, EMailInlineFilterClass)) + +G_BEGIN_DECLS + +typedef struct _EMailInlineFilter EMailInlineFilter; +typedef struct _EMailInlineFilterClass EMailInlineFilterClass; + +struct _EMailInlineFilter { + CamelMimeFilter filter; + + gint state; + + CamelTransferEncoding base_encoding; + CamelContentType *base_type; + + GByteArray *data; + gchar *filename; + GSList *parts; + + gboolean found_any; +}; + +struct _EMailInlineFilterClass { + CamelMimeFilterClass filter_class; +}; + +GType e_mail_inline_filter_get_type (void); +EMailInlineFilter * + e_mail_inline_filter_new (CamelTransferEncoding base_encoding, + CamelContentType *type, + const gchar *filename); +CamelMultipart *e_mail_inline_filter_get_multipart + (EMailInlineFilter *emif); +gboolean e_mail_inline_filter_found_any (EMailInlineFilter *emif); + +G_END_DECLS + +#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..73c86a7a57 --- /dev/null +++ b/em-format/e-mail-parser-application-mbox.c @@ -0,0 +1,173 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserApplicationMBox; +typedef EMailParserExtensionClass EMailParserApplicationMBoxClass; + +GType e_mail_parser_application_mbox_get_type (void); + +G_DEFINE_TYPE ( + EMailParserApplicationMBox, + e_mail_parser_application_mbox, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/mbox", + NULL +}; + +static gboolean +empe_app_mbox_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMimeParser *mime_parser; + CamelStream *mem_stream; + camel_mime_parser_state_t state; + gint old_len; + gint messages; + GError *error = 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); + + camel_mime_parser_init_with_stream (mime_parser, mem_stream, &error); + if (error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Error parsing MBOX part: %s"), + error->message); + g_object_unref (mem_stream); + g_object_unref (mime_parser); + g_error_free (error); + return TRUE; + } + + 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); + + while (state == CAMEL_MIME_PARSER_STATE_FROM) { + GQueue work_queue = G_QUEUE_INIT; + CamelMimeMessage *message; + CamelMimePart *opart; + + 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); + + opart = camel_mime_part_new (); + camel_medium_set_content (CAMEL_MEDIUM (opart), CAMEL_DATA_WRAPPER (message)); + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (opart), "message/rfc822"); + + e_mail_parser_parse_part_as ( + parser, opart, part_id, "message/rfc822", + cancellable, &work_queue); + + /* Wrap every message as attachment */ + e_mail_parser_wrap_as_attachment ( + parser, opart, part_id, &work_queue); + + /* Inline all messages in mbox */ + if (!g_queue_is_empty (&work_queue)) { + EMailPart *p = g_queue_peek_head (&work_queue); + + p->force_inline = TRUE; + } + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, old_len); + + g_object_unref (message); + g_object_unref (opart); + + /* 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 TRUE; +} + +static void +e_mail_parser_application_mbox_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->flags = + E_MAIL_PARSER_EXTENSION_INLINE | + E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; + class->parse = empe_app_mbox_parse; +} + +static void +e_mail_parser_application_mbox_init (EMailParserExtension *extension) +{ +} 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..e84e1e948e --- /dev/null +++ b/em-format/e-mail-parser-application-smime.c @@ -0,0 +1,161 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserApplicationSMIME; +typedef EMailParserExtensionClass EMailParserApplicationSMIMEClass; + +GType e_mail_parser_application_smime_get_type (void); + +G_DEFINE_TYPE ( + EMailParserApplicationSMIME, + e_mail_parser_application_smime, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/xpkcs7mime", + "application/x-pkcs7-mime", + "application/pkcs7-mime", + "application/pkcs7-signature", + "application/xpkcs7-signature", + "application/x-pkcs7-signature", + NULL +}; + +static gboolean +empe_app_smime_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelCipherContext *context; + CamelMimePart *opart; + CamelCipherValidity *valid; + GError *local_error = NULL; + CamelContentType *ct; + + 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") || + camel_content_type_is (ct, "application", "x-pkcs7-signature")) { + return TRUE; + } + + 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 (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse S/MIME message: %s"), + local_error->message); + g_error_free (local_error); + + } else { + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + gint len = part_id->len; + + g_string_append (part_id, ".encrypted"); + + e_mail_parser_parse_part ( + parser, opart, part_id, cancellable, &work_queue); + + g_string_truncate (part_id, len); + + head = g_queue_peek_head_link (&work_queue); + + /* Update validity flags of all the involved subp-arts */ + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_SMIME); + } + + e_queue_transfer (&work_queue, out_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 (!e_mail_part_is_secured (opart)) { + EMailPart *mail_part; + + g_string_append (part_id, ".encrypted.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_SMIME); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + } + + g_object_unref (opart); + g_object_unref (context); + + return TRUE; +} + +static void +e_mail_parser_application_smime_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->flags = E_MAIL_PARSER_EXTENSION_INLINE; + class->parse = empe_app_smime_parse; +} + +static void +e_mail_parser_application_smime_init (EMailParserExtension *extension) +{ +} 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..063221164c --- /dev/null +++ b/em-format/e-mail-parser-attachment-bar.c @@ -0,0 +1,79 @@ +/* + * 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-part-attachment-bar.h" + +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" + +typedef EMailParserExtension EMailParserAttachmentBar; +typedef EMailParserExtensionClass EMailParserAttachmentBarClass; + +GType e_mail_parser_attachment_bar_get_type (void); + +G_DEFINE_TYPE ( + EMailParserAttachmentBar, + e_mail_parser_attachment_bar, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + E_MAIL_PART_ATTACHMENT_BAR_MIME_TYPE, + NULL +}; + +static gboolean +empe_attachment_bar_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + EMailPart *mail_part; + gint len; + + len = part_id->len; + g_string_append (part_id, ".attachment-bar"); + mail_part = e_mail_part_attachment_bar_new (part, part_id->str); + e_mail_part_set_mime_type (mail_part, parser_mime_types[0]); + g_string_truncate (part_id, len); + + g_queue_push_tail (out_mail_parts, mail_part); + + return TRUE; +} + +static void +e_mail_parser_attachment_bar_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_attachment_bar_parse; +} + +static void +e_mail_parser_attachment_bar_init (EMailParserExtension *extension) +{ +} diff --git a/em-format/e-mail-parser-extension.c b/em-format/e-mail-parser-extension.c new file mode 100644 index 0000000000..68aac73cbf --- /dev/null +++ b/em-format/e-mail-parser-extension.c @@ -0,0 +1,93 @@ +/* + * 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 "e-mail-parser-extension.h" + +G_DEFINE_ABSTRACT_TYPE ( + EMailParserExtension, + e_mail_parser_extension, + G_TYPE_OBJECT) + +static void +e_mail_parser_extension_class_init (EMailParserExtensionClass *class) +{ + class->priority = G_PRIORITY_DEFAULT; +} + +static void +e_mail_parser_extension_init (EMailParserExtension *extension) +{ +} + +/** + * 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. + * @cancellable: (allow-none) A #GCancellable + * @out_mail_parts: a #GQueue to deposit #EMailPart instances + * + * A virtual function reimplemented in all mail parser extensions. The function + * decodes and parses the @mime_part, appending one or more #EMailPart<!-//>s + * to the @out_mail_parts queue. + * + * When the function is unable to parse the @mime_part (either because it's + * broken or because it is a different MIME type then the extension is + * specialized for), the function will return %FALSE to indicate to 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 parts are appeded to @out_mail_parts in the correct order. + * + * part1.rfc822.plain_text + * part1.rfc822.attachment + * part2.image + * + * Implementation of this function must be thread-safe. + * + * Returns: %TRUE if the @mime_part was handled (even if no + * #EMailPart<!-//>s were added to @out_mail_parts), or + * %FALSE if the @mime_part was not handled + */ +gboolean +e_mail_parser_extension_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *mime_part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + EMailParserExtensionClass *class; + + g_return_val_if_fail (E_IS_MAIL_PARSER_EXTENSION (extension), FALSE); + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), FALSE); + + class = E_MAIL_PARSER_EXTENSION_GET_CLASS (extension); + g_return_val_if_fail (class->parse != NULL, FALSE); + + /* Check for cancellation before calling the method. */ + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + return class->parse ( + extension, parser, mime_part, part_id, + cancellable, out_mail_parts); +} + diff --git a/em-format/e-mail-parser-extension.h b/em-format/e-mail-parser-extension.h new file mode 100644 index 0000000000..840f84dc3c --- /dev/null +++ b/em-format/e-mail-parser-extension.h @@ -0,0 +1,96 @@ +/* + * 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 <camel/camel.h> +#include <em-format/e-mail-parser.h> +#include <em-format/e-mail-formatter-enums.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_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PARSER_EXTENSION, EMailParserExtensionClass)) +#define E_IS_MAIL_PARSER_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION)) +#define E_IS_MAIL_PARSER_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PARSER_EXTENSION)) +#define E_MAIL_PARSER_EXTENSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PARSER_EXTENSION, EMailParserExtensionClass)) + +G_BEGIN_DECLS + +typedef struct _EMailParserExtension EMailParserExtension; +typedef struct _EMailParserExtensionClass EMailParserExtensionClass; +typedef struct _EMailParserExtensionPrivate EMailParserExtensionPrivate; + +/** + * EMailParserExtension: + * + * The #EMailParserExtension is an abstract interface for all extensions for + * #EMailParser. + */ +struct _EMailParserExtension { + GObject parent; + EMailParserExtensionPrivate *priv; +}; + +struct _EMailParserExtensionClass { + GObjectClass parent_class; + + /* This is a NULL-terminated array of supported MIME types. + * The MIME types can be exact (e.g. "text/plain") or use a + * wildcard (e.g. "text/ *"). */ + const gchar **mime_types; + + /* This is used to prioritize extensions with identical MIME + * types. Lower values win. Defaults to G_PRIORITY_DEFAULT. */ + gint priority; + + /* See the flag descriptions above. */ + EMailParserExtensionFlags flags; + + gboolean (*parse) (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *mime_part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts); +}; + +GType e_mail_parser_extension_get_type + (void) G_GNUC_CONST; +gboolean e_mail_parser_extension_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *mime_part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts); + +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..76cad8b22a --- /dev/null +++ b/em-format/e-mail-parser-headers.c @@ -0,0 +1,80 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> +#include <libemail-engine/e-mail-utils.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-headers.h" + +typedef EMailParserExtension EMailParserHeaders; +typedef EMailParserExtensionClass EMailParserHeadersClass; + +GType e_mail_parser_headers_get_type (void); + +G_DEFINE_TYPE ( + EMailParserHeaders, + e_mail_parser_headers, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + E_MAIL_PART_HEADERS_MIME_TYPE, + NULL +}; + +static gboolean +empe_headers_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + EMailPart *mail_part; + gint len; + + len = part_id->len; + g_string_append (part_id, ".headers"); + + mail_part = e_mail_part_headers_new (part, part_id->str); + g_queue_push_tail (out_mail_parts, mail_part); + + g_string_truncate (part_id, len); + + return TRUE; +} + +static void +e_mail_parser_headers_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_headers_parse; +} + +static void +e_mail_parser_headers_init (EMailParserExtension *extension) +{ +} diff --git a/em-format/e-mail-parser-image.c b/em-format/e-mail-parser-image.c new file mode 100644 index 0000000000..807e72430e --- /dev/null +++ b/em-format/e-mail-parser-image.c @@ -0,0 +1,102 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-image.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserImage; +typedef EMailParserExtensionClass EMailParserImageClass; + +GType e_mail_parser_image_get_type (void); + +G_DEFINE_TYPE ( + EMailParserImage, + e_mail_parser_image, + E_TYPE_MAIL_PARSER_EXTENSION) + +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 gboolean +empe_image_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + gint len; + + len = part_id->len; + g_string_append (part_id, ".image"); + + mail_part = e_mail_part_image_new (part, part_id->str); + + g_string_truncate (part_id, len); + + g_queue_push_tail (&work_queue, mail_part); + + if (!mail_part->is_hidden) + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, &work_queue); + + e_queue_transfer (&work_queue, out_mail_parts); + + return TRUE; +} + +static void +e_mail_parser_image_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_image_parse; +} + +static void +e_mail_parser_image_init (EMailParserExtension *extension) +{ +} 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..ae008dca02 --- /dev/null +++ b/em-format/e-mail-parser-inlinepgp-encrypted.c @@ -0,0 +1,188 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserInlinePGPEncrypted; +typedef EMailParserExtensionClass EMailParserInlinePGPEncryptedClass; + +GType e_mail_parser_inline_pgp_encrypted_get_type (void); + +G_DEFINE_TYPE ( + EMailParserInlinePGPEncrypted, + e_mail_parser_inline_pgp_encrypted, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/x-inlinepgp-encrypted", + NULL +}; + +static gboolean +empe_inlinepgp_encrypted_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelCipherContext *cipher; + CamelCipherValidity *valid; + CamelMimePart *opart; + CamelDataWrapper *dw; + gchar *mime_type; + gint len; + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + GError *local_error = NULL; + + if (g_cancellable_is_cancelled (cancellable) || + /* avoid recursion */ + (part_id->str && part_id->len > 20 && g_str_has_suffix (part_id->str, ".inlinepgp_encrypted"))) + return FALSE; + + 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 (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse PGP message: %s"), + local_error->message); + g_error_free (local_error); + + e_mail_parser_parse_part_as ( + parser, + part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + + g_object_unref (cipher); + g_object_unref (opart); + + return TRUE; + } + + 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; + + snoop = e_mail_part_snoop_type (opart); + + if (snoop != NULL) { + camel_data_wrapper_set_mime_type (dw, snoop); + + /* Set the MIME type on the 'opart' itself as well. + * If it's "text/plain", then we want the TextPlain + * parser extension to treat it as "text/plain" and + * NOT wrap it as an attachment. */ + camel_data_wrapper_set_mime_type ( + CAMEL_DATA_WRAPPER (opart), 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"); + + e_mail_parser_parse_part_as ( + parser, opart, part_id, + camel_data_wrapper_get_mime_type (dw), + cancellable, &work_queue); + + g_string_truncate (part_id, len); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + e_queue_transfer (&work_queue, out_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 (!e_mail_part_is_secured (opart)) { + EMailPart *mail_part; + + g_string_append (part_id, ".inlinepgp_encrypted.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + /* Clean Up */ + camel_cipher_validity_free (valid); + g_object_unref (opart); + g_object_unref (cipher); + + return TRUE; +} + +static void +e_mail_parser_inline_pgp_encrypted_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_inlinepgp_encrypted_parse; +} + +static void +e_mail_parser_inline_pgp_encrypted_init (EMailParserExtension *extension) +{ +} 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..37cf5a2dcf --- /dev/null +++ b/em-format/e-mail-parser-inlinepgp-signed.c @@ -0,0 +1,201 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserInlinePGPSigned; +typedef EMailParserExtensionClass EMailParserInlinePGPSignedClass; + +GType e_mail_parser_inline_pgp_signed_get_type (void); + +G_DEFINE_TYPE ( + EMailParserInlinePGPSigned, + e_mail_parser_inline_pgp_signed, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/x-inlinepgp-signed", + NULL +}; + +static gboolean +empe_inlinepgp_signed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelStream *filtered_stream; + CamelMimeFilterPgp *pgp_filter; + CamelContentType *content_type; + CamelCipherContext *cipher; + CamelCipherValidity *valid; + CamelDataWrapper *dw; + CamelMimePart *opart; + CamelStream *ostream; + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + gchar *type; + gint len; + GError *local_error = NULL; + GByteArray *ba; + + if (g_cancellable_is_cancelled (cancellable) || + /* avoid recursion */ + (part_id->str && part_id->len > 17 && g_str_has_suffix (part_id->str, ".inlinepgp_signed"))) + return FALSE; + + 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 (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Error verifying signature: %s"), + local_error->message); + + g_error_free (local_error); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + + g_object_unref (cipher); + + return TRUE; + } + + /* 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"); + + e_mail_parser_parse_part ( + parser, opart, part_id, cancellable, &work_queue); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_SIGNED | + E_MAIL_PART_VALIDITY_PGP); + } + + e_queue_transfer (&work_queue, out_mail_parts); + + 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)) { + EMailPart *mail_part; + + g_string_append (part_id, ".inlinepgp_signed.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_SIGNED | + E_MAIL_PART_VALIDITY_PGP); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + /* Clean Up */ + camel_cipher_validity_free (valid); + g_object_unref (opart); + g_object_unref (ostream); + g_object_unref (cipher); + + return TRUE; +} + +static void +e_mail_parser_inline_pgp_signed_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_inlinepgp_signed_parse; +} + +static void +e_mail_parser_inline_pgp_signed_init (EMailParserExtension *extension) +{ +} 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..9b672c52ba --- /dev/null +++ b/em-format/e-mail-parser-message-deliverystatus.c @@ -0,0 +1,87 @@ +/* + * 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 <string.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" + +typedef EMailParserExtension EMailParserMessageDeliveryStatus; +typedef EMailParserExtensionClass EMailParserMessageDeliveryStatusClass; + +GType e_mail_parser_message_delivery_status_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMessageDeliveryStatus, + e_mail_parser_message_delivery_status, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "message/delivery-status", + "message/feedback-report", + "message/disposition-notification", + NULL +}; + +static gboolean +empe_msg_deliverystatus_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + gsize len; + + len = part_id->len; + g_string_append (part_id, ".delivery-status"); + mail_part = e_mail_part_new (part, part_id->str); + e_mail_part_set_mime_type (mail_part, "text/plain"); + + g_string_truncate (part_id, len); + + g_queue_push_tail (&work_queue, mail_part); + + /* The only reason for having a separate parser for + * message/delivery-status is to display the part as an attachment */ + e_mail_parser_wrap_as_attachment (parser, part, part_id, &work_queue); + + e_queue_transfer (&work_queue, out_mail_parts); + + return TRUE; +} + +static void +e_mail_parser_message_delivery_status_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_msg_deliverystatus_parse; +} + +static void +e_mail_parser_message_delivery_status_init (EMailParserExtension *extension) +{ +} 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..8668df5fba --- /dev/null +++ b/em-format/e-mail-parser-message-external.c @@ -0,0 +1,182 @@ +/* + * 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 <string.h> +#include <ctype.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" + +typedef EMailParserExtension EMailParserMessageExternal; +typedef EMailParserExtensionClass EMailParserMessageExternalClass; + +GType e_mail_parser_message_external_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMessageExternal, + e_mail_parser_message_external, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "message/external-body", + NULL +}; + +static gboolean +empe_msg_external_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + EMailPart *mail_part; + CamelMimePart *newpart; + CamelContentType *type; + const gchar *access_type; + const gchar *mime_type; + gchar *url = NULL, *desc = NULL; + gchar *content; + gint len; + + 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 = "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 = "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 = "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); + e_mail_part_set_mime_type (mail_part, mime_type); + g_string_truncate (part_id, len); + + g_queue_push_tail (out_mail_parts, mail_part); + + return TRUE; +} + +static void +e_mail_parser_message_external_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_msg_external_parse; +} + +static void +e_mail_parser_message_external_init (EMailParserExtension *extension) +{ +} 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..e9bfab15a3 --- /dev/null +++ b/em-format/e-mail-parser-message-rfc822.c @@ -0,0 +1,136 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-list.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMessageRFC822; +typedef EMailParserExtensionClass EMailParserMessageRFC822Class; + +GType e_mail_parser_message_rfc822_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMessageRFC822, + e_mail_parser_message_rfc822, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "message/rfc822", + "message/news", + NULL +}; + +static gboolean +empe_msg_rfc822_parse (EMailParserExtension *extension, + EMailParser *eparser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + 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); + e_mail_part_set_mime_type (mail_part, "message/rfc822"); + g_queue_push_tail (out_mail_parts, mail_part); + + /* Sometime the actual message is encapsulated in another + * CamelMimePart, sometimes the CamelMimePart itself 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); + } + + e_mail_parser_parse_part_as ( + eparser, message, part_id, + "application/vnd.evolution.message", + cancellable, out_mail_parts); + + 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; + g_queue_push_tail (out_mail_parts, mail_part); + + g_string_truncate (part_id, len); + + if (e_mail_part_is_attachment (message)) + e_mail_parser_wrap_as_attachment ( + eparser, message, part_id, out_mail_parts); + + return TRUE; +} + +static void +e_mail_parser_message_rfc822_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->flags = + E_MAIL_PARSER_EXTENSION_INLINE | + E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; + class->parse = empe_msg_rfc822_parse; +} + +static void +e_mail_parser_message_rfc822_init (EMailParserExtension *extension) +{ +} diff --git a/em-format/e-mail-parser-message.c b/em-format/e-mail-parser-message.c new file mode 100644 index 0000000000..609663201c --- /dev/null +++ b/em-format/e-mail-parser-message.c @@ -0,0 +1,130 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> +#include <libemail-engine/e-mail-utils.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMessage; +typedef EMailParserExtensionClass EMailParserMessageClass; + +GType e_mail_parser_message_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMessage, + e_mail_parser_message, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/vnd.evolution.message", + NULL +}; + +static gboolean +empe_message_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue work_queue = G_QUEUE_INIT; + CamelContentType *ct; + EMailPart *mail_part; + gchar *mime_type; + + /* Headers */ + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.headers", + cancellable, out_mail_parts); + + /* Attachment Bar */ + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.attachment-bar", + cancellable, out_mail_parts); + + ct = camel_mime_part_get_content_type (part); + mime_type = camel_content_type_simple (ct); + + if (mime_type && g_ascii_strcasecmp (mime_type, "message/rfc822") == 0) { + /* get mime type of the content of the message, + * instead of using a generic message/rfc822 */ + CamelDataWrapper *content; + + content = camel_medium_get_content (CAMEL_MEDIUM (part)); + if (content) { + ct = camel_data_wrapper_get_mime_type_field (content); + + g_free (mime_type); + mime_type = camel_content_type_simple (ct); + } + } + + /* Actual message body */ + + e_mail_parser_parse_part_as ( + parser, part, part_id, mime_type, + cancellable, &work_queue); + + /* If the EMailPart representing the message body is marked as an + * attachment, wrap it as such so it gets added to the attachment + * bar but also set the "force_inline" flag since it doesn't make + * sense to collapse the message body if we can render it. */ + mail_part = g_queue_peek_head (&work_queue); + if (mail_part != NULL) { + if (e_mail_part_get_is_attachment (mail_part)) { + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + mail_part->force_inline = TRUE; + } + } + + e_queue_transfer (&work_queue, out_mail_parts); + + g_free (mime_type); + + return TRUE; +} + +static void +e_mail_parser_message_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_message_parse; +} + +static void +e_mail_parser_message_init (EMailParserExtension *extension) +{ +} 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..1ecb7789ce --- /dev/null +++ b/em-format/e-mail-parser-multipart-alternative.c @@ -0,0 +1,158 @@ +/* + * 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 <string.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartAlternative; +typedef EMailParserExtensionClass EMailParserMultipartAlternativeClass; + +GType e_mail_parser_multipart_alternative_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartAlternative, + e_mail_parser_multipart_alternative, + E_TYPE_MAIL_PARSER_EXTENSION) + +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 gboolean +empe_mp_alternative_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMultipart *mp; + gint i, nparts, bestid = 0; + CamelMimePart *best = NULL; + EMailExtensionRegistry *reg; + + 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, out_mail_parts); + + /* 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 TRUE; + + /* 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); + + e_mail_parser_parse_part ( + parser, best, part_id, + cancellable, out_mail_parts); + + g_string_truncate (part_id, len); + } else { + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + } + + return TRUE; +} + +static void +e_mail_parser_multipart_alternative_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_alternative_parse; +} + +static void +e_mail_parser_multipart_alternative_init (EMailParserExtension *extension) +{ +} 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..bc79348783 --- /dev/null +++ b/em-format/e-mail-parser-multipart-appledouble.c @@ -0,0 +1,95 @@ +/* + * 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-parser-extension.h" + +typedef EMailParserExtension EMailParserMultipartAppleDouble; +typedef EMailParserExtensionClass EMailParserMultipartAppleDoubleClass; + +GType e_mail_parser_multipart_apple_double_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartAppleDouble, + e_mail_parser_multipart_apple_double, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/appledouble", + NULL +}; + +static gboolean +empe_mp_appledouble_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMultipart *mp; + + mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + + if (!CAMEL_IS_MULTIPART (mp)) { + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + } 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"); + + e_mail_parser_parse_part ( + parser, mime_part, part_id, + cancellable, out_mail_parts); + + g_string_truncate (part_id, len); + + } else { + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + } + } + + return TRUE; +} + +static void +e_mail_parser_multipart_apple_double_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_appledouble_parse; +} + +static void +e_mail_parser_multipart_apple_double_init (EMailParserExtension *extension) +{ +} 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..ef1090848c --- /dev/null +++ b/em-format/e-mail-parser-multipart-digest.c @@ -0,0 +1,135 @@ +/* + * 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 <string.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" + +typedef EMailParserExtension EMailParserMultipartDigest; +typedef EMailParserExtensionClass EMailParserMultipartDigestClass; + +GType e_mail_parser_multipart_digest_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartDigest, + e_mail_parser_multipart_digest, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/digest", + NULL +}; + +static gboolean +empe_mp_digest_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMultipart *mp; + gint i, nparts, len; + + 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, out_mail_parts); + + len = part_id->len; + nparts = camel_multipart_get_number (mp); + 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); + + e_mail_parser_parse_part_as ( + parser, subpart, part_id, cts, + cancellable, out_mail_parts); + + g_free (cts); + } else { + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + gboolean wrap_as_attachment; + + e_mail_parser_parse_part_as ( + parser, subpart, part_id, "message/rfc822", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + wrap_as_attachment = + (mail_part != NULL) && + !e_mail_part_get_is_attachment (mail_part); + + /* Force the message to be collapsable */ + if (wrap_as_attachment) + e_mail_parser_wrap_as_attachment ( + parser, subpart, part_id, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + /* Force the message to be expanded */ + if (mail_part != NULL) + mail_part->force_inline = TRUE; + + e_queue_transfer (&work_queue, out_mail_parts); + } + + g_string_truncate (part_id, len); + } + + return TRUE; +} + +static void +e_mail_parser_multipart_digest_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->flags = E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; + class->parse = empe_mp_digest_parse; +} + +static void +e_mail_parser_multipart_digest_init (EMailParserExtension *extension) +{ +} 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..85bcb9f41f --- /dev/null +++ b/em-format/e-mail-parser-multipart-encrypted.c @@ -0,0 +1,183 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <libedataserver/libedataserver.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartEncrypted; +typedef EMailParserExtensionClass EMailParserMultipartEncryptedClass; + +GType e_mail_parser_multipart_encrypted_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartEncrypted, + e_mail_parser_multipart_encrypted, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/encrypted", + NULL +}; + +static gboolean +empe_mp_encrypted_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelCipherContext *context; + const gchar *protocol; + CamelMimePart *opart; + CamelCipherValidity *valid; + CamelMultipartEncrypted *mpe; + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + GError *local_error = NULL; + gint len; + + mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part); + if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse MIME message. " + "Displaying as source.")); + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution/source", + cancellable, out_mail_parts); + + return TRUE; + } + + /* 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) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Unsupported encryption type for multipart/encrypted")); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + return TRUE; + } + + 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 (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse PGP/MIME message: %s"), + local_error->message); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + g_object_unref (opart); + g_object_unref (context); + g_error_free (local_error); + + return TRUE; + } + + len = part_id->len; + g_string_append (part_id, ".encrypted"); + + e_mail_parser_parse_part ( + parser, opart, part_id, cancellable, &work_queue); + + g_string_truncate (part_id, len); + + head = g_queue_peek_head_link (&work_queue); + + /* Update validity of all encrypted sub-parts */ + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + e_queue_transfer (&work_queue, out_mail_parts); + + /* 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)) { + EMailPart *mail_part; + + g_string_append (part_id, ".encrypted.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + + e_queue_transfer (&work_queue, out_mail_parts); + + 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 TRUE; +} + +static void +e_mail_parser_multipart_encrypted_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_encrypted_parse; +} + +static void +e_mail_parser_multipart_encrypted_init (EMailParserExtension *extension) +{ +} 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..6fb9ca787f --- /dev/null +++ b/em-format/e-mail-parser-multipart-mixed.c @@ -0,0 +1,133 @@ +/* + * 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 <string.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartMixed; +typedef EMailParserExtensionClass EMailParserMultipartMixedClass; + +GType e_mail_parser_multipart_mixed_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartMixed, + e_mail_parser_multipart_mixed, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/mixed", + "multipart/report", + "multipart/*", + NULL +}; + +static gboolean +empe_mp_mixed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMultipart *mp; + gint i, nparts, len; + + 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, out_mail_parts); + + len = part_id->len; + nparts = camel_multipart_get_number (mp); + for (i = 0; i < nparts; i++) { + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + CamelMimePart *subpart; + CamelContentType *ct; + + subpart = camel_multipart_get_part (mp, i); + + g_string_append_printf (part_id, ".mixed.%d", i); + + e_mail_parser_parse_part ( + parser, subpart, part_id, cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + ct = camel_mime_part_get_content_type (subpart); + + /* Display parts with CID as attachments + * (unless they already are attachments). + * Show also hidden attachments with CID, + * because this is multipart/mixed, + * not multipart/related. */ + if (mail_part != NULL && + e_mail_part_get_cid (mail_part) != NULL && + (!e_mail_part_get_is_attachment (mail_part) || + mail_part->is_hidden)) { + + e_mail_parser_wrap_as_attachment ( + parser, subpart, part_id, &work_queue); + + /* Force messages to be expandable */ + } else if (mail_part == NULL || + (camel_content_type_is (ct, "message", "rfc822") && + mail_part != NULL && + !e_mail_part_get_is_attachment (mail_part))) { + + e_mail_parser_wrap_as_attachment ( + parser, subpart, part_id, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + mail_part->force_inline = TRUE; + } + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + return TRUE; +} + +static void +e_mail_parser_multipart_mixed_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->flags = E_MAIL_PARSER_EXTENSION_COMPOUND_TYPE; + class->parse = empe_mp_mixed_parse; +} + +static void +e_mail_parser_multipart_mixed_init (EMailParserExtension *extension) +{ +} 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..45a5d440f5 --- /dev/null +++ b/em-format/e-mail-parser-multipart-related.c @@ -0,0 +1,155 @@ +/* + * 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 <string.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartRelated; +typedef EMailParserExtensionClass EMailParserMultipartRelatedClass; + +GType e_mail_parser_multipart_related_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartRelated, + e_mail_parser_multipart_related, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/related", + NULL +}; + +static gboolean +empe_mp_related_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMultipart *mp; + CamelMimePart *body_part, *display_part = NULL; + CamelContentType *display_content_type; + gchar *html_body = NULL; + gint i, nparts, partidlen, displayid = 0; + + 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, out_mail_parts); + + 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, out_mail_parts); + + display_content_type = camel_mime_part_get_content_type (display_part); + if (display_content_type && + camel_content_type_is (display_content_type, "text", "html")) { + CamelDataWrapper *dw; + + dw = camel_medium_get_content ((CamelMedium *) display_part); + if (dw) { + CamelStream *mem = camel_stream_mem_new (); + GByteArray *bytes; + + camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL); + camel_stream_close (mem, cancellable, NULL); + + bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem)); + if (bytes && bytes->len) + html_body = g_strndup ((const gchar *) bytes->data, bytes->len); + + g_object_unref (mem); + } + } + + /* The to-be-displayed part goes first */ + partidlen = part_id->len; + g_string_append_printf (part_id, ".related.%d", displayid); + + e_mail_parser_parse_part ( + parser, display_part, part_id, cancellable, out_mail_parts); + + g_string_truncate (part_id, partidlen); + + /* Process the related parts */ + nparts = camel_multipart_get_number (mp); + for (i = 0; i < nparts; i++) { + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + + body_part = camel_multipart_get_part (mp, i); + + if (body_part == display_part) + continue; + + g_string_append_printf (part_id, ".related.%d", i); + + e_mail_parser_parse_part ( + parser, body_part, part_id, + cancellable, &work_queue); + + g_string_truncate (part_id, partidlen); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + const gchar *cid; + + cid = e_mail_part_get_cid (mail_part); + + /* Don't render the part on its own! */ + if (e_mail_part_utils_body_refers (html_body, cid)) + mail_part->is_hidden = TRUE; + } + + e_queue_transfer (&work_queue, out_mail_parts); + } + + g_free (html_body); + + return TRUE; +} + +static void +e_mail_parser_multipart_related_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_related_parse; +} + +static void +e_mail_parser_multipart_related_init (EMailParserExtension *extension) +{ +} 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..440d791f6a --- /dev/null +++ b/em-format/e-mail-parser-multipart-signed.c @@ -0,0 +1,217 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <libedataserver/libedataserver.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartSigned; +typedef EMailParserExtensionClass EMailParserMultipartSignedClass; + +GType e_mail_parser_multipart_signed_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartSigned, + e_mail_parser_multipart_signed, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/signed", + "application/pgp-signature", + NULL +}; + +static gboolean +empe_mp_signed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMimePart *cpart; + CamelMultipartSigned *mps; + CamelCipherContext *cipher = NULL; + CamelSession *session; + guint32 validity_type; + CamelCipherValidity *valid; + GError *local_error = NULL; + gint i, nparts, len; + gboolean secured; + + /* 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 TRUE; + } + } + + 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) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse MIME message. " + "Displaying as source.")); + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + + return TRUE; + } + + 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) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Unsupported signature format")); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + return TRUE; + } + + valid = camel_cipher_context_verify_sync ( + cipher, part, cancellable, &local_error); + + if (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Error verifying signature: %s"), + local_error->message); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + g_object_unref (cipher); + g_error_free (local_error); + + return TRUE; + } + + nparts = camel_multipart_get_number (CAMEL_MULTIPART (mps)); + secured = FALSE; + len = part_id->len; + for (i = 0; i < nparts; i++) { + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + CamelMimePart *subpart; + + subpart = camel_multipart_get_part (CAMEL_MULTIPART (mps), i); + + g_string_append_printf (part_id, ".signed.%d", i); + + e_mail_parser_parse_part ( + parser, subpart, part_id, cancellable, &work_queue); + + g_string_truncate (part_id, len); + + if (!secured) + secured = e_mail_part_is_secured (subpart); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + validity_type | E_MAIL_PART_VALIDITY_SIGNED); + } + + e_queue_transfer (&work_queue, out_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) { + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + + g_string_append (part_id, ".signed.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.widget.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + validity_type | E_MAIL_PART_VALIDITY_SIGNED); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + + g_object_unref (cipher); + + return TRUE; +} + +static void +e_mail_parser_multipart_signed_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_signed_parse; +} + +static void +e_mail_parser_multipart_signed_init (EMailParserExtension *extension) +{ +} 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..2e2d9967fb --- /dev/null +++ b/em-format/e-mail-parser-secure-button.c @@ -0,0 +1,77 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" + +typedef EMailParserExtension EMailParserSecureButton; +typedef EMailParserExtensionClass EMailParserSecureButtonClass; + +GType e_mail_parser_secure_button_get_type (void); + +G_DEFINE_TYPE ( + EMailParserSecureButton, + e_mail_parser_secure_button, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/vnd.evolution.widget.secure-button", + NULL +}; + +static gboolean +empe_secure_button_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + 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); + e_mail_part_set_mime_type (mail_part, parser_mime_types[0]); + g_string_truncate (part_id, len); + + g_queue_push_tail (out_mail_parts, mail_part); + + return TRUE; +} + +static void +e_mail_parser_secure_button_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_secure_button_parse; +} + +static void +e_mail_parser_secure_button_init (EMailParserExtension *extension) +{ +} diff --git a/em-format/e-mail-parser-source.c b/em-format/e-mail-parser-source.c new file mode 100644 index 0000000000..9a8759cb95 --- /dev/null +++ b/em-format/e-mail-parser-source.c @@ -0,0 +1,78 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" + +typedef EMailParserExtension EMailParserSource; +typedef EMailParserExtensionClass EMailParserSourceClass; + +GType e_mail_parser_source_get_type (void); + +G_DEFINE_TYPE ( + EMailParserSource, + e_mail_parser_source, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/vnd.evolution.source", + NULL +}; + +static gboolean +empe_source_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + EMailPart *mail_part; + gint len; + + len = part_id->len; + g_string_append (part_id, ".source"); + + mail_part = e_mail_part_new (part, part_id->str); + e_mail_part_set_mime_type (mail_part, parser_mime_types[0]); + g_string_truncate (part_id, len); + + g_queue_push_tail (out_mail_parts, mail_part); + + return TRUE; +} + +static void +e_mail_parser_source_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_source_parse; +} + +static void +e_mail_parser_source_init (EMailParserExtension *extension) +{ +} 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..f938471e62 --- /dev/null +++ b/em-format/e-mail-parser-text-enriched.c @@ -0,0 +1,109 @@ +/* + * 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 <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserTextEnriched; +typedef EMailParserExtensionClass EMailParserTextEnrichedClass; + +GType e_mail_parser_text_enriched_get_type (void); + +G_DEFINE_TYPE ( + EMailParserTextEnriched, + e_mail_parser_text_enriched, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "text/richtext", + "text/enriched", + NULL +}; + +static gboolean +empe_text_enriched_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + const gchar *cid; + gint len; + CamelContentType *ct; + + len = part_id->len; + g_string_append (part_id, ".text_enriched"); + + mail_part = e_mail_part_new (part, part_id->str); + + ct = camel_mime_part_get_content_type (part); + if (ct != NULL) { + gchar *mime_type; + + mime_type = camel_content_type_simple (ct); + e_mail_part_set_mime_type (mail_part, mime_type); + g_free (mime_type); + } else { + e_mail_part_set_mime_type (mail_part, "text/enriched"); + } + + cid = camel_mime_part_get_content_id (part); + if (cid != NULL) { + gchar *cid_uri; + + cid_uri = g_strdup_printf ("cid:%s", cid); + e_mail_part_set_cid (mail_part, cid_uri); + g_free (cid_uri); + } + + g_string_truncate (part_id, len); + + g_queue_push_tail (&work_queue, mail_part); + + if (e_mail_part_is_attachment (part)) + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, &work_queue); + + e_queue_transfer (&work_queue, out_mail_parts); + + return TRUE; +} + +static void +e_mail_parser_text_enriched_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_text_enriched_parse; +} + +static void +e_mail_parser_text_enriched_init (EMailParserExtension *extension) +{ +} 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..bfb8cae2af --- /dev/null +++ b/em-format/e-mail-parser-text-html.c @@ -0,0 +1,110 @@ +/* + * 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 <string.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserTextHTML; +typedef EMailParserExtensionClass EMailParserTextHTMLClass; + +GType e_mail_parser_text_html_get_type (void); + +G_DEFINE_TYPE ( + EMailParserTextHTML, + e_mail_parser_text_html, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "text/html", + NULL +}; + +static gboolean +empe_text_html_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + const gchar *location; + gchar *cid = NULL; + const gchar *base; + gint len; + + 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"); + + mail_part = e_mail_part_new (part, part_id->str); + e_mail_part_set_mime_type (mail_part, "text/html"); + e_mail_part_set_cid (mail_part, cid); + g_string_truncate (part_id, len); + + g_queue_push_head (&work_queue, mail_part); + + if (e_mail_part_is_attachment (part)) + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, &work_queue); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_free (cid); + + return TRUE; +} + +static void +e_mail_parser_text_html_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_text_html_parse; +} + +static void +e_mail_parser_text_html_init (EMailParserExtension *extension) +{ +} 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..a4550ea0fe --- /dev/null +++ b/em-format/e-mail-parser-text-plain.c @@ -0,0 +1,241 @@ +/* + * 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 <ctype.h> +#include <glib/gi18n-lib.h> + +#include <e-util/e-util.h> + +#include "e-mail-inline-filter.h" +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserTextPlain; +typedef EMailParserExtensionClass EMailParserTextPlainClass; + +GType e_mail_parser_text_plain_get_type (void); + +G_DEFINE_TYPE ( + EMailParserTextPlain, + e_mail_parser_text_plain, + E_TYPE_MAIL_PARSER_EXTENSION) + +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 gboolean +process_part (EMailParser *parser, + GString *part_id, + gint part_number, + CamelMimePart *part, + gboolean is_attachment, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelContentType *type; + EMailPart *mail_part; + gint s_len = part_id->len; + + if (part_is_empty (part)) + return TRUE; + + type = camel_mime_part_get_content_type (part); + if (!camel_content_type_is (type, "text", "*")) { + e_mail_parser_parse_part ( + parser, CAMEL_MIME_PART (part), part_id, + cancellable, out_mail_parts); + + } else if (!camel_content_type_is (type, "text", "calendar")) { + GQueue work_queue = G_QUEUE_INIT; + gchar *mime_type; + + g_string_append_printf (part_id, ".plain_text.%d", part_number); + + mail_part = e_mail_part_new (part, part_id->str); + + mime_type = camel_content_type_simple (type); + e_mail_part_set_mime_type (mail_part, mime_type); + g_free (mime_type); + + g_string_truncate (part_id, s_len); + + g_queue_push_tail (&work_queue, mail_part); + + if (is_attachment) + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, &work_queue); + + e_queue_transfer (&work_queue, out_mail_parts); + + } else { + g_string_append_printf (part_id, ".inline.%d", part_number); + + e_mail_parser_parse_part ( + parser, CAMEL_MIME_PART (part), part_id, + cancellable, out_mail_parts); + + g_string_truncate (part_id, s_len); + } + + return TRUE; +} + +static gboolean +empe_text_plain_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelStream *filtered_stream, *null; + CamelMultipart *mp; + CamelDataWrapper *dw; + CamelContentType *type; + gint ii, count; + EMailInlineFilter *inline_filter; + gboolean charset_added = FALSE; + const gchar *snoop_type = NULL; + gboolean is_attachment; + gboolean handled = FALSE; + + dw = camel_medium_get_content ((CamelMedium *) part); + if (!dw) + return FALSE; + + /* 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_mime_part_get_filename (part)); + + 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); + + if (!e_mail_inline_filter_found_any (inline_filter)) { + g_object_unref (inline_filter); + camel_content_type_unref (type); + + return process_part ( + parser, part_id, 0, + part, e_mail_part_is_attachment (part), + cancellable, out_mail_parts); + } + + 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); + + is_attachment = ((count == 1) && (e_mail_part_is_attachment (part))); + + for (ii = 0; ii < count; ii++) { + CamelMimePart *newpart = camel_multipart_get_part (mp, ii); + + if (newpart != NULL) { + handled |= process_part ( + parser, part_id, ii, + newpart, is_attachment, + cancellable, out_mail_parts); + } + } + + g_object_unref (mp); + + return handled; +} + +static void +e_mail_parser_text_plain_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_text_plain_parse; +} + +static void +e_mail_parser_text_plain_init (EMailParserExtension *extension) +{ +} diff --git a/em-format/e-mail-parser.c b/em-format/e-mail-parser.c new file mode 100644 index 0000000000..57c362e286 --- /dev/null +++ b/em-format/e-mail-parser.c @@ -0,0 +1,776 @@ +/* + * 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 <string.h> + +#include <libebackend/libebackend.h> + +#include <shell/e-shell.h> +#include <shell/e-shell-window.h> + +#include "e-mail-parser-extension.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-utils.h" + +#define E_MAIL_PARSER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PARSER, EMailParserPrivate)) + +#define d(x) + +struct _EMailParserPrivate { + GMutex mutex; + + gint last_error; + + CamelSession *session; +}; + +enum { + PROP_0, + PROP_SESSION +}; + +/* internal parser extensions */ +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 + +void e_mail_parser_internal_extensions_load (EMailExtensionRegistry *ereg); + +static gpointer parent_class; + +static void +mail_parser_run (EMailParser *parser, + EMailPartList *part_list, + GCancellable *cancellable) +{ + EMailExtensionRegistry *reg; + CamelMimeMessage *message; + EMailPart *mail_part; + GQueue *parsers; + GQueue mail_part_queue = G_QUEUE_INIT; + GList *iter; + GString *part_id; + + message = e_mail_part_list_get_message (part_list); + + reg = e_mail_parser_get_extension_registry (parser); + + parsers = e_mail_extension_registry_get_for_mime_type ( + reg, "application/vnd.evolution.message"); + + if (parsers == NULL) + parsers = e_mail_extension_registry_get_for_mime_type ( + reg, "message/*"); + + /* No parsers means the internal Evolution parser + * extensions were not loaded. Something is terribly wrong! */ + g_return_if_fail (parsers != NULL); + + part_id = g_string_new (".message"); + + mail_part = e_mail_part_new (CAMEL_MIME_PART (message), ".message"); + e_mail_part_list_add_part (part_list, mail_part); + g_object_unref (mail_part); + + for (iter = parsers->head; iter; iter = iter->next) { + EMailParserExtension *extension; + gboolean message_handled; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + extension = iter->data; + if (!extension) + continue; + + message_handled = e_mail_parser_extension_parse ( + extension, parser, + CAMEL_MIME_PART (message), + part_id, cancellable, &mail_part_queue); + + if (message_handled) + break; + } + + while (!g_queue_is_empty (&mail_part_queue)) { + mail_part = g_queue_pop_head (&mail_part_queue); + e_mail_part_list_add_part (part_list, mail_part); + g_object_unref (mail_part); + } + + g_string_free (part_id, TRUE); +} + +static void +mail_parser_set_session (EMailParser *parser, + CamelSession *session) +{ + g_return_if_fail (CAMEL_IS_SESSION (session)); + g_return_if_fail (parser->priv->session == NULL); + + parser->priv->session = g_object_ref (session); +} + +static void +e_mail_parser_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SESSION: + mail_parser_set_session ( + E_MAIL_PARSER (object), + 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) +{ + switch (property_id) { + case PROP_SESSION: + g_value_set_object ( + value, + e_mail_parser_get_session ( + E_MAIL_PARSER (object))); + 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_GET_PRIVATE (object); + + g_mutex_clear (&priv->mutex); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +e_mail_parser_base_init (EMailParserClass *class) +{ + /* Register internal extensions. */ + g_type_ensure (e_mail_parser_application_mbox_get_type ()); + g_type_ensure (e_mail_parser_attachment_bar_get_type ()); + g_type_ensure (e_mail_parser_headers_get_type ()); + g_type_ensure (e_mail_parser_message_get_type ()); + g_type_ensure (e_mail_parser_secure_button_get_type ()); + g_type_ensure (e_mail_parser_source_get_type ()); + g_type_ensure (e_mail_parser_image_get_type ()); + g_type_ensure (e_mail_parser_inline_pgp_encrypted_get_type ()); + g_type_ensure (e_mail_parser_inline_pgp_signed_get_type ()); + g_type_ensure (e_mail_parser_message_delivery_status_get_type ()); + g_type_ensure (e_mail_parser_message_external_get_type ()); + g_type_ensure (e_mail_parser_message_rfc822_get_type ()); + g_type_ensure (e_mail_parser_multipart_alternative_get_type ()); + g_type_ensure (e_mail_parser_multipart_apple_double_get_type ()); + g_type_ensure (e_mail_parser_multipart_digest_get_type ()); + g_type_ensure (e_mail_parser_multipart_encrypted_get_type ()); + g_type_ensure (e_mail_parser_multipart_mixed_get_type ()); + g_type_ensure (e_mail_parser_multipart_related_get_type ()); + g_type_ensure (e_mail_parser_multipart_signed_get_type ()); + g_type_ensure (e_mail_parser_text_enriched_get_type ()); + g_type_ensure (e_mail_parser_text_html_get_type ()); + g_type_ensure (e_mail_parser_text_plain_get_type ()); +#ifdef ENABLE_SMIME + g_type_ensure (e_mail_parser_application_smime_get_type ()); +#endif + + class->extension_registry = g_object_new ( + E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, NULL); + + e_mail_parser_extension_registry_load (class->extension_registry); + + e_extensible_load_extensions (E_EXTENSIBLE (class->extension_registry)); +} + +static void +e_mail_parser_base_finalize (EMailParserClass *class) +{ + g_object_unref (class->extension_registry); +} + +static void +e_mail_parser_class_init (EMailParserClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailParserPrivate)); + + object_class = G_OBJECT_CLASS (class); + 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)); +} + +static void +e_mail_parser_init (EMailParser *parser) +{ + parser->priv = E_MAIL_PARSER_GET_PRIVATE (parser); + + g_mutex_init (&parser->priv->mutex); +} + +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) +{ + g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); + + 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 *part_list; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + + part_list = e_mail_part_list_new (message, message_uid, folder); + + mail_parser_run (parser, part_list, cancellable); + + if (camel_debug_start ("emformat:parser")) { + GQueue queue = G_QUEUE_INIT; + + printf ( + "%s finished with EMailPartList:\n", + G_OBJECT_TYPE_NAME (parser)); + + e_mail_part_list_queue_parts (part_list, NULL, &queue); + + while (!g_queue_is_empty (&queue)) { + EMailPart *part; + + part = g_queue_pop_head (&queue); + + printf ( + " id: %s | cid: %s | mime_type: %s | " + "is_hidden: %d | is_attachment: %d\n", + e_mail_part_get_id (part), + e_mail_part_get_cid (part), + e_mail_part_get_mime_type (part), + part->is_hidden ? 1 : 0, + e_mail_part_get_is_attachment (part) ? 1 : 0); + + g_object_unref (part); + } + + camel_debug_end (); + } + + return part_list; +} + +static void +mail_parser_parse_thread (GSimpleAsyncResult *simple, + GObject *source_object, + GCancellable *cancellable) +{ + EMailPartList *part_list; + + part_list = g_simple_async_result_get_op_res_gpointer (simple); + + mail_parser_run ( + E_MAIL_PARSER (source_object), + part_list, cancellable); +} + +/** + * 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 *simple; + EMailPartList *part_list; + + g_return_if_fail (E_IS_MAIL_PARSER (parser)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + part_list = e_mail_part_list_new (message, message_uid, folder); + + simple = g_simple_async_result_new ( + G_OBJECT (parser), callback, + user_data, e_mail_parser_parse); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, part_list, (GDestroyNotify) g_object_unref); + + g_simple_async_result_run_in_thread ( + simple, mail_parser_parse_thread, + G_PRIORITY_DEFAULT, cancellable); + + g_object_unref (simple); +} + +EMailPartList * +e_mail_parser_parse_finish (EMailParser *parser, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + EMailPartList *part_list; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (parser), e_mail_parser_parse), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + part_list = g_simple_async_result_get_op_res_gpointer (simple); + + if (camel_debug_start ("emformat:parser")) { + GQueue queue = G_QUEUE_INIT; + + printf ( + "%s finished with EMailPartList:\n", + G_OBJECT_TYPE_NAME (parser)); + + e_mail_part_list_queue_parts (part_list, NULL, &queue); + + while (!g_queue_is_empty (&queue)) { + EMailPart *part; + + part = g_queue_pop_head (&queue); + + printf ( + " id: %s | cid: %s | mime_type: %s | " + "is_hidden: %d | is_attachment: %d\n", + e_mail_part_get_id (part), + e_mail_part_get_cid (part), + e_mail_part_get_mime_type (part), + part->is_hidden ? 1 : 0, + e_mail_part_get_is_attachment (part) ? 1 : 0); + + g_object_unref (part); + } + + camel_debug_end (); + } + + return g_object_ref (part_list); +} + +gboolean +e_mail_parser_parse_part (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelContentType *ct; + gchar *mime_type; + gint n_parts_queued = 0; + + 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); + } + + n_parts_queued = e_mail_parser_parse_part_as ( + parser, part, part_id, mime_type, + cancellable, out_mail_parts); + + if (ct) { + g_free (mime_type); + } + + return n_parts_queued; +} + +gboolean +e_mail_parser_parse_part_as (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + const gchar *mime_type, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue *parsers; + GList *iter; + EMailExtensionRegistry *reg; + EMailParserClass *parser_class; + gchar *as_mime_type; + gboolean mime_part_handled = FALSE; + + 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 == NULL) { + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, out_mail_parts); + return TRUE; + } + + for (iter = parsers->head; iter; iter = iter->next) { + EMailParserExtension *extension; + + extension = iter->data; + if (!extension) + continue; + + mime_part_handled = e_mail_parser_extension_parse ( + extension, parser, part, part_id, + cancellable, out_mail_parts); + + if (mime_part_handled) + break; + } + + return mime_part_handled; +} + +void +e_mail_parser_error (EMailParser *parser, + GQueue *out_mail_parts, + const gchar *format, + ...) +{ + const gchar *mime_type = "application/vnd.evolution.error"; + EMailPart *mail_part; + CamelMimePart *part; + gchar *errmsg; + gchar *uri; + va_list ap; + + g_return_if_fail (E_IS_MAIL_PARSER (parser)); + g_return_if_fail (out_mail_parts != NULL); + 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), mime_type); + 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); + e_mail_part_set_mime_type (mail_part, mime_type); + mail_part->is_error = TRUE; + + g_free (uri); + g_object_unref (part); + + g_queue_push_tail (out_mail_parts, 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; +} + +void +e_mail_parser_wrap_as_attachment (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GQueue *parts_queue) +{ + EMailPartAttachment *empa; + EAttachment *attachment; + EMailPart *first_part; + const gchar *snoop_mime_type; + 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", "*")) + snoop_mime_type = mime_type; + else + g_free (mime_type); + } + + if (!snoop_mime_type) + snoop_mime_type = e_mail_part_snoop_type (part); + + 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 = e_mail_part_attachment_new (part, part_id->str); + empa->shown = extensions && (!g_queue_is_empty (extensions) && + e_mail_part_is_inline (part, extensions)); + empa->snoop_mime_type = snoop_mime_type; + + first_part = g_queue_peek_head (parts_queue); + if (first_part != NULL) { + const gchar *id = e_mail_part_get_id (first_part); + empa->attachment_view_part_id = g_strdup (id); + first_part->is_hidden = TRUE; + } + + attachment = e_mail_part_attachment_ref_attachment (empa); + + e_attachment_set_shown (attachment, empa->shown); + e_attachment_set_can_show ( + 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; + } else { + size = 0; + } + + /* e_attachment_load_async must be called from main thread */ + /* Prioritize ahead of GTK+ redraws. */ + g_idle_add_full ( + G_PRIORITY_HIGH_IDLE, + (GSourceFunc) load_attachment_idle, + g_object_ref (attachment), + NULL); + + if (size != 0) { + GFileInfo *file_info; + + file_info = e_attachment_ref_file_info (attachment); + + if (file_info == NULL) { + file_info = g_file_info_new (); + g_file_info_set_content_type ( + file_info, empa->snoop_mime_type); + } + + g_file_info_set_size (file_info, size); + e_attachment_set_file_info (attachment, file_info); + + g_object_unref (file_info); + } + + g_object_unref (attachment); + + g_string_truncate (part_id, part_id_len); + + /* Push to head, not tail. */ + g_queue_push_head (parts_queue, 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..77818b86e6 --- /dev/null +++ b/em-format/e-mail-parser.h @@ -0,0 +1,115 @@ +/* + * 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); + +gboolean e_mail_parser_parse_part (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts); + +gboolean e_mail_parser_parse_part_as (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + const gchar *mime_type, + GCancellable *cancellable, + GQueue *out_mail_parts); + +void e_mail_parser_error (EMailParser *parser, + GQueue *out_mail_parts, + const gchar *format, + ...) G_GNUC_PRINTF (3, 4); + +void e_mail_parser_wrap_as_attachment + (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GQueue *parts_queue); + +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.c b/em-format/e-mail-part-attachment-bar.c new file mode 100644 index 0000000000..5cebd9b8dc --- /dev/null +++ b/em-format/e-mail-part-attachment-bar.c @@ -0,0 +1,89 @@ +/* + * e-mail-part-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/> + * + */ + +#include "e-mail-part-attachment-bar.h" + +#define E_MAIL_PART_ATTACHMENT_BAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBarPrivate)) + +struct _EMailPartAttachmentBarPrivate { + EAttachmentStore *store; +}; + +G_DEFINE_TYPE ( + EMailPartAttachmentBar, + e_mail_part_attachment_bar, + E_TYPE_MAIL_PART) + +static void +mail_part_attachment_bar_dispose (GObject *object) +{ + EMailPartAttachmentBarPrivate *priv; + + priv = E_MAIL_PART_ATTACHMENT_BAR_GET_PRIVATE (object); + + g_clear_object (&priv->store); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_part_attachment_bar_parent_class)-> + dispose (object); +} + +static void +e_mail_part_attachment_bar_class_init (EMailPartAttachmentBarClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private ( + class, sizeof (EMailPartAttachmentBarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = mail_part_attachment_bar_dispose; +} + +static void +e_mail_part_attachment_bar_init (EMailPartAttachmentBar *part) +{ + GtkTreeModel *tree_model; + + part->priv = E_MAIL_PART_ATTACHMENT_BAR_GET_PRIVATE (part); + + tree_model = e_attachment_store_new (); + part->priv->store = E_ATTACHMENT_STORE (tree_model); +} + +EMailPart * +e_mail_part_attachment_bar_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART_ATTACHMENT_BAR, + "id", id, "mime-part", mime_part, NULL); +} + +EAttachmentStore * +e_mail_part_attachment_bar_get_store (EMailPartAttachmentBar *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT_BAR (part), NULL); + + return part->priv->store; +} + 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..ca9f93185b --- /dev/null +++ b/em-format/e-mail-part-attachment-bar.h @@ -0,0 +1,71 @@ +/* + * e-mail-part-attachment-bar.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_MAIL_PART_ATTACHMENT_BAR_H +#define E_MAIL_PART_ATTACHMENT_BAR_H + +#include <em-format/e-mail-part.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART_ATTACHMENT_BAR \ + (e_mail_part_attachment_bar_get_type ()) +#define E_MAIL_PART_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBar)) +#define E_MAIL_PART_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBarClass)) +#define E_IS_MAIL_PART_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR)) +#define E_IS_MAIL_PART_ATTACHMENT_BAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART_ATTACHMENT_BAR)) +#define E_MAIL_PART_ATTACHMENT_BAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT_BAR, EMailPartAttachmentBarClass)) + +#define E_MAIL_PART_ATTACHMENT_BAR_MIME_TYPE \ + "application/vnd.evolution.widget.attachment-bar" + +G_BEGIN_DECLS + +typedef struct _EMailPartAttachmentBar EMailPartAttachmentBar; +typedef struct _EMailPartAttachmentBarClass EMailPartAttachmentBarClass; +typedef struct _EMailPartAttachmentBarPrivate EMailPartAttachmentBarPrivate; + +struct _EMailPartAttachmentBar { + EMailPart parent; + EMailPartAttachmentBarPrivate *priv; +}; + +struct _EMailPartAttachmentBarClass { + EMailPartClass parent_class; +}; + +GType e_mail_part_attachment_bar_get_type + (void) G_GNUC_CONST; +EMailPart * e_mail_part_attachment_bar_new (CamelMimePart *mime_part, + const gchar *id); +EAttachmentStore * + e_mail_part_attachment_bar_get_store + (EMailPartAttachmentBar *part); + +G_END_DECLS + +#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..df81357bed --- /dev/null +++ b/em-format/e-mail-part-attachment.c @@ -0,0 +1,170 @@ +/* + * 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" + +#define E_MAIL_PART_ATTACHMENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT, EMailPartAttachmentPrivate)) + +struct _EMailPartAttachmentPrivate { + EAttachment *attachment; +}; + +enum { + PROP_0, + PROP_ATTACHMENT +}; + +G_DEFINE_TYPE ( + EMailPartAttachment, + e_mail_part_attachment, + E_TYPE_MAIL_PART) + +static void +mail_part_attachment_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ATTACHMENT: + g_value_take_object ( + value, + e_mail_part_attachment_ref_attachment ( + E_MAIL_PART_ATTACHMENT (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_attachment_dispose (GObject *object) +{ + EMailPartAttachmentPrivate *priv; + + priv = E_MAIL_PART_ATTACHMENT_GET_PRIVATE (object); + + g_clear_object (&priv->attachment); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_part_attachment_parent_class)-> + dispose (object); +} + +static void +mail_part_attachment_finalize (GObject *object) +{ + EMailPartAttachment *part = E_MAIL_PART_ATTACHMENT (object); + + g_free (part->attachment_view_part_id); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_part_attachment_parent_class)-> + finalize (object); +} + +static void +mail_part_attachment_constructed (GObject *object) +{ + EMailPartAttachmentPrivate *priv; + CamelMimePart *mime_part; + EAttachment *attachment; + EMailPart *part; + const gchar *cid; + + part = E_MAIL_PART (object); + priv = E_MAIL_PART_ATTACHMENT_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_part_attachment_parent_class)-> + constructed (object); + + e_mail_part_set_mime_type (part, E_MAIL_PART_ATTACHMENT_MIME_TYPE); + e_mail_part_set_is_attachment (part, TRUE); + + mime_part = e_mail_part_ref_mime_part (part); + + cid = camel_mime_part_get_content_id (mime_part); + if (cid != NULL) { + gchar *cid_uri; + + cid_uri = g_strconcat ("cid:", cid, NULL); + e_mail_part_set_cid (part, cid_uri); + g_free (cid_uri); + } + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + priv->attachment = g_object_ref (attachment); + g_object_unref (attachment); + + g_object_unref (mime_part); +} + +static void +e_mail_part_attachment_class_init (EMailPartAttachmentClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EMailPartAttachmentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = mail_part_attachment_get_property; + object_class->dispose = mail_part_attachment_dispose; + object_class->finalize = mail_part_attachment_finalize; + object_class->constructed = mail_part_attachment_constructed; + + g_object_class_install_property ( + object_class, + PROP_ATTACHMENT, + g_param_spec_object ( + "attachment", + "Attachment", + "The attachment object", + E_TYPE_ATTACHMENT, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_mail_part_attachment_init (EMailPartAttachment *part) +{ + part->priv = E_MAIL_PART_ATTACHMENT_GET_PRIVATE (part); +} + +EMailPartAttachment * +e_mail_part_attachment_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART_ATTACHMENT, + "id", id, "mime-part", mime_part, NULL); +} + +EAttachment * +e_mail_part_attachment_ref_attachment (EMailPartAttachment *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART_ATTACHMENT (part), NULL); + + return g_object_ref (part->priv->attachment); +} + diff --git a/em-format/e-mail-part-attachment.h b/em-format/e-mail-part-attachment.h new file mode 100644 index 0000000000..5c9fd617b0 --- /dev/null +++ b/em-format/e-mail-part-attachment.h @@ -0,0 +1,75 @@ +/* + * e-mail-part-attachment.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_ATTACHMENT_H +#define E_MAIL_PART_ATTACHMENT_H + +#include <em-format/e-mail-part.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART_ATTACHMENT \ + (e_mail_part_attachment_get_type ()) +#define E_MAIL_PART_ATTACHMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT, EMailPartAttachment)) +#define E_MAIL_PART_ATTACHMENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART_ATTACHMENT, EMailPartAttachmentClass)) +#define E_IS_MAIL_PART_ATTACHMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT)) +#define E_IS_MAIL_PART_ATTACHMENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART_ATTACHMENT)) +#define E_MAIL_PART_ATTACHMENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART_ATTACHMENT, EMailPartAttachmentClass)) + +#define E_MAIL_PART_ATTACHMENT_MIME_TYPE \ + "application/vnd.evolution.attachment" + +G_BEGIN_DECLS + +typedef struct _EMailPartAttachment EMailPartAttachment; +typedef struct _EMailPartAttachmentClass EMailPartAttachmentClass; +typedef struct _EMailPartAttachmentPrivate EMailPartAttachmentPrivate; + +struct _EMailPartAttachment { + EMailPart parent; + EMailPartAttachmentPrivate *priv; + + gchar *attachment_view_part_id; + + gboolean shown; + const gchar *snoop_mime_type; +}; + +struct _EMailPartAttachmentClass { + EMailPartClass parent_class; +}; + +GType e_mail_part_attachment_get_type (void) G_GNUC_CONST; +EMailPartAttachment * + e_mail_part_attachment_new (CamelMimePart *mime_part, + const gchar *id); +EAttachment * e_mail_part_attachment_ref_attachment + (EMailPartAttachment *part); + +G_END_DECLS + +#endif /* E_MAIL_PART_ATTACHMENT_H */ diff --git a/em-format/e-mail-part-headers.c b/em-format/e-mail-part-headers.c new file mode 100644 index 0000000000..9087e8e60d --- /dev/null +++ b/em-format/e-mail-part-headers.c @@ -0,0 +1,382 @@ +/* + * e-mail-part-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/> + * + */ + +#include "e-mail-part-headers.h" + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "e-mail-part-list.h" + +#define E_MAIL_PART_HEADERS_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PART_HEADERS, EMailPartHeadersPrivate)) + +struct _EMailPartHeadersPrivate { + GMutex property_lock; + gchar **default_headers; + GtkTreeModel *print_model; +}; + +enum { + PROP_0, + PROP_DEFAULT_HEADERS +}; + +G_DEFINE_TYPE ( + EMailPartHeaders, + e_mail_part_headers, + E_TYPE_MAIL_PART) + +static const gchar *basic_headers[] = { + N_("From"), + N_("Reply-To"), + N_("To"), + N_("Cc"), + N_("Bcc"), + N_("Subject"), + N_("Date"), + N_("Newsgroups"), + N_("Face"), + NULL +}; + +static GtkTreeModel * +mail_part_headers_build_print_model (EMailPartHeaders *part) +{ + GtkListStore *list_store; + EMailPartList *part_list; + CamelMimeMessage *message; + GArray *array; + gint default_position = 0; + guint ii, length = 0; + + /* If the part list is NULL, it means the function was called + * too early. The part must be added to a part list first so + * we have access to the CamelMimeMessage. */ + part_list = e_mail_part_ref_part_list (E_MAIL_PART (part)); + g_return_val_if_fail (part_list != NULL, NULL); + + list_store = gtk_list_store_new ( + E_MAIL_PART_HEADERS_PRINT_MODEL_NUM_COLUMNS, + G_TYPE_BOOLEAN, /* INCLUDE */ + G_TYPE_STRING, /* HEADER_NAME */ + G_TYPE_STRING); /* HEADER_VALUE */ + + message = e_mail_part_list_get_message (part_list); + array = camel_medium_get_headers (CAMEL_MEDIUM (message)); + + if (array != NULL) + length = array->len; + + for (ii = 0; ii < length; ii++) { + CamelMediumHeader *header; + GtkTreeIter iter; + gboolean include = FALSE; + gint position = -1; + + header = &g_array_index (array, CamelMediumHeader, ii); + + /* EMailFormatterPrintHeaders excludes "Subject" from + * its header table (because it puts it in an <h1> tag + * at the top of the page), so we'll exclude it too. */ + if (g_ascii_strncasecmp (header->name, "Subject", 7) == 0) + continue; + + /* Arrange default headers first and select them to be + * included in the final printout. All other headers + * are excluded by default in the final printout. */ + if (e_mail_part_headers_is_default (part, header->name)) { + position = default_position++; + include = TRUE; + } + + gtk_list_store_insert (list_store, &iter, position); + + gtk_list_store_set ( + list_store, &iter, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_INCLUDE, + include, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_NAME, + header->name, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_VALUE, + header->value, + -1); + } + + if (array != NULL) + camel_medium_free_headers (CAMEL_MEDIUM (message), array); + + g_object_unref (part_list); + + /* Stash the print model internally. */ + + g_mutex_lock (&part->priv->property_lock); + + g_clear_object (&part->priv->print_model); + part->priv->print_model = g_object_ref (list_store); + + g_mutex_unlock (&part->priv->property_lock); + + return GTK_TREE_MODEL (list_store); +} + +static void +mail_part_headers_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DEFAULT_HEADERS: + e_mail_part_headers_set_default_headers ( + E_MAIL_PART_HEADERS (object), + g_value_get_boxed (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_headers_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DEFAULT_HEADERS: + g_value_take_boxed ( + value, + e_mail_part_headers_dup_default_headers ( + E_MAIL_PART_HEADERS (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_headers_dispose (GObject *object) +{ + EMailPartHeadersPrivate *priv; + + priv = E_MAIL_PART_HEADERS_GET_PRIVATE (object); + + g_clear_object (&priv->print_model); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_part_headers_parent_class)->dispose (object); +} + +static void +mail_part_headers_finalize (GObject *object) +{ + EMailPartHeadersPrivate *priv; + + priv = E_MAIL_PART_HEADERS_GET_PRIVATE (object); + + g_mutex_clear (&priv->property_lock); + + g_strfreev (priv->default_headers); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_part_headers_parent_class)->finalize (object); +} + +static void +mail_part_headers_constructed (GObject *object) +{ + EMailPart *part; + + part = E_MAIL_PART (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_part_headers_parent_class)-> + constructed (object); + + e_mail_part_set_mime_type (part, E_MAIL_PART_HEADERS_MIME_TYPE); +} + +static void +mail_part_headers_bind_dom_element (EMailPart *part, + WebKitDOMElement *element) +{ + WebKitDOMDocument *document; + WebKitDOMElement *photo; + gchar *addr, *uri; + + document = webkit_dom_node_get_owner_document ( + WEBKIT_DOM_NODE (element)); + photo = webkit_dom_document_get_element_by_id ( + document, "__evo-contact-photo"); + + /* Contact photos disabled, the <img> tag is not there. */ + if (photo == NULL) + return; + + addr = webkit_dom_element_get_attribute (photo, "data-mailaddr"); + uri = g_strdup_printf ("mail://contact-photo?mailaddr=%s", addr); + + webkit_dom_html_image_element_set_src ( + WEBKIT_DOM_HTML_IMAGE_ELEMENT (photo), uri); + + g_free (addr); + g_free (uri); +} + +static void +e_mail_part_headers_class_init (EMailPartHeadersClass *class) +{ + GObjectClass *object_class; + EMailPartClass *mail_part_class; + + g_type_class_add_private (class, sizeof (EMailPartHeadersPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_part_headers_set_property; + object_class->get_property = mail_part_headers_get_property; + object_class->dispose = mail_part_headers_dispose; + object_class->finalize = mail_part_headers_finalize; + object_class->constructed = mail_part_headers_constructed; + + mail_part_class = E_MAIL_PART_CLASS (class); + mail_part_class->bind_dom_element = mail_part_headers_bind_dom_element; + + g_object_class_install_property ( + object_class, + PROP_DEFAULT_HEADERS, + g_param_spec_boxed ( + "default-headers", + "Default Headers", + "Headers to display by default", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_mail_part_headers_init (EMailPartHeaders *part) +{ + part->priv = E_MAIL_PART_HEADERS_GET_PRIVATE (part); + + g_mutex_init (&part->priv->property_lock); +} + +EMailPart * +e_mail_part_headers_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART_HEADERS, + "id", id, "mime-part", mime_part, NULL); +} + +gchar ** +e_mail_part_headers_dup_default_headers (EMailPartHeaders *part) +{ + gchar **default_headers; + + g_return_val_if_fail (E_IS_MAIL_PART_HEADERS (part), NULL); + + g_mutex_lock (&part->priv->property_lock); + + default_headers = g_strdupv (part->priv->default_headers); + + g_mutex_unlock (&part->priv->property_lock); + + return default_headers; +} + +void +e_mail_part_headers_set_default_headers (EMailPartHeaders *part, + const gchar * const *default_headers) +{ + g_return_if_fail (E_IS_MAIL_PART_HEADERS (part)); + + if (default_headers == NULL) + default_headers = basic_headers; + + g_mutex_lock (&part->priv->property_lock); + + g_strfreev (part->priv->default_headers); + part->priv->default_headers = g_strdupv ((gchar **) default_headers); + + g_mutex_unlock (&part->priv->property_lock); + + g_object_notify (G_OBJECT (part), "default-headers"); +} + +gboolean +e_mail_part_headers_is_default (EMailPartHeaders *part, + const gchar *header_name) +{ + gboolean is_default = FALSE; + guint ii, length = 0; + + g_return_val_if_fail (E_IS_MAIL_PART_HEADERS (part), FALSE); + g_return_val_if_fail (header_name != NULL, FALSE); + + g_mutex_lock (&part->priv->property_lock); + + if (part->priv->default_headers != NULL) + length = g_strv_length (part->priv->default_headers); + + for (ii = 0; ii < length; ii++) { + const gchar *candidate; + + /* g_strv_length() stops on the first NULL pointer, + * so we don't have to worry about this being NULL. */ + candidate = part->priv->default_headers[ii]; + + if (g_ascii_strcasecmp (header_name, candidate) == 0) { + is_default = TRUE; + break; + } + } + + g_mutex_unlock (&part->priv->property_lock); + + return is_default; +} + +GtkTreeModel * +e_mail_part_headers_ref_print_model (EMailPartHeaders *part) +{ + GtkTreeModel *print_model = NULL; + + g_return_val_if_fail (E_IS_MAIL_PART_HEADERS (part), NULL); + + g_mutex_lock (&part->priv->property_lock); + + if (part->priv->print_model != NULL) + print_model = g_object_ref (part->priv->print_model); + + g_mutex_unlock (&part->priv->property_lock); + + if (print_model == NULL) { + /* The print model is built once on demand. */ + print_model = mail_part_headers_build_print_model (part); + } + + return print_model; +} + diff --git a/em-format/e-mail-part-headers.h b/em-format/e-mail-part-headers.h new file mode 100644 index 0000000000..c7da98f6b4 --- /dev/null +++ b/em-format/e-mail-part-headers.h @@ -0,0 +1,84 @@ +/* + * e-mail-part-headers.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_HEADERS_H +#define E_MAIL_PART_HEADERS_H + +#include <em-format/e-mail-part.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART_HEADERS \ + (e_mail_part_headers_get_type ()) +#define E_MAIL_PART_HEADERS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART_HEADERS, EMailPartHeaders)) +#define E_MAIL_PART_HEADERS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART_HEADERS, EMailPartHeadersClass)) +#define E_IS_MAIL_PART_HEADERS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART_HEADERS)) +#define E_IS_MAIL_PART_HEADERS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART_HEADERS)) +#define E_MAIL_PART_HEADERS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART_HEADERS, EMailPartHeadersClass)) + +#define E_MAIL_PART_HEADERS_MIME_TYPE \ + "application/vnd.evolution.headers" + +G_BEGIN_DECLS + +typedef struct _EMailPartHeaders EMailPartHeaders; +typedef struct _EMailPartHeadersClass EMailPartHeadersClass; +typedef struct _EMailPartHeadersPrivate EMailPartHeadersPrivate; + +struct _EMailPartHeaders { + EMailPart parent; + EMailPartHeadersPrivate *priv; +}; + +struct _EMailPartHeadersClass { + EMailPartClass parent_class; +}; + +typedef enum { + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_INCLUDE, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_NAME, + E_MAIL_PART_HEADERS_PRINT_MODEL_COLUMN_HEADER_VALUE, + E_MAIL_PART_HEADERS_PRINT_MODEL_NUM_COLUMNS +} EMailPartHeadersPrintModelColumns; + +GType e_mail_part_headers_get_type (void) G_GNUC_CONST; +EMailPart * e_mail_part_headers_new (CamelMimePart *mime_part, + const gchar *id); +gchar ** e_mail_part_headers_dup_default_headers + (EMailPartHeaders *part); +void e_mail_part_headers_set_default_headers + (EMailPartHeaders *part, + const gchar * const *default_headers); +gboolean e_mail_part_headers_is_default (EMailPartHeaders *part, + const gchar *header_name); +GtkTreeModel * e_mail_part_headers_ref_print_model + (EMailPartHeaders *part); + +G_END_DECLS + +#endif /* E_MAIL_PART_HEADERS_H */ + diff --git a/em-format/e-mail-part-image.c b/em-format/e-mail-part-image.c new file mode 100644 index 0000000000..2a70fe98b6 --- /dev/null +++ b/em-format/e-mail-part-image.c @@ -0,0 +1,100 @@ +/* + * e-mail-part-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/> + * + */ + +#include "e-mail-part-image.h" + +G_DEFINE_TYPE ( + EMailPartImage, + e_mail_part_image, + E_TYPE_MAIL_PART) + +static void +mail_part_image_constructed (GObject *object) +{ + EMailPart *part; + CamelMimePart *mime_part; + CamelContentType *content_type; + const gchar *content_id; + const gchar *disposition; + + part = E_MAIL_PART (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_part_image_parent_class)->constructed (object); + + e_mail_part_set_is_attachment (part, TRUE); + + mime_part = e_mail_part_ref_mime_part (part); + + content_id = camel_mime_part_get_content_id (mime_part); + content_type = camel_mime_part_get_content_type (mime_part); + disposition = camel_mime_part_get_disposition (mime_part); + + if (content_id != NULL) { + gchar *cid; + + cid = g_strconcat ("cid:", content_id, NULL); + e_mail_part_set_cid (part, cid); + g_free (cid); + } + + if (content_type != NULL) { + gchar *mime_type; + + mime_type = camel_content_type_simple (content_type); + e_mail_part_set_mime_type (part, mime_type); + g_free (mime_type); + } else { + e_mail_part_set_mime_type (part, "image/*"); + } + + if (disposition == NULL) + disposition = "inline"; + + part->is_hidden = + (content_id != NULL) && + (g_ascii_strcasecmp (disposition, "attachment") != 0); + + g_object_unref (mime_part); +} + +static void +e_mail_part_image_class_init (EMailPartImageClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = mail_part_image_constructed; +} + +static void +e_mail_part_image_init (EMailPartImage *part) +{ +} + +EMailPart * +e_mail_part_image_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART_IMAGE, + "id", id, "mime-part", mime_part, NULL); +} + diff --git a/em-format/e-mail-part-image.h b/em-format/e-mail-part-image.h new file mode 100644 index 0000000000..0c3e7f437d --- /dev/null +++ b/em-format/e-mail-part-image.h @@ -0,0 +1,65 @@ +/* + * e-mail-part-image.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_IMAGE_H +#define E_MAIL_PART_IMAGE_H + +#include <em-format/e-mail-part.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART_IMAGE \ + (e_mail_part_image_get_type ()) +#define E_MAIL_PART_IMAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART_IMAGE, EMailPartImage)) +#define E_MAIL_PART_IMAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART_IMAGE, EMailPartImageClass)) +#define E_IS_MAIL_PART_IMAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART_IMAGE)) +#define E_IS_MAIL_PART_IMAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART_IMAGE)) +#define E_MAIL_PART_IMAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART_IMAGE, EMailPartImageClass)) + +G_BEGIN_DECLS + +typedef struct _EMailPartImage EMailPartImage; +typedef struct _EMailPartImageClass EMailPartImageClass; +typedef struct _EMailPartImagePrivate EMailPartImagePrivate; + +struct _EMailPartImage { + EMailPart parent; + EMailPartImagePrivate *priv; +}; + +struct _EMailPartImageClass { + EMailPartClass parent_class; +}; + +GType e_mail_part_image_get_type (void) G_GNUC_CONST; +EMailPart * e_mail_part_image_new (CamelMimePart *mime_part, + const gchar *id); + +G_END_DECLS + +#endif /* E_MAIL_PART_IMAGE_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..bcf7490634 --- /dev/null +++ b/em-format/e-mail-part-list.c @@ -0,0 +1,417 @@ +/* + * 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" + +#define E_MAIL_PART_LIST_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PART_LIST, EMailPartListPrivate)) + +struct _EMailPartListPrivate { + CamelFolder *folder; + CamelMimeMessage *message; + gchar *message_uid; + + GQueue queue; + GMutex queue_lock; +}; + +enum { + PROP_0, + PROP_FOLDER, + PROP_MESSAGE, + PROP_MESSAGE_UID +}; + +G_DEFINE_TYPE (EMailPartList, e_mail_part_list, G_TYPE_OBJECT) + +static CamelObjectBag *registry = NULL; +G_LOCK_DEFINE_STATIC (registry); + +static void +mail_part_list_set_folder (EMailPartList *part_list, + CamelFolder *folder) +{ + g_return_if_fail (part_list->priv->folder == NULL); + + /* The folder property is optional. */ + if (folder != NULL) { + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + part_list->priv->folder = g_object_ref (folder); + } +} + +static void +mail_part_list_set_message (EMailPartList *part_list, + CamelMimeMessage *message) +{ + g_return_if_fail (part_list->priv->message == NULL); + + /* The message property is optional. */ + if (message != NULL) { + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + part_list->priv->message = g_object_ref (message); + } +} + +static void +mail_part_list_set_message_uid (EMailPartList *part_list, + const gchar *message_uid) +{ + g_return_if_fail (part_list->priv->message_uid == NULL); + + /* The message_uid property is optional. */ + part_list->priv->message_uid = g_strdup (message_uid); +} + +static void +mail_part_list_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FOLDER: + mail_part_list_set_folder ( + E_MAIL_PART_LIST (object), + g_value_get_object (value)); + return; + + case PROP_MESSAGE: + mail_part_list_set_message ( + E_MAIL_PART_LIST (object), + g_value_get_object (value)); + return; + + case PROP_MESSAGE_UID: + mail_part_list_set_message_uid ( + E_MAIL_PART_LIST (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_list_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FOLDER: + g_value_set_object ( + value, + e_mail_part_list_get_folder ( + E_MAIL_PART_LIST (object))); + return; + + case PROP_MESSAGE: + g_value_set_object ( + value, + e_mail_part_list_get_message ( + E_MAIL_PART_LIST (object))); + return; + + case PROP_MESSAGE_UID: + g_value_set_string ( + value, + e_mail_part_list_get_message_uid ( + E_MAIL_PART_LIST (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_list_dispose (GObject *object) +{ + EMailPartListPrivate *priv; + + priv = E_MAIL_PART_LIST_GET_PRIVATE (object); + + if (priv->folder != NULL) { + g_object_unref (priv->folder); + priv->folder = NULL; + } + + if (priv->message != NULL) { + g_object_unref (priv->message); + priv->message = NULL; + } + + g_mutex_lock (&priv->queue_lock); + while (!g_queue_is_empty (&priv->queue)) + g_object_unref (g_queue_pop_head (&priv->queue)); + g_mutex_unlock (&priv->queue_lock); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_part_list_parent_class)->dispose (object); +} + +static void +mail_part_list_finalize (GObject *object) +{ + EMailPartListPrivate *priv; + + priv = E_MAIL_PART_LIST_GET_PRIVATE (object); + + g_free (priv->message_uid); + + g_warn_if_fail (g_queue_is_empty (&priv->queue)); + g_mutex_clear (&priv->queue_lock); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_part_list_parent_class)->finalize (object); +} + +static void +e_mail_part_list_class_init (EMailPartListClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EMailPartListPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_part_list_set_property; + object_class->get_property = mail_part_list_get_property; + object_class->dispose = mail_part_list_dispose; + object_class->finalize = mail_part_list_finalize; + + g_object_class_install_property ( + object_class, + PROP_FOLDER, + g_param_spec_object ( + "folder", + "Folder", + NULL, + CAMEL_TYPE_FOLDER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MESSAGE, + g_param_spec_object ( + "message", + "Message", + NULL, + CAMEL_TYPE_MIME_MESSAGE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MESSAGE_UID, + g_param_spec_string ( + "message-uid", + "Message UID", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_mail_part_list_init (EMailPartList *part_list) +{ + part_list->priv = E_MAIL_PART_LIST_GET_PRIVATE (part_list); + + g_mutex_init (&part_list->priv->queue_lock); +} + +EMailPartList * +e_mail_part_list_new (CamelMimeMessage *message, + const gchar *message_uid, + CamelFolder *folder) +{ + if (message != NULL) + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + + if (folder != NULL) + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); + + return g_object_new ( + E_TYPE_MAIL_PART_LIST, + "message", message, + "message-uid", message_uid, + "folder", folder, NULL); +} + +CamelFolder * +e_mail_part_list_get_folder (EMailPartList *part_list) +{ + g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL); + + return part_list->priv->folder; +} + +CamelMimeMessage * +e_mail_part_list_get_message (EMailPartList *part_list) +{ + g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL); + + return part_list->priv->message; +} + +const gchar * +e_mail_part_list_get_message_uid (EMailPartList *part_list) +{ + g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL); + + return part_list->priv->message_uid; +} + +void +e_mail_part_list_add_part (EMailPartList *part_list, + EMailPart *part) +{ + g_return_if_fail (E_IS_MAIL_PART_LIST (part_list)); + g_return_if_fail (E_IS_MAIL_PART (part)); + + g_mutex_lock (&part_list->priv->queue_lock); + + g_queue_push_tail ( + &part_list->priv->queue, + g_object_ref (part)); + + g_mutex_unlock (&part_list->priv->queue_lock); + + e_mail_part_set_part_list (part, part_list); +} + +EMailPart * +e_mail_part_list_ref_part (EMailPartList *part_list, + const gchar *part_id) +{ + EMailPart *match = NULL; + GList *head, *link; + gboolean by_cid; + + g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL); + g_return_val_if_fail (part_id != NULL, NULL); + + by_cid = (g_ascii_strncasecmp (part_id, "cid:", 4) == 0); + + g_mutex_lock (&part_list->priv->queue_lock); + + head = g_queue_peek_head_link (&part_list->priv->queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *candidate = E_MAIL_PART (link->data); + const gchar *candidate_id; + + if (by_cid) + candidate_id = e_mail_part_get_cid (candidate); + else + candidate_id = e_mail_part_get_id (candidate); + + if (g_strcmp0 (candidate_id, part_id) == 0) { + match = g_object_ref (candidate); + break; + } + } + + g_mutex_unlock (&part_list->priv->queue_lock); + + return match; +} + +/** + * e_mail_part_list_queue_parts: + * @part_list: an #EMailPartList + * @part_id: the #EMailPart ID to begin queueing from, or %NULL + * @result_queue: a #GQueue in which to deposit #EMailPart instances + * + * Populates @result_queue with a sequence of #EMailPart instances beginning + * with the part having @part_id. If @part_id is %NULL, the entire sequence + * of #EMailPart instances is queued. + * + * Each #EMailPart is referenced for thread-safety and should be unreferenced + * with g_object_unref(). + * + * Returns: the number of parts added to @result_queue + **/ +guint +e_mail_part_list_queue_parts (EMailPartList *part_list, + const gchar *part_id, + GQueue *result_queue) +{ + GList *link; + guint parts_queued = 0; + + g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), FALSE); + g_return_val_if_fail (result_queue != NULL, FALSE); + + g_mutex_lock (&part_list->priv->queue_lock); + + link = g_queue_peek_head_link (&part_list->priv->queue); + + if (part_id != NULL) { + for (; link != NULL; link = g_list_next (link)) { + EMailPart *candidate = E_MAIL_PART (link->data); + const gchar *candidate_id; + + candidate_id = e_mail_part_get_id (candidate); + + if (g_strcmp0 (candidate_id, part_id) == 0) + break; + } + } + + /* We skip the loop entirely if link is NULL. */ + for (; link != NULL; link = g_list_next (link)) { + EMailPart *part = link->data; + + if (part == NULL) + continue; + + g_queue_push_tail (result_queue, g_object_ref (part)); + parts_queued++; + } + + g_mutex_unlock (&part_list->priv->queue_lock); + + return parts_queued; +} + +/** + * e_mail_part_list_get_registry: + * + * Returns a #CamelObjectBag where parsed #EMailPartLists can be stored. + */ +CamelObjectBag * +e_mail_part_list_get_registry (void) +{ + G_LOCK (registry); + if (registry == NULL) { + registry = camel_object_bag_new ( + g_str_hash, g_str_equal, + (CamelCopyFunc) g_strdup, g_free); + } + G_UNLOCK (registry); + + return registry; +} diff --git a/em-format/e-mail-part-list.h b/em-format/e-mail-part-list.h new file mode 100644 index 0000000000..3694075100 --- /dev/null +++ b/em-format/e-mail-part-list.h @@ -0,0 +1,81 @@ +/* + * 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; +typedef struct _EMailPartListPrivate EMailPartListPrivate; + +struct _EMailPartList { + GObject parent; + EMailPartListPrivate *priv; +}; + +struct _EMailPartListClass { + GObjectClass parent_class; +}; + +GType e_mail_part_list_get_type (void) G_GNUC_CONST; +EMailPartList * e_mail_part_list_new (CamelMimeMessage *message, + const gchar *message_uid, + CamelFolder *folder); +CamelFolder * e_mail_part_list_get_folder (EMailPartList *part_list); +CamelMimeMessage * + e_mail_part_list_get_message (EMailPartList *part_list); +const gchar * e_mail_part_list_get_message_uid + (EMailPartList *part_list); +void e_mail_part_list_add_part (EMailPartList *part_list, + EMailPart *part); +EMailPart * e_mail_part_list_ref_part (EMailPartList *part_list, + const gchar *part_id); +guint e_mail_part_list_queue_parts (EMailPartList *part_list, + const gchar *part_id, + GQueue *result_queue); + +CamelObjectBag * + e_mail_part_list_get_registry (void); + +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..0a24cf2ef4 --- /dev/null +++ b/em-format/e-mail-part-utils.c @@ -0,0 +1,582 @@ +/* + * 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 <e-util/e-util.h> +#include <gdk/gdk.h> + +#include <libsoup/soup.h> + +#include <string.h> + +#define d(x) + +/** + * e_mail_part_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_part_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: uid of the message within the @folder + * @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 *encoded_message_uid; + 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"; + } + + encoded_message_uid = soup_uri_encode (message_uid, NULL); + tmp = g_strdup_printf ( + "mail://%s/%s/%s", + service_uid, + folder_name, + encoded_message_uid); + g_free (encoded_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)); + va_end (ap); + 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 + * @mime_type: MIME type 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; + EMailParserExtensionClass *class; + + if ((extensions == NULL) || g_queue_is_empty (extensions)) + return FALSE; + + extension = g_queue_peek_head (extensions); + class = E_MAIL_PARSER_EXTENSION_GET_CLASS (extension); + + /* Some types need to override the disposition. + * e.g. application/x-pkcs7-mime */ + if (class->flags & 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 (class->flags & E_MAIL_PARSER_EXTENSION_INLINE) != 0; +} + +/** + * e_mail_part_utils_body_refers: + * @body: text body to search for references in; can be %NULL, then returns %FALSE + * @cid: a Content-ID to search for; if found in body, it should be of form "cid:xxxxx"; can be %NULL + * + * Returns whether @body contains a reference to @cid enclosed in quotes; + * returns %FALSE if any of the arguments is %NULL. + **/ +gboolean +e_mail_part_utils_body_refers (const gchar *body, + const gchar *cid) +{ + const gchar *ptr; + + if (!body || !cid || !*cid) + return FALSE; + + ptr = body; + while (ptr = strstr (ptr, cid), ptr != NULL) { + if (ptr - body > 1 && ptr[-1] == '\"' && ptr[strlen (cid)] == '\"') + return TRUE; + + ptr++; + } + + return FALSE; +} diff --git a/em-format/e-mail-part-utils.h b/em-format/e-mail-part-utils.h new file mode 100644 index 0000000000..54d98e2c0c --- /dev/null +++ b/em-format/e-mail-part-utils.h @@ -0,0 +1,60 @@ +/* + * 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); + +gboolean e_mail_part_utils_body_refers (const gchar *body, + const gchar *cid); +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..c7b07452eb --- /dev/null +++ b/em-format/e-mail-part.c @@ -0,0 +1,613 @@ +/* + * 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/> + * + */ + +/** + * EMailPart: + * + * The #EMailPart is a wrapper around #CamelMimePart which holds additional + * information about the mime part, like it's ID, encryption type etc. + * + * 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. + */ + +#include "e-mail-part.h" + +#include <string.h> + +#include "e-mail-part-list.h" + +#define E_MAIL_PART_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PART, EMailPartPrivate)) + +struct _EMailPartPrivate { + GWeakRef part_list; + CamelMimePart *mime_part; + + gchar *id; + gchar *cid; + gchar *mime_type; + + gboolean is_attachment; +}; + +enum { + PROP_0, + PROP_CID, + PROP_ID, + PROP_IS_ATTACHMENT, + PROP_MIME_PART, + PROP_MIME_TYPE, + PROP_PART_LIST +}; + +G_DEFINE_TYPE_WITH_CODE ( + EMailPart, + e_mail_part, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE ( + E_TYPE_EXTENSIBLE, NULL)) + +static void +mail_part_validity_pair_free (gpointer ptr) +{ + EMailPartValidityPair *pair = ptr; + + if (!pair) + return; + + camel_cipher_validity_free (pair->validity); + g_free (pair); +} + +static void +mail_part_set_id (EMailPart *part, + const gchar *id) +{ + g_return_if_fail (part->priv->id == NULL); + + part->priv->id = g_strdup (id); +} + +static void +mail_part_set_mime_part (EMailPart *part, + CamelMimePart *mime_part) +{ + g_return_if_fail (part->priv->mime_part == NULL); + + /* The CamelMimePart is optional. */ + if (mime_part != NULL) + part->priv->mime_part = g_object_ref (mime_part); +} + +static void +mail_part_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CID: + e_mail_part_set_cid ( + E_MAIL_PART (object), + g_value_get_string (value)); + return; + + case PROP_ID: + mail_part_set_id ( + E_MAIL_PART (object), + g_value_get_string (value)); + return; + + case PROP_IS_ATTACHMENT: + e_mail_part_set_is_attachment ( + E_MAIL_PART (object), + g_value_get_boolean (value)); + return; + + case PROP_MIME_PART: + mail_part_set_mime_part ( + E_MAIL_PART (object), + g_value_get_object (value)); + return; + + case PROP_MIME_TYPE: + e_mail_part_set_mime_type ( + E_MAIL_PART (object), + g_value_get_string (value)); + return; + + case PROP_PART_LIST: + e_mail_part_set_part_list ( + E_MAIL_PART (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CID: + g_value_set_string ( + value, + e_mail_part_get_cid ( + E_MAIL_PART (object))); + return; + + case PROP_ID: + g_value_set_string ( + value, + e_mail_part_get_id ( + E_MAIL_PART (object))); + return; + + case PROP_IS_ATTACHMENT: + g_value_set_boolean ( + value, + e_mail_part_get_is_attachment ( + E_MAIL_PART (object))); + return; + + case PROP_MIME_PART: + g_value_take_object ( + value, + e_mail_part_ref_mime_part ( + E_MAIL_PART (object))); + return; + + case PROP_MIME_TYPE: + g_value_set_string ( + value, + e_mail_part_get_mime_type ( + E_MAIL_PART (object))); + return; + + case PROP_PART_LIST: + g_value_take_object ( + value, + e_mail_part_ref_part_list ( + E_MAIL_PART (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_dispose (GObject *object) +{ + EMailPartPrivate *priv; + + priv = E_MAIL_PART_GET_PRIVATE (object); + + g_weak_ref_set (&priv->part_list, NULL); + + g_clear_object (&priv->mime_part); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_part_parent_class)->dispose (object); +} + +static void +mail_part_finalize (GObject *object) +{ + EMailPart *part = E_MAIL_PART (object); + EMailPartValidityPair *pair; + + g_free (part->priv->id); + g_free (part->priv->cid); + g_free (part->priv->mime_type); + + while ((pair = g_queue_pop_head (&part->validities)) != NULL) + mail_part_validity_pair_free (pair); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_part_parent_class)->finalize (object); +} + +static void +mail_part_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_part_parent_class)->constructed (object); + + e_extensible_load_extensions (E_EXTENSIBLE (object)); +} + +static void +e_mail_part_class_init (EMailPartClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EMailPartPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_part_set_property; + object_class->get_property = mail_part_get_property; + object_class->dispose = mail_part_dispose; + object_class->finalize = mail_part_finalize; + object_class->constructed = mail_part_constructed; + + g_object_class_install_property ( + object_class, + PROP_CID, + g_param_spec_string ( + "cid", + "Content ID", + "The MIME Content-ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_ID, + g_param_spec_string ( + "id", + "Part ID", + "The part ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_IS_ATTACHMENT, + g_param_spec_boolean ( + "is-attachment", + "Is Attachment", + "Format the part as an attachment", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MIME_PART, + g_param_spec_object ( + "mime-part", + "MIME Part", + "The MIME part", + CAMEL_TYPE_MIME_PART, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MIME_TYPE, + g_param_spec_string ( + "mime-type", + "MIME Type", + "The MIME type", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_PART_LIST, + g_param_spec_object ( + "part-list", + "Part List", + "The part list that owns the part", + E_TYPE_MAIL_PART_LIST, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_mail_part_init (EMailPart *part) +{ + part->priv = E_MAIL_PART_GET_PRIVATE (part); +} + +/** + * e_mail_part_new: + * @mime_part: (allow-none) a #CamelMimePart or %NULL + * @id: part ID + * + * Creates a new #EMailPart for the given @mime_part. + * + * Return value: a new #EMailPart + */ +EMailPart * +e_mail_part_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART, + "id", id, "mime-part", mime_part, NULL); +} + +const gchar * +e_mail_part_get_id (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return part->priv->id; +} + +const gchar * +e_mail_part_get_cid (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return part->priv->cid; +} + +void +e_mail_part_set_cid (EMailPart *part, + const gchar *cid) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + g_free (part->priv->cid); + part->priv->cid = g_strdup (cid); + + g_object_notify (G_OBJECT (part), "cid"); +} + +gboolean +e_mail_part_id_has_prefix (EMailPart *part, + const gchar *prefix) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + g_return_val_if_fail (prefix != NULL, FALSE); + + return g_str_has_prefix (part->priv->id, prefix); +} + +gboolean +e_mail_part_id_has_suffix (EMailPart *part, + const gchar *suffix) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + g_return_val_if_fail (suffix != NULL, FALSE); + + return g_str_has_suffix (part->priv->id, suffix); +} + +gboolean +e_mail_part_id_has_substr (EMailPart *part, + const gchar *substr) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + g_return_val_if_fail (substr != NULL, FALSE); + + return (strstr (part->priv->id, substr) != NULL); +} + +CamelMimePart * +e_mail_part_ref_mime_part (EMailPart *part) +{ + CamelMimePart *mime_part = NULL; + + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + if (part->priv->mime_part != NULL) + mime_part = g_object_ref (part->priv->mime_part); + + return mime_part; +} + +const gchar * +e_mail_part_get_mime_type (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return part->priv->mime_type; +} + +void +e_mail_part_set_mime_type (EMailPart *part, + const gchar *mime_type) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (g_strcmp0 (mime_type, part->priv->mime_type) == 0) + return; + + g_free (part->priv->mime_type); + part->priv->mime_type = g_strdup (mime_type); + + g_object_notify (G_OBJECT (part), "mime-type"); +} + +EMailPartList * +e_mail_part_ref_part_list (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return g_weak_ref_get (&part->priv->part_list); +} + +void +e_mail_part_set_part_list (EMailPart *part, + EMailPartList *part_list) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (part_list != NULL) + g_return_if_fail (E_IS_MAIL_PART_LIST (part_list)); + + g_weak_ref_set (&part->priv->part_list, part_list); + + g_object_notify (G_OBJECT (part), "part-list"); +} + +gboolean +e_mail_part_get_is_attachment (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + + return part->priv->is_attachment; +} + +void +e_mail_part_set_is_attachment (EMailPart *part, + gboolean is_attachment) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (is_attachment == part->priv->is_attachment) + return; + + part->priv->is_attachment = is_attachment; + + g_object_notify (G_OBJECT (part), "is-attachment"); +} + +void +e_mail_part_bind_dom_element (EMailPart *part, + WebKitDOMElement *element) +{ + EMailPartClass *class; + + g_return_if_fail (E_IS_MAIL_PART (part)); + g_return_if_fail (WEBKIT_DOM_IS_ELEMENT (element)); + + class = E_MAIL_PART_GET_CLASS (part); + + if (class->bind_dom_element != NULL) + class->bind_dom_element (part, element); +} + +static EMailPartValidityPair * +mail_part_find_validity_pair (EMailPart *part, + EMailPartValidityFlags validity_type) +{ + GList *head, *link; + + head = g_queue_peek_head_link (&part->validities); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (pair == NULL) + continue; + + if ((pair->validity_type & validity_type) == validity_type) + return pair; + } + + return NULL; +} + +/** + * 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. Validities of the same type (PGP or S/MIME) are + * merged together. + */ +void +e_mail_part_update_validity (EMailPart *part, + CamelCipherValidity *validity, + EMailPartValidityFlags validity_type) +{ + EMailPartValidityPair *pair; + EMailPartValidityFlags mask; + + g_return_if_fail (E_IS_MAIL_PART (part)); + + mask = E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SMIME; + + pair = mail_part_find_validity_pair (part, validity_type & mask); + if (pair != NULL) { + pair->validity_type |= validity_type; + camel_cipher_validity_envelope (pair->validity, validity); + } else { + pair = g_new0 (EMailPartValidityPair, 1); + pair->validity_type = validity_type; + pair->validity = camel_cipher_validity_clone (validity); + + g_queue_push_tail (&part->validities, pair); + } +} + +/** + * e_mail_part_get_validity: + * @part: An #EMailPart + * @validity_type: E_MAIL_PART_VALIDITY_* flags + * + * Returns, validity of @part contains any validity with the same bits + * as @validity_type set. It should contain all bits of it. + * + * Returns: a #CamelCipherValidity of the given type, %NULL if not found + * + * Since: 3.8 + */ +CamelCipherValidity * +e_mail_part_get_validity (EMailPart *part, + EMailPartValidityFlags validity_type) +{ + EMailPartValidityPair *pair; + + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + pair = mail_part_find_validity_pair (part, validity_type); + + return (pair != NULL) ? pair->validity : NULL; +} + +gboolean +e_mail_part_has_validity (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + + return !g_queue_is_empty (&part->validities); +} + +EMailPartValidityFlags +e_mail_part_get_validity_flags (EMailPart *part) +{ + EMailPartValidityFlags flags = 0; + GList *head, *link; + + g_return_val_if_fail (E_IS_MAIL_PART (part), 0); + + head = g_queue_peek_head_link (&part->validities); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (pair != NULL) + flags |= pair->validity_type; + } + + return flags; +} + diff --git a/em-format/e-mail-part.h b/em-format/e-mail-part.h new file mode 100644 index 0000000000..75057c2ee5 --- /dev/null +++ b/em-format/e-mail-part.h @@ -0,0 +1,132 @@ +/* + * 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 <webkit/webkitdom.h> + +#include <e-util/e-util.h> + +#include <em-format/e-mail-formatter-enums.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_PART \ + (e_mail_part_get_type ()) +#define E_MAIL_PART(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_PART, EMailPart)) +#define E_MAIL_PART_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_PART, EMailPartClass)) +#define E_IS_MAIL_PART(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_PART)) +#define E_IS_MAIL_PART_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_PART)) +#define E_MAIL_PART_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_PART, EMailPartClass)) + +G_BEGIN_DECLS + +struct _EMailPartList; + +typedef struct _EMailPart EMailPart; +typedef struct _EMailPartClass EMailPartClass; +typedef struct _EMailPartPrivate EMailPartPrivate; + +typedef struct _EMailPartValidityPair EMailPartValidityPair; + +struct _EMailPartValidityPair { + EMailPartValidityFlags validity_type; + CamelCipherValidity *validity; +}; + +struct _EMailPart { + GObject parent; + EMailPartPrivate *priv; + + GQueue validities; /* element-type: EMailPartValidityPair */ + + /* 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; +}; + +struct _EMailPartClass { + GObjectClass parent_class; + + void (*bind_dom_element) (EMailPart *part, + WebKitDOMElement *element); +}; + +GType e_mail_part_get_type (void) G_GNUC_CONST; +EMailPart * e_mail_part_new (CamelMimePart *mime_part, + const gchar *id); +const gchar * e_mail_part_get_id (EMailPart *part); +const gchar * e_mail_part_get_cid (EMailPart *part); +void e_mail_part_set_cid (EMailPart *part, + const gchar *cid); +gboolean e_mail_part_id_has_prefix (EMailPart *part, + const gchar *prefix); +gboolean e_mail_part_id_has_suffix (EMailPart *part, + const gchar *suffix); +gboolean e_mail_part_id_has_substr (EMailPart *part, + const gchar *substr); +CamelMimePart * e_mail_part_ref_mime_part (EMailPart *part); +const gchar * e_mail_part_get_mime_type (EMailPart *part); +void e_mail_part_set_mime_type (EMailPart *part, + const gchar *mime_type); +struct _EMailPartList * + e_mail_part_ref_part_list (EMailPart *part); +void e_mail_part_set_part_list (EMailPart *part, + struct _EMailPartList *part_list); +gboolean e_mail_part_get_is_attachment (EMailPart *part); +void e_mail_part_set_is_attachment (EMailPart *part, + gboolean is_attachment); +void e_mail_part_bind_dom_element (EMailPart *part, + WebKitDOMElement *element); +void e_mail_part_update_validity (EMailPart *part, + CamelCipherValidity *validity, + EMailPartValidityFlags validity_type); +CamelCipherValidity * + e_mail_part_get_validity (EMailPart *part, + EMailPartValidityFlags validity_type); +gboolean e_mail_part_has_validity (EMailPart *part); +EMailPartValidityFlags + e_mail_part_get_validity_flags (EMailPart *part); + +G_END_DECLS + +#endif /* E_MAIL_PART_H */ diff --git a/em-format/e-mail-stripsig-filter.c b/em-format/e-mail-stripsig-filter.c new file mode 100644 index 0000000000..e861a13a53 --- /dev/null +++ b/em-format/e-mail-stripsig-filter.c @@ -0,0 +1,165 @@ +/* + * + * 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 <stdio.h> +#include <string.h> + +#include "e-mail-stripsig-filter.h" + +G_DEFINE_TYPE (EMailStripSigFilter, e_mail_stripsig_filter, CAMEL_TYPE_MIME_FILTER) + +static void +strip_signature (CamelMimeFilter *filter, + const gchar *in, + gsize len, + gsize prespace, + gchar **out, + gsize *outlen, + gsize *outprespace, + gint flush) +{ + EMailStripSigFilter *stripsig = (EMailStripSigFilter *) filter; + register const gchar *inptr = in; + const gchar *inend = in + len; + const gchar *start = NULL; + + if (stripsig->midline) { + while (inptr < inend && *inptr != '\n') + inptr++; + + if (inptr < inend) { + stripsig->midline = FALSE; + inptr++; + } + } + + while (inptr < inend) { + if ((inend - inptr) >= 4 && !strncmp (inptr, "-- \n", 4)) { + start = inptr; + inptr += 4; + } else if (!stripsig->text_plain_only && + (inend - inptr) >= 7 && + !g_ascii_strncasecmp (inptr, "-- <BR>", 7)) { + start = inptr; + inptr += 7; + } else { + while (inptr < inend && *inptr != '\n') + inptr++; + + if (inptr == inend) { + stripsig->midline = TRUE; + break; + } + + inptr++; + } + } + + if (start != NULL) { + inptr = start; + stripsig->midline = FALSE; + } + + if (!flush && inend > inptr) + camel_mime_filter_backup (filter, inptr, inend - inptr); + else if (!start) + inptr = inend; + + *out = (gchar *)in; + *outlen = inptr - in; + *outprespace = prespace; +} + +static void +filter_filter (CamelMimeFilter *filter, + const gchar *in, + gsize len, + gsize prespace, + gchar **out, + gsize *outlen, + gsize *outprespace) +{ + strip_signature ( + filter, in, len, prespace, out, outlen, outprespace, FALSE); +} + +static void +filter_complete (CamelMimeFilter *filter, + const gchar *in, + gsize len, + gsize prespace, + gchar **out, + gsize *outlen, + gsize *outprespace) +{ + strip_signature ( + filter, in, len, prespace, out, outlen, outprespace, TRUE); +} + +/* should this 'flush' outstanding state/data bytes? */ +static void +filter_reset (CamelMimeFilter *filter) +{ + EMailStripSigFilter *stripsig = (EMailStripSigFilter *) filter; + + stripsig->midline = FALSE; +} + +static void +e_mail_stripsig_filter_class_init (EMailStripSigFilterClass *class) +{ + CamelMimeFilterClass *mime_filter_class; + + mime_filter_class = CAMEL_MIME_FILTER_CLASS (class); + mime_filter_class->filter = filter_filter; + mime_filter_class->complete = filter_complete; + mime_filter_class->reset = filter_reset; +} + +static void +e_mail_stripsig_filter_init (EMailStripSigFilter *filter) +{ +} + +/** + * 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>". + * + * Creates a new stripsig filter. + * + * Returns a new stripsig filter. + **/ +CamelMimeFilter * +e_mail_stripsig_filter_new (gboolean text_plain_only) +{ + EMailStripSigFilter *filter = g_object_new (E_TYPE_MAIL_STRIPSIG_FILTER, NULL); + + filter->text_plain_only = text_plain_only; + + return CAMEL_MIME_FILTER (filter); +} diff --git a/em-format/e-mail-stripsig-filter.h b/em-format/e-mail-stripsig-filter.h new file mode 100644 index 0000000000..75d3719133 --- /dev/null +++ b/em-format/e-mail-stripsig-filter.h @@ -0,0 +1,69 @@ +/* + * 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 E_MAIL_STRIPSIG_FILTER_H +#define E_MAIL_STRIPSIG_FILTER_H + +#include <camel/camel.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_STRIPSIG_FILTER \ + (e_mail_stripsig_filter_get_type ()) +#define E_MAIL_STRIPSIG_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_STRIPSIG_FILTER, EMailStripSigFilter)) +#define E_MAIL_STRIPSIG_FILTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_STRIPSIG_FILTER, EMailStripSigFilterClass)) +#define E_IS_MAIL_STRIPSIG_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_STRIPSIG_FILTER)) +#define E_IS_MAIL_STRIPSIG_FILTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_STRIPSIG_FILTER)) +#define E_MAIL_STRIPSIG_FILTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_STRIPSIG_FILTER, EMailStripSigFilterClass)) + +G_BEGIN_DECLS + +typedef struct _EMailStripSigFilter EMailStripSigFilter; +typedef struct _EMailStripSigFilterClass EMailStripSigFilterClass; + +struct _EMailStripSigFilter { + CamelMimeFilter parent; + + guint32 midline : 1; + guint32 text_plain_only : 1; +}; + +struct _EMailStripSigFilterClass { + CamelMimeFilterClass parent_class; +}; + +GType e_mail_stripsig_filter_get_type (void); +CamelMimeFilter * + e_mail_stripsig_filter_new (gboolean text_plain_only); + +G_END_DECLS + +#endif /* E_MAIL_STRIPSIG_FILTER_H */ |