diff options
author | Dan Vrátil <dvratil@redhat.com> | 2012-06-06 21:27:19 +0800 |
---|---|---|
committer | Dan Vrátil <dvratil@redhat.com> | 2012-06-06 21:27:19 +0800 |
commit | 5b8340563c271fb684a88c6e5bb6dd3bfb629058 (patch) | |
tree | c1c7d606fb4ce9fd2fe459a9226bfb9125423991 /em-format/e-mail-formatter-headers.c | |
parent | 26a4f24188fd89dbabaff192bec9c54af8fe5a80 (diff) | |
download | gsoc2013-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.c | 615 |
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) +{ + +} |