aboutsummaryrefslogtreecommitdiffstats
path: root/mail/em-format-html.c
diff options
context:
space:
mode:
authorDan Vrátil <dvratil@redhat.com>2012-03-29 00:38:11 +0800
committerDan Vrátil <dvratil@redhat.com>2012-03-29 00:38:25 +0800
commit6d2c382788a4042d53f49a080acd11b499aa52f6 (patch)
tree3834f0836340918ba17594a603ba61c13b9929a0 /mail/em-format-html.c
parent6bd1c6833a2c51898ac45865767dd01ba66a95c5 (diff)
downloadgsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.tar
gsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.tar.gz
gsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.tar.bz2
gsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.tar.lz
gsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.tar.xz
gsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.tar.zst
gsoc2013-evolution-6d2c382788a4042d53f49a080acd11b499aa52f6.zip
WebKit port - port formatter and mail module
Diffstat (limited to 'mail/em-format-html.c')
-rw-r--r--mail/em-format-html.c3755
1 files changed, 1640 insertions, 2115 deletions
diff --git a/mail/em-format-html.c b/mail/em-format-html.c
index 3c130e63d4..c481693eed 100644
--- a/mail/em-format-html.c
+++ b/mail/em-format-html.c
@@ -24,6 +24,8 @@
#include <config.h>
#endif
+#define _GNU_SOURCE /* Enable strcasestr in string.h */
+
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
@@ -52,20 +54,20 @@
#include <shell/e-shell.h>
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-stream.h>
-
#include <glib/gi18n.h>
-#include <libemail-utils/mail-mt.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-html-stream.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 \
@@ -73,45 +75,19 @@
#define d(x)
-#define EM_FORMAT_HTML_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), EM_TYPE_FORMAT_HTML, EMFormatHTMLPrivate))
-
-#define EFM_MESSAGE_START_ANAME "evolution#message#start"
-#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>"
-
-struct _EMFormatHTMLCache {
- CamelMultipart *textmp;
-
- gchar partid[1];
-};
-
struct _EMFormatHTMLPrivate {
- EWebView *web_view;
-
- CamelMimeMessage *last_part; /* not reffed, DO NOT dereference */
- volatile gint format_id; /* format thread id */
- guint format_timeout_id;
- struct _format_msg *format_timeout_msg;
-
- /* Table that re-maps text parts into a mutlipart/mixed, EMFormatHTMLCache * */
- GHashTable *text_inline_parts;
-
- GQueue pending_jobs;
- GMutex *lock;
-
GdkColor colors[EM_FORMAT_HTML_NUM_COLOR_TYPES];
EMailImageLoadingPolicy image_loading_policy;
- EMFormatHTMLHeadersState headers_state;
- gboolean headers_collapsable;
-
- guint load_images_now : 1;
+ 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,
@@ -125,345 +101,1214 @@ enum {
PROP_SHOW_SENDER_PHOTO,
PROP_SHOW_REAL_DATE,
PROP_TEXT_COLOR,
- PROP_WEB_VIEW,
- PROP_HEADERS_STATE,
- PROP_HEADERS_COLLAPSABLE
+ PROP_ANIMATE_IMAGES
};
-static void efh_url_requested (GtkHTML *html, const gchar *url, GtkHTMLStream *handle, EMFormatHTML *efh);
-static gboolean efh_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTML *efh);
-static void efh_gtkhtml_destroy (GtkHTML *html, EMFormatHTML *efh);
+#define EFM_MESSAGE_START_ANAME "evolution_message_start"
+#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>"
-static void efh_format_message (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback);
+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;
-static void efh_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- CamelCipherValidity *valid,
- GCancellable *cancellable);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
-static void efh_builtin_init (EMFormatHTMLClass *efhc);
+ 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;
+ }
-static void efh_write_image (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable);
+ 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;
-static gpointer parent_class;
-static CamelDataCache *emfh_http_cache;
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+}
-#define EMFH_HTTP_CACHE_PATH "http"
+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;
-/* Sigh, this is so we have a cancellable, async rendering thread */
-struct _format_msg {
- MailMsg base;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- EMFormatHTML *format;
- EMFormat *format_source;
- EMHTMLStream *estream;
- CamelFolder *folder;
- gchar *uid;
- CamelMimeMessage *message;
- gboolean cancelled;
-};
+ 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);
+ }
-static gchar *
-efh_format_desc (struct _format_msg *m)
+ 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)
{
- return g_strdup(_("Formatting message"));
+ 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_format_exec (struct _format_msg *m,
- GCancellable *cancellable,
- GError **error)
+efh_parse_text_html (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormat *format;
- CamelStream *stream;
- struct _EMFormatHTMLJob *job;
- GNode *puri_level;
+ 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 (m->format->priv->web_view == NULL) {
- m->cancelled = TRUE;
+ 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;
}
- format = EM_FORMAT (m->format);
- stream = CAMEL_STREAM (m->estream);
+ 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 (
- "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
- "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
- "<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n",
- e_color_to_value (
- &m->format->priv->colors[
- EM_FORMAT_HTML_COLOR_BODY]),
- e_color_to_value (
- &m->format->priv->colors[
- EM_FORMAT_HTML_COLOR_HEADER]));
- camel_stream_write_string (stream, content, cancellable, NULL);
+ _("Pointer to unknown external data (\"%s\" type)"),
+ access_type);
+ camel_mime_part_set_content (newpart, content, strlen (content), "text/plain");
g_free (content);
- /* <insert top-header stuff here> */
+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");
- if (format->mode == EM_FORMAT_MODE_SOURCE) {
- em_format_format_source (
- format, stream,
- (CamelMimePart *) m->message, cancellable);
- } else {
- const EMFormatHandler *handle;
- const gchar *mime_type;
-
- mime_type = "x-evolution/message/prefix";
- handle = em_format_find_handler (format, mime_type);
-
- if (handle != NULL)
- handle->handler (
- format, stream,
- CAMEL_MIME_PART (m->message), handle,
- cancellable, FALSE);
-
- mime_type = "x-evolution/message/rfc822";
- handle = em_format_find_handler (format, mime_type);
-
- if (handle != NULL)
- handle->handler (
- format, stream,
- CAMEL_MIME_PART (m->message), handle,
- cancellable, FALSE);
- }
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+}
- camel_stream_flush (stream, cancellable, NULL);
+static void
+efh_parse_message_deliverystatus (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatPURI *puri;
+ gint len;
- puri_level = format->pending_uri_level;
- base = format->base;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- do {
- /* now dispatch any added tasks ... */
- g_mutex_lock (m->format->priv->lock);
- while ((job = g_queue_pop_head (&m->format->priv->pending_jobs))) {
- g_mutex_unlock (m->format->priv->lock);
-
- /* This is an implicit check to see if the gtkhtml has been destroyed */
- if (m->format->priv->web_view == NULL)
- g_cancellable_cancel (cancellable);
-
- /* call jobs even if cancelled, so they can clean up resources */
- format->pending_uri_level = job->puri_level;
- if (job->base)
- format->base = job->base;
- job->callback (job, cancellable);
- format->base = base;
-
- /* clean up the job */
- g_object_unref (job->stream);
- if (job->base)
- camel_url_free (job->base);
- g_free (job);
-
- g_mutex_lock (m->format->priv->lock);
- }
- g_mutex_unlock (m->format->priv->lock);
-
- if (m->estream) {
- /* Closing this base stream can queue more jobs, so we need
- * to check the list again after we've finished */
- d(printf("out of jobs, closing root stream\n"));
- camel_stream_write_string (
- (CamelStream *) m->estream,
- "</body>\n</html>\n", cancellable, NULL);
- camel_stream_close ((CamelStream *) m->estream, cancellable, NULL);
- if (g_cancellable_is_cancelled (cancellable)) {
- m->cancelled = TRUE;
- m->estream->sync.cancel = TRUE;
- }
- g_object_unref (m->estream);
- m->estream = NULL;
- }
+ 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);
- } while (!g_queue_is_empty (&m->format->priv->pending_jobs));
+ opart = camel_mime_part_new ();
+ camel_mime_part_construct_from_parser_sync (opart, parser, cancellable, NULL);
- d(printf("out of jobs, done\n"));
+ em_format_parse_part_as (emf, opart, part_id, &oinfo,
+ "x-evolution/message", cancellable);
- format->pending_uri_level = puri_level;
- m->cancelled = m->cancelled || g_cancellable_is_cancelled (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_format_done (struct _format_msg *m)
+efh_write_image (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- d(printf("formatting finished\n"));
+ gchar *content;
+ EMFormatHTML *efh;
+ CamelDataWrapper *dw;
+ GByteArray *ba;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ efh = (EMFormatHTML *) emf;
+
+ dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part));
+ g_return_if_fail (dw);
+
+ ba = camel_data_wrapper_get_byte_array (dw);
+
+ if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
- m->format->priv->format_id = -1;
- m->format->priv->load_images_now = FALSE;
- m->format->state = EM_FORMAT_HTML_STATE_NONE;
- g_signal_emit_by_name(m->format, "complete");
+ if (!efh->priv->animate_images) {
+
+ gchar *buff;
+ gsize len;
+ gchar *data;
+ GByteArray anim;
+
+ data = g_strndup ((gchar *) ba->data, (gsize) ba->len);
+ anim.data = g_base64_decode (data, (gsize *) &(anim.len));
+ g_free (data);
+
+ em_format_html_animation_extract_frame (&anim, &buff, &len);
+
+ camel_stream_write (stream, buff, len, cancellable, NULL);
+
+ g_free (buff);
+ g_free (anim.data);
+
+ } else {
+ CamelStream *stream_filter;
+ CamelMimeFilter *filter;
+
+ stream_filter = camel_stream_filter_new (stream);
+ filter = camel_mime_filter_basic_new (
+ CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
+
+ camel_stream_write (
+ stream_filter,
+ (gchar *) ba->data, ba->len,
+ cancellable, NULL);
+ g_object_unref (stream_filter);
+ g_object_unref (filter);
+ }
+
+ } else {
+
+ gchar *buffer;
+
+ if (!efh->priv->animate_images) {
+
+ gchar *buff;
+ gsize len;
+ gchar *data;
+ GByteArray raw_data;
+
+ data = g_strndup ((gchar *) ba->data, ba->len);
+ raw_data.data = (guint8 *) g_base64_decode (
+ data, (gsize *) &(raw_data.len));
+ g_free (data);
+
+ em_format_html_animation_extract_frame (&raw_data, &buff, &len);
+
+ content = g_base64_encode ((guchar *) buff, len);
+ g_free (buff);
+ g_free (raw_data.data);
+
+ } else {
+ content = g_strndup ((gchar *) 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);
+ }
}
static void
-efh_format_free (struct _format_msg *m)
+efh_write_text_enriched (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- d(printf("formatter freed\n"));
- g_object_unref (m->format);
- if (m->estream) {
- camel_stream_close ((CamelStream *) m->estream, NULL, NULL);
- if (m->cancelled)
- m->estream->sync.cancel = TRUE;
- g_object_unref (m->estream);
+ 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 (m->folder)
- g_object_unref (m->folder);
- g_free (m->uid);
- if (m->message)
- g_object_unref (m->message);
- if (m->format_source)
- g_object_unref (m->format_source);
+
+ 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 MailMsgInfo efh_format_info = {
- sizeof (struct _format_msg),
- (MailMsgDescFunc) efh_format_desc,
- (MailMsgExecFunc) efh_format_exec,
- (MailMsgDoneFunc) efh_format_done,
- (MailMsgFreeFunc) efh_format_free
-};
+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\">\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);
+
+ g_object_unref (filtered_stream);
+ g_free (content);
-static gboolean
-efh_format_helper (struct _format_msg *m,
- gboolean async)
+ camel_stream_write_string (stream, "</div></div>\n", cancellable, NULL);
+}
+
+static gchar *
+get_tag (const gchar *tag_name,
+ gchar *opening,
+ gchar *closing)
{
- GtkHTMLStream *hstream;
- EMFormatHTML *efh = m->format;
- struct _EMFormatHTMLPrivate *p = efh->priv;
- EWebView *web_view;
+ gchar *t;
+ gboolean has_end;
+
+ for (t = closing - 1; t != opening; t--) {
+ if (*t != ' ')
+ break;
+ }
- web_view = em_format_html_get_web_view (m->format);
+ /* Not a pair tag */
+ if (*t == '/')
+ return g_strndup (opening, closing - opening + 1);
- if (web_view == NULL) {
- mail_msg_unref (m);
- return FALSE;
+ for (t = closing; t && *t; t++) {
+ if (*t == '<')
+ break;
}
- if (async) {
- d(printf("timeout called ...\n"));
- if (p->format_id != -1) {
- d(printf(" still waiting for cancellation to take effect, waiting ...\n"));
- return TRUE;
+ do {
+ if (*t == '/') {
+ has_end = TRUE;
+ break;
}
- }
- g_return_val_if_fail (g_queue_is_empty (&p->pending_jobs), FALSE);
+ 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);
- d(printf(" ready to go, firing off format thread\n"));
+ if (g_strncasecmp (t, tag_name, strlen (tag_name)) == 0) {
- /* call super-class to kick it off */
- /* FIXME Not passing a GCancellable here. */
- EM_FORMAT_CLASS (parent_class)->format_clone (
- EM_FORMAT (efh), m->folder, m->uid,
- m->message, m->format_source, NULL);
- em_format_html_clear_pobject (m->format);
+ closing = strstr (t, ">");
- /* FIXME: method off EMFormat? */
- if (((EMFormat *) efh)->valid) {
- camel_cipher_validity_free (((EMFormat *) efh)->valid);
- ((EMFormat *) efh)->valid = NULL;
- ((EMFormat *) efh)->valid_parent = NULL;
+ return g_strndup (opening, closing - opening + strlen (tag_name));
}
- if (m->message == NULL) {
- hstream = gtk_html_begin (GTK_HTML (web_view));
- gtk_html_stream_close (hstream, GTK_HTML_STREAM_OK);
- mail_msg_unref (m);
- p->last_part = NULL;
- } else {
- efh->state = EM_FORMAT_HTML_STATE_RENDERING;
-#if HAVE_CLUTTER
- if (p->last_part != m->message && !e_shell_get_express_mode (e_shell_get_default ())) {
-#else
- if (p->last_part != m->message) {
-#endif
- hstream = gtk_html_begin (GTK_HTML (web_view));
- gtk_html_stream_printf (hstream, "<h5>%s</h5>", _("Formatting Message..."));
- gtk_html_stream_close (hstream, GTK_HTML_STREAM_OK);
- }
+ /* Broken HTML? */
+ return g_strndup (opening, closing - opening + 1);
+}
- hstream = NULL;
- m->estream = (EMHTMLStream *) em_html_stream_new (
- GTK_HTML (web_view), hstream);
+static void
+efh_write_text_html (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatHTML *efh = (EMFormatHTML *) emf;
- if (p->last_part == m->message) {
- em_html_stream_set_flags (m->estream,
- GTK_HTML_BEGIN_KEEP_SCROLL | GTK_HTML_BEGIN_KEEP_IMAGES
- | GTK_HTML_BEGIN_BLOCK_UPDATES | GTK_HTML_BEGIN_BLOCK_IMAGES);
- } else {
- /* clear cache of inline-scanned text parts */
- g_hash_table_remove_all (p->text_inline_parts);
+ 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);
- p->last_part = m->message;
+ /* 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;
}
- efh->priv->format_id = m->base.seq;
+ /* include the "body" as well -----v */
+ g_string_erase (string, 0, tag - string->str + 4);
+ g_string_prepend (string, "<div ");
- if (async) {
- mail_msg_unordered_push (m);
- } else {
- efh_format_exec (m, NULL, NULL);
+ for (iter = tags; iter; iter = iter->next) {
+ g_string_prepend (string, iter->data);
}
- }
- efh->priv->format_timeout_id = 0;
- efh->priv->format_timeout_msg = NULL;
+ 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;
- return FALSE;
+ 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\" style=\"border-color: #%06x; "
+ "background-color: #%06x;\">"
+ "<div class=\"part-container-inner-margin\">\n"
+ "<iframe width=\"100%%\" height=\"auto\""
+ " frameborder=\"0\" src=\"%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);
+
+ camel_stream_write_string (stream, str, cancellable, NULL);
+
+ g_free (str);
+ g_free (uri);
+ }
}
static void
-efh_free_cache (struct _EMFormatHTMLCache *efhc)
+efh_write_source (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- if (efhc->textmp)
- g_object_unref (efhc->textmp);
- g_free (efhc);
+ 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=\"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_HEADER]));
+
+ 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_gtkhtml_destroy (GtkHTML *html,
- EMFormatHTML *efh)
+efh_write_headers (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- if (efh->priv->format_timeout_id != 0) {
- g_source_remove (efh->priv->format_timeout_id);
- efh->priv->format_timeout_id = 0;
- mail_msg_unref (efh->priv->format_timeout_msg);
- efh->priv->format_timeout_msg = NULL;
+ 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);
}
- /* This probably works ... */
- if (efh->priv->format_id != -1)
- mail_msg_cancel (efh->priv->format_id);
+ 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);
- if (efh->priv->web_view != NULL) {
- g_object_unref (efh->priv->web_view);
- efh->priv->web_view = 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;
+
+ /* 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, info, 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);
}
+
}
-static struct _EMFormatHTMLCache *
-efh_insert_cache (EMFormatHTML *efh,
- const gchar *partid)
+/*****************************************************************************/
+
+/* 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)
{
- struct _EMFormatHTMLCache *efhc;
+ EMFormatClass *emfc;
+ gint ii;
- efhc = g_malloc0 (sizeof (*efh) + strlen (partid));
- strcpy (efhc->partid, partid);
- g_hash_table_insert (efh->priv->text_inline_parts, efhc->partid, efhc);
+ emfc = (EMFormatClass *) efhc;
- return efhc;
+ for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
+ em_format_class_add_handler (
+ emfc, &type_builtin_table[ii]);
}
static void
@@ -544,15 +1389,12 @@ efh_set_property (GObject *object,
EM_FORMAT_HTML_COLOR_TEXT,
g_value_get_boxed (value));
return;
- case PROP_HEADERS_STATE:
- em_format_html_set_headers_state (
- EM_FORMAT_HTML (object),
- g_value_get_int (value));
- return;
- case PROP_HEADERS_COLLAPSABLE:
- em_format_html_set_headers_collapsable (
+
+ 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);
@@ -645,21 +1487,11 @@ efh_get_property (GObject *object,
&color);
g_value_set_boxed (value, &color);
return;
-
- case PROP_WEB_VIEW:
- g_value_set_object (
- value, em_format_html_get_web_view (
- EM_FORMAT_HTML (object)));
- return;
- case PROP_HEADERS_STATE:
- g_value_set_int (
- value, em_format_html_get_headers_state (
- EM_FORMAT_HTML (object)));
- return;
- case PROP_HEADERS_COLLAPSABLE:
+ case PROP_ANIMATE_IMAGES:
g_value_set_boolean (
- value, em_format_html_get_headers_collapsable (
+ value, em_format_html_get_animate_images (
EM_FORMAT_HTML (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -668,230 +1500,198 @@ efh_get_property (GObject *object,
static void
efh_finalize (GObject *object)
{
- EMFormatHTML *efh = EM_FORMAT_HTML (object);
-
- em_format_html_clear_pobject (efh);
- efh_gtkhtml_destroy (GTK_HTML (efh->priv->web_view), efh);
-
- g_hash_table_destroy (efh->priv->text_inline_parts);
-
- g_mutex_free (efh->priv->lock);
-
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
-static gboolean
-efh_format_timeout (struct _format_msg *m)
-{
- return efh_format_helper (m, TRUE);
-}
-
-void
-em_format_html_clone_sync (CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormatHTML *efh,
- EMFormat *source)
-{
- struct _format_msg *m;
-
- m = mail_msg_new (&efh_format_info);
- m->format = g_object_ref (efh);
- if (source)
- m->format_source = g_object_ref (source);
- m->folder = g_object_ref (folder);
- m->uid = g_strdup (uid);
- m->message = g_object_ref (msg);
-
- efh_format_helper (m, FALSE);
- efh_format_free (m);
-}
-
static void
-efh_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormat *emfsource,
- GCancellable *cancellable)
+efh_write_attachment (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- struct _format_msg *m;
-
- /* How to sub-class ? Might need to adjust api ... */
-
- if (efh->priv->web_view == NULL)
- return;
+ gchar *text, *html;
+ CamelContentType *ct;
+ gchar *mime_type;
+ const EMFormatHandler *handler;
- d(printf("efh_format called\n"));
- if (efh->priv->format_timeout_id != 0) {
- d(printf(" timeout for last still active, removing ...\n"));
- g_source_remove (efh->priv->format_timeout_id);
- efh->priv->format_timeout_id = 0;
- mail_msg_unref (efh->priv->format_timeout_msg);
- efh->priv->format_timeout_msg = NULL;
- }
+ /* we display all inlined attachments only */
- if (emfsource != NULL)
- g_object_ref (emfsource);
+ /* 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);
- if (folder != NULL)
- g_object_ref (folder);
+ ct = camel_mime_part_get_content_type (puri->part);
+ mime_type = camel_content_type_simple (ct);
- if (msg != NULL)
- g_object_ref (msg);
+ /* 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);
- m = mail_msg_new (&efh_format_info);
- m->format = g_object_ref (emf);
- m->format_source = emfsource;
- m->folder = folder;
- m->uid = g_strdup (uid);
- m->message = msg;
+ camel_stream_write_string (
+ stream, "</font></td></tr><tr></table>", cancellable, NULL);
- if (efh->priv->format_id == -1) {
- d(printf(" idle, forcing format\n"));
- efh_format_timeout (m);
- } else {
- d(printf(" still busy, cancelling and queuing wait\n"));
- /* cancel and poll for completion */
- mail_msg_cancel (efh->priv->format_id);
- efh->priv->format_timeout_msg = m;
- efh->priv->format_timeout_id = g_timeout_add (
- 100, (GSourceFunc) efh_format_timeout, m);
+ 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_format_error (EMFormat *emf,
- CamelStream *stream,
- const gchar *txt)
+efh_preparse (EMFormat *emf)
{
- GString *buffer;
- gchar *html;
-
- buffer = g_string_new ("<em><font color=\"red\">");
-
- html = camel_text_to_html (
- txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- g_string_append (buffer, html);
- g_free (html);
+ CamelInternetAddress *addr;
- g_string_append (buffer, "</font></em><br>");
+ EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- camel_stream_write (stream, buffer->str, buffer->len, NULL, NULL);
+ if (!emf->message) {
+ efh->priv->can_load_images = FALSE;
+ return;
+ }
- g_string_free (buffer, TRUE);
+ addr = camel_mime_message_get_from (emf->message);
+ efh->priv->can_load_images = em_utils_in_addressbook (addr, FALSE);
}
static void
-efh_format_source (EMFormat *emf,
+efh_write_message (EMFormat *emf,
+ GList *puris,
CamelStream *stream,
- CamelMimePart *part,
+ EMFormatWriterInfo *info,
GCancellable *cancellable)
{
- CamelStream *filtered_stream;
- CamelMimeFilter *filter;
- CamelDataWrapper *dw = (CamelDataWrapper *) part;
+ GList *iter;
+ EMFormatHTML *efh;
+ gchar *header;
- filtered_stream = camel_stream_filter_new (stream);
+ efh = (EMFormatHTML *) emf;
- 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);
+ 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, "<table><tr><td><tt>", cancellable, NULL);
- em_format_format_text (emf, filtered_stream, dw, cancellable);
- camel_stream_write_string (
- stream, "</tt></td></tr></table>", cancellable, NULL);
+ camel_stream_write_string (stream, header, cancellable, NULL);
+ g_free (header);
- g_object_unref (filtered_stream);
-}
+ if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) {
-static void
-efh_format_attachment (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const gchar *mime_type,
- const EMFormatHandler *handle,
- GCancellable *cancellable)
-{
- gchar *text, *html;
+ efh_write_source (emf, emf->mail_part_list->data,
+ stream, info, cancellable);
- /* we display all inlined attachments only */
+ camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
+ return;
+ }
- /* 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);
+ for (iter = puris; iter; iter = iter->next) {
- /* output some info about it */
- text = em_format_describe_part (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);
+ EMFormatPURI *puri = iter->data;
- camel_stream_write_string (
- stream, "</font></td></tr><tr></table>", cancellable, NULL);
+ if (!puri)
+ continue;
- if (handle && em_format_is_inline (emf, emf->part_id->str, part, handle))
- handle->handler (emf, stream, part, handle, cancellable, FALSE);
-}
+ /* 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;
-static gboolean
-efh_busy (EMFormat *emf)
-{
- EMFormatHTMLPrivate *priv;
+ 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;
- priv = EM_FORMAT_HTML_GET_PRIVATE (emf);
+ d(printf(".rfc822 - skipping %s\n", puri->uri));
+ }
+
+ /* Skip the .rfc822.end PURI as well. */
+ if (!iter)
+ break;
- return (priv->format_id != -1);
+ 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 *class)
+efh_base_init (EMFormatHTMLClass *klass)
{
- efh_builtin_init (class);
+ efh_builtin_init (klass);
}
static void
-efh_class_init (EMFormatHTMLClass *class)
+efh_class_init (EMFormatHTMLClass *klass)
{
GObjectClass *object_class;
- EMFormatClass *format_class;
- const gchar *user_cache_dir;
+ EMFormatClass *emf_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (EMFormatHTMLPrivate));
- parent_class = g_type_class_peek_parent (class);
- g_type_class_add_private (class, sizeof (EMFormatHTMLPrivate));
+ emf_class = EM_FORMAT_CLASS (klass);
+ emf_class->preparse = efh_preparse;
+ emf_class->write = efh_write;
- object_class = G_OBJECT_CLASS (class);
+ object_class = G_OBJECT_CLASS (klass);
object_class->set_property = efh_set_property;
object_class->get_property = efh_get_property;
object_class->finalize = efh_finalize;
- format_class = EM_FORMAT_CLASS (class);
- format_class->format_clone = efh_format_clone;
- format_class->format_error = efh_format_error;
- format_class->format_source = efh_format_source;
- format_class->format_attachment = efh_format_attachment;
- format_class->format_secure = efh_format_secure;
- format_class->busy = efh_busy;
-
- class->html_widget_type = E_TYPE_WEB_VIEW;
-
g_object_class_install_property (
object_class,
PROP_BODY_COLOR,
@@ -982,7 +1782,7 @@ efh_class_init (EMFormatHTMLClass *class)
"show-sender-photo",
"Show Sender Photo",
NULL,
- TRUE,
+ FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
@@ -1009,79 +1809,24 @@ efh_class_init (EMFormatHTMLClass *class)
g_object_class_install_property (
object_class,
- PROP_WEB_VIEW,
- g_param_spec_object (
- "web-view",
- "Web View",
- NULL,
- E_TYPE_WEB_VIEW,
- G_PARAM_READABLE));
-
- g_object_class_install_property (
- object_class,
- PROP_HEADERS_STATE,
- g_param_spec_int (
- "headers-state",
- "Headers state",
- NULL,
- EM_FORMAT_HTML_HEADERS_STATE_EXPANDED,
- EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED,
- EM_FORMAT_HTML_HEADERS_STATE_EXPANDED,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_HEADERS_STATE,
+ PROP_ANIMATE_IMAGES,
g_param_spec_boolean (
- "headers-collapsable",
- NULL,
+ "animate-images",
+ "Animate images",
NULL,
FALSE,
G_PARAM_READWRITE));
-
- /* cache expiry - 2 hour access, 1 day max */
- user_cache_dir = e_get_user_cache_dir ();
- emfh_http_cache = camel_data_cache_new (user_cache_dir, NULL);
- if (emfh_http_cache) {
- camel_data_cache_set_expire_age (emfh_http_cache, 24 *60 *60);
- camel_data_cache_set_expire_access (emfh_http_cache, 2 *60 *60);
- }
}
static void
efh_init (EMFormatHTML *efh,
- EMFormatHTMLClass *class)
+ EMFormatHTMLClass *klass)
{
- EWebView *web_view;
GdkColor *color;
efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh);
g_queue_init (&efh->pending_object_list);
- g_queue_init (&efh->priv->pending_jobs);
- efh->priv->lock = g_mutex_new ();
- efh->priv->format_id = -1;
- efh->priv->text_inline_parts = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) NULL,
- (GDestroyNotify) efh_free_cache);
-
- web_view = g_object_new (class->html_widget_type, NULL);
- efh->priv->web_view = g_object_ref_sink (web_view);
-
- gtk_html_set_blocking (GTK_HTML (web_view), FALSE);
- gtk_html_set_caret_first_focus_anchor (
- GTK_HTML (web_view), EFM_MESSAGE_START_ANAME);
- gtk_html_set_default_content_type (
- GTK_HTML (web_view), "text/html; charset=utf-8");
- e_web_view_set_editable (web_view, FALSE);
-
- g_signal_connect (
- web_view, "url-requested",
- G_CALLBACK (efh_url_requested), efh);
- g_signal_connect (
- web_view, "object-requested",
- G_CALLBACK (efh_object_requested), efh);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY];
gdk_color_parse ("#eeeeee", color);
@@ -1103,7 +1848,6 @@ efh_init (EMFormatHTML *efh,
CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
efh->show_icon = TRUE;
- efh->state = EM_FORMAT_HTML_STATE_NONE;
e_extensible_load_extensions (E_EXTENSIBLE (efh));
}
@@ -1144,28 +1888,7 @@ em_format_html_get_type (void)
return type;
}
-EWebView *
-em_format_html_get_web_view (EMFormatHTML *efh)
-{
- g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), NULL);
-
- return efh->priv->web_view;
-}
-
-void
-em_format_html_load_images (EMFormatHTML *efh)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
- if (efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS)
- return;
-
- /* This will remain set while we're still
- * rendering the same message, then it wont be. */
- efh->priv->load_images_now = TRUE;
- em_format_queue_redraw (EM_FORMAT (efh));
-}
-
+/*****************************************************************************/
void
em_format_html_get_color (EMFormatHTML *efh,
EMFormatHTMLColorType type,
@@ -1338,42 +2061,23 @@ em_format_html_set_show_real_date (EMFormatHTML *efh,
g_object_notify (G_OBJECT (efh), "show-real-date");
}
-EMFormatHTMLHeadersState
-em_format_html_get_headers_state (EMFormatHTML *efh)
-{
- g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), EM_FORMAT_HTML_HEADERS_STATE_EXPANDED);
-
- return efh->priv->headers_state;
-}
-
-void
-em_format_html_set_headers_state (EMFormatHTML *efh,
- EMFormatHTMLHeadersState state)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
- efh->priv->headers_state = state;
-
- g_object_notify (G_OBJECT (efh), "headers-state");
-}
-
gboolean
-em_format_html_get_headers_collapsable (EMFormatHTML *efh)
+em_format_html_get_animate_images (EMFormatHTML *efh)
{
g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
- return efh->priv->headers_collapsable;
+ return efh->priv->animate_images;
}
void
-em_format_html_set_headers_collapsable (EMFormatHTML *efh,
- gboolean collapsable)
+em_format_html_set_animate_images (EMFormatHTML *efh,
+ gboolean animate_images)
{
g_return_if_fail (EM_IS_FORMAT_HTML (efh));
- efh->priv->headers_collapsable = collapsable;
+ efh->priv->animate_images = animate_images;
- g_object_notify (G_OBJECT (efh), "headers-collapsable");
+ g_object_notify (G_OBJECT (efh), "animate-images");
}
CamelMimePart *
@@ -1407,1184 +2111,60 @@ em_format_html_file_part (EMFormatHTML *efh,
return part;
}
-/* all this api is a pain in the bum ... */
-
-EMFormatHTMLPObject *
-em_format_html_add_pobject (EMFormatHTML *efh,
- gsize size,
- const gchar *classid,
- CamelMimePart *part,
- EMFormatHTMLPObjectFunc func)
-{
- EMFormatHTMLPObject *pobj;
-
- if (size < sizeof (EMFormatHTMLPObject)) {
- g_warning ("size is less than the size of EMFormatHTMLPObject\n");
- size = sizeof (EMFormatHTMLPObject);
- }
-
- pobj = g_malloc0 (size);
- if (classid)
- pobj->classid = g_strdup (classid);
- else
- pobj->classid = g_strdup_printf("e-object:///%s", ((EMFormat *)efh)->part_id->str);
-
- pobj->format = efh;
- pobj->func = func;
- pobj->part = part;
-
- g_queue_push_tail (&efh->pending_object_list, pobj);
-
- return pobj;
-}
-
-EMFormatHTMLPObject *
-em_format_html_find_pobject (EMFormatHTML *emf,
- const gchar *classid)
-{
- GList *link;
-
- g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL);
- g_return_val_if_fail (classid != NULL, NULL);
-
- link = g_queue_peek_head_link (&emf->pending_object_list);
-
- while (link != NULL) {
- EMFormatHTMLPObject *pw = link->data;
-
- if (!strcmp (pw->classid, classid))
- return pw;
-
- link = g_list_next (link);
- }
-
- return NULL;
-}
-
-EMFormatHTMLPObject *
-em_format_html_find_pobject_func (EMFormatHTML *emf,
- CamelMimePart *part,
- EMFormatHTMLPObjectFunc func)
-{
- GList *link;
-
- g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL);
-
- link = g_queue_peek_head_link (&emf->pending_object_list);
-
- while (link != NULL) {
- EMFormatHTMLPObject *pw = link->data;
-
- if (pw->func == func && pw->part == part)
- return pw;
-
- link = g_list_next (link);
- }
-
- return NULL;
-}
-
-void
-em_format_html_remove_pobject (EMFormatHTML *emf,
- EMFormatHTMLPObject *pobject)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (emf));
- g_return_if_fail (pobject != NULL);
-
- g_queue_remove (&emf->pending_object_list, pobject);
-
- if (pobject->free != NULL)
- pobject->free (pobject);
-
- g_free (pobject->classid);
- g_free (pobject);
-}
-
-void
-em_format_html_clear_pobject (EMFormatHTML *emf)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (emf));
-
- while (!g_queue_is_empty (&emf->pending_object_list)) {
- EMFormatHTMLPObject *pobj;
-
- pobj = g_queue_pop_head (&emf->pending_object_list);
- em_format_html_remove_pobject (emf, pobj);
- }
-}
-
-struct _EMFormatHTMLJob *
-em_format_html_job_new (EMFormatHTML *emfh,
- void (*callback) (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable),
- gpointer data)
-{
- struct _EMFormatHTMLJob *job = g_malloc0 (sizeof (*job));
-
- job->format = emfh;
- job->puri_level = ((EMFormat *) emfh)->pending_uri_level;
- job->callback = callback;
- job->u.data = data;
- if (((EMFormat *) emfh)->base)
- job->base = camel_url_copy (((EMFormat *) emfh)->base);
-
- return job;
-}
-
void
-em_format_html_job_queue (EMFormatHTML *emfh,
- struct _EMFormatHTMLJob *job)
-{
- g_mutex_lock (emfh->priv->lock);
- g_queue_push_tail (&emfh->priv->pending_jobs, job);
- g_mutex_unlock (emfh->priv->lock);
-}
-
-/* ********************************************************************** */
-
-static void
-emfh_getpuri (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable)
-{
- d(printf(" running getpuri task\n"));
- if (!g_cancellable_is_cancelled (cancellable))
- job->u.puri->func (
- EM_FORMAT (job->format), job->stream,
- job->u.puri, cancellable);
-}
-
-static void
-emfh_configure_stream_for_proxy (CamelHttpStream *stream,
- const gchar *uri)
-{
- EProxy *proxy;
- SoupURI *proxy_uri;
- gchar *basic;
- gchar *basic64;
- const gchar *user = "";
- const gchar *password = "";
-
- proxy = em_utils_get_proxy ();
-
- if (!e_proxy_require_proxy_for_uri (proxy, uri))
- return;
-
- proxy_uri = e_proxy_peek_uri_for (proxy, uri);
-
- if (proxy_uri == NULL)
- return;
-
- if (proxy_uri->user != NULL)
- user = proxy_uri->user;
-
- if (proxy_uri->password != NULL)
- password = proxy_uri->password;
-
- if (*user == '\0' && *password == '\0')
- return;
-
- basic = g_strdup_printf ("%s:%s", user, password);
- basic64 = g_base64_encode ((guchar *) basic, strlen (basic));
- camel_http_stream_set_proxy_authpass (stream, basic64);
- g_free (basic64);
- g_free (basic);
-}
-
-static void
-emfh_gethttp (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable)
-{
- CamelStream *cistream = NULL, *costream = NULL, *instream = NULL;
- CamelURL *url;
- CamelHttpStream *tmp_stream;
- gssize n, total = 0, pc_complete = 0, nread = 0;
- gchar buffer[1500];
- const gchar *length;
-
- if (g_cancellable_is_cancelled (cancellable)
- || (url = camel_url_new (job->u.uri, NULL)) == NULL)
- goto badurl;
-
- d(printf(" running load uri task: %s\n", job->u.uri));
-
- if (emfh_http_cache)
- instream = cistream = camel_data_cache_get (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
-
- if (instream == NULL) {
- EMailImageLoadingPolicy policy;
-
- policy = em_format_html_get_image_loading_policy (job->format);
-
- if (!(job->format->priv->load_images_now
- || policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS
- || (policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES
- && em_utils_in_addressbook ((CamelInternetAddress *) camel_mime_message_get_from (job->format->parent.message), FALSE)))) {
- /* TODO: Ideally we would put the http requests into
- * another queue and only send them out if the user
- * selects 'load images', when they do. The problem
- * is how to maintain this state with multiple
- * renderings, and how to adjust the thread
- * dispatch/setup routine to handle it */
- camel_url_free (url);
- goto done;
- }
-
- instream = camel_http_stream_new (CAMEL_HTTP_METHOD_GET, ((EMFormat *) job->format)->session, url);
- camel_http_stream_set_user_agent((CamelHttpStream *) instream, "CamelHttpStream/1.0 Evolution/" VERSION);
- emfh_configure_stream_for_proxy ((CamelHttpStream *) instream, job->u.uri);
-
- camel_operation_push_message (
- cancellable, _("Retrieving '%s'"), job->u.uri);
- tmp_stream = (CamelHttpStream *) instream;
- if (camel_stream_read (CAMEL_STREAM (instream), NULL, 0, cancellable, NULL) == 0) {
- CamelContentType *content_type;
-
- content_type = camel_http_stream_get_content_type (tmp_stream);
- length = camel_header_raw_find(&tmp_stream->headers, "Content-Length", NULL);
- d(printf(" Content-Length: %s\n", length));
- if (length != NULL)
- total = atoi (length);
- camel_content_type_unref (content_type);
- }
- } else
- camel_operation_push_message (
- cancellable, _("Retrieving '%s'"), job->u.uri);
-
- camel_url_free (url);
-
- if (instream == NULL)
- goto done;
-
- if (emfh_http_cache != NULL && cistream == NULL)
- costream = camel_data_cache_add (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
-
- do {
- if (g_cancellable_is_cancelled (cancellable)) {
- n = -1;
- break;
- }
- /* FIXME: progress reporting in percentage, can we get the length always? do we care? */
- n = camel_stream_read (instream, buffer, sizeof (buffer), cancellable, NULL);
- if (n > 0) {
- nread += n;
- /* If we didn't get a valid Content-Length header, do not try to calculate percentage */
- if (total != 0) {
- pc_complete = ((nread * 100) / total);
- camel_operation_progress (cancellable, pc_complete);
- }
- d(printf(" read %d bytes\n", n));
- if (costream && camel_stream_write (costream, buffer, n, cancellable, NULL) == -1) {
- n = -1;
- break;
- }
-
- camel_stream_write (job->stream, buffer, n, cancellable, NULL);
- }
- } while (n > 0);
-
- /* indicates success */
- if (n == 0)
- camel_stream_close (job->stream, cancellable, NULL);
-
- if (costream) {
- /* do not store broken files in a cache */
- if (n != 0)
- camel_data_cache_remove (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
- g_object_unref (costream);
- }
-
- g_object_unref (instream);
-done:
- camel_operation_pop_message (cancellable);
-badurl:
- g_free (job->u.uri);
-}
-
-/* ********************************************************************** */
-
-static void
-efh_url_requested (GtkHTML *html,
- const gchar *url,
- GtkHTMLStream *handle,
- EMFormatHTML *efh)
-{
- EMFormatPURI *puri;
- struct _EMFormatHTMLJob *job = NULL;
-
- d(printf("url requested, html = %p, url '%s'\n", html, url));
-
- puri = em_format_find_visible_puri ((EMFormat *) efh, url);
- if (puri) {
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
- CamelContentType *ct = dw ? dw->mime_type : NULL;
-
- /* GtkHTML only handles text and images.
- * application/octet-stream parts are the only ones
- * which are snooped for other content. So only try
- * to pass these to it - any other types are badly
- * formed or intentionally malicious emails. They
- * will still show as attachments anyway */
-
- if (ct && (camel_content_type_is(ct, "text", "*")
- || camel_content_type_is(ct, "image", "*")
- || camel_content_type_is(ct, "application", "octet-stream"))) {
- puri->use_count++;
-
- d(printf(" adding puri job\n"));
- job = em_format_html_job_new (efh, emfh_getpuri, puri);
- } else {
- d(printf(" part is unknown type '%s', not using\n", ct?camel_content_type_format(ct):"<unset>"));
- gtk_html_stream_close (handle, GTK_HTML_STREAM_ERROR);
- }
- } else if (g_ascii_strncasecmp(url, "http:", 5) == 0 || g_ascii_strncasecmp(url, "https:", 6) == 0) {
- d(printf(" adding job, get %s\n", url));
- job = em_format_html_job_new (efh, emfh_gethttp, g_strdup (url));
- } else if (g_str_has_prefix (url, "file://")) {
- gchar *data = NULL;
- gsize length = 0;
- gboolean status;
- gchar *path;
-
- path = g_filename_from_uri (url, NULL, NULL);
- g_return_if_fail (path != NULL);
-
- status = g_file_get_contents (path, &data, &length, NULL);
- if (status)
- gtk_html_stream_write (handle, data, length);
-
- gtk_html_stream_close (handle, status ? GTK_HTML_STREAM_OK : GTK_HTML_STREAM_ERROR);
- g_free (data);
- g_free (path);
- } else {
- d(printf("HTML Includes reference to unknown uri '%s'\n", url));
- gtk_html_stream_close (handle, GTK_HTML_STREAM_ERROR);
- }
-
- if (job) {
- job->stream = em_html_stream_new (html, handle);
- em_format_html_job_queue (efh, job);
- }
-
- g_signal_stop_emission_by_name (html, "url-requested");
-}
-
-static gboolean
-efh_object_requested (GtkHTML *html,
- GtkHTMLEmbedded *eb,
- EMFormatHTML *efh)
-{
- EMFormatHTMLPObject *pobject;
- gint res = FALSE;
-
- if (eb->classid == NULL)
- return FALSE;
-
- pobject = em_format_html_find_pobject (efh, eb->classid);
- if (pobject) {
- /* This stops recursion of the part */
- g_queue_remove (&efh->pending_object_list, pobject);
- res = pobject->func (efh, eb, pobject);
- g_queue_push_head (&efh->pending_object_list, pobject);
- } else {
- d(printf("HTML Includes reference to unknown object '%s'\n", eb->classid));
- }
-
- return res;
-}
-
-/* ********************************************************************** */
-#include "em-format/em-inline-filter.h"
-
-/* FIXME: This is duplicated in em-format-html-display, should be exported or in security module */
-static const struct {
- const gchar *icon, *shortdesc;
-} smime_sign_table[5] = {
- { "stock_signature-bad", N_("Unsigned") },
- { "stock_signature-ok", N_("Valid signature") },
- { "stock_signature-bad", N_("Invalid signature") },
- { "stock_signature", N_("Valid signature, but cannot verify sender") },
- { "stock_signature-bad", N_("Signature exists, but need public key") },
-};
-
-static const struct {
- const gchar *icon, *shortdesc;
-} smime_encrypt_table[4] = {
- { "stock_lock-broken", N_("Unencrypted") },
- { "stock_lock", N_("Encrypted, weak"),},
- { "stock_lock-ok", N_("Encrypted") },
- { "stock_lock-ok", N_("Encrypted, strong") },
-};
-
-static const gchar *smime_sign_colour[4] = {
- "", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\""
-};
-
-/* TODO: this could probably be virtual on em-format-html
- * then we only need one version of each type handler */
-static void
-efh_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- CamelCipherValidity *valid,
- GCancellable *cancellable)
-{
- EMFormatClass *format_class;
-
- format_class = EM_FORMAT_CLASS (parent_class);
- g_return_if_fail (format_class->format_secure != NULL);
- format_class->format_secure (emf, stream, part, valid, cancellable);
-
- /* To explain, if the validity is the same, then we are the
- * base validity and now have a combined sign/encrypt validity
- * we can display. Primarily a new verification context is
- * created when we have an embeded message. */
- if (emf->valid == valid
- && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
- || valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
- gchar *classid, *iconpath;
- const gchar *icon;
- CamelMimePart *iconpart;
- GString *buffer;
-
- buffer = g_string_sized_new (1024);
-
- g_string_append_printf (
- buffer,
- "<table border=0 width=\"100%%\" "
- "cellpadding=3 cellspacing=0%s><tr>",
- smime_sign_colour[valid->sign.status]);
-
- classid = g_strdup_printf (
- "smime:///em-format-html/%s/icon/signed",
- emf->part_id->str);
- g_string_append_printf (
- buffer,
- "<td valign=\"top\"><img src=\"%s\"></td>"
- "<td valign=\"top\" width=\"100%%\">", classid);
-
- if (valid->sign.status != 0)
- icon = smime_sign_table[valid->sign.status].icon;
- else
- icon = smime_encrypt_table[valid->encrypt.status].icon;
- iconpath = e_icon_factory_get_icon_filename (icon, GTK_ICON_SIZE_DIALOG);
- iconpart = em_format_html_file_part((EMFormatHTML *)emf, "image/png", iconpath, cancellable);
- if (iconpart) {
- (void) em_format_add_puri (emf, sizeof (EMFormatPURI), classid, iconpart, efh_write_image);
- g_object_unref (iconpart);
- }
- g_free (iconpath);
- g_free (classid);
-
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
- g_string_append (
- buffer, _(smime_sign_table[valid->sign.status].shortdesc));
-
- em_format_html_format_cert_infos (
- &valid->sign.signers, buffer);
- }
-
- if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
- g_string_append (buffer, "<br>");
-
- g_string_append (
- buffer, _(smime_encrypt_table[valid->encrypt.status].shortdesc));
- }
-
- g_string_append (buffer, "</td></tr></table>");
-
- camel_stream_write (
- stream, buffer->str,
- buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
- }
-}
-
-static void
-efh_text_plain (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- CamelStream *filtered_stream;
- CamelMimeFilter *html_filter;
- CamelMultipart *mp;
- CamelDataWrapper *dw;
- CamelContentType *type;
- const gchar *format;
- guint32 flags;
- guint32 rgb;
- gint i, count, len;
- struct _EMFormatHTMLCache *efhc;
-
- flags = efh->text_html_flags;
-
- dw = camel_medium_get_content ((CamelMedium *) part);
- if (!dw)
- return;
-
- /* 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;
-
- /* 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 ... */
-
- efhc = g_hash_table_lookup (
- efh->priv->text_inline_parts,
- emf->part_id->str);
-
- if (efhc == NULL || (mp = efhc->textmp) == NULL) {
- EMInlineFilter *inline_filter;
- CamelStream *null;
- CamelContentType *ct;
- gboolean charset_added = FALSE;
-
- /* if we had to snoop the part type to get here, then
- * use that as the base type, yuck */
- if (emf->snoop_mime_type == NULL
- || (ct = camel_content_type_decode (emf->snoop_mime_type)) == NULL) {
- ct = dw->mime_type;
- camel_content_type_ref (ct);
- }
-
- if (dw->mime_type && ct != dw->mime_type && camel_content_type_param (dw->mime_type, "charset")) {
- camel_content_type_set_param (ct, "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), ct);
- 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 (efhc == NULL)
- efhc = efh_insert_cache (efh, emf->part_id->str);
- efhc->textmp = mp;
-
- if (charset_added) {
- camel_content_type_set_param (ct, "charset", NULL);
- }
-
- g_object_unref (inline_filter);
- camel_content_type_unref (ct);
- }
-
- 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);
-
- /* We handle our made-up multipart here, so we don't recursively call ourselves */
-
- len = emf->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", "*") && (is_fallback || !camel_content_type_is (type, "text", "calendar"))) {
- gchar *content;
-
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; padding: 10px; "
- "color: #%06x;\">\n<tt>\n" EFH_MESSAGE_START,
- 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);
- g_free (content);
-
- em_format_format_text (
- emf, filtered_stream,
- (CamelDataWrapper *) newpart,
- cancellable);
- camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL);
- camel_stream_write_string (stream, "</tt>\n", cancellable, NULL);
- camel_stream_write_string (stream, "</div>\n", cancellable, NULL);
- } else {
- g_string_append_printf (emf->part_id, ".inline.%d", i);
- em_format_part (
- emf, stream, newpart, cancellable);
- g_string_truncate (emf->part_id, len);
- }
- }
-
- g_object_unref (filtered_stream);
-}
-
-static void
-efh_text_enriched (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- CamelStream *filtered_stream;
- CamelMimeFilter *enriched;
- guint32 flags = 0;
- gchar *content;
-
- if (!strcmp(info->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);
- }
-
- 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);
-
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; padding: 10px; "
- "color: #%06x;\">\n" EFH_MESSAGE_START,
- 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);
- g_free (content);
-
- em_format_format_text (
- emf, (CamelStream *) filtered_stream,
- (CamelDataWrapper *) part, cancellable);
-
- g_object_unref (filtered_stream);
- camel_stream_write_string (stream, "</div>", cancellable, NULL);
-}
-
-static void
-efh_write_text_html (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
+em_format_html_format_cert_infos (GQueue *cert_infos,
+ GString *output_buffer)
{
-#if d(!)0
- CamelStream *out;
- gint fd;
- CamelDataWrapper *dw;
-
- fd = dup (STDOUT_FILENO);
- out = camel_stream_fs_new_with_fd (fd);
- printf("writing text content to frame '%s'\n", puri->cid);
- dw = camel_medium_get_content (puri->part);
- if (dw)
- camel_data_wrapper_write_to_stream (dw, out);
- g_object_unref (out);
-#endif
- em_format_format_text (
- emf, stream, (CamelDataWrapper *) puri->part, cancellable);
-}
+ GQueue valid = G_QUEUE_INIT;
+ GList *head, *link;
-static void
-efh_text_html (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- const gchar *location;
- gchar *cid = NULL;
- gchar *content;
+ g_return_if_fail (cert_infos != NULL);
+ g_return_if_fail (output_buffer != NULL);
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; color: #%06x;\">\n"
- "<!-- text/html -->\n" EFH_MESSAGE_START,
- 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);
- g_free (content);
+ head = g_queue_peek_head_link (cert_infos);
- /* TODO: perhaps we don't need to calculate this anymore now base is handled better */
- /* calculate our own location string so add_puri doesn't do it
- * for us. our iframes are special cases, we need to use the
- * proper base url to access them, but other children parts
- * shouldn't blindly inherit the container's location. */
- location = camel_mime_part_get_content_location (part);
- if (location == NULL) {
- if (emf->base)
- cid = camel_url_to_string (emf->base, 0);
- else
- cid = g_strdup (emf->part_id->str);
- } else {
- if (strchr (location, ':') == NULL && emf->base != NULL) {
- CamelURL *uri;
+ /* 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;
- uri = camel_url_new_with_base (emf->base, location);
- cid = camel_url_to_string (uri, 0);
- camel_url_free (uri);
- } else {
- cid = g_strdup (location);
+ if ((cinfo->name != NULL && *cinfo->name != '\0') ||
+ (cinfo->email != NULL && *cinfo->email != '\0')) {
+ g_queue_push_tail (&valid, cinfo);
}
}
- em_format_add_puri (
- emf, sizeof (EMFormatPURI), cid,
- part, efh_write_text_html);
- d(printf("adding iframe, location %s\n", cid));
- content = g_strdup_printf (
- "<iframe src=\"%s\" frameborder=0 scrolling=no>"
- "could not get %s</iframe>\n</div>\n", cid, cid);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
- g_free (cid);
-}
-
-/* This is a lot of code for something useless ... */
-static void
-efh_message_external (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelContentType *type;
- const gchar *access_type;
- gchar *url = NULL, *desc = NULL;
- gchar *content;
-
- if (!part) {
- camel_stream_write_string (
- stream, _("Unknown external-body part."),
- cancellable, NULL);
- return;
- }
-
- /* needs to be cleaner */
- type = camel_mime_part_get_content_type (part);
- access_type = camel_content_type_param (type, "access-type");
- if (!access_type) {
- camel_stream_write_string (
- stream, _("Malformed external-body part."),
- cancellable, NULL);
- return;
- }
-
- 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) {
- /* FIXME: use camel_isspace */
- 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_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-
- g_free (url);
- g_free (desc);
-
- return;
-
-fail:
- content = g_strdup_printf (
- _("Pointer to unknown external data (\"%s\" type)"),
- access_type);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-}
-
-static void
-efh_message_deliverystatus (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- CamelStream *filtered_stream;
- CamelMimeFilter *html_filter;
- guint32 rgb = 0x737373;
- gchar *content;
-
- /* Yuck, this is copied from efh_text_plain */
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; padding: 10px; "
- "color: #%06x;\">\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);
- g_free (content);
-
- filtered_stream = camel_stream_filter_new (stream);
- html_filter = camel_mime_filter_tohtml_new (efh->text_html_flags, rgb);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream), html_filter);
- g_object_unref (html_filter);
-
- camel_stream_write_string (stream, "<tt>\n" EFH_MESSAGE_START, cancellable, NULL);
- em_format_format_text (
- emf, filtered_stream,
- (CamelDataWrapper *) part, cancellable);
- camel_stream_flush (filtered_stream, cancellable, NULL);
- camel_stream_write_string (stream, "</tt>\n", cancellable, NULL);
-
- camel_stream_write_string (stream, "</div>", cancellable, NULL);
-}
-
-static void
-emfh_write_related (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- em_format_format_content (emf, stream, puri->part, cancellable);
-
- camel_stream_close (stream, cancellable, NULL);
-}
-
-static void
-emfh_multipart_related_check (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable)
-{
- EMFormat *format;
- GList *link;
- gchar *oldpartid;
-
- if (g_cancellable_is_cancelled (cancellable))
+ if (g_queue_is_empty (&valid))
return;
- format = EM_FORMAT (job->format);
+ g_string_append (output_buffer, " (");
- d(printf(" running multipart/related check task\n"));
- oldpartid = g_strdup (format->part_id->str);
+ while (!g_queue_is_empty (&valid)) {
+ CamelCipherCertInfo *cinfo;
- link = g_queue_peek_head_link (job->puri_level->data);
+ cinfo = g_queue_pop_head (&valid);
- if (!link) {
- g_string_printf (format->part_id, "%s", oldpartid);
- g_free (oldpartid);
- return;
- }
+ if (cinfo->name != NULL && *cinfo->name != '\0') {
+ g_string_append (output_buffer, cinfo->name);
- while (link != NULL) {
- EMFormatPURI *puri = link->data;
-
- if (puri->use_count == 0) {
- d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count));
- if (puri->func == emfh_write_related) {
- g_string_printf (format->part_id, "%s", puri->part_id);
- /* FIXME Not passing a GCancellable here. */
- em_format_part (
- format, CAMEL_STREAM (job->stream),
- puri->part, NULL);
+ 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 it was probably added by a previous format this loop */
- }
-
- link = g_list_next (link);
- }
-
- g_string_printf (format->part_id, "%s", oldpartid);
- g_free (oldpartid);
-}
-
-/* RFC 2387 */
-static void
-efh_multipart_related (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMultipart *mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
- CamelMimePart *body_part, *display_part = NULL;
- CamelContentType *content_type;
- const gchar *start;
- gint i, nparts, partidlen, displayid = 0;
- struct _EMFormatHTMLJob *job;
-
- if (!CAMEL_IS_MULTIPART (mp)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- nparts = camel_multipart_get_number (mp);
- content_type = camel_mime_part_get_content_type (part);
- start = camel_content_type_param (content_type, "start");
- if (start && strlen (start) > 2) {
- gint len;
- const gchar *cid;
- /* strip <>'s */
- len = strlen (start) - 2;
- start++;
-
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- cid = camel_mime_part_get_content_id (body_part);
-
- if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
- display_part = body_part;
- displayid = i;
- break;
- }
+ } else if (cinfo->email != NULL && *cinfo->email != '\0') {
+ g_string_append (output_buffer, cinfo->email);
}
- } else {
- display_part = camel_multipart_get_part (mp, 0);
- }
- if (display_part == NULL) {
- em_format_part_as (
- emf, stream, part,
- "multipart/mixed", cancellable);
- return;
- }
-
- em_format_push_level (emf);
-
- partidlen = emf->part_id->len;
-
- /* queue up the parts for possible inclusion */
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- if (body_part != display_part) {
- g_string_append_printf(emf->part_id, "related.%d", i);
- em_format_add_puri (emf, sizeof (EMFormatPURI), NULL, body_part, emfh_write_related);
- g_string_truncate (emf->part_id, partidlen);
- d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid));
- }
+ if (!g_queue_is_empty (&valid))
+ g_string_append (output_buffer, ", ");
}
- g_string_append_printf(emf->part_id, "related.%d", displayid);
- em_format_part (emf, stream, display_part, cancellable);
- g_string_truncate (emf->part_id, partidlen);
- camel_stream_flush (stream, cancellable, NULL);
-
- /* queue a job to check for un-referenced parts to add as attachments */
- job = em_format_html_job_new (
- EM_FORMAT_HTML (emf), emfh_multipart_related_check, NULL);
- job->stream = stream;
- g_object_ref (stream);
- em_format_html_job_queue ((EMFormatHTML *) emf, job);
-
- em_format_pull_level (emf);
-}
-
-static void
-efh_write_image (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
-
- d(printf("writing image '%s'\n", puri->cid));
- camel_data_wrapper_decode_to_stream_sync (
- dw, stream, cancellable, NULL);
- camel_stream_close (stream, cancellable, NULL);
-}
-
-static void
-efh_image (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatPURI *puri;
- gchar *content;
-
- puri = em_format_add_puri (
- emf, sizeof (EMFormatPURI), NULL, part, efh_write_image);
-
- content = g_strdup_printf (
- "<img hspace=10 vspace=10 src=\"%s\">", puri->cid);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-}
-
-/* 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_image },
- { (gchar *) "image/jpeg", efh_image },
- { (gchar *) "image/png", efh_image },
- { (gchar *) "image/x-png", efh_image },
- { (gchar *) "image/x-bmp", efh_image },
- { (gchar *) "image/bmp", efh_image },
- { (gchar *) "image/svg", efh_image },
- { (gchar *) "image/x-cmu-raster", efh_image },
- { (gchar *) "image/x-ico", efh_image },
- { (gchar *) "image/x-portable-anymap", efh_image },
- { (gchar *) "image/x-portable-bitmap", efh_image },
- { (gchar *) "image/x-portable-graymap", efh_image },
- { (gchar *) "image/x-portable-pixmap", efh_image },
- { (gchar *) "image/x-xpixmap", efh_image },
- { (gchar *) "text/enriched", efh_text_enriched },
- { (gchar *) "text/plain", efh_text_plain },
- { (gchar *) "text/html", efh_text_html },
- { (gchar *) "text/richtext", efh_text_enriched },
- { (gchar *) "text/*", efh_text_plain },
- { (gchar *) "message/external-body", efh_message_external },
- { (gchar *) "message/delivery-status", efh_message_deliverystatus },
- { (gchar *) "multipart/related", efh_multipart_related },
-
- /* 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_image },
- { (gchar *) "image/pjpeg", efh_image },
-
- /* special internal types */
-
- { (gchar *) "x-evolution/message/rfc822", efh_format_message }
-};
-
-static void
-efh_builtin_init (EMFormatHTMLClass *efhc)
-{
- EMFormatClass *efc;
- gint ii;
-
- efc = (EMFormatClass *) efhc;
-
- for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
- em_format_class_add_handler (
- efc, &type_builtin_table[ii]);
+ g_string_append_c (output_buffer, ')');
}
-/* ********************************************************************** */
-
static void
efh_format_text_header (EMFormatHTML *emfh,
GString *buffer,
@@ -2608,37 +2188,34 @@ efh_format_text_header (EMFormatHTML *emfh,
html = value;
is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
- if (emfh->simple_headers) {
- fmt = "<b>%s</b>: %s<br>";
+
+ 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>&nbsp;</b></th></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
} else {
- if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) {
- if (flags & EM_FORMAT_HEADER_BOLD) {
- fmt = "<tr><td><b>%s:</b> %s</td></tr>";
- } else {
- fmt = "<tr><td>%s: %s</td></tr>";
- }
- } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) {
+ if (flags & EM_FORMAT_HEADER_BOLD) {
if (is_rtl)
- fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+ 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>&nbsp;</b></th></tr>";
else
- fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
} else {
-
- if (flags & EM_FORMAT_HEADER_BOLD) {
- if (is_rtl)
- fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
- else
- fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
- } else {
- if (is_rtl)
- fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
- else
- fmt = "<tr><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
- }
+ 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>&nbsp;</b></td></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
}
}
- g_string_append_printf (buffer, fmt, label, html);
+ g_string_append_printf (buffer, fmt,
+ (flags & EM_FORMAT_HTML_HEADER_HIDDEN ? "none" : "table-row"), label, html);
g_free (mhtml);
}
@@ -2653,22 +2230,15 @@ static gchar *
efh_format_address (EMFormatHTML *efh,
GString *out,
struct _camel_header_address *a,
- gchar *field)
+ gchar *field,
+ gboolean no_links)
{
guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
gchar *name, *mailto, *addr;
gint i = 0;
- gboolean wrap = FALSE;
gchar *str = NULL;
gint limit = mail_config_get_address_count ();
- if (field ) {
- if ((!strcmp (field, _("To")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_TO))
- || (!strcmp (field, _("Cc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_CC))
- || (!strcmp (field, _("Bcc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_BCC)))
- wrap = TRUE;
- }
-
while (a) {
if (a->name)
name = camel_text_to_html (a->name, flags, 0);
@@ -2700,7 +2270,10 @@ efh_format_address (EMFormatHTML *efh,
mailto = camel_url_encode (a->v.addr, "?=&()");
}
addr = camel_text_to_html (a->v.addr, flags, 0);
- g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr);
+ 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);
@@ -2709,7 +2282,7 @@ efh_format_address (EMFormatHTML *efh,
break;
case CAMEL_HEADER_ADDRESS_GROUP:
g_string_append_printf (out, "%s: ", name);
- efh_format_address (efh, out, a->v.members, field);
+ efh_format_address (efh, out, a->v.members, field, no_links);
g_string_append_printf (out, ";");
break;
default:
@@ -2725,48 +2298,51 @@ efh_format_address (EMFormatHTML *efh,
g_string_append (out, ", ");
/* Let us add a '...' if we have more addresses */
- if (limit > 0 && wrap && a && (i > (limit - 1))) {
- gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
-
- if (!strcmp (field, _("To"))) {
- g_string_append (out, "<a href=\"##TO##\">...</a>");
- str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/plus.png\"></a> ", evolution_imagesdir);
- }
- else if (!strcmp (field, _("Cc"))) {
- g_string_append (out, "<a href=\"##CC##\">...</a>");
- str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/plus.png\"></a> ", evolution_imagesdir);
+ 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";
}
- else if (!strcmp (field, _("Bcc"))) {
- g_string_append (out, "<a href=\"##BCC##\">...</a>");
- str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/plus.png\"></a> ", evolution_imagesdir);
- }
-
- g_free (evolution_imagesdir);
- if (str)
- return str;
+ 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 (limit > 0 && i > (limit)) {
- gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+ if (str) {
+ const gchar *id = NULL;
- if (!strcmp (field, _("To"))) {
- str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/minus.png\"></a> ", evolution_imagesdir);
- }
- else if (!strcmp (field, _("Cc"))) {
- str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/minus.png\"></a> ", evolution_imagesdir);
- }
- else if (!strcmp (field, _("Bcc"))) {
- str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/minus.png\"></a> ", evolution_imagesdir);
+ if (strcmp (field, _("To")) == 0) {
+ id = "to";
+ } else if (strcmp (field, _("Cc")) == 0) {
+ id = "cc";
+ } else if (strcmp (field, _("Bcc")) == 0) {
+ id = "bcc";
}
- g_free (evolution_imagesdir);
+ 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
@@ -2793,15 +2369,15 @@ canon_header_name (gchar *name)
}
}
-static void
-efh_format_header (EMFormat *emf,
- GString *buffer,
- CamelMedium *part,
- struct _camel_header_raw *header,
- guint32 flags,
- const gchar *charset)
+void
+em_format_html_format_header (EMFormat *emf,
+ GString *buffer,
+ CamelMedium *part,
+ struct _camel_header_raw *header,
+ guint32 flags,
+ const gchar *charset)
{
- EMFormatHTML *efh = (EMFormatHTML *) emf;
+ EMFormatHTML *efh = EM_FORMAT_HTML (emf);
gchar *name, *buf, *value = NULL;
const gchar *label, *txt;
gboolean addrspec = FALSE;
@@ -2825,9 +2401,11 @@ efh_format_header (EMFormat *emf,
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, emf->charset ? emf->charset : emf->default_charset))) {
+ if (!(addrs = camel_header_address_decode (buf, charset))) {
g_free (buf);
return;
}
@@ -2835,7 +2413,8 @@ efh_format_header (EMFormat *emf,
g_free (buf);
html = g_string_new("");
- img = efh_format_address (efh, html, addrs, (gchar *) label);
+ 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);
@@ -2921,7 +2500,11 @@ efh_format_header (EMFormat *emf,
html = g_string_new("");
scan = ng;
while (scan) {
- g_string_append_printf(html, "<a href=\"news:%s\">%s</a>", scan->newsgroup, scan->newsgroup);
+ 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, ", ");
@@ -2949,100 +2532,129 @@ efh_format_header (EMFormat *emf,
}
static void
-efh_format_headers (EMFormatHTML *efh,
- GString *buffer,
- CamelMedium *part,
- GCancellable *cancellable)
+efh_format_short_headers (EMFormatHTML *efh,
+ GString *buffer,
+ CamelMedium *part,
+ gboolean visible,
+ GCancellable *cancellable)
{
- EMFormat *emf = (EMFormat *) efh;
+ 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;
- 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;
+ gchar *subject = NULL;
+ struct _camel_header_address *addrs = NULL;
+ struct _camel_header_raw *header;
+ GString *from;
+ gboolean is_rtl;
- if (!part)
+ 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);
- if (!efh->simple_headers)
- g_string_append_printf (
- buffer, "<font color=\"#%06x\">\n"
- "<table cellpadding=\"0\" width=\"100%%\">",
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_HEADER]));
-
- hdr_charset = emf->charset ? emf->charset : emf->default_charset;
evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+ from = g_string_new ("");
- /* If the header is collapsed, display just subject and sender in one row and leave */
- if (efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED && efh->priv->headers_collapsable) {
- gchar *subject = NULL;
- struct _camel_header_address *addrs = NULL;
- GString *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);
-
- 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;
- buf = camel_header_unfold (header->value);
- g_free (subject);
- subject = camel_header_decode_string (buf, hdr_charset);
- g_free (buf);
+ 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;
}
- header = header->next;
+ 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=\"20\" valign=\"top\">"
- "<a href=\"##HEADERS##\">"
- "<img src=\"%s/plus.png\">"
- "</a></td>"
- "<td><strong>%s</strong> %s%s%s</td>"
- "</tr>",
- evolution_imagesdir,
- subject ? subject : _("(no subject)"),
- from->len ? "(" : "",
- from->str,
- from->len ? ")" : "");
-
- g_free (subject);
- if (addrs)
- camel_header_address_list_clear (&addrs);
- g_string_free (from, TRUE);
+ "<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_append (buffer, "</table>");
+ g_string_free (from, TRUE);
+ g_free (evolution_imagesdir);
+}
- g_free (evolution_imagesdir);
+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;
+ 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;
- }
+
+ 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) {
@@ -3054,7 +2666,7 @@ efh_format_headers (EMFormatHTML *efh,
break;
html = g_string_new("");
- name = efh_format_address (efh, html, addrs, header->name);
+ name = efh_format_address (efh, html, addrs, header->name, FALSE);
header_sender = html->str;
camel_header_address_list_clear (&addrs);
@@ -3069,7 +2681,7 @@ efh_format_headers (EMFormatHTML *efh,
break;
html = g_string_new("");
- name = efh_format_address (efh, html, addrs, header->name);
+ name = efh_format_address (efh, html, addrs, header->name, FALSE);
header_from = html->str;
camel_header_address_list_clear (&addrs);
@@ -3113,50 +2725,15 @@ efh_format_headers (EMFormatHTML *efh,
g_free (header_sender);
g_free (header_from);
- if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) {
- if (efh->priv->headers_collapsable)
- g_string_append_printf (
- buffer,
- "<tr>"
- "<td valign=\"top\" width=\"20\">"
- "<a href=\"##HEADERS##\">"
- "<img src=\"%s/minus.png\">"
- "</a></td>"
- "<td><table width=\"100%%\" border=0 "
- "cellpadding=\"0\">\n",
- evolution_imagesdir);
- else
- g_string_append (
- buffer,
- "<tr><td>"
- "<table width=\"100%%\" border=0 "
- "cellpadding=\"0\">\n");
-
- } else {
- if (efh->priv->headers_collapsable)
- g_string_append_printf (
- buffer,
- "<tr>"
- "<td valign=\"top\" width=\"20\">"
- "<a href=\"##HEADERS##\">"
- "<img src=\"%s/minus.png\">"
- "</a></td>"
- "<td><table border=0 cellpadding=\"0\">\n",
- evolution_imagesdir);
- else
- g_string_append (
- buffer,
- "<tr><td>"
- "<table border=0 cellpadding=\"0\">\n");
- }
+ g_string_append (buffer, "<tr><td><table border=0 cellpadding=\"0\">\n");
g_free (evolution_imagesdir);
/* dump selected headers */
- if (emf->mode == EM_FORMAT_MODE_ALLHEADERS) {
+ if (all_headers) {
header = ((CamelMimePart *) part)->headers;
while (header) {
- efh_format_header (
+ em_format_html_format_header (
emf, buffer, part, header,
EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset);
header = header->next;
@@ -3205,7 +2782,7 @@ efh_format_headers (EMFormatHTML *efh,
xmailer.value = use_header->value;
mailer_shown = TRUE;
- efh_format_header (
+ em_format_html_format_header (
emf, buffer, part,
&xmailer, h->flags, charset);
if (strstr(use_header->value, "Evolution"))
@@ -3226,7 +2803,7 @@ efh_format_headers (EMFormatHTML *efh,
face_decoded = TRUE;
/* Showing an encoded "Face" header makes little sense */
} else if (!g_ascii_strcasecmp (header->name, h->name) && !face) {
- efh_format_header (
+ em_format_html_format_header (
emf, buffer, part,
header, h->flags, charset);
}
@@ -3238,214 +2815,162 @@ efh_format_headers (EMFormatHTML *efh,
}
}
- if (!efh->simple_headers) {
- g_string_append (buffer, "</table></td>");
-
- if (photo_name) {
- gchar *classid;
- 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 (cia, only_local_photo);
-
- if (photopart) {
- contact_has_photo = TRUE;
- classid = g_strdup_printf (
- "icon:///em-format-html/%s/photo/header",
- emf->part_id->str);
- g_string_append_printf (
- buffer,
- "<td align=\"right\" valign=\"top\">"
- "<img width=64 src=\"%s\"></td>",
- classid);
- em_format_add_puri (emf, sizeof (EMFormatPURI), classid,
- photopart, efh_write_image);
- g_object_unref (photopart);
-
- g_free (classid);
- }
- g_object_unref (cia);
- }
+ g_string_append (buffer, "</table></td>");
- if (!contact_has_photo && face_decoded) {
- gchar *classid;
- CamelMimePart *part;
-
- part = camel_mime_part_new ();
- camel_mime_part_set_content (
- (CamelMimePart *) part,
- (const gchar *) face_header_value,
- face_header_len, "image/png");
- classid = g_strdup_printf (
- "icon:///em-format-html/face/photo/header");
- g_string_append_printf (
- buffer,
- "<td align=\"right\" valign=\"top\">"
- "<img width=48 src=\"%s\"></td>",
- classid);
- em_format_add_puri (
- emf, sizeof (EMFormatPURI),
- classid, part, efh_write_image);
- g_object_unref (part);
- }
+ if (photo_name) {
+ const gchar *classid;
+ CamelMimePart *photopart;
+ gboolean only_local_photo;
- if (have_icon && efh->show_icon) {
- GtkIconInfo *icon_info;
- gchar *classid;
- CamelMimePart *iconpart = NULL;
+ 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 (cia, only_local_photo);
- classid = g_strdup_printf (
- "icon:///em-format-html/%s/icon/header",
- emf->part_id->str);
+ if (photopart) {
+ EMFormatPURI *puri;
+ contact_has_photo = TRUE;
+ classid = "icon:///em-format-html/headers/photo";
g_string_append_printf (
buffer,
"<td align=\"right\" valign=\"top\">"
- "<img width=16 height=16 src=\"%s\"></td>",
+ "<img width=64 src=\"%s\"></td>",
classid);
-
- 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) {
- em_format_add_puri (
- emf, sizeof (EMFormatPURI),
- classid, iconpart, efh_write_image);
- g_object_unref (iconpart);
- }
- g_free (classid);
+ puri = em_format_puri_new (
+ emf, sizeof (EMFormatPURI), photopart, classid);
+ puri->write_func = efh_write_image;
+ em_format_add_puri (emf, puri);
+ g_object_unref (photopart);
}
-
- g_string_append (buffer, "</tr></table>\n</font>\n");
+ g_object_unref (cia);
}
-}
-
-static void
-efh_format_message (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- const EMFormatHandler *handle;
- GString *buffer;
-
- /* TODO: make this validity stuff a method */
- EMFormatHTML *efh = (EMFormatHTML *) emf;
- CamelCipherValidity *save = emf->valid, *save_parent = emf->valid_parent;
-
- emf->valid = NULL;
- emf->valid_parent = NULL;
-
- buffer = g_string_sized_new (1024);
- if (emf->message != (CamelMimeMessage *) part)
- g_string_append (buffer, "<blockquote>\n");
-
- if (!efh->hide_headers)
- efh_format_headers (
- efh, buffer, CAMEL_MEDIUM (part), cancellable);
-
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
+ if (!contact_has_photo && face_decoded) {
+ const gchar *classid;
+ CamelMimePart *part;
+ EMFormatPURI *puri;
+
+ part = camel_mime_part_new ();
+ camel_mime_part_set_content (
+ (CamelMimePart *) part,
+ (const gchar *) face_header_value,
+ face_header_len, "image/png");
+ classid = "icon:///em-format-html/headers/face/photo";
+ g_string_append_printf (
+ buffer,
+ "<td align=\"right\" valign=\"top\">"
+ "<img width=48 src=\"%s\"></td>",
+ classid);
- handle = em_format_find_handler(emf, "x-evolution/message/post-header");
- if (handle)
- handle->handler (
- emf, stream, part, handle, cancellable, FALSE);
+ puri = em_format_puri_new (
+ emf, sizeof (EMFormatPURI), part, classid);
+ puri->write_func = efh_write_image;
+ em_format_add_puri (emf, puri);
- camel_stream_write_string (
- stream, EM_FORMAT_HTML_VPAD, cancellable, NULL);
- em_format_part (emf, stream, part, cancellable);
+ g_object_unref (part);
+ g_free (face_header_value);
+ }
- if (emf->message != (CamelMimeMessage *) part)
- camel_stream_write_string (
- stream, "</blockquote>\n", cancellable, NULL);
+ if (have_icon && efh->show_icon) {
+ GtkIconInfo *icon_info;
+ const gchar *classid;
+ CamelMimePart *iconpart = NULL;
+ EMFormatPURI *puri;
- camel_cipher_validity_free (emf->valid);
+ classid = "icon:///em-format-html/header/icon";
+ g_string_append_printf (
+ buffer,
+ "<td align=\"right\" valign=\"top\">"
+ "<img width=16 height=16 src=\"%s\"></td>",
+ classid);
+ 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) {
+ puri = em_format_puri_new (
+ emf, sizeof (EMFormatPURI), iconpart, classid);
+ puri->write_func = efh_write_image;
+ em_format_add_puri (emf, puri);
+ g_object_unref (iconpart);
+ }
+ }
- emf->valid = save;
- emf->valid_parent = save_parent;
+ g_string_append (buffer, "</tr></table>");
}
-void
-em_format_html_format_cert_infos (GQueue *cert_infos,
- GString *output_buffer)
+gboolean
+em_format_html_can_load_images (EMFormatHTML *efh)
{
- GQueue valid = G_QUEUE_INIT;
- GList *head, *link;
+ g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
- g_return_if_fail (cert_infos != NULL);
- g_return_if_fail (output_buffer != NULL);
+ 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));
+}
- head = g_queue_peek_head_link (cert_infos);
+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);
+
+ /* 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;
+ }
- /* 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;
+ 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) {
- if ((cinfo->name != NULL && *cinfo->name != '\0') ||
- (cinfo->email != NULL && *cinfo->email != '\0'))
- g_queue_push_tail (&valid, cinfo);
+ *frame = g_memdup (anim->data, anim->len);
+ *len = anim->len;
+ g_object_unref (loader);
+ return;
}
- if (g_queue_is_empty (&valid))
+ /* 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;
-
- 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, " &lt;");
- g_string_append (output_buffer, cinfo->email);
- g_string_append (output_buffer, "&gt;");
- }
-
- } 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, ')');
-}
+ /* 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);
-/* unref returned pointer with g_object_unref(), if not NULL */
-CamelStream *
-em_format_html_get_cached_image (EMFormatHTML *efh,
- const gchar *image_uri)
-{
- g_return_val_if_fail (efh != NULL, NULL);
- g_return_val_if_fail (image_uri != NULL, NULL);
-
- if (!emfh_http_cache)
- return NULL;
-
- return camel_data_cache_get (
- emfh_http_cache, EMFH_HTTP_CACHE_PATH, image_uri, NULL);
+ g_object_unref (loader);
}
-