diff options
Diffstat (limited to 'mail/em-format-html.c')
-rw-r--r-- | mail/em-format-html.c | 3011 |
1 files changed, 0 insertions, 3011 deletions
diff --git a/mail/em-format-html.c b/mail/em-format-html.c deleted file mode 100644 index bb80d3957b..0000000000 --- a/mail/em-format-html.c +++ /dev/null @@ -1,3011 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the program; if not, see <http://www.gnu.org/licenses/> - * - * - * Authors: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#define _GNU_SOURCE /* Enable strcasestr in string.h */ - -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#include <gtk/gtk.h> -#ifdef G_OS_WIN32 -/* Work around 'DATADIR' and 'interface' lossage in <windows.h> */ -#define DATADIR crap_DATADIR -#include <windows.h> -#undef DATADIR -#undef interface -#endif - -#include <libebackend/libebackend.h> - -#include "e-util/e-datetime-format.h" -#include "e-util/e-icon-factory.h" -#include "e-util/e-util-private.h" -#include "e-util/e-util.h" -#include "misc/e-web-view.h" - -#include <shell/e-shell.h> - -#include <glib/gi18n.h> - -#include <JavaScriptCore/JavaScript.h> -#include <webkit/webkit.h> - -#include <libemail-utils/mail-mt.h> -#include <libemail-engine/e-mail-enumtypes.h> -#include <libemail-engine/e-mail-utils.h> -#include <libemail-engine/mail-config.h> - -#include "em-format-html.h" -#include "em-utils.h" -#include "e-mail-display.h" -#include <em-format/em-inline-filter.h> - -#define EM_FORMAT_HTML_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), EM_TYPE_FORMAT_HTML, EMFormatHTMLPrivate)) - -#define d(x) - -struct _EMFormatHTMLPrivate { - GdkColor colors[EM_FORMAT_HTML_NUM_COLOR_TYPES]; - EMailImageLoadingPolicy image_loading_policy; - - guint can_load_images : 1; - guint only_local_photos : 1; - guint show_sender_photo : 1; - guint show_real_date : 1; - guint animate_images : 1; -}; - -static gpointer parent_class; - -enum { - PROP_0, - PROP_BODY_COLOR, - PROP_CITATION_COLOR, - PROP_CONTENT_COLOR, - PROP_FRAME_COLOR, - PROP_HEADER_COLOR, - PROP_IMAGE_LOADING_POLICY, - PROP_MARK_CITATIONS, - PROP_ONLY_LOCAL_PHOTOS, - PROP_SHOW_SENDER_PHOTO, - PROP_SHOW_REAL_DATE, - PROP_TEXT_COLOR, - PROP_ANIMATE_IMAGES -}; - -#define EFM_MESSAGE_START_ANAME "evolution_message_start" -#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>" - -static void efh_parse_image (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_text_enriched (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_text_plain (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_text_html (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_message_external (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_message_deliverystatus (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); -static void efh_parse_message_rfc822 (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable); - -static void efh_write_image (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_text_enriched (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_text_plain (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_text_html (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_source (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_headers (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_error (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); -static void efh_write_message_rfc822 (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -static void efh_format_full_headers (EMFormatHTML *efh, GString *buffer, CamelMedium *part, gboolean all_headers, gboolean visible, GCancellable *cancellable); -static void efh_format_short_headers (EMFormatHTML *efh, GString *buffer, CamelMedium *part, gboolean visible, GCancellable *cancellable); - -static void efh_write_message (EMFormat *emf, GList *puris, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable); - -/*****************************************************************************/ -static void -efh_parse_image (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - const gchar *tmp; - gchar *cid; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - tmp = camel_mime_part_get_content_id (part); - if (!tmp) { - em_format_parse_part_as (emf, part, part_id, info, - "x-evolution/message/attachment", cancellable); - return; - } - - cid = g_strdup_printf ("cid:%s", tmp); - len = part_id->len; - g_string_append (part_id, ".image"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->cid = cid; - puri->write_func = efh_write_image; - puri->mime_type = g_strdup (info->handler->mime_type); - puri->is_attachment = TRUE; - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_text_enriched (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - const gchar *tmp; - gchar *cid; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - tmp = camel_mime_part_get_content_id (part); - if (!tmp) { - cid = g_strdup_printf ("em-no-cid:%s", part_id->str); - } else { - cid = g_strdup_printf ("cid:%s", tmp); - } - - len = part_id->len; - g_string_append (part_id, ".text_enriched"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->cid = cid; - puri->mime_type = g_strdup (info->handler->mime_type); - puri->write_func = efh_write_text_enriched; - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_text_plain (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - CamelStream *filtered_stream, *null; - CamelMultipart *mp; - CamelDataWrapper *dw; - CamelContentType *type; - gint i, count, len; - EMInlineFilter *inline_filter; - gboolean charset_added = FALSE; - const gchar *snoop_type = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - dw = camel_medium_get_content ((CamelMedium *) part); - if (!dw) - return; - - /* This scans the text part for inline-encoded data, creates - * a multipart of all the parts inside it. */ - - /* FIXME: We should discard this multipart if it only contains - * the original text, but it makes this hash lookup more complex */ - - /* TODO: We could probably put this in the superclass, since - * no knowledge of html is required - but this messes with - * filters a bit. Perhaps the superclass should just deal with - * html anyway and be done with it ... */ - - if (!dw->mime_type) - snoop_type = em_format_snoop_type (part); - - /* if we had to snoop the part type to get here, then - * use that as the base type, yuck */ - if (snoop_type == NULL - || (type = camel_content_type_decode (snoop_type)) == NULL) { - type = dw->mime_type; - camel_content_type_ref (type); - } - - if (dw->mime_type && type != dw->mime_type && camel_content_type_param (dw->mime_type, "charset")) { - camel_content_type_set_param (type, "charset", camel_content_type_param (dw->mime_type, "charset")); - charset_added = TRUE; - } - - null = camel_stream_null_new (); - filtered_stream = camel_stream_filter_new (null); - g_object_unref (null); - inline_filter = em_inline_filter_new (camel_mime_part_get_encoding (part), type); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), - CAMEL_MIME_FILTER (inline_filter)); - camel_data_wrapper_decode_to_stream_sync ( - dw, (CamelStream *) filtered_stream, cancellable, NULL); - camel_stream_close ((CamelStream *) filtered_stream, cancellable, NULL); - g_object_unref (filtered_stream); - - mp = em_inline_filter_get_multipart (inline_filter); - - if (charset_added) { - camel_content_type_set_param (type, "charset", NULL); - } - - g_object_unref (inline_filter); - camel_content_type_unref (type); - - /* We handle our made-up multipart here, so we don't recursively call ourselves */ - len = part_id->len; - count = camel_multipart_get_number (mp); - for (i = 0; i < count; i++) { - CamelMimePart *newpart = camel_multipart_get_part (mp, i); - - if (!newpart) - continue; - - type = camel_mime_part_get_content_type (newpart); - if (camel_content_type_is (type, "text", "*") && (!camel_content_type_is (type, "text", "calendar"))) { - gint s_len = part_id->len; - - g_string_append (part_id, ".plain_text"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), newpart, part_id->str); - puri->write_func = efh_write_text_plain; - puri->mime_type = g_strdup ("text/html"); - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - g_string_truncate (part_id, s_len); - em_format_add_puri (emf, puri); - } else { - g_string_append_printf (part_id, ".inline.%d", i); - em_format_parse_part (emf, CAMEL_MIME_PART (newpart), part_id, info, cancellable); - g_string_truncate (part_id, len); - } - } - - g_object_unref (mp); -} - -static void -efh_parse_text_html (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - const gchar *location; - gchar *cid = NULL; - CamelURL *base; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - base = em_format_get_base_url (emf); - location = camel_mime_part_get_content_location (part); - if (location == NULL) { - if (base) - cid = camel_url_to_string (base, 0); - else - cid = g_strdup (part_id->str); - } else { - if (strchr (location, ':') == NULL && base != NULL) { - CamelURL *uri; - - uri = camel_url_new_with_base (base, location); - cid = camel_url_to_string (uri, 0); - camel_url_free (uri); - } else { - cid = g_strdup (location); - } - } - - len = part_id->len; - g_string_append (part_id, ".text_html"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_text_html; - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); - - if (cid) - g_free (cid); -} - -static void -efh_parse_message_external (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - CamelMimePart *newpart; - CamelContentType *type; - const gchar *access_type; - gchar *url = NULL, *desc = NULL; - gchar *content; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - newpart = camel_mime_part_new (); - - /* needs to be cleaner */ - type = camel_mime_part_get_content_type (part); - access_type = camel_content_type_param (type, "access-type"); - if (!access_type) { - const gchar *msg = _("Malformed external-body part"); - camel_mime_part_set_content (newpart, msg, strlen (msg), - "text/plain"); - goto addPart; - } - - if (!g_ascii_strcasecmp(access_type, "ftp") || - !g_ascii_strcasecmp(access_type, "anon-ftp")) { - const gchar *name, *site, *dir, *mode; - gchar *path; - gchar ftype[16]; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - dir = camel_content_type_param (type, "directory"); - mode = camel_content_type_param (type, "mode"); - if (name == NULL || site == NULL) - goto fail; - - /* Generate the path. */ - if (dir) - path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name); - else - path = g_strdup_printf("/%s", *name=='/'?name+1:name); - - if (mode && *mode) - sprintf(ftype, ";type=%c", *mode); - else - ftype[0] = 0; - - url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype); - g_free (path); - desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url); - } else if (!g_ascii_strcasecmp (access_type, "local-file")) { - const gchar *name, *site; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - if (name == NULL) - goto fail; - - url = g_filename_to_uri (name, NULL, NULL); - if (site) - desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site); - else - desc = g_strdup_printf(_("Pointer to local file (%s)"), name); - } else if (!g_ascii_strcasecmp (access_type, "URL")) { - const gchar *urlparam; - gchar *s, *d; - - /* RFC 2017 */ - urlparam = camel_content_type_param (type, "url"); - if (urlparam == NULL) - goto fail; - - /* For obscure MIMEy reasons, the URL may be split into words */ - url = g_strdup (urlparam); - s = d = url; - while (*s) { - if (!isspace ((guchar) * s)) - *d++ = *s; - s++; - } - *d = 0; - desc = g_strdup_printf (_("Pointer to remote data (%s)"), url); - } else - goto fail; - - content = g_strdup_printf ("<a href=\"%s\">%s</a>", url, desc); - camel_mime_part_set_content (newpart, content, strlen (content), "text/html"); - g_free (content); - - g_free (url); - g_free (desc); - -fail: - content = g_strdup_printf ( - _("Pointer to unknown external data (\"%s\" type)"), - access_type); - camel_mime_part_set_content (newpart, content, strlen (content), "text/plain"); - g_free (content); - -addPart: - len = part_id->len; - g_string_append (part_id, ".msg_external"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_text_html; - puri->mime_type = g_strdup ("text/html"); - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_message_deliverystatus (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - EMFormatPURI *puri; - gint len; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - len = part_id->len; - g_string_append (part_id, ".deliverystatus"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_source; - puri->mime_type = g_strdup ("text/html"); - puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL; - puri->validity_type = info->validity_type; - puri->is_attachment = info->is_attachment; - - em_format_add_puri (emf, puri); - g_string_truncate (part_id, len); -} - -static void -efh_parse_message_rfc822 (EMFormat *emf, - CamelMimePart *part, - GString *part_id, - EMFormatParserInfo *info, - GCancellable *cancellable) -{ - CamelDataWrapper *dw; - CamelMimePart *opart; - CamelStream *stream; - CamelMimeParser *parser; - gint len; - EMFormatParserInfo oinfo = *info; - EMFormatPURI *puri; - - len = part_id->len; - g_string_append (part_id, ".rfc822"); - - /* Create an empty PURI that will represent start of the RFC message */ - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str); - puri->write_func = efh_write_message_rfc822; - puri->mime_type = g_strdup ("text/html"); - puri->is_attachment = info->is_attachment; - em_format_add_puri (emf, puri); - - /* Now parse the message, creating multiple sub-PURIs */ - stream = camel_stream_mem_new (); - dw = camel_medium_get_content ((CamelMedium *) part); - camel_data_wrapper_write_to_stream_sync (dw, stream, cancellable, NULL); - g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, cancellable, NULL); - - parser = camel_mime_parser_new (); - camel_mime_parser_init_with_stream (parser, stream, NULL); - - opart = camel_mime_part_new (); - camel_mime_part_construct_from_parser_sync (opart, parser, cancellable, NULL); - - em_format_parse_part_as (emf, opart, part_id, &oinfo, - "x-evolution/message", cancellable); - - /* Add another generic PURI that represents end of the RFC message. - * The em_format_write() function will skip all PURIs between the ".rfc822" - * PURI and ".rfc822.end" PURI as they will be rendered in an <iframe> */ - g_string_append (part_id, ".end"); - puri = em_format_puri_new (emf, sizeof (EMFormatPURI), NULL, part_id->str); - em_format_add_puri (emf, puri); - - g_string_truncate (part_id, len); - - g_object_unref (opart); - g_object_unref (parser); - g_object_unref (stream); -} - -/*****************************************************************************/ - -static void -efh_write_image (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *content; - EMFormatHTML *efh; - CamelDataWrapper *dw; - GByteArray *ba; - CamelStream *raw_content; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - efh = (EMFormatHTML *) emf; - - dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part)); - g_return_if_fail (dw); - - raw_content = camel_stream_mem_new (); - camel_data_wrapper_decode_to_stream_sync (dw, raw_content, cancellable, NULL); - ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (raw_content)); - - if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - - if (!efh->priv->animate_images) { - - gchar *buff; - gsize len; - - em_format_html_animation_extract_frame (ba, &buff, &len); - - camel_stream_write (stream, buff, len, cancellable, NULL); - - g_free (buff); - - } else { - - camel_stream_write_to_stream (raw_content, stream, cancellable, NULL); - } - - } else { - - gchar *buffer; - - if (!efh->priv->animate_images) { - - gchar *buff; - gsize len; - - em_format_html_animation_extract_frame (ba, &buff, &len); - - content = g_base64_encode ((guchar *) buff, len); - g_free (buff); - - } else { - content = g_base64_encode ((guchar *) ba->data, ba->len); - } - - /* The image is already base64-encrypted so we can directly - * paste it to the output */ - buffer = g_strdup_printf ( - "<img src=\"data:%s;base64,%s\" style=\"max-width: 100%%;\" />", - puri->mime_type ? puri->mime_type : "image/*", content); - - camel_stream_write_string (stream, buffer, cancellable, NULL); - g_free (buffer); - g_free (content); - } - - g_object_unref (raw_content); -} - -static void -efh_write_text_enriched (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - CamelStream *filtered_stream; - CamelMimeFilter *enriched; - guint32 flags = 0; - GString *buffer; - CamelContentType *ct; - gchar *mime_type = NULL; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - ct = camel_mime_part_get_content_type (puri->part); - if (ct) { - mime_type = camel_content_type_simple (ct); - } - - if (!g_strcmp0(mime_type, "text/richtext")) { - flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; - camel_stream_write_string ( - stream, "\n<!-- text/richtext -->\n", - cancellable, NULL); - } else { - camel_stream_write_string ( - stream, "\n<!-- text/enriched -->\n", - cancellable, NULL); - } - - if (mime_type) - g_free (mime_type); - - enriched = camel_mime_filter_enriched_new (flags); - filtered_stream = camel_stream_filter_new (stream); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), enriched); - g_object_unref (enriched); - - buffer = g_string_new (""); - - g_string_append_printf (buffer, - "<div class=\"part-container\" style=\"border-color: #%06x; " - "background-color: #%06x; color: #%06x;\">" - "<div class=\"part-container-inner-margin\">\n", - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_CONTENT]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - - camel_stream_write_string (stream, buffer->str, cancellable, NULL); - g_string_free (buffer, TRUE); - - em_format_format_text ( - emf, (CamelStream *) filtered_stream, - (CamelDataWrapper *) puri->part, cancellable); - - g_object_unref (filtered_stream); - camel_stream_write_string (stream, "</div></div>", cancellable, NULL); -} - -static void -efh_write_text_plain (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - CamelDataWrapper *dw; - CamelStream *filtered_stream; - CamelMimeFilter *html_filter; - EMFormatHTML *efh = (EMFormatHTML *) emf; - gchar *content; - const gchar *format; - guint32 flags; - guint32 rgb; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - flags = efh->text_html_flags; - - dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part)); - - /* Check for RFC 2646 flowed text. */ - if (camel_content_type_is(dw->mime_type, "text", "plain") - && (format = camel_content_type_param(dw->mime_type, "format")) - && !g_ascii_strcasecmp(format, "flowed")) - flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; - - rgb = e_color_to_value ( - &efh->priv->colors[EM_FORMAT_HTML_COLOR_CITATION]); - filtered_stream = camel_stream_filter_new (stream); - html_filter = camel_mime_filter_tohtml_new (flags, rgb); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), html_filter); - g_object_unref (html_filter); - - content = g_strdup_printf ( - "<div class=\"part-container\" style=\"border-color: #%06x; " - "background-color: #%06x; color: #%06x;\">" - "<div class=\"part-container-inner-margin pre\">\n", - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_CONTENT]), - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - - camel_stream_write_string (stream, content, cancellable, NULL); - em_format_format_text (emf, filtered_stream, (CamelDataWrapper *) puri->part, cancellable); - camel_stream_flush (filtered_stream, cancellable, NULL); - - g_object_unref (filtered_stream); - g_free (content); - - camel_stream_write_string (stream, "</div></div>\n", cancellable, NULL); -} - -static gchar * -get_tag (const gchar *tag_name, - gchar *opening, - gchar *closing) -{ - gchar *t; - gboolean has_end; - - for (t = closing - 1; t != opening; t--) { - if (*t != ' ') - break; - } - - /* Not a pair tag */ - if (*t == '/') - return g_strndup (opening, closing - opening + 1); - - for (t = closing; t && *t; t++) { - if (*t == '<') - break; - } - - do { - if (*t == '/') { - has_end = TRUE; - break; - } - - if (*t == '>') { - has_end = FALSE; - break; - } - - t++; - - } while (t && *t); - - /* Broken HTML? */ - if (!has_end) - return g_strndup (opening, closing - opening + 1); - - do { - if ((*t != ' ') && (*t != '/')) - break; - - t++; - } while (t && *t); - - if (g_strncasecmp (t, tag_name, strlen (tag_name)) == 0) { - - closing = strstr (t, ">"); - - return g_strndup (opening, closing - opening + strlen (tag_name)); - } - - /* Broken HTML? */ - return g_strndup (opening, closing - opening + 1); -} - -static void -efh_write_text_html (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatHTML *efh = (EMFormatHTML *) emf; - - if (g_cancellable_is_cancelled (cancellable)) - return; - - if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - em_format_format_text (emf, stream, - (CamelDataWrapper *) puri->part, cancellable); - - } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { - GString *string; - GByteArray *ba; - gchar *pos; - GList *tags, *iter; - gboolean valid; - gchar *tag; - const gchar *document_end; - gint length; - gint i; - CamelStream *decoded_stream; - - decoded_stream = camel_stream_mem_new (); - em_format_format_text (emf, decoded_stream, - (CamelDataWrapper *) puri->part, cancellable); - g_seekable_seek (G_SEEKABLE (decoded_stream), 0, G_SEEK_SET, cancellable, NULL); - - ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (decoded_stream)); - string = g_string_new_len ((gchar *) ba->data, ba->len); - - g_object_unref (decoded_stream); - - tags = NULL; - pos = string->str; - valid = FALSE; - do { - gchar *closing; - gchar *opening; - - pos = strstr (pos + 1, "<"); - if (!pos) - break; - - opening = pos; - closing = strstr (pos, ">"); - - /* Find where the actual tag name begins */ - for (tag = pos + 1; tag && *tag; tag++) { - if (*tag != ' ') - break; - } - - if (g_ascii_strncasecmp (tag, "style", 5) == 0) { - tags = g_list_append ( - tags, - get_tag ("style", opening, closing)); - } else if (g_ascii_strncasecmp (tag, "script", 6) == 0) { - tags = g_list_append ( - tags, - get_tag ("script", opening, closing)); - } else if (g_ascii_strncasecmp (tag, "link", 4) == 0) { - tags = g_list_append ( - tags, - get_tag ("link", opening, closing)); - } else if (g_ascii_strncasecmp (tag, "body", 4) == 0) { - valid = TRUE; - break; - } - - } while (TRUE); - - /* Something's wrong, let's write the entire HTML and hope - * that WebKit can handle it */ - if (!valid) { - EMFormatWriterInfo i = *info; - i.mode = EM_FORMAT_WRITE_MODE_RAW; - efh_write_text_html (emf, puri, stream, &i, cancellable); - return; - } - - /* include the "body" as well -----v */ - g_string_erase (string, 0, tag - string->str + 4); - g_string_prepend (string, "<div "); - - for (iter = tags; iter; iter = iter->next) { - g_string_prepend (string, iter->data); - } - - g_list_free_full (tags, g_free); - - /* that's reversed </body></html>... */ - document_end = ">lmth/<>ydob/<"; - length = strlen (document_end); - tag = string->str + string->len - 1; - i = 0; - valid = FALSE; - while (i < length - 1) { - gchar c; - - if (g_ascii_isspace (*tag)) { - tag--; - continue; - } - - if ((*tag >= 'A') && (*tag <= 'Z')) - c = *tag + 32; - else - c = *tag; - - if (c == document_end[i]) { - tag--; - i++; - valid = TRUE; - continue; - } - - valid = FALSE; - } - - if (valid) - g_string_truncate (string, tag - string->str); - - camel_stream_write_string (stream, string->str, cancellable, NULL); - - g_string_free (string, TRUE); - } else { - gchar *str; - gchar *uri; - - uri = em_format_build_mail_uri ( - emf->folder, emf->message_uid, - "part_id", G_TYPE_STRING, puri->uri, - "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, - NULL); - - str = g_strdup_printf ( - "<div class=\"part-container-nostyle\">" - "<iframe width=\"100%%\" height=\"auto\"" - " frameborder=\"0\" src=\"%s\" " - " style=\"border: 1px solid #%06x; background-color: #%06x;\">" - "</iframe>" - "</div>", - uri, - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT])); - - camel_stream_write_string (stream, str, cancellable, NULL); - - g_free (str); - g_free (uri); - } -} - -static void -efh_write_source (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - EMFormatHTML *efh = (EMFormatHTML *) emf; - GString *buffer; - CamelStream *filtered_stream; - CamelMimeFilter *filter; - CamelDataWrapper *dw = (CamelDataWrapper *) puri->part; - - filtered_stream = camel_stream_filter_new (stream); - - filter = camel_mime_filter_tohtml_new ( - CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | - CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); - camel_stream_filter_add ( - CAMEL_STREAM_FILTER (filtered_stream), filter); - g_object_unref (filter); - - buffer = g_string_new (""); - - g_string_append_printf ( - buffer, "<div class=\"part-container\" style=\"border: 0; background: #%06x; color: #%06x;\" >", - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_BODY]), - e_color_to_value ( - &efh->priv->colors[ - EM_FORMAT_HTML_COLOR_TEXT])); - - camel_stream_write_string ( - stream, buffer->str, cancellable, NULL); - camel_stream_write_string ( - stream, "<code class=\"pre\">", cancellable, NULL); - camel_data_wrapper_write_to_stream_sync (dw, filtered_stream, - cancellable, NULL); - camel_stream_write_string ( - stream, "</code>", cancellable, NULL); - - g_object_unref (filtered_stream); - g_string_free (buffer, TRUE); -} - -static void -efh_write_headers (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GString *buffer; - EMFormatHTML *efh = (EMFormatHTML *) emf; - gint bg_color; - - if (!puri->part) - return; - - buffer = g_string_new (""); - - if (info->mode & EM_FORMAT_WRITE_MODE_PRINTING) { - GdkColor white = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 }; - bg_color = e_color_to_value (&white); - } else { - bg_color = e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]); - } - - g_string_append_printf ( - buffer, - "<div class=\"headers\" style=\"background: #%06x;\">" - "<table border=\"0\" width=\"100%%\" style=\"color: #%06x;\">\n" - "<tr><td valign=\"top\" width=\"16\">\n", - bg_color, - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER])); - - if (info->headers_collapsable) { - g_string_append_printf (buffer, - "<img src=\"evo-file://%s/%s\" class=\"navigable\" " - "id=\"__evo-collapse-headers-img\" />" - "</td><td>", - EVOLUTION_IMAGESDIR, - (info->headers_collapsed) ? "plus.png" : "minus.png"); - - efh_format_short_headers (efh, buffer, (CamelMedium *) puri->part, - info->headers_collapsed, - cancellable); - } - - efh_format_full_headers (efh, buffer, (CamelMedium *) puri->part, - (info->mode == EM_FORMAT_WRITE_MODE_ALL_HEADERS), - !info->headers_collapsed, - cancellable); - - g_string_append (buffer, "</td></tr></table></div>"); - - camel_stream_write_string (stream, buffer->str, cancellable, NULL); - - g_string_free (buffer, true); -} - -static void -efh_write_error (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - CamelStream *filtered_stream; - CamelMimeFilter *filter; - CamelDataWrapper *dw; - - dw = camel_medium_get_content ((CamelMedium *) puri->part); - - camel_stream_write_string (stream, "<em><font color=\"red\">", cancellable, NULL); - - filtered_stream = camel_stream_filter_new (stream); - filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), filter); - g_object_unref (filter); - - camel_data_wrapper_decode_to_stream_sync (dw, filtered_stream, cancellable, NULL); - - g_object_unref (filtered_stream); - - camel_stream_write_string (stream, "</font></em><br>", cancellable, NULL); -} - -static void -efh_write_message_rfc822 (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - if (info->mode == EM_FORMAT_WRITE_MODE_RAW) { - - GList *puris; - GList *iter; - EMFormatWriterInfo msgInfo = *info; - msgInfo.mode = EM_FORMAT_WRITE_MODE_NORMAL; - - /* Create a new fake list of PURIs which will contain only - * PURIs from this message. */ - iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); - if (!iter || !iter->next) - return; - - iter = iter->next; - puris = NULL; - while (iter) { - - EMFormatPURI *p; - p = iter->data; - - if (g_str_has_suffix (p->uri, ".rfc822.end")) - break; - - puris = g_list_append (puris, p); - iter = iter->next; - - }; - - efh_write_message (emf, puris, stream, &msgInfo, cancellable); - - g_list_free (puris); - - } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) { - - GList *iter; - gboolean can_write = FALSE; - - iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); - if (!iter || !iter->next) - return; - - /* Skip everything before attachment bar, inclusive */\ - iter = iter->next; - while (iter) { - - EMFormatPURI *p = iter->data; - - /* EMFormatHTMLPrint has registered a special writer - * for headers, try to find it and use it. */ - if (g_str_has_suffix (p->uri, ".headers")) { - - const EMFormatHandler *handler; - - handler = em_format_find_handler ( - emf, "x-evolution/message/headers"); - if (handler && handler->write_func) - handler->write_func (emf, p, stream, info, cancellable); - - iter = iter->next; - continue; - } - - if (g_str_has_suffix (p->uri, ".rfc822.end")) - break; - - if (g_str_has_suffix (p->uri, ".attachment-bar")) - can_write = TRUE; - - if (can_write && p->write_func) { - p->write_func ( - emf, p, stream, info, cancellable); - } - - iter = iter->next; - } - - } else { - gchar *str; - gchar *uri; - - EMFormatHTML *efh = (EMFormatHTML *) emf; - EMFormatPURI *p; - GList *iter; - - iter = g_hash_table_lookup (emf->mail_part_table, puri->uri); - if (!iter || !iter->next) - return; - - iter = iter->next; - p = iter->data; - - uri = em_format_build_mail_uri (emf->folder, emf->message_uid, - "part_id", G_TYPE_STRING, p->uri, - "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, - NULL); - - str = g_strdup_printf ( - "<div class=\"part-container\" style=\"border-color: #%06x; " - "background-color: #%06x;\">" - "<div class=\"part-container-inner-margin\">\n" - "<iframe width=\"100%%\" height=\"auto\"" - " frameborder=\"0\" src=\"%s\" name=\"%s\"></iframe>" - "</div></div>", - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]), - e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]), - uri, puri->uri); - - camel_stream_write_string (stream, str, cancellable, NULL); - - g_free (str); - g_free (uri); - } - -} - -/*****************************************************************************/ - -/* Notes: - * - * image/tiff is omitted because it's a multi-page image format, but - * gdk-pixbuf unconditionally renders the first page only, and doesn't - * even indicate through meta-data whether multiple pages are present - * (see bug 335959). Therefore, make no attempt to render TIFF images - * inline and defer to an application that can handle multi-page TIFF - * files properly like Evince or Gimp. Once the referenced bug is - * fixed we can reevaluate this policy. - */ -static EMFormatHandler type_builtin_table[] = { - { (gchar *) "image/gif", efh_parse_image, efh_write_image, }, - { (gchar *) "image/jpeg", efh_parse_image, efh_write_image, }, - { (gchar *) "image/png", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-png", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-bmp", efh_parse_image, efh_write_image, }, - { (gchar *) "image/bmp", efh_parse_image, efh_write_image, }, - { (gchar *) "image/svg", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-cmu-raster", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-ico", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-anymap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-bitmap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-graymap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-portable-pixmap", efh_parse_image, efh_write_image, }, - { (gchar *) "image/x-xpixmap", efh_parse_image, efh_write_image, }, - { (gchar *) "text/enriched", efh_parse_text_enriched, efh_write_text_enriched, }, - { (gchar *) "text/plain", efh_parse_text_plain, efh_write_text_plain, }, - { (gchar *) "text/html", efh_parse_text_html, efh_write_text_html, }, - { (gchar *) "text/richtext", efh_parse_text_enriched, efh_write_text_enriched, }, - { (gchar *) "text/*", efh_parse_text_plain, efh_write_text_plain, }, - { (gchar *) "message/rfc822", efh_parse_message_rfc822, efh_write_message_rfc822, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "message/news", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE }, - { (gchar *) "message/delivery-status", efh_parse_message_deliverystatus, efh_write_text_plain, }, - { (gchar *) "message/external-body", efh_parse_message_external, efh_write_text_plain, }, - { (gchar *) "message/*", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE }, - - /* This is where one adds those busted, non-registered types, - * that some idiot mailer writers out there decide to pull out - * of their proverbials at random. */ - { (gchar *) "image/jpg", efh_parse_image, efh_write_image, }, - { (gchar *) "image/pjpeg", efh_parse_image, efh_write_image, }, - - /* special internal types */ - { (gchar *) "x-evolution/message/rfc822", 0, efh_write_text_plain, }, - { (gchar *) "x-evolution/message/headers", 0, efh_write_headers, }, - { (gchar *) "x-evolution/message/source", 0, efh_write_source, }, - { (gchar *) "x-evolution/message/attachment", 0, efh_write_attachment, }, - { (gchar *) "x-evolution/message/error", 0, efh_write_error, }, -}; - -static void -efh_builtin_init (EMFormatHTMLClass *efhc) -{ - EMFormatClass *emfc; - gint ii; - - emfc = (EMFormatClass *) efhc; - - for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++) - em_format_class_add_handler ( - emfc, &type_builtin_table[ii]); -} - -static void -efh_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_BODY_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_BODY, - g_value_get_boxed (value)); - return; - - case PROP_CITATION_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CITATION, - g_value_get_boxed (value)); - return; - - case PROP_CONTENT_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CONTENT, - g_value_get_boxed (value)); - return; - - case PROP_FRAME_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_FRAME, - g_value_get_boxed (value)); - return; - - case PROP_HEADER_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_HEADER, - g_value_get_boxed (value)); - return; - - case PROP_IMAGE_LOADING_POLICY: - em_format_html_set_image_loading_policy ( - EM_FORMAT_HTML (object), - g_value_get_enum (value)); - return; - - case PROP_MARK_CITATIONS: - em_format_html_set_mark_citations ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_ONLY_LOCAL_PHOTOS: - em_format_html_set_only_local_photos ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_SHOW_SENDER_PHOTO: - em_format_html_set_show_sender_photo ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_SHOW_REAL_DATE: - em_format_html_set_show_real_date ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - - case PROP_TEXT_COLOR: - em_format_html_set_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_TEXT, - g_value_get_boxed (value)); - return; - - case PROP_ANIMATE_IMAGES: - em_format_html_set_animate_images ( - EM_FORMAT_HTML (object), - g_value_get_boolean (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -efh_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GdkColor color; - - switch (property_id) { - case PROP_BODY_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_BODY, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_CITATION_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CITATION, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_CONTENT_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_CONTENT, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_FRAME_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_FRAME, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_HEADER_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_HEADER, - &color); - g_value_set_boxed (value, &color); - return; - - case PROP_IMAGE_LOADING_POLICY: - g_value_set_enum ( - value, - em_format_html_get_image_loading_policy ( - EM_FORMAT_HTML (object))); - return; - - case PROP_MARK_CITATIONS: - g_value_set_boolean ( - value, em_format_html_get_mark_citations ( - EM_FORMAT_HTML (object))); - return; - - case PROP_ONLY_LOCAL_PHOTOS: - g_value_set_boolean ( - value, em_format_html_get_only_local_photos ( - EM_FORMAT_HTML (object))); - return; - - case PROP_SHOW_SENDER_PHOTO: - g_value_set_boolean ( - value, em_format_html_get_show_sender_photo ( - EM_FORMAT_HTML (object))); - return; - - case PROP_SHOW_REAL_DATE: - g_value_set_boolean ( - value, em_format_html_get_show_real_date ( - EM_FORMAT_HTML (object))); - return; - - case PROP_TEXT_COLOR: - em_format_html_get_color ( - EM_FORMAT_HTML (object), - EM_FORMAT_HTML_COLOR_TEXT, - &color); - g_value_set_boxed (value, &color); - return; - case PROP_ANIMATE_IMAGES: - g_value_set_boolean ( - value, em_format_html_get_animate_images ( - EM_FORMAT_HTML (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -efh_finalize (GObject *object) -{ - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -efh_constructed (GObject *object) -{ - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (parent_class)->constructed (object); - - e_extensible_load_extensions (E_EXTENSIBLE (object)); -} - -static void -efh_write_attachment (EMFormat *emf, - EMFormatPURI *puri, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - gchar *text, *html; - CamelContentType *ct; - gchar *mime_type; - const EMFormatHandler *handler; - - /* we display all inlined attachments only */ - - /* this could probably be cleaned up ... */ - camel_stream_write_string ( - stream, - "<table border=1 cellspacing=0 cellpadding=0><tr><td>" - "<table width=10 cellspacing=0 cellpadding=0>" - "<tr><td></td></tr></table></td>" - "<td><table width=3 cellspacing=0 cellpadding=0>" - "<tr><td></td></tr></table></td><td><font size=-1>\n", - cancellable, NULL); - - ct = camel_mime_part_get_content_type (puri->part); - mime_type = camel_content_type_simple (ct); - - /* output some info about it */ - text = em_format_describe_part (puri->part, mime_type); - html = camel_text_to_html ( - text, ((EMFormatHTML *) emf)->text_html_flags & - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_write_string (stream, html, cancellable, NULL); - g_free (html); - g_free (text); - - camel_stream_write_string ( - stream, "</font></td></tr><tr></table>", cancellable, NULL); - - handler = em_format_find_handler (emf, mime_type); - if (handler && handler->write_func && handler->write_func != efh_write_attachment) { - if (em_format_is_inline (emf, puri->uri, puri->part, handler)) - handler->write_func (emf, puri, stream, info, cancellable); - } - - g_free (mime_type); -} - -static void -efh_preparse (EMFormat *emf) -{ - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - CamelInternetAddress *addr; - CamelSession *session; - ESourceRegistry *registry; - - if (!emf->message) { - efh->priv->can_load_images = FALSE; - return; - } - - session = em_format_get_session (emf); - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - addr = camel_mime_message_get_from (emf->message); - efh->priv->can_load_images = em_utils_in_addressbook ( - registry, addr, FALSE); -} - -static void -efh_write_message (EMFormat *emf, - GList *puris, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - GList *iter; - EMFormatHTML *efh; - gchar *header; - - efh = (EMFormatHTML *) emf; - - header = g_strdup_printf ( - "<!DOCTYPE HTML>\n<html>\n" - "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\" />\n" - "<title>Evolution Mail Display</title>\n" - "<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\" />\n" - "<style type=\"text/css\">\n" - " table th { color: #000; font-weight: bold; }\n" - "</style>\n" - "</head><body bgcolor=\"#%06x\">", - e_color_to_value (&efh->priv->colors[ - EM_FORMAT_HTML_COLOR_BODY])); - - camel_stream_write_string (stream, header, cancellable, NULL); - g_free (header); - - if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) { - - efh_write_source (emf, emf->mail_part_list->data, - stream, info, cancellable); - - camel_stream_write_string (stream, "</body></html>", cancellable, NULL); - return; - } - - for (iter = puris; iter; iter = iter->next) { - - EMFormatPURI *puri = iter->data; - - if (!puri) - continue; - - /* If current PURI has suffix .rfc822 then iterate through all - * subsequent PURIs until PURI with suffix .rfc822.end is found. - * These skipped PURIs contain entire RFC message which will - * be written in <iframe> as attachment. - */ - if (g_str_has_suffix (puri->uri, ".rfc822")) { - - /* If the PURI is not an attachment, then we must - * inline it here otherwise it would not be displayed. */ - if (!puri->is_attachment && puri->write_func) { - /* efh_write_message_rfc822 starts parsing _after_ - * the passed PURI, so we must give it previous PURI here */ - EMFormatPURI *p; - if (!iter->prev) - continue; - - p = iter->prev->data; - puri->write_func (emf, p, stream, info, cancellable); - } - - while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) { - - iter = iter->next; - if (iter) - puri = iter->data; - - d(printf(".rfc822 - skipping %s\n", puri->uri)); - } - - /* Skip the .rfc822.end PURI as well. */ - if (!iter) - break; - - continue; - } - - if (puri->write_func && !puri->is_attachment) { - puri->write_func (emf, puri, stream, info, cancellable); - d(printf("Writing PURI %s\n", puri->uri)); - } else { - d(printf("Skipping PURI %s\n", puri->uri)); - } - } - - camel_stream_write_string (stream, "</body></html>", cancellable, NULL); -} - -static void -efh_write (EMFormat *emf, - CamelStream *stream, - EMFormatWriterInfo *info, - GCancellable *cancellable) -{ - efh_write_message (emf, emf->mail_part_list, stream, info, cancellable); -} - -static void -efh_base_init (EMFormatHTMLClass *klass) -{ - efh_builtin_init (klass); -} - -static void -efh_class_init (EMFormatHTMLClass *klass) -{ - GObjectClass *object_class; - EMFormatClass *emf_class; - - parent_class = g_type_class_peek_parent (klass); - g_type_class_add_private (klass, sizeof (EMFormatHTMLPrivate)); - - emf_class = EM_FORMAT_CLASS (klass); - emf_class->preparse = efh_preparse; - emf_class->write = efh_write; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = efh_constructed; - object_class->set_property = efh_set_property; - object_class->get_property = efh_get_property; - object_class->finalize = efh_finalize; - - g_object_class_install_property ( - object_class, - PROP_BODY_COLOR, - g_param_spec_boxed ( - "body-color", - "Body Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_CITATION_COLOR, - g_param_spec_boxed ( - "citation-color", - "Citation Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_CONTENT_COLOR, - g_param_spec_boxed ( - "content-color", - "Content Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_FRAME_COLOR, - g_param_spec_boxed ( - "frame-color", - "Frame Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_HEADER_COLOR, - g_param_spec_boxed ( - "header-color", - "Header Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); - - /* FIXME Make this a proper enum property. */ - g_object_class_install_property ( - object_class, - PROP_IMAGE_LOADING_POLICY, - g_param_spec_enum ( - "image-loading-policy", - "Image Loading Policy", - NULL, - E_TYPE_MAIL_IMAGE_LOADING_POLICY, - E_MAIL_IMAGE_LOADING_POLICY_ALWAYS, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_MARK_CITATIONS, - g_param_spec_boolean ( - "mark-citations", - "Mark Citations", - NULL, - TRUE, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_ONLY_LOCAL_PHOTOS, - g_param_spec_boolean ( - "only-local-photos", - "Only Local Photos", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); - - g_object_class_install_property ( - object_class, - PROP_SHOW_SENDER_PHOTO, - g_param_spec_boolean ( - "show-sender-photo", - "Show Sender Photo", - NULL, - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); - - g_object_class_install_property ( - object_class, - PROP_SHOW_REAL_DATE, - g_param_spec_boolean ( - "show-real-date", - "Show real Date header value", - NULL, - TRUE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT)); - - g_object_class_install_property ( - object_class, - PROP_TEXT_COLOR, - g_param_spec_boxed ( - "text-color", - "Text Color", - NULL, - GDK_TYPE_COLOR, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_ANIMATE_IMAGES, - g_param_spec_boolean ( - "animate-images", - "Animate images", - NULL, - FALSE, - G_PARAM_READWRITE)); -} - -static void -efh_init (EMFormatHTML *efh, - EMFormatHTMLClass *klass) -{ - GdkColor *color; - - efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh); - - g_queue_init (&efh->pending_object_list); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]; - gdk_color_parse ("#eeeeee", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]; - gdk_color_parse ("#ffffff", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]; - gdk_color_parse ("#3f3f3f", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER]; - gdk_color_parse ("#eeeeee", color); - - color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_TEXT]; - gdk_color_parse ("#000000", color); - - efh->text_html_flags = - CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - efh->show_icon = TRUE; -} - -GType -em_format_html_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMFormatHTMLClass), - (GBaseInitFunc) efh_base_init, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) efh_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMFormatHTML), - 0, /* n_preallocs */ - (GInstanceInitFunc) efh_init, - NULL /* value_table */ - }; - - static const GInterfaceInfo extensible_info = { - (GInterfaceInitFunc) NULL, - (GInterfaceFinalizeFunc) NULL, - NULL /* interface_data */ - }; - - type = g_type_register_static ( - em_format_get_type(), "EMFormatHTML", - &type_info, G_TYPE_FLAG_ABSTRACT); - - g_type_add_interface_static ( - type, E_TYPE_EXTENSIBLE, &extensible_info); - } - - return type; -} - -/*****************************************************************************/ -void -em_format_html_get_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - GdkColor *color) -{ - GdkColor *format_color; - - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES); - g_return_if_fail (color != NULL); - - format_color = &efh->priv->colors[type]; - - color->red = format_color->red; - color->green = format_color->green; - color->blue = format_color->blue; -} - -void -em_format_html_set_color (EMFormatHTML *efh, - EMFormatHTMLColorType type, - const GdkColor *color) -{ - GdkColor *format_color; - const gchar *property_name; - - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES); - g_return_if_fail (color != NULL); - - format_color = &efh->priv->colors[type]; - - if (gdk_color_equal (color, format_color)) - return; - - format_color->red = color->red; - format_color->green = color->green; - format_color->blue = color->blue; - - switch (type) { - case EM_FORMAT_HTML_COLOR_BODY: - property_name = "body-color"; - break; - case EM_FORMAT_HTML_COLOR_CITATION: - property_name = "citation-color"; - break; - case EM_FORMAT_HTML_COLOR_CONTENT: - property_name = "content-color"; - break; - case EM_FORMAT_HTML_COLOR_FRAME: - property_name = "frame-color"; - break; - case EM_FORMAT_HTML_COLOR_HEADER: - property_name = "header-color"; - break; - case EM_FORMAT_HTML_COLOR_TEXT: - property_name = "text-color"; - break; - default: - g_return_if_reached (); - } - - g_object_notify (G_OBJECT (efh), property_name); -} - -EMailImageLoadingPolicy -em_format_html_get_image_loading_policy (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0); - - return efh->priv->image_loading_policy; -} - -void -em_format_html_set_image_loading_policy (EMFormatHTML *efh, - EMailImageLoadingPolicy policy) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (policy == efh->priv->image_loading_policy) - return; - - efh->priv->image_loading_policy = policy; - - g_object_notify (G_OBJECT (efh), "image-loading-policy"); -} - -gboolean -em_format_html_get_mark_citations (EMFormatHTML *efh) -{ - guint32 flags; - - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - flags = efh->text_html_flags; - - return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0); -} - -void -em_format_html_set_mark_citations (EMFormatHTML *efh, - gboolean mark_citations) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - if (mark_citations) - efh->text_html_flags |= - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - else - efh->text_html_flags &= - ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; - - g_object_notify (G_OBJECT (efh), "mark-citations"); -} - -gboolean -em_format_html_get_only_local_photos (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->only_local_photos; -} - -void -em_format_html_set_only_local_photos (EMFormatHTML *efh, - gboolean only_local_photos) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->only_local_photos = only_local_photos; - - g_object_notify (G_OBJECT (efh), "only-local-photos"); -} - -gboolean -em_format_html_get_show_sender_photo (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->show_sender_photo; -} - -void -em_format_html_set_show_sender_photo (EMFormatHTML *efh, - gboolean show_sender_photo) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->show_sender_photo = show_sender_photo; - - g_object_notify (G_OBJECT (efh), "show-sender-photo"); -} - -gboolean -em_format_html_get_show_real_date (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->show_real_date; -} - -void -em_format_html_set_show_real_date (EMFormatHTML *efh, - gboolean show_real_date) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->show_real_date = show_real_date; - - g_object_notify (G_OBJECT (efh), "show-real-date"); -} - -gboolean -em_format_html_get_animate_images (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return efh->priv->animate_images; -} - -void -em_format_html_set_animate_images (EMFormatHTML *efh, - gboolean animate_images) -{ - g_return_if_fail (EM_IS_FORMAT_HTML (efh)); - - efh->priv->animate_images = animate_images; - - g_object_notify (G_OBJECT (efh), "animate-images"); -} - -CamelMimePart * -em_format_html_file_part (EMFormatHTML *efh, - const gchar *mime_type, - const gchar *filename, - GCancellable *cancellable) -{ - CamelMimePart *part; - CamelStream *stream; - CamelDataWrapper *dw; - gchar *basename; - - stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL); - if (stream == NULL) - return NULL; - - dw = camel_data_wrapper_new (); - camel_data_wrapper_construct_from_stream_sync ( - dw, stream, cancellable, NULL); - g_object_unref (stream); - if (mime_type) - camel_data_wrapper_set_mime_type (dw, mime_type); - part = camel_mime_part_new (); - camel_medium_set_content ((CamelMedium *) part, dw); - g_object_unref (dw); - basename = g_path_get_basename (filename); - camel_mime_part_set_filename (part, basename); - g_free (basename); - - return part; -} - -void -em_format_html_format_cert_infos (GQueue *cert_infos, - GString *output_buffer) -{ - GQueue valid = G_QUEUE_INIT; - GList *head, *link; - - g_return_if_fail (cert_infos != NULL); - g_return_if_fail (output_buffer != NULL); - - head = g_queue_peek_head_link (cert_infos); - - /* Make sure we have a valid CamelCipherCertInfo before - * appending anything to the output buffer, so we don't - * end up with "()". */ - for (link = head; link != NULL; link = g_list_next (link)) { - CamelCipherCertInfo *cinfo = link->data; - - if ((cinfo->name != NULL && *cinfo->name != '\0') || - (cinfo->email != NULL && *cinfo->email != '\0')) { - g_queue_push_tail (&valid, cinfo); - } - } - - if (g_queue_is_empty (&valid)) - return; - - g_string_append (output_buffer, " ("); - - while (!g_queue_is_empty (&valid)) { - CamelCipherCertInfo *cinfo; - - cinfo = g_queue_pop_head (&valid); - - if (cinfo->name != NULL && *cinfo->name != '\0') { - g_string_append (output_buffer, cinfo->name); - - if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, " <"); - g_string_append (output_buffer, cinfo->email); - g_string_append (output_buffer, ">"); - } - - } else if (cinfo->email != NULL && *cinfo->email != '\0') { - g_string_append (output_buffer, cinfo->email); - } - - if (!g_queue_is_empty (&valid)) - g_string_append (output_buffer, ", "); - } - - g_string_append_c (output_buffer, ')'); -} - -static void -efh_format_text_header (EMFormatHTML *emfh, - GString *buffer, - const gchar *label, - const gchar *value, - guint32 flags) -{ - const gchar *fmt, *html; - gchar *mhtml = NULL; - gboolean is_rtl; - - if (value == NULL) - return; - - while (*value == ' ') - value++; - - if (!(flags & EM_FORMAT_HTML_HEADER_HTML)) - html = mhtml = camel_text_to_html (value, emfh->text_html_flags, 0); - else - html = value; - - is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; - - if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) { - if (flags & EM_FORMAT_HEADER_BOLD) { - fmt = "<tr class=\"header-item\" style=\"display: %s\"><td><b>%s:</b> %s</td></tr>"; - } else { - fmt = "<tr class=\"header-item\" style=\"display: %s\"><td>%s: %s</td></tr>"; - } - } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) { - if (is_rtl) - fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b> </b></th></tr>"; - else - fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s<b> </b></th><td valign=top>%s</td></tr>"; - } else { - if (flags & EM_FORMAT_HEADER_BOLD) { - if (is_rtl) - fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b> </b></th></tr>"; - else - fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s:<b> </b></th><td>%s</td></tr>"; - } else { - if (is_rtl) - fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b> </b></td></tr>"; - else - fmt = "<tr class=\"header-item\" style=\"display: %s\"><td align=\"right\" valign=\"top\" nowrap>%s:<b> </b></td><td>%s</td></tr>"; - } - } - - g_string_append_printf (buffer, fmt, - (flags & EM_FORMAT_HTML_HEADER_HIDDEN ? "none" : "table-row"), label, html); - - g_free (mhtml); -} - -static const gchar *addrspec_hdrs[] = { - "Sender", "From", "Reply-To", "To", "Cc", "Bcc", - "Resent-Sender", "Resent-From", "Resent-Reply-To", - "Resent-To", "Resent-Cc", "Resent-Bcc", NULL -}; - -static gchar * -efh_format_address (EMFormatHTML *efh, - GString *out, - struct _camel_header_address *a, - gchar *field, - gboolean no_links) -{ - guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; - gchar *name, *mailto, *addr; - gint i = 0; - gchar *str = NULL; - gint limit = mail_config_get_address_count (); - - while (a) { - if (a->name) - name = camel_text_to_html (a->name, flags, 0); - else - name = NULL; - - switch (a->type) { - case CAMEL_HEADER_ADDRESS_NAME: - if (name && *name) { - gchar *real, *mailaddr; - - if (strchr (a->name, ',') || strchr (a->name, ';')) - g_string_append_printf (out, ""%s"", name); - else - g_string_append (out, name); - - g_string_append (out, " <"); - - /* rfc2368 for mailto syntax and url encoding extras */ - if ((real = camel_header_encode_phrase ((guchar *) a->name))) { - mailaddr = g_strdup_printf("%s <%s>", real, a->v.addr); - g_free (real); - mailto = camel_url_encode (mailaddr, "?=&()"); - g_free (mailaddr); - } else { - mailto = camel_url_encode (a->v.addr, "?=&()"); - } - } else { - mailto = camel_url_encode (a->v.addr, "?=&()"); - } - addr = camel_text_to_html (a->v.addr, flags, 0); - if (no_links) - g_string_append_printf (out, "%s", addr); - else - g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr); - g_free (mailto); - g_free (addr); - - if (name && *name) - g_string_append (out, ">"); - break; - case CAMEL_HEADER_ADDRESS_GROUP: - g_string_append_printf (out, "%s: ", name); - efh_format_address (efh, out, a->v.members, field, no_links); - g_string_append_printf (out, ";"); - break; - default: - g_warning ("Invalid address type"); - break; - } - - g_free (name); - - i++; - a = a->next; - if (a) - g_string_append (out, ", "); - - /* Let us add a '...' if we have more addresses */ - if (limit > 0 && (i == limit - 1)) { - const gchar *id = NULL; - - if (strcmp (field, _("To")) == 0) { - id = "to"; - } else if (strcmp (field, _("Cc")) == 0) { - id = "cc"; - } else if (strcmp (field, _("Bcc")) == 0) { - id = "bcc"; - } - - if (id) { - g_string_append_printf (out, - "<span id=\"__evo-moreaddr-%s\" " - "style=\"display: none;\">", id); - str = g_strdup_printf ( - "<img src=\"evo-file://%s/plus.png\" " - "id=\"__evo-moreaddr-img-%s\" class=\"navigable\">", - EVOLUTION_IMAGESDIR, id); - } - } - } - - if (str) { - const gchar *id = NULL; - - if (strcmp (field, _("To")) == 0) { - id = "to"; - } else if (strcmp (field, _("Cc")) == 0) { - id = "cc"; - } else if (strcmp (field, _("Bcc")) == 0) { - id = "bcc"; - } - - if (id) { - g_string_append_printf (out, - "</span>" - "<span class=\"navigable\" " - "id=\"__evo-moreaddr-ellipsis-%s\" " - "style=\"display: inline;\">...</span>", - id); - } - } - - return str; -} - -static void -canon_header_name (gchar *name) -{ - gchar *inptr = name; - - /* canonicalise the header name... first letter is - * capitalised and any letter following a '-' also gets - * capitalised */ - - if (*inptr >= 'a' && *inptr <= 'z') - *inptr -= 0x20; - - inptr++; - - while (*inptr) { - if (inptr[-1] == '-' && *inptr >= 'a' && *inptr <= 'z') - *inptr -= 0x20; - else if (*inptr >= 'A' && *inptr <= 'Z') - *inptr += 0x20; - - inptr++; - } -} - -void -em_format_html_format_header (EMFormat *emf, - GString *buffer, - CamelMedium *part, - struct _camel_header_raw *header, - guint32 flags, - const gchar *charset) -{ - EMFormatHTML *efh = EM_FORMAT_HTML (emf); - gchar *name, *buf, *value = NULL; - const gchar *label, *txt; - gboolean addrspec = FALSE; - gchar *str_field = NULL; - gint i; - - name = g_alloca (strlen (header->name) + 1); - strcpy (name, header->name); - canon_header_name (name); - - for (i = 0; addrspec_hdrs[i]; i++) { - if (!strcmp (name, addrspec_hdrs[i])) { - addrspec = TRUE; - break; - } - } - - label = _(name); - - if (addrspec) { - struct _camel_header_address *addrs; - GString *html; - gchar *img; - const gchar *charset = em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf); - - buf = camel_header_unfold (header->value); - if (!(addrs = camel_header_address_decode (buf, charset))) { - g_free (buf); - return; - } - - g_free (buf); - - html = g_string_new(""); - img = efh_format_address (efh, html, addrs, (gchar *) label, - (flags & EM_FORMAT_HTML_HEADER_NOLINKS)); - - if (img) { - str_field = g_strdup_printf ("%s%s:", img, label); - label = str_field; - flags |= EM_FORMAT_HTML_HEADER_NODEC; - g_free (img); - } - - camel_header_address_list_clear (&addrs); - txt = value = html->str; - g_string_free (html, FALSE); - - flags |= EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_HTML; - } else if (!strcmp (name, "Subject")) { - buf = camel_header_unfold (header->value); - txt = value = camel_header_decode_string (buf, charset); - g_free (buf); - - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp(name, "X-evolution-mailer")) { - /* pseudo-header */ - label = _("Mailer"); - txt = value = camel_header_format_ctext (header->value, charset); - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) { - gint msg_offset, local_tz; - time_t msg_date; - struct tm local; - gchar *html; - gboolean hide_real_date; - - hide_real_date = !em_format_html_get_show_real_date (efh); - - txt = header->value; - while (*txt == ' ' || *txt == '\t') - txt++; - - html = camel_text_to_html (txt, efh->text_html_flags, 0); - - msg_date = camel_header_decode_date (txt, &msg_offset); - e_localtime_with_offset (msg_date, &local, &local_tz); - - /* Convert message offset to minutes (e.g. -0400 --> -240) */ - msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100); - /* Turn into offset from localtime, not UTC */ - msg_offset -= local_tz / 60; - - /* value will be freed at the end */ - if (!hide_real_date && !msg_offset) { - /* No timezone difference; just show the real Date: header */ - txt = value = html; - } else { - gchar *date_str; - - date_str = e_datetime_format_format ("mail", "header", - DTFormatKindDateTime, msg_date); - - if (hide_real_date) { - /* Show only the local-formatted date, losing all timezone - * information like Outlook does. Should we attempt to show - * it somehow? */ - txt = value = date_str; - } else { - txt = value = g_strdup_printf ("%s (<I>%s</I>)", html, date_str); - g_free (date_str); - } - g_free (html); - } - flags |= EM_FORMAT_HTML_HEADER_HTML | EM_FORMAT_HEADER_BOLD; - } else if (!strcmp(name, "Newsgroups")) { - struct _camel_header_newsgroup *ng, *scan; - GString *html; - - buf = camel_header_unfold (header->value); - - if (!(ng = camel_header_newsgroups_decode (buf))) { - g_free (buf); - return; - } - - g_free (buf); - - html = g_string_new(""); - scan = ng; - while (scan) { - if (flags & EM_FORMAT_HTML_HEADER_NOLINKS) - g_string_append_printf (html, "%s", scan->newsgroup); - else - g_string_append_printf(html, "<a href=\"news:%s\">%s</a>", - scan->newsgroup, scan->newsgroup); - scan = scan->next; - if (scan) - g_string_append_printf(html, ", "); - } - - camel_header_newsgroups_free (ng); - - txt = html->str; - g_string_free (html, FALSE); - flags |= EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_HTML; - } else if (!strcmp (name, "Received") || !strncmp (name, "X-", 2)) { - /* don't unfold Received nor extension headers */ - txt = value = camel_header_decode_string (header->value, charset); - } else { - /* don't unfold Received nor extension headers */ - buf = camel_header_unfold (header->value); - txt = value = camel_header_decode_string (buf, charset); - g_free (buf); - } - - efh_format_text_header (efh, buffer, label, txt, flags); - - g_free (value); - g_free (str_field); -} - -static void -efh_format_short_headers (EMFormatHTML *efh, - GString *buffer, - CamelMedium *part, - gboolean visible, - GCancellable *cancellable) -{ - EMFormat *emf = EM_FORMAT (efh); - const gchar *charset; - CamelContentType *ct; - const gchar *hdr_charset; - gchar *evolution_imagesdir; - gchar *subject = NULL; - struct _camel_header_address *addrs = NULL; - struct _camel_header_raw *header; - GString *from; - gboolean is_rtl; - - if (cancellable && g_cancellable_is_cancelled (cancellable)) - return; - - ct = camel_mime_part_get_content_type ((CamelMimePart *) part); - charset = camel_content_type_param (ct, "charset"); - charset = camel_iconv_charset_name (charset); - hdr_charset = em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf); - - evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); - from = g_string_new (""); - - g_string_append_printf (buffer, - "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" " - "id=\"__evo-short-headers\" style=\"display: %s\">", - visible ? "block" : "none"); - - header = ((CamelMimePart *) part)->headers; - while (header) { - if (!g_ascii_strcasecmp (header->name, "From")) { - GString *tmp; - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) { - header = header->next; - continue; - } - tmp = g_string_new (""); - efh_format_address (efh, tmp, addrs, header->name, FALSE); - - if (tmp->len) - g_string_printf (from, _("From: %s"), tmp->str); - g_string_free (tmp, TRUE); - - } else if (!g_ascii_strcasecmp (header->name, "Subject")) { - gchar *buf = NULL; - subject = camel_header_unfold (header->value); - buf = camel_header_decode_string (subject, hdr_charset); - g_free (subject); - subject = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0); - g_free (buf); - } - header = header->next; - } - - is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL; - if (is_rtl) { - g_string_append_printf ( - buffer, - "<tr><td width=\"100%%\" align=\"right\">%s%s%s <strong>%s</strong></td></tr>", - from->len ? "(" : "", from->str, from->len ? ")" : "", - subject ? subject : _("(no subject)")); - } else { - g_string_append_printf ( - buffer, - "<tr><td><strong>%s</strong> %s%s%s</td></tr>", - subject ? subject : _("(no subject)"), - from->len ? "(" : "", from->str, from->len ? ")" : ""); - } - - g_string_append (buffer, "</table>"); - - g_free (subject); - if (addrs) - camel_header_address_list_clear (&addrs); - - g_string_free (from, TRUE); - g_free (evolution_imagesdir); -} - -static void -write_contact_picture (CamelMimePart *part, - gint size, - GString *buffer) -{ - gchar *b64, *content_type; - CamelDataWrapper *dw; - CamelContentType *ct; - GByteArray *ba; - - ba = NULL; - dw = camel_medium_get_content (CAMEL_MEDIUM (part)); - if (dw) { - ba = camel_data_wrapper_get_byte_array (dw); - } - - if (!ba || ba->len == 0) { - - if (camel_mime_part_get_filename (part)) { - - if (size >= 0) { - g_string_append_printf ( - buffer, - "<img width=\"%d\" src=\"evo-file://%s\" />", - size, camel_mime_part_get_filename (part)); - } else { - g_string_append_printf ( - buffer, - "<img src=\"evo-file://%s\" />", - camel_mime_part_get_filename (part)); - } - } - - return; - } - - b64 = g_base64_encode (ba->data, ba->len); - ct = camel_mime_part_get_content_type (part); - content_type = camel_content_type_simple (ct); - - if (size >= 0) { - g_string_append_printf ( - buffer, - "<img width=\"%d\" src=\"data:%s;base64,%s\">", - size, content_type, b64); - } else { - g_string_append_printf ( - buffer, - "<img src=\"data:%s;base64,%s\">", - content_type, b64); - } - - g_free (b64); - g_free (content_type); -} - -static void -efh_format_full_headers (EMFormatHTML *efh, - GString *buffer, - CamelMedium *part, - gboolean all_headers, - gboolean visible, - GCancellable *cancellable) -{ - EMFormat *emf = EM_FORMAT (efh); - const gchar *charset; - CamelContentType *ct; - struct _camel_header_raw *header; - gboolean have_icon = FALSE; - const gchar *photo_name = NULL; - CamelInternetAddress *cia = NULL; - CamelSession *session; - ESourceRegistry *registry; - gboolean face_decoded = FALSE, contact_has_photo = FALSE; - guchar *face_header_value = NULL; - gsize face_header_len = 0; - gchar *header_sender = NULL, *header_from = NULL, *name; - gboolean mail_from_delegate = FALSE; - const gchar *hdr_charset; - gchar *evolution_imagesdir; - - if (cancellable && g_cancellable_is_cancelled (cancellable)) - return; - - session = em_format_get_session (emf); - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - ct = camel_mime_part_get_content_type ((CamelMimePart *) part); - charset = camel_content_type_param (ct, "charset"); - charset = camel_iconv_charset_name (charset); - hdr_charset = em_format_get_charset (emf) ? - em_format_get_charset (emf) : em_format_get_default_charset (emf); - - evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL); - - g_string_append_printf (buffer, - "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" " - "id=\"__evo-full-headers\" style=\"display: %s\" width=\"100%%\">", - visible ? "block" : "none"); - - header = ((CamelMimePart *) part)->headers; - while (header) { - if (!g_ascii_strcasecmp (header->name, "Sender")) { - struct _camel_header_address *addrs; - GString *html; - - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) - break; - - html = g_string_new(""); - name = efh_format_address (efh, html, addrs, header->name, FALSE); - - header_sender = html->str; - camel_header_address_list_clear (&addrs); - - g_string_free (html, FALSE); - g_free (name); - } else if (!g_ascii_strcasecmp (header->name, "From")) { - struct _camel_header_address *addrs; - GString *html; - - if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) - break; - - html = g_string_new(""); - name = efh_format_address (efh, html, addrs, header->name, FALSE); - - header_from = html->str; - camel_header_address_list_clear (&addrs); - - g_string_free (html, FALSE); - g_free (name); - } else if (!g_ascii_strcasecmp (header->name, "X-Evolution-Mail-From-Delegate")) { - mail_from_delegate = TRUE; - } - - header = header->next; - } - - if (header_sender && header_from && mail_from_delegate) { - gchar *bold_sender, *bold_from; - - g_string_append ( - buffer, - "<tr><td><table border=1 width=\"100%%\" " - "cellspacing=2 cellpadding=2><tr>"); - if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) - g_string_append ( - buffer, "<td align=\"right\" width=\"100%%\">"); - else - g_string_append ( - buffer, "<td align=\"left\" width=\"100%%\">"); - bold_sender = g_strconcat ("<b>", header_sender, "</b>", NULL); - bold_from = g_strconcat ("<b>", header_from, "</b>", NULL); - /* Translators: This message suggests to the receipients - * that the sender of the mail is different from the one - * listed in From field. */ - g_string_append_printf ( - buffer, - _("This message was sent by %s on behalf of %s"), - bold_sender, bold_from); - g_string_append (buffer, "</td></tr></table></td></tr>"); - g_free (bold_sender); - g_free (bold_from); - } - - g_free (header_sender); - g_free (header_from); - - g_string_append (buffer, "<tr><td width=\"100%%\"><table border=0 cellpadding=\"0\">\n"); - - g_free (evolution_imagesdir); - - /* dump selected headers */ - if (all_headers) { - header = ((CamelMimePart *) part)->headers; - while (header) { - em_format_html_format_header ( - emf, buffer, part, header, - EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset); - header = header->next; - } - } else { - GList *link; - gint mailer_shown = FALSE; - - link = g_queue_peek_head_link (&emf->header_list); - - while (link != NULL) { - EMFormatHeader *h = link->data; - gint mailer, face; - - header = ((CamelMimePart *) part)->headers; - mailer = !g_ascii_strcasecmp (h->name, "X-Evolution-Mailer"); - face = !g_ascii_strcasecmp (h->name, "Face"); - - while (header) { - if (em_format_html_get_show_sender_photo (efh) && - !photo_name && !g_ascii_strcasecmp (header->name, "From")) - photo_name = header->value; - - if (!mailer_shown && mailer && ( - !g_ascii_strcasecmp (header->name, "X-Mailer") || - !g_ascii_strcasecmp (header->name, "User-Agent") || - !g_ascii_strcasecmp (header->name, "X-Newsreader") || - !g_ascii_strcasecmp (header->name, "X-MimeOLE"))) { - struct _camel_header_raw xmailer, *use_header = NULL; - - if (!g_ascii_strcasecmp (header->name, "X-MimeOLE")) { - for (use_header = header->next; use_header; use_header = use_header->next) { - if (!g_ascii_strcasecmp (use_header->name, "X-Mailer") || - !g_ascii_strcasecmp (use_header->name, "User-Agent") || - !g_ascii_strcasecmp (use_header->name, "X-Newsreader")) { - /* even we have X-MimeOLE, then use rather the standard one, when available */ - break; - } - } - } - - if (!use_header) - use_header = header; - - xmailer.name = (gchar *) "X-Evolution-Mailer"; - xmailer.value = use_header->value; - mailer_shown = TRUE; - - em_format_html_format_header ( - emf, buffer, part, - &xmailer, h->flags, charset); - if (strstr(use_header->value, "Evolution")) - have_icon = TRUE; - } else if (!face_decoded && face && !g_ascii_strcasecmp (header->name, "Face")) { - gchar *cp = header->value; - - /* Skip over spaces */ - while (*cp == ' ') - cp++; - - face_header_value = g_base64_decode ( - cp, &face_header_len); - face_header_value = g_realloc ( - face_header_value, - face_header_len + 1); - face_header_value[face_header_len] = 0; - face_decoded = TRUE; - /* Showing an encoded "Face" header makes little sense */ - } else if (!g_ascii_strcasecmp (header->name, h->name) && !face) { - em_format_html_format_header ( - emf, buffer, part, - header, h->flags, charset); - } - - header = header->next; - } - - link = g_list_next (link); - } - } - - g_string_append (buffer, "</table></td>"); - - if (photo_name) { - CamelMimePart *photopart; - gboolean only_local_photo; - - cia = camel_internet_address_new (); - camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name); - only_local_photo = - em_format_html_get_only_local_photos (efh); - photopart = em_utils_contact_photo ( - registry, cia, only_local_photo); - - if (photopart) { - g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); - write_contact_picture (photopart, -1, buffer); - g_string_append (buffer, "</td>"); - g_object_unref (photopart); - } - g_object_unref (cia); - } - - if (!contact_has_photo && face_decoded) { - CamelMimePart *part; - - part = camel_mime_part_new (); - camel_mime_part_set_content ( - (CamelMimePart *) part, - (const gchar *) face_header_value, - face_header_len, "image/png"); - - g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); - write_contact_picture (part, 48, buffer); - g_string_append (buffer, "</td>"); - - g_object_unref (part); - g_free (face_header_value); - } - - if (have_icon && efh->show_icon) { - GtkIconInfo *icon_info; - CamelMimePart *iconpart = NULL; - - icon_info = gtk_icon_theme_lookup_icon ( - gtk_icon_theme_get_default (), - "evolution", 16, GTK_ICON_LOOKUP_NO_SVG); - if (icon_info != NULL) { - iconpart = em_format_html_file_part ( - (EMFormatHTML *) emf, "image/png", - gtk_icon_info_get_filename (icon_info), - cancellable); - gtk_icon_info_free (icon_info); - } - if (iconpart) { - g_string_append (buffer, "<td align=\"right\" valign=\"top\">"); - write_contact_picture (iconpart, 16, buffer); - g_string_append (buffer, "</td>"); - - g_object_unref (iconpart); - } - } - - g_string_append (buffer, "</tr></table>"); -} - -gboolean -em_format_html_can_load_images (EMFormatHTML *efh) -{ - g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE); - - return ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) || - ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES) && - efh->priv->can_load_images)); -} - -void -em_format_html_animation_extract_frame (const GByteArray *anim, - gchar **frame, - gsize *len) -{ - GdkPixbufLoader *loader; - GdkPixbufAnimation *animation; - GdkPixbuf *frame_buf; - - /* GIF89a (GIF image signature) */ - const gchar GIF_HEADER[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; - const gint GIF_HEADER_LEN = sizeof (GIF_HEADER); - - /* NETSCAPE2.0 (extension describing animated GIF, starts on 0x310) */ - const gchar GIF_APPEXT[] = { 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, - 0x50, 0x45, 0x32, 0x2E, 0x30 }; - const gint GIF_APPEXT_LEN = sizeof (GIF_APPEXT); - - if ((anim == NULL) || (anim->data == NULL)) { - *frame = NULL; - *len = 0; - return; - } - - /* Check if the image is an animated GIF. We don't care about any - * other animated formats (APNG or MNG) as WebKit does not support them - * and displays only the first frame. */ - if ((anim->len < 0x331) - || (memcmp (anim->data, GIF_HEADER, GIF_HEADER_LEN) != 0) - || (memcmp (&anim->data[0x310], GIF_APPEXT, GIF_APPEXT_LEN) != 0)) { - - *frame = g_memdup (anim->data, anim->len); - *len = anim->len; - return; - } - - loader = gdk_pixbuf_loader_new (); - gdk_pixbuf_loader_write (loader, (guchar *) anim->data, anim->len, NULL); - gdk_pixbuf_loader_close (loader, NULL); - animation = gdk_pixbuf_loader_get_animation (loader); - if (!animation) { - - *frame = g_memdup (anim->data, anim->len); - *len = anim->len; - g_object_unref (loader); - return; - } - - /* Extract first frame */ - frame_buf = gdk_pixbuf_animation_get_static_image (animation); - if (!frame_buf) { - *frame = g_memdup (anim->data, anim->len); - *len = anim->len; - g_object_unref (loader); - g_object_unref (animation); - return; - } - - /* Unforunatelly, GdkPixbuf cannot save to GIF, but WebKit does not - * have any trouble displaying PNG image despite the part having - * image/gif mime-type */ - gdk_pixbuf_save_to_buffer (frame_buf, frame, len, "png", NULL, NULL); - - g_object_unref (loader); -} |