aboutsummaryrefslogtreecommitdiffstats
path: root/em-format/e-mail-formatter-headers.c
diff options
context:
space:
mode:
authorDan Vrátil <dvratil@redhat.com>2012-06-06 21:27:19 +0800
committerDan Vrátil <dvratil@redhat.com>2012-06-06 21:27:19 +0800
commit5b8340563c271fb684a88c6e5bb6dd3bfb629058 (patch)
treec1c7d606fb4ce9fd2fe459a9226bfb9125423991 /em-format/e-mail-formatter-headers.c
parent26a4f24188fd89dbabaff192bec9c54af8fe5a80 (diff)
downloadgsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.gz
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.zst
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.zip
Mail formatter rewrite
All mail-parsing and formatting code has been moved to em-format. Parsing is handeled by EMailParser class, formatting by EMailFormatter. Both classes have registry which hold extensions - simple classes that do actual parsing and formatting. Each supported mime-type has it's own parser and formatter extension class.
Diffstat (limited to 'em-format/e-mail-formatter-headers.c')
-rw-r--r--em-format/e-mail-formatter-headers.c615
1 files changed, 615 insertions, 0 deletions
diff --git a/em-format/e-mail-formatter-headers.c b/em-format/e-mail-formatter-headers.c
new file mode 100644
index 0000000000..24d27139b5
--- /dev/null
+++ b/em-format/e-mail-formatter-headers.c
@@ -0,0 +1,615 @@
+/*
+ * e-mail-formatter-headers.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-format-extensions.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <em-format/e-mail-formatter-extension.h>
+#include <em-format/e-mail-formatter.h>
+#include <em-format/e-mail-formatter-utils.h>
+#include <em-format/e-mail-inline-filter.h>
+#include <libemail-engine/e-mail-utils.h>
+#include <libedataserver/libedataserver.h>
+#include <e-util/e-util.h>
+#include <shell/e-shell.h>
+
+#include <camel/camel.h>
+
+#include <string.h>
+
+typedef struct _EMailFormatterHeaders {
+ GObject parent;
+} EMailFormatterHeaders;
+
+typedef struct _EMailFormatterHeadersClass {
+ GObjectClass parent_class;
+} EMailFormatterHeadersClass;
+
+static const gchar *formatter_mime_types[] = { "application/vnd.evolution.headers", NULL };
+
+static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface);
+static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (
+ EMailFormatterHeaders,
+ e_mail_formatter_headers,
+ G_TYPE_OBJECT,
+ 0,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_MAIL_EXTENSION,
+ e_mail_formatter_mail_extension_interface_init)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_MAIL_FORMATTER_EXTENSION,
+ e_mail_formatter_formatter_extension_interface_init))
+
+static void
+format_short_headers (EMailFormatter *formatter,
+ GString *buffer,
+ CamelMedium *part,
+ guint32 flags,
+ GCancellable *cancellable)
+{
+ const gchar *charset;
+ CamelContentType *ct;
+ const gchar *hdr_charset;
+ gchar *evolution_imagesdir;
+ gchar *subject = NULL;
+ struct _camel_header_address *addrs = NULL;
+ struct _camel_header_raw *header;
+ GString *from;
+ gboolean is_rtl;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
+ charset = camel_content_type_param (ct, "charset");
+ charset = camel_iconv_charset_name (charset);
+ hdr_charset = e_mail_formatter_get_charset (formatter) ?
+ e_mail_formatter_get_charset (formatter) :
+ e_mail_formatter_get_default_charset (formatter);
+
+ evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+ from = g_string_new ("");
+
+ g_string_append_printf (buffer,
+ "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" "
+ "id=\"__evo-short-headers\" style=\"display: %s\">",
+ flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED ? "block" : "none");
+
+ header = ((CamelMimePart *) part)->headers;
+ while (header) {
+ if (!g_ascii_strcasecmp (header->name, "From")) {
+ GString *tmp;
+ if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) {
+ header = header->next;
+ continue;
+ }
+ tmp = g_string_new ("");
+ e_mail_formatter_format_address (
+ formatter, tmp, addrs, header->name, FALSE,
+ !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE));
+
+ if (tmp->len)
+ g_string_printf (from, _("From: %s"), tmp->str);
+ g_string_free (tmp, TRUE);
+
+ } else if (!g_ascii_strcasecmp (header->name, "Subject")) {
+ gchar *buf = NULL;
+ subject = camel_header_unfold (header->value);
+ buf = camel_header_decode_string (subject, hdr_charset);
+ g_free (subject);
+ subject = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
+ g_free (buf);
+ }
+ header = header->next;
+ }
+
+ is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
+ if (is_rtl) {
+ g_string_append_printf (
+ buffer,
+ "<tr><td width=\"100%%\" align=\"right\">%s%s%s <strong>%s</strong></td></tr>",
+ from->len ? "(" : "", from->str, from->len ? ")" : "",
+ subject ? subject : _("(no subject)"));
+ } else {
+ g_string_append_printf (
+ buffer,
+ "<tr><td><strong>%s</strong> %s%s%s</td></tr>",
+ subject ? subject : _("(no subject)"),
+ from->len ? "(" : "", from->str, from->len ? ")" : "");
+ }
+
+ g_string_append (buffer, "</table>");
+
+ g_free (subject);
+ if (addrs)
+ camel_header_address_list_clear (&addrs);
+
+ g_string_free (from, TRUE);
+ g_free (evolution_imagesdir);
+}
+
+static void
+write_contact_picture (CamelMimePart *part,
+ gint size,
+ GString *buffer)
+{
+ gchar *b64, *content_type;
+ CamelDataWrapper *dw;
+ CamelContentType *ct;
+ GByteArray *ba;
+
+ ba = NULL;
+ dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+ if (dw) {
+ ba = camel_data_wrapper_get_byte_array (dw);
+ }
+
+ if (!ba || ba->len == 0) {
+
+ if (camel_mime_part_get_filename (part)) {
+
+ if (size >= 0) {
+ g_string_append_printf (
+ buffer,
+ "<img width=\"%d\" src=\"evo-file://%s\" />",
+ size, camel_mime_part_get_filename (part));
+ } else {
+ g_string_append_printf (
+ buffer,
+ "<img src=\"evo-file://%s\" />",
+ camel_mime_part_get_filename (part));
+ }
+ }
+
+ return;
+ }
+
+ b64 = g_base64_encode (ba->data, ba->len);
+ ct = camel_mime_part_get_content_type (part);
+ content_type = camel_content_type_simple (ct);
+
+ if (size >= 0) {
+ g_string_append_printf (
+ buffer,
+ "<img width=\"%d\" src=\"data:%s;base64,%s\">",
+ size, content_type, b64);
+ } else {
+ g_string_append_printf (
+ buffer,
+ "<img src=\"data:%s;base64,%s\">",
+ content_type, b64);
+ }
+
+ g_free (b64);
+ g_free (content_type);
+}
+
+static CamelMimePart *
+load_picture_from_file (const gchar *mime_type,
+ const gchar *filename,
+ GCancellable *cancellable)
+{
+ CamelMimePart *part;
+ CamelStream *stream;
+ CamelDataWrapper *dw;
+ gchar *basename;
+
+ stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL);
+ if (stream == NULL)
+ return NULL;
+
+ dw = camel_data_wrapper_new ();
+ camel_data_wrapper_construct_from_stream_sync (
+ dw, stream, cancellable, NULL);
+ g_object_unref (stream);
+ if (mime_type)
+ camel_data_wrapper_set_mime_type (dw, mime_type);
+ part = camel_mime_part_new ();
+ camel_medium_set_content ((CamelMedium *) part, dw);
+ g_object_unref (dw);
+ basename = g_path_get_basename (filename);
+ camel_mime_part_set_filename (part, basename);
+ g_free (basename);
+
+ return part;
+}
+
+static void
+format_full_headers (EMailFormatter *formatter,
+ GString *buffer,
+ CamelMedium *part,
+ guint32 flags,
+ GCancellable *cancellable)
+{
+ const gchar *charset;
+ CamelContentType *ct;
+ struct _camel_header_raw *header;
+ gboolean have_icon = FALSE;
+ const gchar *photo_name = NULL;
+ CamelInternetAddress *cia = NULL;
+ EShell *shell;
+ ESourceRegistry *registry;
+ gboolean face_decoded = FALSE, contact_has_photo = FALSE;
+ guchar *face_header_value = NULL;
+ gsize face_header_len = 0;
+ gchar *header_sender = NULL, *header_from = NULL, *name;
+ gboolean mail_from_delegate = FALSE;
+ const gchar *hdr_charset;
+ gchar *evolution_imagesdir;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ shell = e_shell_get_default ();
+ registry = e_shell_get_registry (shell);
+
+ ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
+ charset = camel_content_type_param (ct, "charset");
+ charset = camel_iconv_charset_name (charset);
+ hdr_charset = e_mail_formatter_get_charset (formatter) ?
+ e_mail_formatter_get_charset (formatter) :
+ e_mail_formatter_get_default_charset (formatter);
+
+ evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+
+ g_string_append_printf (buffer,
+ "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" "
+ "id=\"__evo-full-headers\" style=\"display: %s\" width=\"100%%\">",
+ flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED ? "none" : "block");
+
+ header = ((CamelMimePart *) part)->headers;
+ while (header) {
+ if (!g_ascii_strcasecmp (header->name, "Sender")) {
+ struct _camel_header_address *addrs;
+ GString *html;
+
+ if (!(addrs = camel_header_address_decode (header->value, hdr_charset)))
+ break;
+
+ html = g_string_new("");
+ name = e_mail_formatter_format_address (
+ formatter, html, addrs, header->name, FALSE,
+ ~(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE));
+
+ header_sender = html->str;
+ camel_header_address_list_clear (&addrs);
+
+ g_string_free (html, FALSE);
+ g_free (name);
+ } else if (!g_ascii_strcasecmp (header->name, "From")) {
+ struct _camel_header_address *addrs;
+ GString *html;
+
+ if (!(addrs = camel_header_address_decode (header->value, hdr_charset)))
+ break;
+
+ html = g_string_new("");
+ name = e_mail_formatter_format_address (
+ formatter, html, addrs, header->name, FALSE,
+ !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE));
+
+ header_from = html->str;
+ camel_header_address_list_clear (&addrs);
+
+ g_string_free (html, FALSE);
+ g_free (name);
+ } else if (!g_ascii_strcasecmp (header->name, "X-Evolution-Mail-From-Delegate")) {
+ mail_from_delegate = TRUE;
+ }
+
+ header = header->next;
+ }
+
+ if (header_sender && header_from && mail_from_delegate) {
+ gchar *bold_sender, *bold_from;
+
+ g_string_append (
+ buffer,
+ "<tr><td><table border=1 width=\"100%%\" "
+ "cellspacing=2 cellpadding=2><tr>");
+ if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
+ g_string_append (
+ buffer, "<td align=\"right\" width=\"100%%\">");
+ else
+ g_string_append (
+ buffer, "<td align=\"left\" width=\"100%%\">");
+ bold_sender = g_strconcat ("<b>", header_sender, "</b>", NULL);
+ bold_from = g_strconcat ("<b>", header_from, "</b>", NULL);
+ /* Translators: This message suggests to the receipients
+ * that the sender of the mail is different from the one
+ * listed in From field. */
+ g_string_append_printf (
+ buffer,
+ _("This message was sent by %s on behalf of %s"),
+ bold_sender, bold_from);
+ g_string_append (buffer, "</td></tr></table></td></tr>");
+ g_free (bold_sender);
+ g_free (bold_from);
+ }
+
+ g_free (header_sender);
+ g_free (header_from);
+
+ g_string_append (buffer, "<tr><td width=\"100%%\"><table border=0 cellpadding=\"0\">\n");
+
+ g_free (evolution_imagesdir);
+
+ /* dump selected headers */
+ if (flags & E_MAIL_FORMATTER_MODE_ALL_HEADERS) {
+ header = ((CamelMimePart *) part)->headers;
+ while (header) {
+ e_mail_formatter_format_header (
+ formatter, buffer, part, header,
+ E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS, charset);
+ header = header->next;
+ }
+ } else {
+ GList *link;
+ gint mailer_shown = FALSE;
+
+ link = g_queue_peek_head_link (
+ (GQueue *) e_mail_formatter_get_headers (formatter));
+
+ while (link != NULL) {
+ EMailFormatterHeader *h = link->data;
+ gint mailer, face;
+
+ header = ((CamelMimePart *) part)->headers;
+ mailer = !g_ascii_strcasecmp (h->name, "X-Evolution-Mailer");
+ face = !g_ascii_strcasecmp (h->name, "Face");
+
+ while (header) {
+ if (e_mail_formatter_get_show_sender_photo (formatter) &&
+ !photo_name && !g_ascii_strcasecmp (header->name, "From"))
+ photo_name = header->value;
+
+ if (!mailer_shown && mailer && (
+ !g_ascii_strcasecmp (header->name, "X-Mailer") ||
+ !g_ascii_strcasecmp (header->name, "User-Agent") ||
+ !g_ascii_strcasecmp (header->name, "X-Newsreader") ||
+ !g_ascii_strcasecmp (header->name, "X-MimeOLE"))) {
+ struct _camel_header_raw xmailer, *use_header = NULL;
+
+ if (!g_ascii_strcasecmp (header->name, "X-MimeOLE")) {
+ for (use_header = header->next; use_header; use_header = use_header->next) {
+ if (!g_ascii_strcasecmp (use_header->name, "X-Mailer") ||
+ !g_ascii_strcasecmp (use_header->name, "User-Agent") ||
+ !g_ascii_strcasecmp (use_header->name, "X-Newsreader")) {
+ /* even we have X-MimeOLE, then use rather the standard one, when available */
+ break;
+ }
+ }
+ }
+
+ if (!use_header)
+ use_header = header;
+
+ xmailer.name = (gchar *) "X-Evolution-Mailer";
+ xmailer.value = use_header->value;
+ mailer_shown = TRUE;
+
+ e_mail_formatter_format_header (
+ formatter, buffer, part,
+ &xmailer, h->flags, charset);
+ if (strstr(use_header->value, "Evolution"))
+ have_icon = TRUE;
+ } else if (!face_decoded && face && !g_ascii_strcasecmp (header->name, "Face")) {
+ gchar *cp = header->value;
+
+ /* Skip over spaces */
+ while (*cp == ' ')
+ cp++;
+
+ face_header_value = g_base64_decode (
+ cp, &face_header_len);
+ face_header_value = g_realloc (
+ face_header_value,
+ face_header_len + 1);
+ face_header_value[face_header_len] = 0;
+ face_decoded = TRUE;
+ /* Showing an encoded "Face" header makes little sense */
+ } else if (!g_ascii_strcasecmp (header->name, h->name) && !face) {
+ e_mail_formatter_format_header (
+ formatter, buffer, part,
+ header, h->flags, charset);
+ }
+
+ header = header->next;
+ }
+
+ link = g_list_next (link);
+ }
+ }
+
+ g_string_append (buffer, "</table></td>");
+
+ if (photo_name) {
+ CamelMimePart *photopart;
+ gboolean only_local_photo;
+
+ cia = camel_internet_address_new ();
+ camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name);
+ only_local_photo = e_mail_formatter_get_only_local_photos (formatter);
+ photopart = em_utils_contact_photo (
+ registry, cia, only_local_photo);
+
+ if (photopart) {
+ g_string_append (buffer, "<td align=\"right\" valign=\"top\">");
+ write_contact_picture (photopart, -1, buffer);
+ g_string_append (buffer, "</td>");
+ g_object_unref (photopart);
+ }
+ g_object_unref (cia);
+ }
+
+ if (!contact_has_photo && face_decoded) {
+ CamelMimePart *part;
+
+ part = camel_mime_part_new ();
+ camel_mime_part_set_content (
+ (CamelMimePart *) part,
+ (const gchar *) face_header_value,
+ face_header_len, "image/png");
+
+ g_string_append (buffer, "<td align=\"right\" valign=\"top\">");
+ write_contact_picture (part, 48, buffer);
+ g_string_append (buffer, "</td>");
+
+ g_object_unref (part);
+ g_free (face_header_value);
+ }
+
+ if (have_icon) {
+ GtkIconInfo *icon_info;
+ CamelMimePart *iconpart = NULL;
+
+ icon_info = gtk_icon_theme_lookup_icon (
+ gtk_icon_theme_get_default (),
+ "evolution", 16, GTK_ICON_LOOKUP_NO_SVG);
+ if (icon_info != NULL) {
+ iconpart = load_picture_from_file (
+ "image/png", gtk_icon_info_get_filename (icon_info),
+ cancellable);
+ gtk_icon_info_free (icon_info);
+ }
+ if (iconpart) {
+ g_string_append (buffer, "<td align=\"right\" valign=\"top\">");
+ write_contact_picture (iconpart, 16, buffer);
+ g_string_append (buffer, "</td>");
+
+ g_object_unref (iconpart);
+ }
+ }
+
+ g_string_append (buffer, "</tr></table>");
+}
+
+static gboolean
+emfe_headers_format (EMailFormatterExtension *extension,
+ EMailFormatter *formatter,
+ EMailFormatterContext *context,
+ EMailPart *part,
+ CamelStream *stream,
+ GCancellable *cancellable)
+{
+ GString *buffer;
+ gint bg_color;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return FALSE;
+
+ if (!part->part)
+ return FALSE;
+
+ buffer = g_string_new ("");
+
+ if (context->mode == E_MAIL_FORMATTER_MODE_PRINTING) {
+ GdkColor white = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 };
+ bg_color = e_color_to_value (&white);
+ } else {
+ bg_color = e_color_to_value ((GdkColor *)
+ e_mail_formatter_get_color (
+ formatter, E_MAIL_FORMATTER_COLOR_BODY));
+ }
+
+ g_string_append_printf (
+ buffer,
+ "<div class=\"headers\" style=\"background: #%06x;\">"
+ "<table border=\"0\" width=\"100%%\" style=\"color: #%06x;\">\n"
+ "<tr><td valign=\"top\" width=\"16\">\n",
+ bg_color,
+ e_color_to_value ((GdkColor *)
+ e_mail_formatter_get_color (
+ formatter,
+ E_MAIL_FORMATTER_COLOR_HEADER)));
+
+ if (context->flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSABLE) {
+ g_string_append_printf (buffer,
+ "<img src=\"evo-file://%s/%s\" class=\"navigable\" "
+ "id=\"__evo-collapse-headers-img\" />"
+ "</td><td>",
+ EVOLUTION_IMAGESDIR,
+ (context->flags & E_MAIL_FORMATTER_HEADER_FLAG_COLLAPSED) ?
+ "plus.png" : "minus.png");
+
+ format_short_headers (formatter, buffer,
+ (CamelMedium *) part->part, context->flags, cancellable);
+ }
+
+ format_full_headers (formatter, buffer,
+ (CamelMedium *) part->part, context->flags, cancellable);
+
+ g_string_append (buffer, "</td></tr></table></div>");
+
+ camel_stream_write_string (stream, buffer->str, cancellable, NULL);
+
+ g_string_free (buffer, TRUE);
+
+ return TRUE;
+}
+
+static const gchar *
+emfe_headers_get_display_name (EMailFormatterExtension *extension)
+{
+ return NULL;
+}
+
+static const gchar *
+emfe_headers_get_description (EMailFormatterExtension *extension)
+{
+ return NULL;
+}
+
+static const gchar **
+emfe_headers_mime_types (EMailExtension *extension)
+{
+ return formatter_mime_types;
+}
+
+static void
+e_mail_formatter_headers_class_init (EMailFormatterHeadersClass *klass)
+{
+ e_mail_formatter_headers_parent_class = g_type_class_peek_parent (klass);
+}
+
+static void
+e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface)
+{
+ iface->format = emfe_headers_format;
+ iface->get_display_name = emfe_headers_get_display_name;
+ iface->get_description = emfe_headers_get_description;
+}
+
+static void
+e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface)
+{
+ iface->mime_types = emfe_headers_mime_types;
+}
+
+static void
+e_mail_formatter_headers_init (EMailFormatterHeaders *formatter)
+{
+
+}