[evolution] WebKit port - port formatter and mail module



commit 6d2c382788a4042d53f49a080acd11b499aa52f6
Author: Dan VrÃtil <dvratil redhat com>
Date:   Wed Mar 28 18:38:11 2012 +0200

    WebKit port - port formatter and mail module

 data/Makefile.am                                |    2 +
 data/webview-print.css                          |   62 +
 data/webview.css                                |  147 +
 em-format/Makefile.am                           |    6 +-
 em-format/em-format-quote.c                     |  792 ++--
 em-format/em-format-quote.h                     |    3 +
 em-format/em-format.c                           | 3928 +++++++++++----------
 em-format/em-format.h                           |  566 ++--
 mail/Makefile.am                                |   20 +-
 mail/e-mail-attachment-bar.c                    |   94 +-
 mail/e-mail-attachment-bar.h                    |    4 +-
 mail/e-mail-browser.c                           |  113 +-
 mail/e-mail-browser.h                           |    6 +-
 mail/e-mail-display.c                           | 1430 +++++++-
 mail/e-mail-display.h                           |   50 +-
 mail/e-mail-notebook-view.c                     |   18 +-
 mail/e-mail-paned-view.c                        |   82 +-
 mail/e-mail-paned-view.h                        |    1 +
 mail/e-mail-printer.c                           |  859 +++++
 mail/e-mail-printer.h                           |   85 +
 mail/e-mail-reader-utils.c                      |  151 +-
 mail/e-mail-reader-utils.h                      |    3 +-
 mail/e-mail-reader.c                            |  445 ++-
 mail/e-mail-reader.h                            |    6 +-
 mail/e-mail-request.c                           |  771 ++++
 mail/e-mail-request.h                           |   36 +
 mail/em-account-editor.c                        |    6 +-
 mail/em-composer-utils.c                        |   31 +-
 mail/em-format-hook.c                           |   23 +-
 mail/em-format-hook.h                           |    4 +-
 mail/em-format-html-display.c                   | 1350 +++----
 mail/em-format-html-display.h                   |   49 +-
 mail/em-format-html-print.c                     |  681 +++-
 mail/em-format-html-print.h                     |   19 +-
 mail/em-format-html.c                           | 4457 ++++++++++-------------
 mail/em-format-html.h                           |  191 +-
 mail/em-html-stream.c                           |  182 -
 mail/em-html-stream.h                           |   77 -
 mail/em-utils.c                                 |   74 +-
 mail/mail.error.xml                             |    5 +
 modules/Makefile.am                             |    1 +
 modules/addressbook/e-book-shell-content.c      |    9 -
 modules/mail/e-mail-config-format-html.c        |    5 +
 modules/mail/e-mail-config-web-view.c           |    6 -
 modules/mail/e-mail-shell-backend.c             |   57 +-
 modules/mail/e-mail-shell-content.c             |    9 +-
 modules/mail/e-mail-shell-view-actions.c        |  116 +-
 modules/mail/e-mail-shell-view-private.c        |  100 +-
 modules/mail/em-mailer-prefs.c                  |    6 +-
 modules/web-inspector/Makefile.am               |   23 +
 modules/web-inspector/evolution-web-inspector.c |  185 +
 51 files changed, 10200 insertions(+), 7146 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index bb6b2c1..9d9a886 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -39,6 +39,8 @@ convert_DATA = evolution.convert
 themedir = $(privdatadir)/theme
 dist_theme_DATA =	\
 	default.css	\
+	webview.css	\
+	webview-print.css \
 	tab-bar-background.png \
 	tab-switcher.png 	\
 	tab-switcher-hover.png	\
diff --git a/data/webview-print.css b/data/webview-print.css
new file mode 100644
index 0000000..a2c5292
--- /dev/null
+++ b/data/webview-print.css
@@ -0,0 +1,62 @@
+html, body {
+        padding: 0;
+        margin: 0;
+}
+
+body {
+        /* Use margin so that children can safely use width=100% */
+        margin: 10px;
+}
+
+h1,h2,h3 {
+        color: #7f7f7f;
+}
+
+th {
+        color: #7f7f7f;
+        text-align: left;
+        font-weight: normal;
+        vertical-align: top;
+}
+
+.header {
+        color: #7f7f7f;
+}
+
+.pre {
+        font-family: monospace;
+}
+
+.part-container {
+        width: 100%;
+        background: #FFF;
+        margin-top: 2px;
+        margin-bottom: 3px;
+        border-width: 0px;
+        border-style: none;
+}
+
+.part-container-inner-margin {
+        margin: 8px;
+}
+
+/***** PRINTING *******/
+
+.printing-header {
+        margin-bottom: 20px;
+}
+
+.printing-header h1,
+.attachments-list h1 {
+        font-size: 20px;
+}
+
+.printing-header th {
+        text-align: right;
+        font-weight: bold;
+}
+
+.attachments-list th {
+        font-weight: bold;
+}
+
diff --git a/data/webview.css b/data/webview.css
new file mode 100644
index 0000000..9ff8220
--- /dev/null
+++ b/data/webview.css
@@ -0,0 +1,147 @@
+html, body {
+  padding: 0;
+  margin: 0;
+}
+
+body {
+  /* Use margin so that children can safely use width=100% */
+  margin: 10px;
+}
+
+h1, h2, h3 {
+  color: #7f7f7f;
+}
+
+th {
+  color: #7f7f7f;
+  text-align: left;
+  font-weight: normal;
+  vertical-align: top;
+}
+
+.header {
+  color: #7f7f7f;
+}
+
+.pre {
+  font-family: monospace;
+}
+
+span.navigable, div.navigable, p.navigable {
+  cursor: pointer;
+  text-decoration: underline;
+  color: #003399;
+}
+
+img.navigable {
+  cursor: pointer;
+  margin-right: 4px;
+}
+
+.attachments {
+  background: #FFF;
+  border: 1px solid silver;
+  margin: 10px 10px 10px 10px;
+  border-left: 0;
+  border-right: 0;
+  border-bottom: 0;
+}
+
+.attachment {
+  margin-left: 8px;
+  margin-right: 8px;
+}
+
+.attachment-wrapper
+{
+  margin-right: 8px;
+}
+
+.part-container {
+  width: 100%;
+  height: 100%;
+  background: #FFF;
+  margin-top: 2px;
+  margin-bottom: 3px;
+  border-width: 1px;
+  border-style: solid;
+}
+
+.part-container-inner-margin {
+  margin: 8px;
+}
+
+object { /* GtkWidgets */
+  margin-top: 2px;
+  margin-bottom: 2px;
+}
+
+.__evo-highlight {
+  color: purple;
+  font-weight: bold;
+}
+
+/***** PRINTING *******/
+
+.printing-header {
+  margin-bottom: 20px;
+}
+
+.printing-header h1,
+.attachments-list h1 {
+  font-size: 20px;
+}
+
+.printing-header th {
+  text-align: right;
+  font-weight: bold;
+}
+
+.attachments-list th {
+  font-weight: bold;
+}
+
+/******* ITIP *********/
+.itip.icon {
+  float: left;
+  margin-right: 5px;
+}
+
+.itip.content {
+  float: left;
+  max-width: 90%;
+}
+
+.itip.description {
+  margin: 5px;
+}
+
+.itip tr {
+  vertical-align: middle;
+  padding-top: 5px;
+  padding-bottom: 5px;
+}
+
+.itip th {
+  color: #000;
+  vertical-align: middle;
+}
+
+#table_row_summary td {
+  font-weight: bold;
+}
+
+#table_row_buttons button {
+  line-height: 28px;
+  min-width: 150px;
+  white-space: nowrap;
+}
+
+#table_row_buttons img {
+  margin-right: 5px;
+  vertical-align: middle;
+}
+
+#text_row_buttons td {
+  text-align: center;
+}
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index 278bcb2..392a195 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -13,7 +13,8 @@ libemformat_la_CPPFLAGS =				\
 	-I$(top_srcdir)					\
 	-I$(top_srcdir)/widgets				\
 	$(EVOLUTION_DATA_SERVER_CFLAGS)			\
-	$(GNOME_PLATFORM_CFLAGS)
+	$(GNOME_PLATFORM_CFLAGS)			\
+	$(LIBSOUP_CFLAGS)
 
 libemformat_la_SOURCES =				\
 	$(emformatinclude_HEADERS)			\
@@ -28,6 +29,7 @@ libemformat_la_LIBADD =					\
 	$(top_builddir)/e-util/libeutil.la		\
 	$(top_builddir)/shell/libeshell.la		\
 	$(EVOLUTION_DATA_SERVER_LIBS)			\
-	$(GNOME_PLATFORM_LIBS)
+	$(GNOME_PLATFORM_LIBS)				\
+	$(LIBSOUP_LIBS)
 
 -include $(top_srcdir)/git.mk
diff --git a/em-format/em-format-quote.c b/em-format/em-format-quote.c
index c3f75ec..4822f11 100644
--- a/em-format/em-format-quote.c
+++ b/em-format/em-format-quote.c
@@ -39,237 +39,73 @@
 
 struct _EMFormatQuotePrivate {
 	gchar *credits;
-	CamelStream *stream;
 	EMFormatQuoteFlags flags;
 	guint32 text_html_flags;
 };
 
 static void emfq_builtin_init (EMFormatQuoteClass *efhc);
 
-static gpointer parent_class;
-
-static void
-emfq_dispose (GObject *object)
-{
-	EMFormatQuotePrivate *priv;
-
-	priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
-
-	if (priv->stream != NULL) {
-		g_object_unref (priv->stream);
-		priv->stream = NULL;
-	}
-
-	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-emfq_finalize (GObject *object)
-{
-	EMFormatQuotePrivate *priv;
-
-	priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
-
-	g_free (priv->credits);
-
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (parent_class)->finalize (object);
-}
+static CamelMimePart * decode_inline_parts (CamelMimePart *part, GCancellable *cancellable);
 
-static void
-emfq_format_clone (EMFormat *emf,
-                   CamelFolder *folder,
-                   const gchar *uid,
-                   CamelMimeMessage *msg,
-                   EMFormat *src,
-                   GCancellable *cancellable)
-{
-	EMFormatQuote *emfq = (EMFormatQuote *) emf;
-	const EMFormatHandler *handle;
-	GSettings *settings;
+static void emfq_parse_text_plain       (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emfq_parse_text_enriched    (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emfq_parse_text_html        (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emfq_parse_attachment       (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
 
-	/* Chain up to parent's format_clone() method. */
-	EM_FORMAT_CLASS (parent_class)->format_clone (
-		emf, folder, uid, msg, src, cancellable);
+static void emfq_write_text_plain	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emfq_write_text_enriched	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emfq_write_text_html	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
 
-	g_seekable_seek (
-		G_SEEKABLE (emfq->priv->stream),
-		0, G_SEEK_SET, NULL, NULL);
-
-	settings = g_settings_new ("org.gnome.evolution.mail");
-	if (g_settings_get_boolean (
-		settings, "composer-top-signature"))
-		camel_stream_write_string (
-			emfq->priv->stream, "<br>\n", cancellable, NULL);
-	g_object_unref (settings);
-	handle = em_format_find_handler(emf, "x-evolution/message/prefix");
-	if (handle)
-		handle->handler (
-			emf, emfq->priv->stream,
-			CAMEL_MIME_PART (msg),
-			handle, cancellable, FALSE);
-	handle = em_format_find_handler(emf, "x-evolution/message/rfc822");
-	if (handle)
-		handle->handler (
-			emf, emfq->priv->stream,
-			CAMEL_MIME_PART (msg),
-			handle, cancellable, FALSE);
-
-	camel_stream_flush (emfq->priv->stream, cancellable, NULL);
-
-	g_signal_emit_by_name(emf, "complete");
-}
-
-static void
-emfq_format_error (EMFormat *emf,
-                   CamelStream *stream,
-                   const gchar *errmsg)
-{
-	/* Nothing to do. */
-}
+static gpointer parent_class;
 
-static void
-emfq_format_source (EMFormat *emf,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    GCancellable *cancellable)
+/* Decodes inline encoded parts of 'part'. The returned pointer,
+ * if not NULL, should be unreffed with g_object_unref(). */
+static CamelMimePart *
+decode_inline_parts (CamelMimePart *part,
+                     GCancellable *cancellable)
 {
+	CamelMultipart *mp;
+	CamelStream *null;
 	CamelStream *filtered_stream;
-	CamelMimeFilter *html_filter;
+	EMInlineFilter *inline_filter;
 
-	filtered_stream = camel_stream_filter_new (stream);
-	html_filter = camel_mime_filter_tohtml_new (
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
-		CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT, 0);
-	camel_stream_filter_add (
-		CAMEL_STREAM_FILTER (filtered_stream), html_filter);
-	g_object_unref (html_filter);
+	g_return_val_if_fail (part != NULL, NULL);
 
-	em_format_format_text (
-		emf, filtered_stream,
-		CAMEL_DATA_WRAPPER (part), cancellable);
+	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),
+		camel_mime_part_get_content_type (part));
+	camel_stream_filter_add (
+		CAMEL_STREAM_FILTER (filtered_stream),
+		CAMEL_MIME_FILTER (inline_filter));
+	camel_data_wrapper_decode_to_stream_sync (
+		camel_medium_get_content (CAMEL_MEDIUM (part)),
+		filtered_stream, cancellable, NULL);
+	camel_stream_close (filtered_stream, cancellable, NULL);
 	g_object_unref (filtered_stream);
-}
-
-static void
-emfq_format_attachment (EMFormat *emf,
-                        CamelStream *stream,
-                        CamelMimePart *part,
-                        const gchar *mime_type,
-                        const EMFormatHandler *handle,
-                        GCancellable *cancellable)
-{
-	EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
-	gchar *text, *html;
-
-	if (!em_format_is_inline (emf, emf->part_id->str, part, handle))
-		return;
-
-	camel_stream_write_string (
-		stream, "<table border=1 cellspacing=0 cellpadding=0>"
-		"<tr><td><font size=-1>\n", cancellable, NULL);
-
-	/* output some info about it */
-	text = em_format_describe_part (part, mime_type);
-	html = camel_text_to_html (
-		text, emfq->priv->text_html_flags &
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
-	camel_stream_write_string (stream, html, cancellable, NULL);
-	g_free (html);
-	g_free (text);
-
-	camel_stream_write_string (
-		stream, "</font></td></tr></table>", cancellable, NULL);
-
-	handle->handler (emf, stream, part, handle, cancellable, FALSE);
-}
-
-static void
-emfq_base_init (EMFormatQuoteClass *class)
-{
-	emfq_builtin_init (class);
-}
-
-static void
-emfq_class_init (EMFormatQuoteClass *class)
-{
-	GObjectClass *object_class;
-	EMFormatClass *format_class;
-
-	parent_class = g_type_class_peek_parent (class);
-	g_type_class_add_private (class, sizeof (EMFormatQuotePrivate));
-
-	object_class = G_OBJECT_CLASS (class);
-	object_class->dispose = emfq_dispose;
-	object_class->finalize = emfq_finalize;
-
-	format_class = EM_FORMAT_CLASS (class);
-	format_class->format_clone = emfq_format_clone;
-	format_class->format_error = emfq_format_error;
-	format_class->format_source = emfq_format_source;
-	format_class->format_attachment = emfq_format_attachment;
-}
-
-static void
-emfq_init (EMFormatQuote *emfq)
-{
-	emfq->priv = EM_FORMAT_QUOTE_GET_PRIVATE (emfq);
-
-	/* we want to convert url's etc */
-	emfq->priv->text_html_flags =
-		CAMEL_MIME_FILTER_TOHTML_PRE |
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
-}
 
-GType
-em_format_quote_get_type (void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY (type == 0)) {
-		static const GTypeInfo type_info = {
-			sizeof (EMFormatQuoteClass),
-			(GBaseInitFunc) emfq_base_init,
-			(GBaseFinalizeFunc) NULL,
-			(GClassInitFunc) emfq_class_init,
-			(GClassFinalizeFunc) NULL,
-			NULL,  /* class_data */
-			sizeof (EMFormatQuote),
-			0,     /* n_preallocs */
-			(GInstanceInitFunc) emfq_init,
-			NULL   /* value_table */
-		};
-
-		type = g_type_register_static (
-			EM_TYPE_FORMAT, "EMFormatQuote", &type_info, 0);
+	if (!em_inline_filter_found_any (inline_filter)) {
+		g_object_unref (inline_filter);
+		return NULL;
 	}
 
-	return type;
-}
-
-EMFormatQuote *
-em_format_quote_new (const gchar *credits,
-                     CamelStream *stream,
-                     EMFormatQuoteFlags flags)
-{
-	EMFormatQuote *emfq;
-
-	g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
-
-	/* Steam must also be seekable so we can reset its position. */
-	g_return_val_if_fail (G_IS_SEEKABLE (stream), NULL);
+	mp = em_inline_filter_get_multipart (inline_filter);
 
-	emfq = g_object_new (EM_TYPE_FORMAT_QUOTE, NULL);
+	g_object_unref (inline_filter);
 
-	emfq->priv->credits = g_strdup (credits);
-	emfq->priv->stream = g_object_ref (stream);
-	emfq->priv->flags = flags;
+	if (mp) {
+		part = camel_mime_part_new ();
+		camel_medium_set_content (
+			CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp));
+		g_object_unref (mp);
+	} else {
+		g_object_ref (part);
+	}
 
-	return emfq;
+	return part;
 }
 
 static void
@@ -296,18 +132,18 @@ emfq_format_text_header (EMFormatQuote *emfq,
 
 	if (flags & EM_FORMAT_HEADER_BOLD)
 		g_string_append_printf (
-			buffer, "<b>%s</b>: %s<br>", label, html);
+                        buffer, "<b>%s</b>: %s<br>", label, html);
 	else
 		g_string_append_printf (
-			buffer, "%s: %s<br>", label, html);
+                        buffer, "%s: %s<br>", label, html);
 
 	g_free (mhtml);
 }
 
 static const gchar *addrspec_hdrs[] = {
-	"Sender", "From", "Reply-To", "To", "Cc", "Bcc",
-	"Resent-Sender", "Resent-from", "Resent-Reply-To",
-	"Resent-To", "Resent-cc", "Resent-Bcc", NULL
+        "Sender", "From", "Reply-To", "To", "Cc", "Bcc",
+        "Resent-Sender", "Resent-from", "Resent-Reply-To",
+        "Resent-To", "Resent-cc", "Resent-Bcc", NULL
 };
 
 #if 0
@@ -315,7 +151,7 @@ static const gchar *addrspec_hdrs[] = {
 /* For Translators only: The following strings are
  * used in the header table in the preview pane. */
 static gchar *i18n_hdrs[] = {
-	N_("From"), N_("Reply-To"), N_("To"), N_("Cc"), N_("Bcc")
+        N_("From"), N_("Reply-To"), N_("To"), N_("Cc"), N_("Bcc")
 };
 #endif
 
@@ -337,18 +173,18 @@ emfq_format_address (GString *out,
 			if (name && *name) {
 				gchar *real, *mailaddr;
 
-				g_string_append_printf (out, "%s &lt;", name);
-				/* rfc2368 for mailto syntax and url encoding extras */
+                                g_string_append_printf (out, "%s &lt;", name);
+                                /* rfc2368 for mailto syntax and url encoding extras */
 				if ((real = camel_header_encode_phrase ((guchar *) a->name))) {
-					mailaddr = g_strdup_printf ("%s <%s>", real, a->v.addr);
+                                        mailaddr = g_strdup_printf ("%s <%s>", real, a->v.addr);
 					g_free (real);
-					mailto = camel_url_encode (mailaddr, "?=&()");
+                                        mailto = camel_url_encode (mailaddr, "?=&()");
 					g_free (mailaddr);
 				} else {
-					mailto = camel_url_encode (a->v.addr, "?=&()");
+                                        mailto = camel_url_encode (a->v.addr, "?=&()");
 				}
 			} else {
-				mailto = camel_url_encode (a->v.addr, "?=&()");
+                                mailto = camel_url_encode (a->v.addr, "?=&()");
 			}
 			addr = camel_text_to_html (a->v.addr, flags, 0);
 			g_string_append_printf (
@@ -358,15 +194,15 @@ emfq_format_address (GString *out,
 			g_free (addr);
 
 			if (name && *name)
-				g_string_append (out, "&gt;");
+                                g_string_append (out, "&gt;");
 			break;
 		case CAMEL_HEADER_ADDRESS_GROUP:
-			g_string_append_printf (out, "%s: ", name);
+                        g_string_append_printf (out, "%s: ", name);
 			emfq_format_address (out, a->v.members);
-			g_string_append_printf (out, ";");
+                        g_string_append_printf (out, ";");
 			break;
 		default:
-			g_warning ("Invalid address type");
+                        g_warning ("Invalid address type");
 			break;
 		}
 
@@ -374,7 +210,7 @@ emfq_format_address (GString *out,
 
 		a = a->next;
 		if (a)
-			g_string_append (out, ", ");
+                        g_string_append (out, ", ");
 	}
 }
 
@@ -383,20 +219,20 @@ canon_header_name (gchar *name)
 {
 	gchar *inptr = name;
 
-	/* canonicalise the header name... first letter is
-	 * capitalised and any letter following a '-' also gets
-	 * capitalised */
+        /* canonicalise the header name... first letter is
+         * capitalised and any letter following a '-' also gets
+         * capitalised */
 
 	if (g_ascii_islower (*inptr))
-		*inptr = g_ascii_toupper (*inptr);
+                *inptr = g_ascii_toupper (*inptr);
 
 	inptr++;
 
 	while (*inptr) {
 		if (inptr[-1] == '-' && g_ascii_islower (*inptr))
-			*inptr = g_ascii_toupper (*inptr);
+                        *inptr = g_ascii_toupper (*inptr);
 		else if (g_ascii_isupper (*inptr))
-			*inptr = g_ascii_tolower (*inptr);
+                        *inptr = g_ascii_tolower (*inptr);
 
 		inptr++;
 	}
@@ -422,8 +258,8 @@ emfq_format_header (EMFormat *emf,
 	strcpy (name, namein);
 	canon_header_name (name);
 
-	/* Never quote Bcc headers */
-	if (g_str_equal (name, "Bcc") || g_str_equal (name, "Resent-Bcc"))
+        /* Never quote Bcc headers */
+        if (g_str_equal (name, "Bcc") || g_str_equal (name, "Resent-Bcc"))
 		return;
 
 	for (i = 0; addrspec_hdrs[i]; i++) {
@@ -444,8 +280,8 @@ emfq_format_header (EMFormat *emf,
 
 		buf = camel_header_unfold (txt);
 		addrs = camel_header_address_decode (
-			txt, emf->charset ?
-			emf->charset : emf->default_charset);
+			txt, em_format_get_charset (emf) ?
+			em_format_get_charset (emf) : em_format_get_default_charset (emf));
 		if (addrs == NULL) {
 			g_free (buf);
 			return;
@@ -453,29 +289,29 @@ emfq_format_header (EMFormat *emf,
 
 		g_free (buf);
 
-		html = g_string_new ("");
+                html = g_string_new ("");
 		emfq_format_address (html, addrs);
 		camel_header_address_unref (addrs);
 		txt = value = html->str;
 		g_string_free (html, FALSE);
 		flags |= EM_FORMAT_HEADER_BOLD;
 		is_html = TRUE;
-	} else if (!strcmp (name, "Subject")) {
+        } else if (!strcmp (name, "Subject")) {
 		txt = camel_mime_message_get_subject (msg);
-		label = _("Subject");
+                label = _("Subject");
 		flags |= EM_FORMAT_HEADER_BOLD;
-	} else if (!strcmp (name, "X-Evolution-Mailer")) { /* pseudo-header */
-		if (!(txt = camel_medium_get_header (part, "x-mailer")))
-			if (!(txt = camel_medium_get_header (part, "user-agent")))
-				if (!(txt = camel_medium_get_header (part, "x-newsreader")))
-					if (!(txt = camel_medium_get_header (part, "x-mimeole")))
+        } else if (!strcmp (name, "X-Evolution-Mailer")) { /* pseudo-header */
+                if (!(txt = camel_medium_get_header (part, "x-mailer")))
+                        if (!(txt = camel_medium_get_header (part, "user-agent")))
+                                if (!(txt = camel_medium_get_header (part, "x-newsreader")))
+                                        if (!(txt = camel_medium_get_header (part, "x-mimeole")))
 						return;
 
 		txt = value = camel_header_format_ctext (txt, charset);
 
-		label = _("Mailer");
+                label = _("Mailer");
 		flags |= EM_FORMAT_HEADER_BOLD;
-	} else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) {
+        } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) {
 		if (!(txt = camel_medium_get_header (part, name)))
 			return;
 
@@ -506,10 +342,10 @@ emfq_format_headers (EMFormatQuote *emfq,
 		return;
 
 	ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
-	charset = camel_content_type_param (ct, "charset");
+        charset = camel_content_type_param (ct, "charset");
 	charset = camel_iconv_charset_name (charset);
 
-	/* dump selected headers */
+        /* dump selected headers */
 	link = g_queue_peek_head_link (&emf->header_list);
 	while (link != NULL) {
 		EMFormatHeader *h = link->data;
@@ -518,154 +354,335 @@ emfq_format_headers (EMFormatQuote *emfq,
 		link = g_list_next (link);
 	}
 
-	g_string_append (buffer, "<br>\n");
+        g_string_append (buffer, "<br>\n");
 }
 
 static void
-emfq_format_message_prefix (EMFormat *emf,
-                            CamelStream *stream,
-                            CamelMimePart *part,
-                            const EMFormatHandler *info,
-                            GCancellable *cancellable,
-                            gboolean is_fallback)
+emfq_dispose (GObject *object)
 {
-	EMFormatQuote *emfq = (EMFormatQuote *) emf;
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
 
-	if (emfq->priv->credits != NULL) {
-		camel_stream_write_string (
-			stream, emfq->priv->credits, NULL, NULL);
-		camel_stream_write_string (
-			stream, "<br>\n", NULL, NULL);
-	}
+static void
+emfq_finalize (GObject *object)
+{
+	EMFormatQuotePrivate *priv;
+
+	priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
+
+	g_free (priv->credits);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+/******************************************************************************/
 static void
-emfq_format_message (EMFormat *emf,
-                     CamelStream *stream,
-                     CamelMimePart *part,
-                     const EMFormatHandler *info,
-                     GCancellable *cancellable,
-                     gboolean is_fallback)
+emfq_parse_text_plain (EMFormat * emf,
+                       CamelMimePart * part,
+                       GString * part_id,
+                       EMFormatParserInfo * info,
+                       GCancellable * cancellable)
 {
-	EMFormatQuote *emfq = (EMFormatQuote *) emf;
-	GString *buffer;
+	EMFormatPURI *puri;
+		CamelMimePart *mp;
+	gint len;
 
-	buffer = g_string_sized_new (1024);
+	len = part_id->len;
+        g_string_append (part_id, ".text_plain");
 
-	if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
-		g_string_append (
-			buffer,
-			"<!--+GtkHTML:<DATA class=\"ClueFlow\" "
-			"key=\"orig\" value=\"1\">-->\n"
-			"<blockquote type=cite>\n");
+		mp = decode_inline_parts (part, cancellable);
+		if (mp) {
 
-	if (((CamelMimePart *) emf->message) != part) {
-		g_string_append_printf (
-			buffer,
-			"%s</br>\n",
-			_("-------- Forwarded Message --------"));
-		emfq_format_headers (emfq, buffer, (CamelMedium *) part);
-	} else if (emfq->priv->flags & EM_FORMAT_QUOTE_HEADERS)
-		emfq_format_headers (emfq, buffer, (CamelMedium *) part);
+			if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) {
+				em_format_parse_part (emf, mp, part_id, info, cancellable);
+			}
 
-	camel_stream_write (
-		stream, buffer->str, buffer->len, cancellable, NULL);
+			g_object_unref (mp);
+		}
 
-	em_format_part (emf, stream, part, cancellable);
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emfq_write_text_plain;
+        puri->mime_type = g_strdup ("text/html");
+	em_format_add_puri (emf, puri);
 
-	if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
-		camel_stream_write_string (
-			stream, "</blockquote><!--+GtkHTML:"
-			"<DATA class=\"ClueFlow\" clear=\"orig\">-->",
-			cancellable, NULL);
+	g_string_truncate (part_id, len);
 }
 
-/* Decodes inline encoded parts of 'part'. The returned pointer,
- * if not NULL, should be unreffed with g_object_unref(). */
-static CamelMimePart *
-decode_inline_parts (CamelMimePart *part,
-                     GCancellable *cancellable)
+static void
+emfq_parse_text_html (EMFormat * emf,
+                      CamelMimePart * part,
+                      GString * part_id,
+                      EMFormatParserInfo * info,
+                      GCancellable * cancellable)
 {
-	CamelMultipart *mp;
-	CamelStream *null;
-	CamelStream *filtered_stream;
-	EMInlineFilter *inline_filter;
+	EMFormatPURI *puri;
+	gint len;
 
-	g_return_val_if_fail (part != NULL, NULL);
+	len = part_id->len;
+        g_string_append (part_id, ".text_html");
 
-	null = camel_stream_null_new ();
-	filtered_stream = camel_stream_filter_new (null);
-	g_object_unref (null);
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emfq_write_text_html;
+        puri->mime_type = g_strdup ("text/html");
+	em_format_add_puri (emf, puri);
 
-	inline_filter = em_inline_filter_new (
-		camel_mime_part_get_encoding (part),
-		camel_mime_part_get_content_type (part));
-	camel_stream_filter_add (
-		CAMEL_STREAM_FILTER (filtered_stream),
-		CAMEL_MIME_FILTER (inline_filter));
-	camel_data_wrapper_decode_to_stream_sync (
-		camel_medium_get_content (CAMEL_MEDIUM (part)),
-		filtered_stream, cancellable, NULL);
-	camel_stream_close (filtered_stream, cancellable, NULL);
-	g_object_unref (filtered_stream);
+	g_string_truncate (part_id, len);
+}
 
-	if (!em_inline_filter_found_any (inline_filter)) {
-		g_object_unref (inline_filter);
-		return NULL;
+static void
+emfq_parse_text_enriched (EMFormat * emf,
+                          CamelMimePart * part,
+                          GString * part_id,
+                          EMFormatParserInfo * info,
+                          GCancellable * cancellable)
+{
+	EMFormatPURI *puri;
+	gint len;
+
+	len = part_id->len;
+        g_string_append (part_id, ".text_enriched");
+
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emfq_write_text_enriched;
+        puri->mime_type = g_strdup ("text/html");
+	em_format_add_puri (emf, puri);
+
+	g_string_truncate (part_id, len);
+}
+
+static void
+emfq_parse_attachment (EMFormat * emf,
+                       CamelMimePart * part,
+                       GString * part_id,
+                       EMFormatParserInfo * info,
+                       GCancellable * cancellable)
+{
+	EMFormatPURI *puri;
+	gint len;
+
+	len = part_id->len;
+        g_string_append (part_id, ".attachment");
+
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emfq_write_text_html;
+        puri->mime_type = g_strdup ("text/html");
+	puri->is_attachment = TRUE;
+	em_format_add_puri (emf, puri);
+
+	g_string_truncate (part_id, len);
+}
+
+/******************************************************************************/
+
+static void
+emfq_write_attachment (EMFormat *emf,
+                       EMFormatPURI *puri,
+                       CamelStream *stream,
+                       EMFormatWriterInfo *info,
+                       GCancellable *cancellable)
+{
+	EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
+	const EMFormatHandler *handler;
+	gchar *text, *html;
+	CamelContentType *ct;
+	const gchar *mime_type;
+
+	ct = camel_mime_part_get_content_type (puri->part);
+	if (ct) {
+		mime_type = camel_content_type_simple (ct);
+		camel_content_type_unref (ct);
+	} else {
+		mime_type = "application/octet-stream";
 	}
 
-	mp = em_inline_filter_get_multipart (inline_filter);
+	handler = em_format_find_handler (emf, mime_type);
 
-	g_object_unref (inline_filter);
+	if (!em_format_is_inline (emf, puri->uri, puri->part, handler))
+		return;
 
-	if (mp) {
-		part = camel_mime_part_new ();
-		camel_medium_set_content (
-			CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp));
-		g_object_unref (mp);
+	camel_stream_write_string (
+		stream, "<table border=1 cellspacing=0 cellpadding=0>"
+		"<tr><td><font size=-1>\n", cancellable, NULL);
+
+	/* output some info about it */
+	text = em_format_describe_part (puri->part, mime_type);
+	html = camel_text_to_html (
+		text, emfq->priv->text_html_flags &
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+	camel_stream_write_string (stream, html, cancellable, NULL);
+	g_free (html);
+	g_free (text);
+
+	camel_stream_write_string (
+		stream, "</font></td></tr></table>", cancellable, NULL);
+
+	if (handler && handler->write_func)
+		handler->write_func (emf, puri, stream, info, cancellable);
+}
+
+static void
+emfq_base_init (EMFormatQuoteClass *klass)
+{
+	emfq_builtin_init (klass);
+}
+
+static void
+emfq_class_init (EMFormatQuoteClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (EMFormatQuotePrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->dispose = emfq_dispose;
+	object_class->finalize = emfq_finalize;
+}
+
+static void
+emfq_init (EMFormatQuote *emfq)
+{
+	emfq->priv = EM_FORMAT_QUOTE_GET_PRIVATE (emfq);
+
+	/* we want to convert url's etc */
+	emfq->priv->text_html_flags =
+		CAMEL_MIME_FILTER_TOHTML_PRE |
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+}
+
+GType
+em_format_quote_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EMFormatQuoteClass),
+			(GBaseInitFunc) emfq_base_init,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) emfq_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EMFormatQuote),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) emfq_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			EM_TYPE_FORMAT, "EMFormatQuote", &type_info, 0);
+	}
+
+	return type;
+}
+
+EMFormatQuote *
+em_format_quote_new (const gchar *credits,
+                     CamelStream *stream,
+                     EMFormatQuoteFlags flags)
+{
+	EMFormatQuote *emfq;
+
+	g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
+
+	/* Steam must also be seekable so we can reset its position. */
+	g_return_val_if_fail (G_IS_SEEKABLE (stream), NULL);
+
+	emfq = g_object_new (EM_TYPE_FORMAT_QUOTE, NULL);
+
+	emfq->priv->credits = g_strdup (credits);
+	emfq->priv->flags = flags;
+
+	return emfq;
+}
+
+void
+em_format_quote_write (EMFormatQuote * emfq,
+                       CamelStream * stream,
+                       GCancellable * cancellable)
+{
+	EMFormat *emf;
+	GSettings *settings;
+	GList *iter;
+	EMFormatWriterInfo info = { 0 };
+
+	emf = (EMFormat *) emfq;
+
+	g_seekable_seek (
+		G_SEEKABLE (stream),
+		0, G_SEEK_SET, NULL, NULL);
+
+	settings = g_settings_new ("org.gnome.evolution.mail");
+	if (g_settings_get_boolean (
+		settings, "composer-top-signature"))
+		camel_stream_write_string (
+			stream, "<br>\n", cancellable, NULL);
+	g_object_unref (settings);
+
+	if (emfq->priv->credits && *emfq->priv->credits) {
+                gchar *credits = g_strdup_printf ("%s<br/>", emfq->priv->credits);
+		camel_stream_write_string (stream, credits, cancellable, NULL);
+		g_free (credits);
 	} else {
-		g_object_ref (part);
+                camel_stream_write_string (stream, "<br/>", cancellable, NULL);
 	}
 
-	return part;
+	if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
+		camel_stream_write_string (stream,
+                        "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
+                        "key=\"orig\" value=\"1\">-->\n"
+                        "<blockquote type=cite>\n", cancellable, NULL);
+
+	for (iter = emf->mail_part_list; iter; iter = iter->next) {
+		EMFormatPURI *puri = iter->data;
+
+		if (puri->is_attachment || !puri->write_func)
+			continue;
+
+		puri = iter->data;
+
+		if (emfq->priv->flags & EM_FORMAT_QUOTE_HEADERS) {
+                        GString *buffer = g_string_new ("");
+			emfq_format_headers (emfq, buffer, (CamelMedium *) puri->part);
+			camel_stream_write_string (stream, buffer->str, cancellable, NULL);
+			g_string_free (buffer, TRUE);
+		}
+
+		puri->write_func (emf, puri, stream, &info, cancellable);
+	}
+
+	if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
+		camel_stream_write_string (
+                        stream, "</blockquote><!--+GtkHTML:"
+                        "<DATA class=\"ClueFlow\" clear=\"orig\">-->",
+			cancellable, NULL);
 }
 
 static void
-emfq_text_plain (EMFormat *emf,
-                 CamelStream *stream,
-                 CamelMimePart *part,
-                 const EMFormatHandler *info,
-                 GCancellable *cancellable,
-                 gboolean is_fallback)
+emfq_write_text_plain (EMFormat *emf,
+                       EMFormatPURI *puri,
+                       CamelStream *stream,
+                       EMFormatWriterInfo *info,
+                       GCancellable *cancellable)
 {
 	EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
 	CamelStream *filtered_stream;
 	CamelMimeFilter *html_filter;
 	CamelMimeFilter *sig_strip;
-	CamelMimePart *mp;
 	CamelContentType *type;
 	const gchar *format;
 	guint32 rgb = 0x737373, flags;
 
-	if (!part)
+	if (!puri->part)
 		return;
 
-	mp = decode_inline_parts (part, cancellable);
-	if (mp) {
-		if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) {
-			em_format_part (emf, stream, mp, cancellable);
-			g_object_unref (mp);
-
-			return;
-		}
-
-		g_object_unref (mp);
-	}
-
 	flags = emfq->priv->text_html_flags;
 
 	/* Check for RFC 2646 flowed text. */
-	type = camel_mime_part_get_content_type (part);
+	type = camel_mime_part_get_content_type (puri->part);
 	if (camel_content_type_is(type, "text", "plain")
 	    && (format = camel_content_type_param(type, "format"))
 	    && !g_ascii_strcasecmp(format, "flowed"))
@@ -687,25 +704,32 @@ emfq_text_plain (EMFormat *emf,
 
 	em_format_format_text (
 		EM_FORMAT (emfq), filtered_stream,
-		CAMEL_DATA_WRAPPER (part), cancellable);
+		CAMEL_DATA_WRAPPER (puri->part), cancellable);
 
 	camel_stream_flush (filtered_stream, cancellable, NULL);
 	g_object_unref (filtered_stream);
 }
 
 static void
-emfq_text_enriched (EMFormat *emf,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    const EMFormatHandler *info,
-                    GCancellable *cancellable,
-                    gboolean is_fallback)
+emfq_write_text_enriched (EMFormat *emf,
+                          EMFormatPURI *puri,
+                              CamelStream *stream,
+                              EMFormatWriterInfo *info,
+                              GCancellable *cancellable)
 {
 	CamelStream *filtered_stream;
 	CamelMimeFilter *enriched;
 	guint32 flags = 0;
+	CamelContentType *ct;
+	const gchar *mime_type = NULL;
+
+	ct = camel_mime_part_get_content_type (puri->part);
+	if (ct) {
+		mime_type = camel_content_type_simple (ct);
+		camel_content_type_unref (ct);
+	}
 
-	if (g_strcmp0 (info->mime_type, "text/richtext") == 0) {
+	if (g_strcmp0 (mime_type, "text/richtext") == 0) {
 		flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
 		camel_stream_write_string (
 			stream, "\n<!-- text/richtext -->\n",
@@ -724,18 +748,17 @@ emfq_text_enriched (EMFormat *emf,
 
 	camel_stream_write_string (stream, "<br><hr><br>", cancellable, NULL);
 	em_format_format_text (
-		emf, filtered_stream, CAMEL_DATA_WRAPPER (part), cancellable);
+		emf, filtered_stream, CAMEL_DATA_WRAPPER (puri->part), cancellable);
 	camel_stream_flush (filtered_stream, cancellable, NULL);
 	g_object_unref (filtered_stream);
 }
 
 static void
-emfq_text_html (EMFormat *emf,
-                CamelStream *stream,
-                CamelMimePart *part,
-                const EMFormatHandler *info,
-                GCancellable *cancellable,
-                gboolean is_fallback)
+emfq_write_text_html (EMFormat *emf,
+                      EMFormatPURI *puri,
+                      CamelStream *stream,
+                      EMFormatWriterInfo *info,
+                      GCancellable *cancellable)
 {
 	EMFormatQuotePrivate *priv;
 
@@ -757,40 +780,29 @@ emfq_text_html (EMFormat *emf,
 
 		em_format_format_text (
 			emf, filtered_stream,
-			(CamelDataWrapper *) part, cancellable);
+			(CamelDataWrapper *) puri->part, cancellable);
 		camel_stream_flush (filtered_stream, cancellable, NULL);
 		g_object_unref (filtered_stream);
 	} else {
 		em_format_format_text (
 			emf, stream,
-			(CamelDataWrapper *) part, cancellable);
+			(CamelDataWrapper *) puri->part, cancellable);
 	}
 }
 
-static void
-emfq_ignore (EMFormat *emf,
-             CamelStream *stream,
-             CamelMimePart *part,
-             const EMFormatHandler *info,
-             GCancellable *cancellable,
-             gboolean is_fallback)
-{
-	/* NOOP */
-}
-
+/****************************************************************************/
 static EMFormatHandler type_builtin_table[] = {
-	{ (gchar *) "text/plain", emfq_text_plain },
-	{ (gchar *) "text/enriched", emfq_text_enriched },
-	{ (gchar *) "text/richtext", emfq_text_enriched },
-	{ (gchar *) "text/html", emfq_text_html },
-	{ (gchar *) "text/*", emfq_text_plain },
-	{ (gchar *) "message/external-body", emfq_ignore },
-	{ (gchar *) "multipart/appledouble", emfq_ignore },
+	{ (gchar *) "text/plain", emfq_parse_text_plain, emfq_write_text_plain, },
+	{ (gchar *) "text/enriched", emfq_parse_text_enriched, emfq_write_text_enriched, },
+	{ (gchar *) "text/richtext", emfq_parse_text_enriched, emfq_write_text_enriched, },
+	{ (gchar *) "text/html", emfq_parse_text_html, emfq_write_text_html, },
+	{ (gchar *) "text/*", emfq_parse_text_plain, emfq_write_text_plain, },
+	{ (gchar *) "message/external-body", em_format_empty_parser, em_format_empty_writer, },
+	{ (gchar *) "multipart/appledouble", em_format_empty_parser, em_format_empty_writer, },
 
 	/* internal evolution types */
-	{ (gchar *) "x-evolution/evolution-rss-feed", emfq_text_html },
-	{ (gchar *) "x-evolution/message/rfc822", emfq_format_message },
-	{ (gchar *) "x-evolution/message/prefix", emfq_format_message_prefix },
+	{ (gchar *) "x-evolution/evolution-rss-feed", 0, emfq_write_text_html, },
+	{ (gchar *) "x-evolution/message/attachment", emfq_parse_attachment, emfq_write_attachment, },
 };
 
 static void
@@ -798,7 +810,9 @@ emfq_builtin_init (EMFormatQuoteClass *efhc)
 {
 	gint ii;
 
+	EMFormatClass *emfc = (EMFormatClass *) efhc;
+
 	for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
 		em_format_class_add_handler (
-			EM_FORMAT_CLASS (efhc), &type_builtin_table[ii]);
+			emfc, &type_builtin_table[ii]);
 }
diff --git a/em-format/em-format-quote.h b/em-format/em-format-quote.h
index 5c1882e..be36407 100644
--- a/em-format/em-format-quote.h
+++ b/em-format/em-format-quote.h
@@ -69,6 +69,9 @@ GType		em_format_quote_get_type	(void);
 EMFormatQuote *	em_format_quote_new		(const gchar *credits,
 						 CamelStream *stream,
 						 EMFormatQuoteFlags flags);
+void            em_format_quote_write           (EMFormatQuote *emfq,
+                                                 CamelStream *stream,
+                                                 GCancellable *cancellable);
 
 G_END_DECLS
 
diff --git a/em-format/em-format.c b/em-format/em-format.c
index d476036..4abe354 100644
--- a/em-format/em-format.c
+++ b/em-format/em-format.c
@@ -25,1036 +25,1197 @@
 #include <config.h>
 #endif
 
-#include <stdio.h>
 #include <string.h>
-
 #include <gio/gio.h>
 #include <glib/gi18n-lib.h>
+#include <libsoup/soup-uri.h>
 
 #include "em-format.h"
 #include "e-util/e-util.h"
 #include "shell/e-shell.h"
 #include "shell/e-shell-settings.h"
 
+#define d(x)
+
 #define EM_FORMAT_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), EM_TYPE_FORMAT, EMFormatPrivate))
 
-#define d(x)
-
-typedef struct _EMFormatCache EMFormatCache;
-
 struct _EMFormatPrivate {
-	guint redraw_idle_id;
-};
-
-/* Used to cache various data/info for redraws
- * The validity stuff could be cached at a higher level but this is easier
- * This absolutely relies on the partid being _globally unique_
- * This is still kind of yucky, we should maintain a full tree of all this data,
- * along with/as part of the puri tree */
-struct _EMFormatCache {
-	CamelCipherValidity *valid; /* validity copy */
-	CamelMimePart *secured;	/* encrypted subpart */
+	GNode *current_node;
 
-	guint state:2;		/* inline state */
+	CamelSession *session;
 
-	gchar partid[1];
-};
+	CamelURL *base_url;
 
-#define INLINE_UNSET (0)
-#define INLINE_ON (1)
-#define INLINE_OFF (2)
+	gchar *charset;
+	gchar *default_charset;
+	gboolean composer;
 
-static void emf_builtin_init (EMFormatClass *);
+	gint last_error;
+};
 
 enum {
-	EMF_COMPLETE,
-	EMF_LAST_SIGNAL
+	PROP_0,
+	PROP_CHARSET,
+	PROP_DEFAULT_CHARSET,
+	PROP_COMPOSER,
+	PROP_BASE_URL
 };
 
-static gpointer parent_class;
-static guint signals[EMF_LAST_SIGNAL];
-
-static void
-emf_free_cache (EMFormatCache *efc)
-{
-	if (efc->valid)
-		camel_cipher_validity_free (efc->valid);
-	if (efc->secured)
-		g_object_unref (efc->secured);
-	g_free (efc);
-}
-
-static EMFormatCache *
-emf_insert_cache (EMFormat *emf,
-                  const gchar *partid)
-{
-	EMFormatCache *new;
-
-	new = g_malloc0 (sizeof (*new) + strlen (partid));
-	strcpy (new->partid, partid);
-	g_hash_table_insert (emf->inline_table, new->partid, new);
+enum {
+	REDRAW_REQUESTED,
+	LAST_SIGNAL
+};
 
-	return new;
-}
+gint signals[LAST_SIGNAL];
 
-static void
-emf_clone_inlines (gpointer key,
-                   gpointer val,
-                   gpointer data)
-{
-	EMFormatCache *emfc = val, *new;
+static gpointer parent_class;
 
-	new = emf_insert_cache ((EMFormat *) data, emfc->partid);
-	new->state = emfc->state;
-	if (emfc->valid)
-		new->valid = camel_cipher_validity_clone (emfc->valid);
-	if (emfc->secured)
-		g_object_ref ((new->secured = emfc->secured));
-}
+/* PARSERS */
+static void emf_parse_application_xpkcs7mime	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_application_mbox		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_alternative	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_appledouble	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_encrypted	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_mixed		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_signed		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_related		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_digest		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_message_deliverystatus	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_inlinepgp_signed		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_inlinepgp_encrypted	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_message			(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_headers			(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_post_headers		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_source			(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+
+/* WRITERS */
+static void emf_write_text			(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emf_write_source			(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emf_write_error			(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
+/**************************************************************************/
 
 static gboolean
-emf_clear_puri_node (GNode *node)
+is_secured (CamelMimePart *part)
 {
-	GQueue *queue = node->data;
-	EMFormatPURI *pn;
-
-	while ((pn = g_queue_pop_head (queue)) != NULL) {
-		if (pn->free != NULL)
-			pn->free (pn);
-		g_free (pn->uri);
-		g_free (pn->cid);
-		g_free (pn->part_id);
-		if (pn->part != NULL)
-			g_object_unref (pn->part);
-		g_free (pn);
-	}
-
-	g_queue_free (queue);
+	CamelContentType *ct = camel_mime_part_get_content_type (part);
 
-	return FALSE;
+	return (camel_content_type_is (ct, "multipart", "signed") ||
+		camel_content_type_is (ct, "multipart", "encrypted") ||
+		camel_content_type_is (ct, "application", "x-inlinepgp-signed") ||
+		camel_content_type_is (ct, "application", "x-inlinepgp-encrypted") ||
+		camel_content_type_is (ct, "application", "x-pkcs7-mime") ||
+		camel_content_type_is (ct, "application", "pkcs7-mime"));
 }
 
 static void
-emf_finalize (GObject *object)
+preserve_charset_in_content_type (CamelMimePart *ipart,
+                                  CamelMimePart *opart)
 {
-	EMFormat *emf = EM_FORMAT (object);
-
-	if (emf->priv->redraw_idle_id > 0)
-		g_source_remove (emf->priv->redraw_idle_id);
-
-	if (emf->session)
-		g_object_unref (emf->session);
+	CamelDataWrapper *data_wrapper;
+	CamelContentType *content_type;
+	const gchar *charset;
 
-	if (emf->message)
-		g_object_unref (emf->message);
+	g_return_if_fail (ipart != NULL);
+	g_return_if_fail (opart != NULL);
 
-	g_hash_table_destroy (emf->inline_table);
+	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
+	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
 
-	em_format_clear_headers (emf);
-	camel_cipher_validity_free (emf->valid);
-	g_free (emf->charset);
-	g_free (emf->default_charset);
-	g_string_free (emf->part_id, TRUE);
-	g_free (emf->current_message_part_id);
-	g_free (emf->uid);
+	if (content_type == NULL)
+		return;
 
-	if (emf->pending_uri_table != NULL)
-		g_hash_table_destroy (emf->pending_uri_table);
+	charset = camel_content_type_param (content_type, "charset");
 
-	if (emf->pending_uri_tree != NULL) {
-		g_node_traverse (
-			emf->pending_uri_tree,
-			G_IN_ORDER, G_TRAVERSE_ALL, -1,
-			(GNodeTraverseFunc) emf_clear_puri_node, NULL);
-		g_node_destroy (emf->pending_uri_tree);
-	}
+	if (charset == NULL || *charset == '\0')
+		return;
 
-	/* FIXME: check pending jobs */
+	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
+	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
 
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (parent_class)->finalize (object);
+	camel_content_type_set_param (content_type, "charset", charset);
 }
 
-static const EMFormatHandler *
-emf_find_handler (EMFormat *emf,
-                  const gchar *mime_type)
+static CamelMimePart *
+get_related_display_part (CamelMimePart *part,
+                            gint *out_displayid)
 {
-	EMFormatClass *emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf);
-
-	return g_hash_table_lookup (emfc->type_handlers, mime_type);
-}
+	CamelMultipart *mp;
+	CamelMimePart *body_part, *display_part = NULL;
+	CamelContentType *content_type;
+	const gchar *start;
+	gint i, nparts, displayid = 0;
 
-static void
-emf_format_clone (EMFormat *emf,
-                  CamelFolder *folder,
-                  const gchar *uid,
-                  CamelMimeMessage *msg,
-                  EMFormat *emfsource,
-                  GCancellable *cancellable)
-{
-	/* Cancel any pending redraws. */
-	if (emf->priv->redraw_idle_id > 0) {
-		g_source_remove (emf->priv->redraw_idle_id);
-		emf->priv->redraw_idle_id = 0;
-	}
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	em_format_clear_puri_tree (emf);
+	if (!CAMEL_IS_MULTIPART (mp))
+		return NULL;
 
-	if (emf != emfsource) {
-		g_hash_table_remove_all (emf->inline_table);
-		if (emfsource) {
-			GList *link;
+	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;
 
-			/* We clone the current state here */
-			g_hash_table_foreach (emfsource->inline_table, emf_clone_inlines, emf);
-			emf->mode = emfsource->mode;
-			g_free (emf->charset);
-			emf->charset = g_strdup (emfsource->charset);
-			g_free (emf->default_charset);
-			emf->default_charset = g_strdup (emfsource->default_charset);
+		/* strip <>'s from CID */
+		len = strlen (start) - 2;
+		start++;
 
-			em_format_clear_headers (emf);
+		for (i = 0; i < nparts; i++) {
+			body_part = camel_multipart_get_part (mp, i);
+			cid = camel_mime_part_get_content_id (body_part);
 
-			link = g_queue_peek_head_link (&emfsource->header_list);
-			while (link != NULL) {
-				struct _EMFormatHeader *h = link->data;
-				em_format_add_header (emf, h->name, h->flags);
-				link = g_list_next (link);
+			if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
+				display_part = body_part;
+				displayid = i;
+				break;
 			}
 		}
+	} else {
+		display_part = camel_multipart_get_part (mp, 0);
 	}
 
-	/* what a mess */
-	if (folder != emf->folder) {
-		if (emf->folder)
-			g_object_unref (emf->folder);
-		if (folder)
-			g_object_ref (folder);
-		emf->folder = folder;
-	}
-
-	if (uid != emf->uid) {
-		g_free (emf->uid);
-		emf->uid = g_strdup (uid);
-	}
-
-	if (msg != emf->message) {
-		if (emf->message)
-			g_object_unref (emf->message);
-		if (msg)
-			g_object_ref (msg);
-		emf->message = msg;
-	}
+	if (out_displayid)
+		*out_displayid = displayid;
 
-	g_free (emf->current_message_part_id);
-	emf->current_message_part_id = g_strdup ("root-message");
-	g_string_truncate (emf->part_id, 0);
-	if (folder != NULL)
-		/* TODO build some string based on the folder name/location? */
-		g_string_append_printf(emf->part_id, ".%p", (gpointer) folder);
-	if (uid != NULL)
-		g_string_append_printf(emf->part_id, ".%s", uid);
+	return display_part;
 }
 
-static void
-emf_format_secure (EMFormat *emf,
-                   CamelStream *stream,
-                   CamelMimePart *part,
-                   CamelCipherValidity *valid,
-                   GCancellable *cancellable)
+static gboolean
+related_display_part_is_attachment (EMFormat *emf,
+                                    CamelMimePart *part)
 {
-	CamelCipherValidity *save = emf->valid_parent;
-	gint len;
-
-	/* Note that this also requires support from higher up in the class chain
-	 *  - validity needs to be cleared when you start output
-	 *  - also needs to be cleared (but saved) whenever you start a new message. */
-
-	if (emf->valid == NULL) {
-		emf->valid = valid;
-	} else {
-		g_queue_push_tail (&emf->valid_parent->children, valid);
-		camel_cipher_validity_envelope (emf->valid_parent, valid);
-	}
-
-	emf->valid_parent = valid;
-
-	len = emf->part_id->len;
-	g_string_append_printf(emf->part_id, ".secured");
-	em_format_part (emf, stream, part, cancellable);
-	g_string_truncate (emf->part_id, len);
+	CamelMimePart *display_part;
 
-	emf->valid_parent = save;
+	display_part = get_related_display_part (part, NULL);
+	return display_part && em_format_is_attachment (emf, display_part);
 }
 
-static gboolean
-emf_busy (EMFormat *emf)
+/**************************************************************************/
+void
+em_format_empty_parser (EMFormat *emf,
+                        CamelMimePart *part,
+                        GString *part_id,
+                        EMFormatParserInfo *info,
+                        GCancellable *cancellable)
 {
-	return FALSE;
+	/* DO NOTHING */
 }
 
-static gboolean
-emf_is_inline (EMFormat *emf,
-               const gchar *part_id,
-               CamelMimePart *mime_part,
-               const EMFormatHandler *handle)
+#ifdef ENABLE_SMIME
+static void
+emf_parse_application_xpkcs7mime (EMFormat *emf,
+                                  CamelMimePart *part,
+                                  GString *part_id,
+                                  EMFormatParserInfo *info,
+                                  GCancellable *cancellable)
 {
-	EMFormatCache *emfc;
-	const gchar *disposition;
+	CamelCipherContext *context;
+	CamelMimePart *opart;
+	CamelCipherValidity *valid;
+	GError *local_error = NULL;
 
-	if (handle == NULL)
-		return FALSE;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	emfc = g_hash_table_lookup (emf->inline_table, part_id);
-	if (emfc && emfc->state != INLINE_UNSET)
-		return emfc->state & 1;
+	context = camel_smime_context_new (emf->priv->session);
 
-	/* Some types need to override the disposition.
-	 * e.g. application/x-pkcs7-mime */
-	if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION)
-		return TRUE;
+	opart = camel_mime_part_new ();
+	valid = camel_cipher_context_decrypt_sync (
+		context, part, opart, cancellable, &local_error);
+	preserve_charset_in_content_type (part, opart);
+	if (valid == NULL) {
+		em_format_format_error (
+			emf, "%s",
+			local_error->message ? local_error->message :
+			_("Could not parse S/MIME message: Unknown error"));
+		g_clear_error (&local_error);
+	} else {
+		EMFormatParserInfo encinfo = {
+				info->handler,
+				info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_SMIME,
+				valid
+		};
+		gint len = part_id->len;
+
+		g_string_append (part_id, ".encrypted");
+		em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+		g_string_truncate (part_id, len);
+
+		/* Add a widget with details about the encryption, but only when
+		 * the encrypted isn't itself secured, in that case it has created
+		 * the button itself */
+		if (!is_secured (opart)) {
+			g_string_append (part_id, ".encrypted.button");
+			em_format_parse_part_as (emf, part, part_id, &encinfo,
+				"x-evolution/message/x-secure-button", cancellable);
+			g_string_truncate (part_id, len);
+		}
 
-	disposition = camel_mime_part_get_disposition (mime_part);
-	if (disposition != NULL)
-		return g_ascii_strcasecmp (disposition, "inline") == 0;
+		camel_cipher_validity_free (valid);
+	}
 
-	/* Otherwise, use the default for this handler type. */
-	return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0;
+	g_object_unref (opart);
+	g_object_unref (context);
 }
+#endif
 
+/* RFC 4155 */
 static void
-emf_base_init (EMFormatClass *class)
+emf_parse_application_mbox (EMFormat *emf,
+                                  CamelMimePart *mime_part,
+                                  GString *part_id,
+                                  EMFormatParserInfo *info,
+                                  GCancellable *cancellable)
 {
-	class->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
-	emf_builtin_init (class);
-}
+	CamelMimeParser *parser;
+	CamelStream *mem_stream;
+	camel_mime_parser_state_t state;
+	gint old_len;
+	gint messages;
 
-static void
-emf_class_init (EMFormatClass *class)
-{
-	GObjectClass *object_class;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	parent_class = g_type_class_peek_parent (class);
-	g_type_class_add_private (class, sizeof (EMFormatPrivate));
+	/* Extract messages from the application/mbox part and
+	 * render them as a flat list of messages. */
 
-	object_class = G_OBJECT_CLASS (class);
-	object_class->finalize = emf_finalize;
+	/* XXX If the mbox has multiple messages, maybe render them
+	 *     as a multipart/digest so each message can be expanded
+	 *     or collapsed individually.
+	 *
+	 *     See attachment_handler_mail_x_uid_list() for example. */
 
-	class->find_handler = emf_find_handler;
-	class->format_clone = emf_format_clone;
-	class->format_secure = emf_format_secure;
-	class->busy = emf_busy;
-	class->is_inline = emf_is_inline;
+	/* XXX This is based on em_utils_read_messages_from_stream().
+	 *     Perhaps refactor that function to return an array of
+	 *     messages instead of assuming we want to append them
+	 *     to a folder? */
 
-	signals[EMF_COMPLETE] = g_signal_new (
-		"complete",
-		G_OBJECT_CLASS_TYPE (class),
-		G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET (EMFormatClass, complete),
-		NULL, NULL,
-		g_cclosure_marshal_VOID__VOID,
-		G_TYPE_NONE, 0);
-}
+	parser = camel_mime_parser_new ();
+	camel_mime_parser_scan_from (parser, TRUE);
 
-static void
-emf_init (EMFormat *emf)
-{
-	EShell *shell;
-	EShellSettings *shell_settings;
+	mem_stream = camel_stream_mem_new ();
+	camel_data_wrapper_decode_to_stream_sync (
+		camel_medium_get_content (CAMEL_MEDIUM (mime_part)),
+		mem_stream, NULL, NULL);
+	g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
+	camel_mime_parser_init_with_stream (parser, mem_stream, NULL);
+	g_object_unref (mem_stream);
 
-	emf->priv = EM_FORMAT_GET_PRIVATE (emf);
+	old_len = part_id->len;
 
-	emf->inline_table = g_hash_table_new_full (
-		g_str_hash, g_str_equal,
-		(GDestroyNotify) NULL,
-		(GDestroyNotify) emf_free_cache);
-	emf->composer = FALSE;
-	emf->print = FALSE;
-	g_queue_init (&emf->header_list);
-	em_format_default_headers (emf);
-	emf->part_id = g_string_new("");
-	emf->current_message_part_id = NULL;
-	emf->validity_found = 0;
+	/* Extract messages from the mbox. */
+	messages = 0;
+	state = camel_mime_parser_step (parser, NULL, NULL);
 
-	shell = e_shell_get_default ();
-	shell_settings = e_shell_get_shell_settings (shell);
+	while (state == CAMEL_MIME_PARSER_STATE_FROM) {
+		CamelMimeMessage *message;
 
-	emf->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
-	g_return_if_fail (emf->session != NULL);
+		message = camel_mime_message_new ();
+		mime_part = CAMEL_MIME_PART (message);
 
-	g_object_ref (emf->session);
-}
+		if (!camel_mime_part_construct_from_parser_sync (
+			mime_part, parser, NULL, NULL)) {
+			g_object_unref (message);
+			break;
+		}
 
-GType
-em_format_get_type (void)
-{
-	static GType type = 0;
+		g_string_append_printf (part_id, ".mbox.%d", messages);
+		em_format_parse_part_as (emf, CAMEL_MIME_PART (message),
+				part_id, info, "message/rfc822", cancellable);
+		g_string_truncate (part_id, old_len);
 
-	if (G_UNLIKELY (type == 0)) {
-		static const GTypeInfo type_info = {
-			sizeof (EMFormatClass),
-			(GBaseInitFunc) emf_base_init,
-			(GBaseFinalizeFunc) NULL,
-			(GClassInitFunc) emf_class_init,
-			(GClassFinalizeFunc) NULL,
-			NULL,  /* class_data */
-			sizeof (EMFormat),
-			0,     /* n_preallocs */
-			(GInstanceInitFunc) emf_init,
-			NULL   /* value_table */
-		};
+		g_object_unref (message);
 
-		type = g_type_register_static (
-			G_TYPE_OBJECT, "EMFormat", &type_info, 0);
+		/* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
+		camel_mime_parser_step (parser, NULL, NULL);
+
+		state = camel_mime_parser_step (parser, NULL, NULL);
+
+		messages++;
 	}
 
-	return type;
+	g_object_unref (parser);
 }
 
-/**
- * em_format_class_add_handler:
- * @emfc: EMFormatClass
- * @info: Callback information.
- *
- * Add a mime type handler to this class.  This is only used by
- * implementing classes.  The @info.old pointer will automatically be
- * setup to point to the old handler if one was already set.  This can
- * be used for overrides a fallback.
- *
- * When a mime type described by @info is encountered, the callback will
- * be invoked.  Note that @info may be extended by sub-classes if
- * they require additional context information.
- *
- * Use a mime type of "foo/ *" to insert a fallback handler for type "foo".
- **/
-void
-em_format_class_add_handler (EMFormatClass *emfc,
-                             EMFormatHandler *info)
+/* RFC 1740 */
+static void
+emf_parse_multipart_alternative (EMFormat *emf,
+                                    CamelMimePart *part,
+                                 GString *part_id,
+                                            EMFormatParserInfo *info,
+                                 GCancellable *cancellable)
 {
-	info->old = g_hash_table_lookup (emfc->type_handlers, info->mime_type);
-	g_hash_table_insert (emfc->type_handlers, (gpointer) info->mime_type, info);
-}
+	CamelMultipart *mp;
+	gint i, nparts, bestid = 0;
+	CamelMimePart *best = NULL;
 
-struct _class_handlers {
-	EMFormatClass *old;
-	EMFormatClass *new;
-};
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-static void
-merge_missing (gpointer key,
-               gpointer value,
-               gpointer userdata)
-{
-	struct _class_handlers *classes = (struct _class_handlers *) userdata;
-	EMFormatHandler *info;
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	info = g_hash_table_lookup (classes->new->type_handlers, key);
-	if (!info) {
-		/* Might be from a plugin */
-		g_hash_table_insert (classes->new->type_handlers, key, value);
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
 	}
 
-}
+	/* as per rfc, find the last part we know how to display */
+	nparts = camel_multipart_get_number (mp);
+	for (i = 0; i < nparts; i++) {
+		CamelMimePart *mpart;
+		CamelDataWrapper *data_wrapper;
+		CamelContentType *type;
+		CamelStream *null_stream;
+		gchar *mime_type;
+		gsize content_size;
 
-void
-em_format_merge_handler (EMFormat *new,
-                         EMFormat *old)
-{
-	EMFormatClass *oldc = (EMFormatClass *) G_OBJECT_GET_CLASS (old);
-	EMFormatClass *newc = (EMFormatClass *) G_OBJECT_GET_CLASS (new);
-	struct _class_handlers fclasses;
+		if (g_cancellable_is_cancelled (cancellable))
+			return;
 
-	fclasses.old = oldc;
-	fclasses.new = newc;
+		/* is it correct to use the passed in *part here? */
+		mpart = camel_multipart_get_part (mp, i);
 
-	g_hash_table_foreach (oldc->type_handlers, merge_missing, &fclasses);
+		if (mpart == NULL)
+			continue;
 
-}
+		/* This may block even though the stream does not.
+		 * XXX Pretty inefficient way to test if the MIME part
+		 *     is empty.  Surely there's a quicker way? */
+		null_stream = camel_stream_null_new ();
+		data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (mpart));
+		camel_data_wrapper_decode_to_stream_sync (
+			data_wrapper, null_stream, cancellable, NULL);
+		content_size = CAMEL_STREAM_NULL (null_stream)->written;
+		g_object_unref (null_stream);
 
-/**
- * em_format_class_remove_handler:
- * @emfc:
- * @info:
- *
- * Remove a handler.  @info must be a value which was previously
- * added.
- **/
-void
-em_format_class_remove_handler (EMFormatClass *emfc,
-                                EMFormatHandler *info)
-{
-	EMFormatHandler *current;
+		if (content_size == 0)
+			continue;
 
-	/* TODO: thread issues? */
+		type = camel_mime_part_get_content_type (mpart);
+		mime_type = camel_content_type_simple (type);
 
-	current = g_hash_table_lookup (emfc->type_handlers, info->mime_type);
-	if (current == info) {
-		current = info->old;
-		if (current)
-			g_hash_table_insert (
-				emfc->type_handlers,
-				(gpointer) current->mime_type, current);
-		else
-			g_hash_table_remove (
-				emfc->type_handlers, info->mime_type);
-	} else {
-		while (current && current->old != info)
-			current = current->old;
-		g_return_if_fail (current != NULL);
-		current->old = info->old;
-	}
-}
+		camel_strdown (mime_type);
 
-/**
- * em_format_find_handler:
- * @emf:
- * @mime_type:
- *
- * Find a format handler by @mime_type.
- *
- * Return value: NULL if no handler is available.
- **/
-const EMFormatHandler *
-em_format_find_handler (EMFormat *emf,
-                        const gchar *mime_type)
-{
-	EMFormatClass *class;
+		if (!em_format_is_attachment (emf, mpart) &&
+			 ((camel_content_type_is (type, "multipart", "related") == 0) ||
+		       !related_display_part_is_attachment (emf, mpart)) &&
+		     (em_format_find_handler (emf, mime_type)
+		       || (best == NULL && em_format_fallback_handler (emf, mime_type)))) {
+			best = mpart;
+			bestid = i;
+		}
 
-	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
-	g_return_val_if_fail (mime_type != NULL, NULL);
+		g_free (mime_type);
+	}
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_val_if_fail (class->find_handler != NULL, NULL);
+	if (best) {
+		gint len = part_id->len;
 
-	return class->find_handler (emf, mime_type);
+		g_string_append_printf(part_id, ".alternative.%d", bestid);
+		em_format_parse_part (emf, best, part_id, info, cancellable);
+		g_string_truncate (part_id, len);
+	} else
+		emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
 }
 
-/**
- * em_format_fallback_handler:
- * @emf:
- * @mime_type:
- *
- * Try to find a format handler based on the major type of the @mime_type.
- *
- * The subtype is replaced with "*" and a lookup performed.
- *
- * Return value:
- **/
-const EMFormatHandler *
-em_format_fallback_handler (EMFormat *emf,
-                            const gchar *mime_type)
+/* RFC 1740 */
+static void
+emf_parse_multipart_appledouble (EMFormat *emf,
+                                    CamelMimePart *part,
+                                 GString *part_id,
+                                           EMFormatParserInfo *info,
+                                 GCancellable *cancellable)
 {
-	gchar *mime, *s;
+	CamelMultipart *mp;
+	CamelMimePart *mime_part;
 
-	s = strchr (mime_type, '/');
-	if (s == NULL)
-		mime = (gchar *) mime_type;
-	else {
-		gsize len = (s - mime_type) + 1;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-		mime = g_alloca (len + 2);
-		strncpy (mime, mime_type, len);
-		strcpy(mime+len, "*");
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
 	}
 
-	return em_format_find_handler (emf, mime);
+	mime_part = camel_multipart_get_part (mp, 1);
+	if (mime_part) {
+		gint len;
+		/* try the data fork for something useful, doubtful but who knows */
+		len = part_id->len;
+		g_string_append_printf(part_id, ".appledouble.1");
+		em_format_parse_part (emf, mime_part, part_id, info, cancellable);
+		g_string_truncate (part_id, len);
+	} else {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+	}
 }
 
-/**
- * em_format_add_puri:
- * @emf:
- * @size:
- * @cid: Override the autogenerated content id.
- * @part:
- * @func:
- *
- * Add a pending-uri handler.  When formatting parts that reference
- * other parts, a pending-uri (PURI) can be used to track the reference.
- *
- * @size is used to allocate the structure, so that it can be directly
- * subclassed by implementors.
- *
- * @cid can be used to override the key used to retreive the PURI, if NULL,
- * then the content-location and the content-id of the @part are stored
- * as lookup keys for the part.
- *
- * FIXME: This may need a free callback.
- *
- * Return value: A new PURI, with a referenced copy of @part, and the cid
- * always set.  The uri will be set if one is available.  Clashes
- * are resolved by forgetting the old PURI in the global index.
- **/
-EMFormatPURI *
-em_format_add_puri (EMFormat *emf,
-                    gsize size,
-                    const gchar *cid,
-                    CamelMimePart *part,
-                    EMFormatPURIFunc func)
+static void
+emf_parse_multipart_encrypted (EMFormat *emf,
+                               CamelMimePart *part,
+                               GString *part_id,
+                               EMFormatParserInfo *info,
+                               GCancellable *cancellable)
 {
-	EMFormatPURI *puri;
-	const gchar *tmp;
+	CamelCipherContext *context;
+	const gchar *protocol;
+	CamelMimePart *opart;
+	CamelCipherValidity *valid;
+	CamelMultipartEncrypted *mpe;
+	GError *local_error = NULL;
 
-	d(printf("adding puri for part: %s\n", emf->part_id->str));
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	if (size < sizeof (*puri)) {
-		g_warning (
-			"size (%" G_GSIZE_FORMAT
-			") less than size of puri\n", size);
-		size = sizeof (*puri);
+	mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part);
+	if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) {
+		em_format_format_error (
+			emf, _("Could not parse MIME message. "
+			"Displaying as source."));
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
 	}
 
-	puri = g_malloc0 (size);
-
-	puri->format = emf;
-	puri->func = func;
-	puri->use_count = 0;
-	puri->cid = g_strdup (cid);
-	puri->part_id = g_strdup (emf->part_id->str);
-
-	if (part) {
-		g_object_ref (part);
-		puri->part = part;
+	/* Currently we only handle RFC2015-style PGP encryption. */
+	protocol = camel_content_type_param (
+		((CamelDataWrapper *)mpe)->mime_type, "protocol");
+	if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) {
+		em_format_format_error (emf, _("Unsupported encryption type for multipart/encrypted"));
+		emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
+		return;
 	}
 
-	if (part != NULL && cid == NULL) {
-		tmp = camel_mime_part_get_content_id (part);
-		if (tmp)
-			puri->cid = g_strdup_printf("cid:%s", tmp);
-		else
-			puri->cid = g_strdup_printf("em-no-cid:%s", emf->part_id->str);
+	context = camel_gpg_context_new (emf->priv->session);
+	opart = camel_mime_part_new ();
+	valid = camel_cipher_context_decrypt_sync (
+		context, part, opart, cancellable, &local_error);
+	preserve_charset_in_content_type (part, opart);
+	if (valid == NULL) {
+		em_format_format_error (
+			emf, local_error->message ?
+			_("Could not parse PGP/MIME message") :
+			_("Could not parse PGP/MIME message: Unknown error"));
+		if (local_error->message != NULL)
+			em_format_format_error (
+				emf, "%s", local_error->message);
+		g_clear_error (&local_error);
+		emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
+	} else {
+		gint len = part_id->len;
 
-		d(printf("built cid '%s'\n", puri->cid));
+		EMFormatParserInfo encinfo = {
+				info->handler,
+				info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP,
+		};
 
-		/* Not quite same as old behaviour, it also put in the
-		 * relative uri and a fallback for no parent uri. */
-		tmp = camel_mime_part_get_content_location (part);
-		puri->uri = NULL;
-		if (tmp == NULL) {
-			/* No location, don't set a uri at all,
-			 * html parts do this themselves. */
-		} else {
-			if (strchr (tmp, ':') == NULL && emf->base != NULL) {
-				CamelURL *uri;
-
-				uri = camel_url_new_with_base (emf->base, tmp);
-				puri->uri = camel_url_to_string (uri, 0);
-				camel_url_free (uri);
-			} else {
-				puri->uri = g_strdup (tmp);
-			}
-		}
-	}
+		if (info->validity)
+			camel_cipher_validity_envelope (valid, info->validity);
 
-	g_return_val_if_fail (puri->cid != NULL, NULL);
-	g_return_val_if_fail (emf->pending_uri_level != NULL, NULL);
-	g_return_val_if_fail (emf->pending_uri_table != NULL, NULL);
+		encinfo.validity = valid;
 
-	g_queue_push_tail (emf->pending_uri_level->data, puri);
+		g_string_append (part_id, ".encrypted");
+		em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+		g_string_truncate (part_id, len);
 
-	if (puri->uri)
-		g_hash_table_insert (emf->pending_uri_table, puri->uri, puri);
-	g_hash_table_insert (emf->pending_uri_table, puri->cid, puri);
+		/* Add a widget with details about the encryption, but only when
+		 * the encrypted isn't itself secured, in that case it has created
+		 * the button itself */
+		if (!is_secured (opart)) {
+			g_string_append (part_id, ".encrypted.button");
+			em_format_parse_part_as (emf, part, part_id, &encinfo,
+				"x-evolution/message/x-secure-button", cancellable);
+			g_string_truncate (part_id, len);
+		}
 
-	return puri;
+		camel_cipher_validity_free (valid);
+	}
+
+	/* TODO: Make sure when we finalize this part, it is zero'd out */
+	g_object_unref (opart);
+	g_object_unref (context);
 }
 
-/**
- * em_format_push_level:
- * @emf:
- *
- * This is used to build a hierarchy of visible PURI objects based on
- * the structure of the message.  Used by multipart/alternative formatter.
- *
- * FIXME: This could probably also take a uri so it can automatically update
- * the base location.
- **/
-void
-em_format_push_level (EMFormat *emf)
+/* RFC 2046 */
+static void
+emf_parse_multipart_mixed (EMFormat *emf,
+                                CamelMimePart *part,
+                                GString *part_id,
+                                EMFormatParserInfo *info,
+                                GCancellable *cancellable)
 {
-	GNode *node;
+	CamelMultipart *mp;
+	gint i, nparts, len;
 
-	g_return_if_fail (EM_IS_FORMAT (emf));
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	node = g_node_new (g_queue_new ());
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	if (emf->pending_uri_tree == NULL)
-		emf->pending_uri_tree = node;
-	else
-		g_node_append (emf->pending_uri_tree, node);
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
-	emf->pending_uri_level = node;
-}
+	len = part_id->len;
+	nparts = camel_multipart_get_number (mp);
+	for (i = 0; i < nparts; i++) {
+		CamelMimePart *subpart;
 
-/**
- * em_format_pull_level:
- * @emf:
- *
- * Drop a level of visibility back to the parent.  Note that
- * no PURI values are actually freed.
- **/
-void
-em_format_pull_level (EMFormat *emf)
-{
-	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (emf->pending_uri_level != NULL);
+		subpart = camel_multipart_get_part (mp, i);
 
-	emf->pending_uri_level = emf->pending_uri_level->parent;
+		g_string_append_printf(part_id, ".mixed.%d", i);
+		em_format_parse_part (emf, subpart, part_id, info, cancellable);
+		g_string_truncate (part_id, len);
+	}
 }
 
-/**
- * em_format_find_visible_puri:
- * @emf:
- * @uri:
- *
- * Search for a PURI based on the visibility defined by :push_level()
- * and :pull_level().
- *
- * Return value:
- **/
-EMFormatPURI *
-em_format_find_visible_puri (EMFormat *emf,
-                             const gchar *uri)
+static void
+emf_parse_multipart_signed (EMFormat *emf,
+                                  CamelMimePart *part,
+                                  GString *part_id,
+                                  EMFormatParserInfo *info,
+                                  GCancellable *cancellable)
 {
-	GNode *node;
-
-	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
-	g_return_val_if_fail (uri != NULL, NULL);
+	CamelMimePart *cpart;
+	CamelMultipartSigned *mps;
+	CamelCipherContext *cipher = NULL;
+	guint32 validity_type;
 
-	node = emf->pending_uri_level;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	while (node != NULL) {
-		GQueue *queue = node->data;
-		GList *link;
+	mps = (CamelMultipartSigned *) camel_medium_get_content ((CamelMedium *) part);
+	if (!CAMEL_IS_MULTIPART_SIGNED (mps)
+	    || (cpart = camel_multipart_get_part ((CamelMultipart *) mps,
+		CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
+		em_format_format_error (
+			emf, _("Could not parse MIME message. "
+			"Displaying as source."));
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
-		link = g_queue_peek_head_link (queue);
+	/* FIXME: Should be done via a plugin interface */
+	/* FIXME: duplicated in em-format-html-display.c */
+	if (mps->protocol) {
+#ifdef ENABLE_SMIME
+		if (g_ascii_strcasecmp("application/x-pkcs7-signature", mps->protocol) == 0
+		    || g_ascii_strcasecmp("application/pkcs7-signature", mps->protocol) == 0) {
+			cipher = camel_smime_context_new (emf->priv->session);
+			validity_type = EM_FORMAT_VALIDITY_FOUND_SMIME;
+		} else
+#endif
+			if (g_ascii_strcasecmp("application/pgp-signature", mps->protocol) == 0) {
+				cipher = camel_gpg_context_new (emf->priv->session);
+				validity_type = EM_FORMAT_VALIDITY_FOUND_PGP;
+			}
+	}
 
-		while (link != NULL) {
-			EMFormatPURI *pw = link->data;
+	if (cipher == NULL) {
+		em_format_format_error(emf, _("Unsupported signature format"));
+		emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
+	} else {
+		CamelCipherValidity *valid;
+		GError *local_error = NULL;
 
-			if (g_strcmp0 (pw->uri, uri) == 0)
-				return pw;
+		valid = camel_cipher_context_verify_sync (
+			cipher, part, cancellable, &local_error);
+		if (valid == NULL) {
+			em_format_format_error (
+				emf, local_error->message ?
+				_("Error verifying signature") :
+				_("Unknown error verifying signature"));
+			if (local_error->message != NULL)
+				em_format_format_error (
+					emf, "%s",
+					local_error->message);
+			g_clear_error (&local_error);
+			emf_parse_multipart_mixed (emf, part, part_id,info,  cancellable);
+		} else {
+			gint i, nparts, len = part_id->len;
+			gboolean secured;
+
+			EMFormatParserInfo signinfo = {
+					info->handler,
+					info->validity_type | validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED,
+			};
+
+			if (info->validity)
+				camel_cipher_validity_envelope (valid, info->validity);
+			signinfo.validity = valid;
+
+			nparts = camel_multipart_get_number (CAMEL_MULTIPART (mps));
+			secured = FALSE;
+			for (i = 0; i < nparts; i++) {
+				CamelMimePart *subpart;
+				subpart = camel_multipart_get_part (CAMEL_MULTIPART (mps), i);
+
+				g_string_append_printf(part_id, ".signed.%d", i);
+				em_format_parse_part (emf, subpart, part_id, &signinfo, cancellable);
+				g_string_truncate (part_id, len);
+
+				if (!secured)
+					secured = is_secured (subpart);
+			}
 
-			if (g_strcmp0 (pw->cid, uri) == 0)
-				return pw;
+			/* Add a widget with details about the encryption, but only when
+			 * the encrypted isn't itself secured, in that case it has created
+			 * the button itself */
+			if (!secured) {
+				g_string_append (part_id, ".signed.button");
+				em_format_parse_part_as (emf, part, part_id, &signinfo,
+				"x-evolution/message/x-secure-button", cancellable);
+				g_string_truncate (part_id, len);
+			}
 
-			link = g_list_next (link);
+			camel_cipher_validity_free (valid);
 		}
-
-		node = node->parent;
 	}
 
-	return NULL;
-}
-
-/**
- * em_format_find_puri:
- * @emf:
- * @uri:
- *
- * Search for a PURI based on a uri.  Both the content-id
- * and content-location are checked.
- *
- * Return value:
- **/
-EMFormatPURI *
-em_format_find_puri (EMFormat *emf,
-                     const gchar *uri)
-{
-	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
-	g_return_val_if_fail (uri != NULL, NULL);
-
-	g_return_val_if_fail (emf->pending_uri_table != NULL, NULL);
-
-	return g_hash_table_lookup (emf->pending_uri_table, uri);
+	g_object_unref (cipher);
 }
 
-/**
- * em_format_clear_puri_tree:
- * @emf:
- *
- * For use by implementors to clear out the message structure
- * data.
- **/
-void
-em_format_clear_puri_tree (EMFormat *emf)
+/* RFC 2046 */
+static void
+emf_parse_multipart_digest (EMFormat *emf,
+                                 CamelMimePart *part,
+                                 GString *part_id,
+                                 EMFormatParserInfo *info,
+                                 GCancellable *cancellable)
 {
-	if (emf->pending_uri_table == NULL)
-		emf->pending_uri_table =
-			g_hash_table_new (g_str_hash, g_str_equal);
+	CamelMultipart *mp;
+	gint i, nparts, len;
 
-	else {
-		g_hash_table_remove_all (emf->pending_uri_table);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-		g_node_traverse (
-			emf->pending_uri_tree,
-			G_IN_ORDER, G_TRAVERSE_ALL, -1,
-			(GNodeTraverseFunc) emf_clear_puri_node, NULL);
-		g_node_destroy (emf->pending_uri_tree);
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-		emf->pending_uri_tree = NULL;
-		emf->pending_uri_level = NULL;
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
 	}
 
-	em_format_push_level (emf);
-}
+	len = part_id->len;
+	nparts = camel_multipart_get_number (mp);
+	for (i = 0; i < nparts; i++) {
+		CamelMimePart *subpart;
+		CamelContentType *ct;
+		gchar *cts;
+		const EMFormatHandler *handler;
 
-/* use mime_type == NULL  to force showing as application/octet-stream */
-void
-em_format_part_as (EMFormat *emf,
-                   CamelStream *stream,
-                   CamelMimePart *part,
-                   const gchar *mime_type,
-                   GCancellable *cancellable)
-{
-	const EMFormatHandler *handle = NULL;
-	const gchar *snoop_save = emf->snoop_mime_type, *tmp;
-	CamelURL *base_save = emf->base, *base = NULL;
-	gchar *basestr = NULL;
+		subpart = camel_multipart_get_part (mp, i);
 
-	d(printf("format_part_as()\n"));
+		if (!subpart)
+			continue;
 
-	emf->snoop_mime_type = NULL;
+		g_string_append_printf(part_id, ".digest.%d", i);
 
-	/* RFC 2110, we keep track of content-base, and absolute content-location headers
-	 * This is actually only required for html, but, *shrug * */
-	tmp = camel_medium_get_header((CamelMedium *)part, "Content-Base");
-	if (tmp == NULL) {
-		tmp = camel_mime_part_get_content_location (part);
-		if (tmp && strchr (tmp, ':') == NULL)
-			tmp = NULL;
-	} else {
-		tmp = basestr = camel_header_location_decode (tmp);
-	}
-	d(printf("content-base is '%s'\n", tmp?tmp:"<unset>"));
-	if (tmp
-	    && (base = camel_url_new (tmp, NULL))) {
-		emf->base = base;
-		d(printf("Setting content base '%s'\n", tmp));
-	}
-	g_free (basestr);
-
-	if (mime_type != NULL) {
-		gboolean is_fallback = FALSE;
-		if (g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) {
-			emf->snoop_mime_type = mime_type = em_format_snoop_type (part);
-			if (mime_type == NULL)
-				mime_type = "application/octet-stream";
+		ct = camel_mime_part_get_content_type (subpart);
+		/* According to RFC this shouldn't happen, but who knows... */
+		if (ct && !camel_content_type_is (ct, "message", "rfc822")) {
+			cts = camel_content_type_simple (ct);
+			em_format_parse_part_as (emf, part, part_id, info, cts, cancellable);
+			g_free (cts);
+			g_string_truncate (part_id, len);
+			continue;
 		}
 
-		handle = em_format_find_handler (emf, mime_type);
-		if (handle == NULL) {
-			handle = em_format_fallback_handler (emf, mime_type);
-			is_fallback = TRUE;
-		}
+		handler = em_format_find_handler (emf, "message/rfc822");
+		if (handler && handler->parse_func)
+			handler->parse_func (emf, subpart, part_id, info, cancellable);
 
-		if (handle != NULL
-		    && !em_format_is_attachment (emf, part)) {
-			d(printf("running handler for type '%s'\n", mime_type));
-			handle->handler (
-				emf, stream, part, handle,
-				cancellable, is_fallback);
-			goto finish;
-		}
-		d(printf("this type is an attachment? '%s'\n", mime_type));
-	} else {
-		mime_type = "application/octet-stream";
+		g_string_truncate (part_id, len);
 	}
+}
 
-	EM_FORMAT_GET_CLASS (emf)->format_attachment (
-		emf, stream, part, mime_type, handle, cancellable);
+/* RFC 2387 */
+static void
+emf_parse_multipart_related (EMFormat *emf,
+                                    CamelMimePart *part,
+                                    GString *part_id,
+                                    EMFormatParserInfo *info,
+                                    GCancellable *cancellable)
+{
+	CamelMultipart *mp;
+	CamelMimePart *body_part, *display_part = NULL;
+	gint i, nparts, partidlen, displayid = 0;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
+
+	display_part = get_related_display_part (part, &displayid);
+
+	if (display_part == NULL) {
+		emf_parse_multipart_mixed (
+			emf, part, part_id, info, cancellable);
+		return;
+	}
 
-finish:
-	emf->base = base_save;
-	emf->snoop_mime_type = snoop_save;
+	/* The to-be-displayed part goes first */
+	partidlen = part_id->len;
+	g_string_append_printf(part_id, ".related.%d", displayid);
+	em_format_parse_part (emf, display_part, part_id, info, cancellable);
+	g_string_truncate (part_id, partidlen);
 
-	if (base)
-		camel_url_free (base);
+	/* Process the related parts */
+	nparts = camel_multipart_get_number (mp);
+	for (i = 0; i < nparts; i++) {
+		body_part = camel_multipart_get_part (mp, i);
+		if (body_part != display_part) {
+			g_string_append_printf(part_id, ".related.%d", i);
+			em_format_parse_part (emf, body_part, part_id, info, cancellable);
+			g_string_truncate (part_id, partidlen);
+		}
+	}
 }
 
-void
-em_format_part (EMFormat *emf,
-                CamelStream *stream,
-                CamelMimePart *mime_part,
-                GCancellable *cancellable)
+static void
+emf_parse_message_deliverystatus (EMFormat *emf,
+                                      CamelMimePart *part,
+                                      GString *part_id,
+                                      EMFormatParserInfo *info,
+                                      GCancellable *cancellable)
 {
-	gchar *mime_type;
-	CamelDataWrapper *dw;
+	EMFormatPURI *puri;
+	gint len;
 
-	dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
-	mime_type = camel_data_wrapper_get_mime_type (dw);
-	if (mime_type != NULL) {
-		camel_strdown (mime_type);
-		em_format_part_as (
-			emf, stream, mime_part, mime_type, cancellable);
-		g_free (mime_type);
-	} else
-		em_format_part_as (
-			emf, stream, mime_part, "text/plain", cancellable);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	len = part_id->len;
+	g_string_append (part_id, ".deliverystatus");
+
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emf_write_text;
+	puri->mime_type = g_strdup ("text/html");
+	puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+	puri->validity_type = info->validity_type;
+
+	g_string_truncate (part_id, len);
+
+	em_format_add_puri (emf, puri);
 }
 
-/**
- * em_format_format_clone:
- * @emf: an #EMFormat
- * @folder: a #CamelFolder or %NULL
- * @uid: Message UID or %NULL
- * @msg: a #CamelMimeMessage or %NULL
- * @emfsource: Used as a basis for user-altered layout, e.g. inline viewed
- * attachments.
- * @cancellable: a #GCancellable, or %NULL
- *
- * Format a message @msg.  If @emfsource is non NULL, then the status of
- * inlined expansion and so forth is copied direction from @emfsource.
- *
- * By passing the same value for @emf and @emfsource, you can perform
- * a display refresh, or it can be used to generate an identical layout,
- * e.g. to print what the user has shown inline.
- **/
-void
-em_format_format_clone (EMFormat *emf,
-                        CamelFolder *folder,
-                        const gchar *uid,
-                        CamelMimeMessage *message,
-                        EMFormat *source,
-                        GCancellable *cancellable)
+static void
+emf_parse_inlinepgp_signed (EMFormat *emf,
+                                  CamelMimePart *ipart,
+                                  GString *part_id,
+                            EMFormatParserInfo *info,
+                            GCancellable *cancellable)
 {
-	EMFormatClass *class;
+	CamelStream *filtered_stream;
+	CamelMimeFilterPgp *pgp_filter;
+	CamelContentType *content_type;
+	CamelCipherContext *cipher;
+	CamelCipherValidity *valid;
+	CamelDataWrapper *dw;
+	CamelMimePart *opart;
+	CamelStream *ostream;
+	gchar *type;
+	gint len;
+	GError *local_error = NULL;
+	EMFormatParserInfo signinfo;
+	GByteArray *ba;
 
-	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (folder == NULL || CAMEL_IS_FOLDER (folder));
-	g_return_if_fail (message == NULL || CAMEL_IS_MIME_MESSAGE (message));
-	g_return_if_fail (source == NULL || EM_IS_FORMAT (source));
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_if_fail (class->format_clone != NULL);
+	if (!ipart) {
+		em_format_format_error(emf, _("Unknown error verifying signature"));
+		return;
+	}
 
-	class->format_clone (emf, folder, uid, message, source, cancellable);
+	cipher = camel_gpg_context_new (emf->priv->session);
+	/* Verify the signature of the message */
+	valid = camel_cipher_context_verify_sync (
+		cipher, ipart, cancellable, &local_error);
+	if (!valid) {
+		em_format_format_error (
+			emf, local_error->message ?
+			_("Error verifying signature") :
+			_("Unknown error verifying signature"));
+		if (local_error->message)
+			em_format_format_error (
+				emf, "%s", local_error->message);
+		emf_parse_source (emf, ipart, part_id, info, cancellable);
+		/* XXX I think this will loop:
+		 * em_format_part_as(emf, stream, part, "text/plain"); */
+		g_clear_error (&local_error);
+		g_object_unref (cipher);
+		return;
+	}
+
+	/* Setup output stream */
+	ostream = camel_stream_mem_new ();
+	filtered_stream = camel_stream_filter_new (ostream);
+
+	/* Add PGP header / footer filter */
+	pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new ();
+	camel_stream_filter_add (
+		CAMEL_STREAM_FILTER (filtered_stream),
+		CAMEL_MIME_FILTER (pgp_filter));
+	g_object_unref (pgp_filter);
+
+	/* Pass through the filters that have been setup */
+	dw = camel_medium_get_content ((CamelMedium *) ipart);
+	camel_data_wrapper_decode_to_stream_sync (
+		dw, (CamelStream *) filtered_stream, cancellable, NULL);
+	camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL);
+	g_object_unref (filtered_stream);
+
+	/* Create a new text/plain MIME part containing the signed
+	 * content preserving the original part's Content-Type params. */
+	content_type = camel_mime_part_get_content_type (ipart);
+	type = camel_content_type_format (content_type);
+	content_type = camel_content_type_decode (type);
+	g_free (type);
+
+	g_free (content_type->type);
+	content_type->type = g_strdup ("text");
+	g_free (content_type->subtype);
+	content_type->subtype = g_strdup ("plain");
+	type = camel_content_type_format (content_type);
+	camel_content_type_unref (content_type);
+
+	ba = camel_stream_mem_get_byte_array ((CamelStreamMem *) ostream);
+	opart = camel_mime_part_new ();
+	camel_mime_part_set_content (opart, (gchar *) ba->data, ba->len, type);
+	g_free (type);
+
+	if (info->validity)
+		camel_cipher_validity_envelope (valid, info->validity);
+
+	/* Pass it off to the real formatter */
+	len = part_id->len;
+	g_string_append (part_id, ".inlinepgp_signed");
+	signinfo.handler = info->handler;
+	signinfo.validity_type = info->validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED | EM_FORMAT_VALIDITY_FOUND_PGP;
+	signinfo.validity = valid;
+	em_format_parse_part (emf, opart, part_id, &signinfo, cancellable);
+	g_string_truncate (part_id, len);
+
+	/* Add a widget with details about the encryption, but only when
+	 * the encrypted isn't itself secured, in that case it has created
+	 * the button itself */
+	if (!is_secured (opart)) {
+		g_string_append (part_id, ".inlinepgp_signed.button");
+		em_format_parse_part_as (emf, opart, part_id, &signinfo,
+			"x-evolution/message/x-secure-button", cancellable);
+		g_string_truncate (part_id, len);
+	}
+
+	/* Clean Up */
+	camel_cipher_validity_free (valid);
+	g_object_unref (dw);
+	g_object_unref (opart);
+	g_object_unref (ostream);
+	g_object_unref (cipher);
 }
 
-void
-em_format_format (EMFormat *emf,
-                  CamelFolder *folder,
-                  const gchar *uid,
-                  CamelMimeMessage *message,
-                  GCancellable *cancellable)
+static void
+emf_parse_inlinepgp_encrypted (EMFormat *emf,
+                               CamelMimePart *ipart,
+                               GString *part_id,
+                               EMFormatParserInfo *info,
+                               GCancellable *cancellable)
 {
-	/* em_format_format_clone() will check the arguments. */
-	em_format_format_clone (emf, folder, uid, message, NULL, cancellable);
+	CamelCipherContext *cipher;
+	CamelCipherValidity *valid;
+	CamelMimePart *opart;
+	CamelDataWrapper *dw;
+	gchar *mime_type;
+	gint len;
+	GError *local_error = NULL;
+	EMFormatParserInfo encinfo;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	cipher = camel_gpg_context_new (emf->priv->session);
+	opart = camel_mime_part_new ();
+
+	/* Decrypt the message */
+	valid = camel_cipher_context_decrypt_sync (
+		cipher, ipart, opart, cancellable, &local_error);
+
+	if (!valid) {
+		em_format_format_error (
+			emf, _("Could not parse PGP message: "));
+		if (local_error->message != NULL)
+			em_format_format_error (
+				emf, "%s", local_error->message);
+		else
+			em_format_format_error (
+				emf, _("Unknown error"));
+		emf_parse_source (emf, ipart, part_id, info, cancellable);
+		/* XXX I think this will loop:
+		 * em_format_part_as(emf, stream, part, "text/plain"); */
+
+		g_clear_error (&local_error);
+		g_object_unref (cipher);
+		g_object_unref (opart);
+		return;
+	}
+
+	dw = camel_medium_get_content ((CamelMedium *) opart);
+	mime_type = camel_data_wrapper_get_mime_type (dw);
+
+	/* this ensures to show the 'opart' as inlined, if possible */
+	if (mime_type && g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) {
+		const gchar *snoop = em_format_snoop_type (opart);
+
+		if (snoop)
+			camel_data_wrapper_set_mime_type (dw, snoop);
+	}
+
+	preserve_charset_in_content_type (ipart, opart);
+	g_free (mime_type);
+
+	if (info->validity)
+		camel_cipher_validity_envelope (valid, info->validity);
+
+	/* Pass it off to the real formatter */
+	len = part_id->len;
+	g_string_append (part_id, ".inlinepgp_encrypted");
+	encinfo.handler = info->handler;
+	encinfo.validity_type = info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP;
+	encinfo.validity = valid;
+	em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+	g_string_truncate (part_id, len);
+
+	/* Add a widget with details about the encryption, but only when
+	 * the encrypted isn't itself secured, in that case it has created
+	 * the button itself */
+	if (!is_secured (opart)) {
+		g_string_append (part_id, ".inlinepgp_encrypted.button");
+		em_format_parse_part_as (emf, opart, part_id, &encinfo,
+			"x-evolution/message/x-secure-button", cancellable);
+		g_string_truncate (part_id, len);
+	}
+
+	/* Clean Up */
+	camel_cipher_validity_free (valid);
+	g_object_unref (opart);
+	g_object_unref (cipher);
 }
 
-static gboolean
-format_redraw_idle_cb (EMFormat *emf)
+static void
+emf_parse_message (EMFormat *emf,
+                   CamelMimePart *part,
+                   GString *part_id,
+                   EMFormatParserInfo *info,
+                   GCancellable *cancellable)
 {
-	emf->priv->redraw_idle_id = 0;
+	/* Headers */
+	info->force_handler = TRUE;
+	em_format_parse_part_as (emf, part, part_id, info,
+			"x-evolution/message/headers", cancellable);
 
-	/* FIXME Not passing a GCancellable here. */
-	em_format_format_clone (
-		emf, emf->folder, emf->uid, emf->message, emf, NULL);
+	/* Anything that comes between headers and message body */
+	info->force_handler = TRUE;
+	em_format_parse_part_as (emf, part, part_id, info,
+			"x-evolution/message/post-headers", cancellable);
 
-	return FALSE;
+	/* Begin parsing the message */
+	info->force_handler = FALSE;
+	em_format_parse_part (emf, part, part_id, info, cancellable);
 }
 
-void
-em_format_queue_redraw (EMFormat *emf)
+static void
+emf_parse_headers (EMFormat *emf,
+                   CamelMimePart *part,
+                   GString *part_id,
+                   EMFormatParserInfo *info,
+                   GCancellable *cancellable)
 {
-	g_return_if_fail (EM_IS_FORMAT (emf));
+	EMFormatPURI *puri;
+	gint len;
+
+	len = part_id->len;
+	g_string_append (part_id, ".headers");
+
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = info->handler->write_func;
+	puri->mime_type = g_strdup ("text/html");
+	em_format_add_puri (emf, puri);
 
-	if (emf->priv->redraw_idle_id == 0)
-		emf->priv->redraw_idle_id = g_idle_add (
-			(GSourceFunc) format_redraw_idle_cb, emf);
+	g_string_truncate (part_id, len);
 }
 
-/**
- * em_format_set_mode:
- * @emf:
- * @type:
- *
- * Set display mode, EM_FORMAT_MODE_SOURCE, EM_FORMAT_MODE_ALLHEADERS,
- * or EM_FORMAT_MODE_NORMAL.
- **/
-void
-em_format_set_mode (EMFormat *emf,
-                    EMFormatMode mode)
+static void
+emf_parse_post_headers (EMFormat *emf,
+                        CamelMimePart *part,
+                        GString *part_id,
+                        EMFormatParserInfo *info,
+                        GCancellable *cancellable)
 {
-	g_return_if_fail (EM_IS_FORMAT (emf));
+	/* Add attachment bar */
+	info->force_handler = TRUE;
+	em_format_parse_part_as (emf, part, part_id, info,
+		"x-evolution/message/attachment-bar", cancellable);
+}
+
+static void
+emf_parse_source (EMFormat *emf,
+                  CamelMimePart *part,
+                  GString *part_id,
+                  EMFormatParserInfo *info,
+                  GCancellable *cancellable)
+{
+	EMFormatPURI *puri;
+	gint len;
 
-	if (emf->mode == mode)
+	if (g_cancellable_is_cancelled (cancellable))
 		return;
 
-	emf->mode = mode;
+	len = part_id->len;
+	g_string_append (part_id, ".source");
+
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = info->handler->write_func;
+	puri->mime_type = g_strdup ("text/html");
+	g_string_truncate (part_id, len);
 
-	/* force redraw if type changed afterwards */
-	if (emf->message != NULL)
-		em_format_queue_redraw (emf);
+	em_format_add_puri (emf, puri);
 }
 
-/**
- * em_format_set_charset:
- * @emf:
- * @charset:
- *
- * set override charset on formatter.  message will be redisplayed if
- * required.
- **/
+/**************************************************************************/
+
 void
-em_format_set_charset (EMFormat *emf,
-                       const gchar *charset)
+em_format_empty_writer (EMFormat *emf,
+                        EMFormatPURI *puri,
+                        CamelStream *stream,
+                        EMFormatWriterInfo *info,
+                        GCancellable *cancellable)
 {
-	if ((emf->charset && charset && g_ascii_strcasecmp (emf->charset, charset) == 0)
-	    || (emf->charset == NULL && charset == NULL)
-	    || (emf->charset == charset))
-		return;
-
-	g_free (emf->charset);
-	emf->charset = g_strdup (charset);
+	/* DO NOTHING */
+}
 
-	if (emf->message)
-		em_format_queue_redraw (emf);
+static void
+emf_write_error (EMFormat * emf,
+                 EMFormatPURI * puri,
+                 CamelStream * stream,
+                 EMFormatWriterInfo * info,
+                 GCancellable * cancellable)
+{
+	camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) puri->part,
+		stream, cancellable, NULL);
 }
 
-/**
- * em_format_set_default_charset:
- * @emf:
- * @charset:
- *
- * Set the fallback, default system charset to use when no other charsets
- * are present.  Message will be redisplayed if required (and sometimes
- * redisplayed when it isn't).
- **/
-void
-em_format_set_default_charset (EMFormat *emf,
-                               const gchar *charset)
+static void
+emf_write_text (EMFormat *emf,
+                EMFormatPURI *puri,
+                CamelStream *stream,
+                EMFormatWriterInfo *info,
+                GCancellable *cancellable)
 {
-	if ((emf->default_charset && charset &&
-	    g_ascii_strcasecmp (emf->default_charset, charset) == 0)
-	    || (emf->default_charset == NULL && charset == NULL)
-	    || (emf->default_charset == charset))
+	CamelContentType *ct;
+
+	ct = camel_mime_part_get_content_type (puri->part);
+	if (!camel_content_type_is (ct, "text", "plain")) {
+		camel_stream_write_string (stream, _("Cannot proccess non-text mime/part"),
+			cancellable, NULL);
 		return;
+	}
 
-	g_free (emf->default_charset);
-	emf->default_charset = g_strdup (charset);
+	camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) puri->part,
+		stream, cancellable, NULL);
+}
 
-	if (emf->message && emf->charset == NULL)
-		em_format_queue_redraw (emf);
+static void
+emf_write_source (EMFormat *emf,
+                  EMFormatPURI *puri,
+                  CamelStream *stream,
+                  EMFormatWriterInfo *info,
+                  GCancellable *cancellable)
+{
+	GByteArray *ba;
+	gchar *data;
+
+	g_return_if_fail (EM_IS_FORMAT (emf));
+
+	ba = camel_data_wrapper_get_byte_array ((CamelDataWrapper *) puri->part);
+
+	data = g_strndup ((gchar *) ba->data, ba->len);
+	camel_stream_write_string (stream, data, cancellable, NULL);
+	g_free (data);
 }
 
-/**
- * em_format_clear_headers:
- * @emf:
- *
- * Clear the list of headers to be displayed.  This will force all headers to
- * be shown.
- **/
-void
-em_format_clear_headers (EMFormat *emf)
+/**************************************************************************/
+
+static gboolean
+emf_is_inline (EMFormat *emf,
+               const gchar *part_id,
+               CamelMimePart *mime_part,
+               const EMFormatHandler *handle)
 {
-	EMFormatHeader *eh;
+	//EMFormatCache *emfc;
+	const gchar *disposition;
+
+	if (handle == NULL)
+		return FALSE;
+
+	/* Some types need to override the disposition.
+	 * e.g. application/x-pkcs7-mime */
+	if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION)
+		return TRUE;
+
+	disposition = camel_mime_part_get_disposition (mime_part);
+	if (disposition != NULL)
+		return g_ascii_strcasecmp (disposition, "inline") == 0;
 
-	while ((eh = g_queue_pop_head (&emf->header_list)) != NULL)
-		g_free (eh);
+	/* Otherwise, use the default for this handler type. */
+	return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0;
 }
 
+/**************************************************************************/
+
+static EMFormatHandler type_handlers[] = {
+#ifdef ENABLE_SMIME
+		{ (gchar *) "application/x-pkcs7-mime", emf_parse_application_xpkcs7mime, 0, EM_FORMAT_HANDLER_INLINE_DISPOSITION },
+#endif
+		{ (gchar *) "application/mbox", emf_parse_application_mbox, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE },
+		{ (gchar *) "multipart/alternative", emf_parse_multipart_alternative, },
+		{ (gchar *) "multipart/appledouble", emf_parse_multipart_appledouble, },
+		{ (gchar *) "multipart/encrypted", emf_parse_multipart_encrypted, },
+		{ (gchar *) "multipart/mixed", emf_parse_multipart_mixed, },
+		{ (gchar *) "multipart/signed", emf_parse_multipart_signed, },
+		{ (gchar *) "multipart/related", emf_parse_multipart_related, },
+		{ (gchar *) "multipart/digest", emf_parse_multipart_digest, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE },
+		{ (gchar *) "multipart/*", emf_parse_multipart_mixed, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE },
+		{ (gchar *) "message/deliverystatus", emf_parse_message_deliverystatus, 0, },
+
+		/* Ignore PGP signature part */
+		{ (gchar *) "application/pgp-signature", em_format_empty_parser, },
+
+		/* Insert brokenly-named parts here */
+#ifdef ENABLE_SMIME
+		{ (gchar *) "application/pkcs7-mime", emf_parse_application_xpkcs7mime, 0, EM_FORMAT_HANDLER_INLINE_DISPOSITION },
+#endif
+
+		/* internal types */
+		{ (gchar *) "application/x-inlinepgp-signed", emf_parse_inlinepgp_signed, },
+		{ (gchar *) "application/x-inlinepgp-encrypted", emf_parse_inlinepgp_encrypted, },
+		{ (gchar *) "x-evolution/message", emf_parse_message, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE },
+		{ (gchar *) "x-evolution/message/headers", emf_parse_headers, },
+		{ (gchar *) "x-evolution/message/post-headers", emf_parse_post_headers, },
+		{ (gchar *) "x-evolution/message/source", emf_parse_source, emf_write_source },
+};
+
 /* note: also copied in em-mailer-prefs.c */
 static const struct {
 	const gchar *name;
@@ -1071,1338 +1232,1085 @@ static const struct {
 	{ N_("Face"), 0 },
 };
 
-/**
- * em_format_default_headers:
- * @emf:
- *
- * Set the headers to show to the default list.
- *
- * From, Reply-To, To, Cc, Bcc, Subject and Date.
- **/
-void
-em_format_default_headers (EMFormat *emf)
+static void
+em_format_get_property (GObject *object,
+                        guint property_id,
+                        GValue *value,
+                        GParamSpec *pspec)
 {
-	gint ii;
+	EMFormat *emf = EM_FORMAT (object);
 
-	em_format_clear_headers (emf);
+	switch (property_id) {
+		case PROP_CHARSET:
+			g_value_set_string (
+					value, em_format_get_charset (emf));
+			return;
+		case PROP_DEFAULT_CHARSET:
+			g_value_set_string (
+					value, em_format_get_default_charset (emf));
+			return;
+		case PROP_COMPOSER:
+			g_value_set_boolean (
+					value, em_format_get_composer (emf));
+			return;
+		case PROP_BASE_URL:
+			g_value_set_object (
+					value, em_format_get_base_url (emf));
+			return;
+	}
 
-	for (ii = 0; ii < G_N_ELEMENTS (default_headers); ii++)
-		em_format_add_header (
-			emf, default_headers[ii].name,
-			default_headers[ii].flags);
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
-/**
- * em_format_add_header:
- * @emf:
- * @name: The name of the header, as it will appear during output.
- * @flags: EM_FORMAT_HEAD_* defines to control display attributes.
- *
- * Add a specific header to show.  If any headers are set, they will
- * be displayed in the order set by this function.  Certain known
- * headers included in this list will be shown using special
- * formatting routines.
- **/
-void
-em_format_add_header (EMFormat *emf,
-                      const gchar *name,
-                      guint32 flags)
+static void
+em_format_set_property (GObject *object,
+                        guint property_id,
+                        const GValue *value,
+                        GParamSpec *pspec)
 {
-	EMFormatHeader *h;
+	EMFormat *emf = EM_FORMAT (object);
+
+	switch (property_id) {
+		case PROP_CHARSET:
+			em_format_set_charset (emf,
+					g_value_get_string (value));
+			return;
+		case PROP_DEFAULT_CHARSET:
+			em_format_set_default_charset (emf,
+					g_value_get_string (value));
+			return;
+		case PROP_COMPOSER:
+			em_format_set_composer (emf,
+					g_value_get_boolean (value));
+			return;
+		case PROP_BASE_URL:
+			em_format_set_base_url (emf,
+					g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
-	h = g_malloc (sizeof (*h) + strlen (name));
-	h->flags = flags;
-	strcpy (h->name, name);
-	g_queue_push_tail (&emf->header_list, h);
 }
 
-/**
- * em_format_is_attachment:
- * @emf:
- * @part: Part to check.
- *
- * Returns true if the part is an attachment.
- *
- * A part is not considered an attachment if it is a
- * multipart, or a text part with no filename.  It is used
- * to determine if an attachment header should be displayed for
- * the part.
- *
- * Content-Disposition is not checked.
- *
- * Return value: TRUE/FALSE
- **/
-gint
-em_format_is_attachment (EMFormat *emf,
-                         CamelMimePart *part)
+static void
+em_format_finalize (GObject *object)
 {
-	/*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
-	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+	EMFormat *emf = EM_FORMAT (object);
 
-	if (!dw)
-		return 0;
+	if (emf->message_uid) {
+		g_free (emf->message_uid);
+		emf->message_uid = NULL;
+	}
 
-	/*printf("checking is attachment %s/%s\n", ct->type, ct->subtype);*/
-	return !(camel_content_type_is (dw->mime_type, "multipart", "*")
-		 || camel_content_type_is (
-			dw->mime_type, "application", "x-pkcs7-mime")
-		 || camel_content_type_is (
-			dw->mime_type, "application", "pkcs7-mime")
-		 || camel_content_type_is (
-			dw->mime_type, "application", "x-inlinepgp-signed")
-		 || camel_content_type_is (
-			dw->mime_type, "application", "x-inlinepgp-encrypted")
-		 || camel_content_type_is (
-			dw->mime_type, "x-evolution", "evolution-rss-feed")
-		 || camel_content_type_is (dw->mime_type, "text", "calendar")
-		 || camel_content_type_is (dw->mime_type, "text", "x-calendar")
-		 || (camel_content_type_is (dw->mime_type, "text", "*")
-		     && camel_mime_part_get_filename (part) == NULL));
+	if (emf->uri_base) {
+		g_free (emf->uri_base);
+		emf->uri_base = NULL;
+	}
+
+	if (emf->message) {
+		g_object_unref (emf->message);
+		emf->message = NULL;
+	}
+
+	if (emf->folder) {
+		g_object_unref (emf->folder);
+		emf->folder = NULL;
+	}
+
+	if (emf->mail_part_table) {
+		/* This will destroy all the EMFormatPURI objects stored
+		 * inside!!!! */
+		g_hash_table_destroy (emf->mail_part_table);
+		emf->mail_part_table = NULL;
+	}
+
+	if (emf->mail_part_list) {
+		g_list_free (emf->mail_part_list);
+		emf->mail_part_list = NULL;
+	}
+
+	if (emf->priv->base_url) {
+		camel_url_free (emf->priv->base_url);
+		emf->priv->base_url = NULL;
+	}
+
+	if (emf->priv->session) {
+		g_object_unref (emf->priv->session);
+		emf->priv->session = NULL;
+	}
+
+	if (emf->priv->charset) {
+		g_free (emf->priv->charset);
+		emf->priv->charset = NULL;
+	}
+
+	em_format_clear_headers (emf);
+
+	/* Chain up to parent's finalize() method */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-/**
- * em_format_is_inline:
- * @emf:
- * @part:
- * @part_id: format->part_id part id of this part.
- * @handle: handler for this part
- *
- * Returns true if the part should be displayed inline.  Any part with
- * a Content-Disposition of inline, or if the @handle has a default
- * inline set, will be shown inline.
- *
- * :set_inline() called on the same part will override any calculated
- * value.
- *
- * Return value:
- **/
-gboolean
-em_format_is_inline (EMFormat *emf,
-                     const gchar *part_id,
-                     CamelMimePart *mime_part,
-                     const EMFormatHandler *handle)
+static void
+em_format_base_init (EMFormatClass *klass)
 {
-	EMFormatClass *class;
+	gint i;
 
-	g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
-	g_return_val_if_fail (part_id != NULL, FALSE);
-	g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), FALSE);
+	klass->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_val_if_fail (class->is_inline != NULL, FALSE);
+	for (i = 0; i < G_N_ELEMENTS (type_handlers); i++) {
+		g_hash_table_insert (klass->type_handlers,
+				type_handlers[i].mime_type,
+				&type_handlers[i]);
+	}
+}
+
+static void
+em_format_class_init (EMFormatClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	g_type_class_add_private (klass, sizeof (EMFormatPrivate));
+
+	klass->is_inline = emf_is_inline;
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = em_format_finalize;
+	object_class->get_property = em_format_get_property;
+	object_class->set_property = em_format_set_property;
+
+	g_object_class_install_property (object_class,
+			PROP_CHARSET,
+			g_param_spec_string ("charset",
+					NULL,
+					NULL,
+					NULL,
+					G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+			PROP_DEFAULT_CHARSET,
+			g_param_spec_string ("default-charset",
+					NULL,
+					NULL,
+					NULL,
+					G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+			PROP_COMPOSER,
+			g_param_spec_boolean ("composer",
+					NULL,
+					NULL,
+					FALSE,
+					G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+			PROP_BASE_URL,
+			g_param_spec_pointer ("base-url",
+					NULL,
+					NULL,
+					G_PARAM_READWRITE));
+
+	signals[REDRAW_REQUESTED] = g_signal_new (
+		"redraw-requested",
+		G_TYPE_FROM_CLASS (klass),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EMFormatClass, redraw_requested),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE,0);
+}
+
+static void
+mail_part_table_item_free (gpointer data)
+{
+	GList *iter = data;
+	EMFormatPURI *puri = iter->data;
 
-	return class->is_inline (emf, part_id, mime_part, handle);
+	em_format_puri_free (puri);
 }
 
-/**
- * em_format_set_inline:
- * @emf:
- * @part_id: id of part
- * @state:
- *
- * Force the attachment @part to be expanded or hidden explictly to match
- * @state.  This is used only to record the change for a redraw or
- * cloned layout render and does not force a redraw.
- **/
-void
-em_format_set_inline (EMFormat *emf,
-                      const gchar *part_id,
-                      gint state)
+static void
+em_format_init (EMFormat *emf)
 {
-	EMFormatCache *emfc;
+	EShell *shell;
+	EShellSettings *shell_settings;
 
-	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (part_id != NULL);
+	emf->priv = G_TYPE_INSTANCE_GET_PRIVATE (emf,
+			EM_TYPE_FORMAT, EMFormatPrivate);
 
-	emfc = g_hash_table_lookup (emf->inline_table, part_id);
-	if (emfc == NULL) {
-		emfc = emf_insert_cache (emf, part_id);
-	} else if (emfc->state != INLINE_UNSET && (emfc->state & 1) == state)
-		return;
+	emf->message = NULL;
+	emf->folder = NULL;
+	emf->mail_part_list = NULL;
+	emf->mail_part_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+			NULL, (GDestroyNotify) mail_part_table_item_free);
+	/* No need to free the key, because it's owned and free'd by the PURI */
 
-	emfc->state = state ? INLINE_ON : INLINE_OFF;
+	shell = e_shell_get_default ();
+	shell_settings = e_shell_get_shell_settings (shell);
 
-	if (emf->message)
-		em_format_queue_redraw (emf);
-}
+	emf->priv->last_error = 0;
 
-void
-em_format_format_attachment (EMFormat *emf,
-                             CamelStream *stream,
-                             CamelMimePart *mime_part,
-                             const gchar *mime_type,
-                             const EMFormatHandler *info,
-                             GCancellable *cancellable)
-{
-	EMFormatClass *class;
+	emf->priv->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
+	g_return_if_fail (emf->priv->session);
 
-	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (CAMEL_IS_STREAM (stream));
-	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
-	g_return_if_fail (mime_type != NULL);
-	g_return_if_fail (info != NULL);
+	g_object_ref (emf->priv->session);
+
+	em_format_default_headers (emf);
+}
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_if_fail (class->format_attachment != NULL);
+EMFormat *
+em_format_new (void)
+{
+	EMFormat *emf = g_object_new (EM_TYPE_FORMAT, NULL);
 
-	class->format_attachment (
-		emf, stream, mime_part, mime_type, info, cancellable);
+	return emf;
 }
 
-void
-em_format_format_error (EMFormat *emf,
-                        CamelStream *stream,
-                        const gchar *format,
-                        ...)
+GType
+em_format_get_type (void)
 {
-	EMFormatClass *class;
-	gchar *errmsg;
-	va_list ap;
+	static GType type = 0;
 
-	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (CAMEL_IS_STREAM (stream));
-	g_return_if_fail (format != NULL);
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+				sizeof (EMFormatClass),
+				(GBaseInitFunc) em_format_base_init,
+				(GBaseFinalizeFunc) NULL,
+				(GClassInitFunc) em_format_class_init,
+				(GClassFinalizeFunc) NULL,
+				NULL,	/* class_data */
+				sizeof (EMFormat),
+				0,		/* n_preallocs */
+				(GInstanceInitFunc) em_format_init,
+				NULL	/* value_table */
+		};
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_if_fail (class->format_error != NULL);
+		type = g_type_register_static (
+				G_TYPE_OBJECT, "EMFormat", &type_info, 0);
+	}
 
-	va_start (ap, format);
-	errmsg = g_strdup_vprintf (format, ap);
-	class->format_error (emf, stream, errmsg);
-	g_free (errmsg);
-	va_end (ap);
+	return type;
 }
 
 void
-em_format_format_secure (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *mime_part,
-                         CamelCipherValidity *valid,
-                         GCancellable *cancellable)
+em_format_set_charset (EMFormat *emf,
+                       const gchar *charset)
 {
-	EMFormatClass *class;
-
 	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (CAMEL_IS_STREAM (stream));
-	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
-	g_return_if_fail (valid != NULL);
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_if_fail (class->format_secure != NULL);
+	if (emf->priv->charset)
+		g_free (emf->priv->charset);
 
-	class->format_secure (emf, stream, mime_part, valid, cancellable);
+	emf->priv->charset = g_strdup (charset);
 
-	if (emf->valid_parent == NULL && emf->valid != NULL) {
-		camel_cipher_validity_free (emf->valid);
-		emf->valid = NULL;
-	}
+	g_object_notify (G_OBJECT (emf), "charset");
 }
 
-void
-em_format_format_source (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *mime_part,
-                         GCancellable *cancellable)
+const gchar *
+em_format_get_charset (EMFormat *emf)
 {
-	EMFormatClass *class;
-
-	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (CAMEL_IS_STREAM (stream));
-	g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
-
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_if_fail (class->format_source != NULL);
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
 
-	class->format_source (emf, stream, mime_part, cancellable);
+	return emf->priv->charset;
 }
 
-gboolean
-em_format_busy (EMFormat *emf)
+void
+em_format_set_default_charset (EMFormat *emf,
+                               const gchar *charset)
 {
-	EMFormatClass *class;
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
+	if (emf->priv->default_charset)
+		g_free (emf->priv->default_charset);
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_val_if_fail (class->busy != NULL, FALSE);
+	emf->priv->default_charset = g_strdup (charset);
 
-	return class->busy (emf);
+	g_object_notify (G_OBJECT (emf), "default-charset");
 }
 
-/* should this be virtual? */
-void
-em_format_format_content (EMFormat *emf,
-                          CamelStream *stream,
-                          CamelMimePart *part,
-                          GCancellable *cancellable)
+const gchar *
+em_format_get_default_charset (EMFormat *emf)
 {
-	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
 
-	if (camel_content_type_is (dw->mime_type, "text", "*"))
-		em_format_format_text (
-			emf, stream, (CamelDataWrapper *) part, cancellable);
-	else
-		camel_data_wrapper_decode_to_stream_sync (
-			dw, stream, cancellable, NULL);
+	return emf->priv->default_charset;
 }
 
-/**
- * em_format_format_content:
- * @emf:
- * @stream: Where to write the converted text
- * @part: Part whose container is to be formatted
- * @cancellable: optional #GCancellable object, or %NULL
- *
- * Decode/output a part's content to @stream.
- **/
 void
-em_format_format_text (EMFormat *emf,
-                       CamelStream *stream,
-                       CamelDataWrapper *dw,
-                       GCancellable *cancellable)
+em_format_set_composer (EMFormat *emf,
+                        gboolean composer)
 {
-	CamelStream *filter_stream;
-	CamelMimeFilter *filter;
-	const gchar *charset = NULL;
-	CamelMimeFilterWindows *windows = NULL;
-	CamelStream *mem_stream = NULL;
-	gsize size;
-	gsize max;
-	GSettings *settings;
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	if (emf->charset) {
-		charset = emf->charset;
-	} else if (dw->mime_type
-		   && (charset = camel_content_type_param (dw->mime_type, "charset"))
-		   && g_ascii_strncasecmp(charset, "iso-8859-", 9) == 0) {
-		CamelStream *null;
+	if (emf->priv->composer && composer)
+		return;
 
-		/* Since a few Windows mailers like to claim they sent
-		 * out iso-8859-# encoded text when they really sent
-		 * out windows-cp125#, do some simple sanity checking
-		 * before we move on... */
+	emf->priv->composer = composer;
 
-		null = camel_stream_null_new ();
-		filter_stream = camel_stream_filter_new (null);
-		g_object_unref (null);
+	g_object_notify (G_OBJECT (emf), "composer");
+}
 
-		windows = (CamelMimeFilterWindows *) camel_mime_filter_windows_new (charset);
-		camel_stream_filter_add (
-			CAMEL_STREAM_FILTER (filter_stream),
-			CAMEL_MIME_FILTER (windows));
+gboolean
+em_format_get_composer (EMFormat *emf)
+{
+	g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
 
-		camel_data_wrapper_decode_to_stream_sync (
-			dw, (CamelStream *) filter_stream, cancellable, NULL);
-		camel_stream_flush ((CamelStream *) filter_stream, cancellable, NULL);
-		g_object_unref (filter_stream);
+	return emf->priv->composer;
+}
 
-		charset = camel_mime_filter_windows_real_charset (windows);
-	} else if (charset == NULL) {
-		charset = emf->default_charset;
-	}
+void
+em_format_set_base_url (EMFormat *emf,
+                        CamelURL *url)
+{
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (url);
 
-	mem_stream = (CamelStream *) camel_stream_mem_new ();
-	filter_stream = camel_stream_filter_new (mem_stream);
+	if (emf->priv->base_url)
+		camel_url_free (emf->priv->base_url);
 
-	if ((filter = camel_mime_filter_charset_new (charset, "UTF-8"))) {
-		camel_stream_filter_add (
-			CAMEL_STREAM_FILTER (filter_stream),
-			CAMEL_MIME_FILTER (filter));
-		g_object_unref (filter);
-	}
+	emf->priv->base_url = camel_url_copy (url);
 
-	max = -1;
+	g_object_notify (G_OBJECT (emf), "base-url");
+}
 
-	settings = g_settings_new ("org.gnome.evolution.mail");
-	if (g_settings_get_boolean (settings, "force-message-limit")) {
-		max = g_settings_get_int (settings, "message-text-part-limit");
-		if (max == 0)
-			max = -1;
-	}
-	g_object_unref (settings);
+void
+em_format_set_base_url_string (EMFormat *emf,
+                               const gchar *url_string)
+{
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (url_string && *url_string);
 
-	size = camel_data_wrapper_decode_to_stream_sync (
-		emf->mode == EM_FORMAT_MODE_SOURCE ?
-			(CamelDataWrapper *) dw :
-			camel_medium_get_content ((CamelMedium *) dw),
-		(CamelStream *) filter_stream, cancellable, NULL);
-	camel_stream_flush ((CamelStream *) filter_stream, cancellable, NULL);
-	g_object_unref (filter_stream);
+	if (emf->priv->base_url)
+		camel_url_free (emf->priv->base_url);
 
-	g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
+	emf->priv->base_url = camel_url_new (url_string, NULL);
 
-	if (max == -1 || size == -1 || size < (max * 1024) || emf->composer) {
-		camel_stream_write_to_stream (
-			mem_stream, (CamelStream *) stream, cancellable, NULL);
-		camel_stream_flush ((CamelStream *) stream, cancellable, NULL);
-	} else {
-		EM_FORMAT_GET_CLASS (emf)->format_optional (
-			emf, stream, (CamelMimePart *) dw,
-			mem_stream, cancellable);
-	}
+	g_object_notify (G_OBJECT (emf), "base-url");
+}
 
-	if (windows)
-		g_object_unref (windows);
+CamelURL *
+em_format_get_base_url (EMFormat *emf)
+{
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
 
-	g_object_unref (mem_stream);
+	return emf->priv->base_url;
 }
 
 /**
- * em_format_describe_part:
- * @part:
- * @mimetype:
- *
- * Generate a simple textual description of a part, @mime_type represents the
- * the content.
+ * em_format_clear_headers:
+ * @emf:
  *
- * Return value:
+ * Clear the list of headers to be displayed.  This will force all headers to
+ * be shown.
  **/
-gchar *
-em_format_describe_part (CamelMimePart *part,
-                         const gchar *mime_type)
+void
+em_format_clear_headers (EMFormat *emf)
 {
-	GString *stext;
-	const gchar *filename, *description;
-	gchar *content_type, *desc;
-
-	stext = g_string_new("");
-	content_type = g_content_type_from_mime_type (mime_type);
-	desc = g_content_type_get_description (
-		content_type != NULL ? content_type : mime_type);
-	g_free (content_type);
-	g_string_append_printf (
-		stext, _("%s attachment"), desc ? desc : mime_type);
-	g_free (desc);
-
-	filename = camel_mime_part_get_filename (part);
-	description = camel_mime_part_get_description (part);
-
-	if (!filename || !*filename) {
-		CamelDataWrapper *content;
-
-		content = camel_medium_get_content (CAMEL_MEDIUM (part));
+	EMFormatHeader *eh;
 
-		if (CAMEL_IS_MIME_MESSAGE (content))
-			filename = camel_mime_message_get_subject (
-				CAMEL_MIME_MESSAGE (content));
-	}
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	if (filename != NULL && *filename != '\0') {
-		gchar *basename = g_path_get_basename (filename);
-		g_string_append_printf (stext, " (%s)", basename);
-		g_free (basename);
+	while ((eh = g_queue_pop_head (&emf->header_list)) != NULL) {
+		em_format_header_free (eh);
 	}
 
-	if (description != NULL && *description != '\0' &&
-		g_strcmp0 (filename, description) != 0)
-		g_string_append_printf (stext, ", \"%s\"", description);
-
-	return g_string_free (stext, FALSE);
 }
 
-static void
-add_validity_found (EMFormat *emf,
-                    CamelCipherValidity *valid)
+void
+em_format_default_headers (EMFormat *emf)
 {
-	g_return_if_fail (emf != NULL);
-
-	if (!valid)
-		return;
+	gint ii;
 
-	if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
-		emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SIGNED;
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE)
-		emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_ENCRYPTED;
+	/* Set the default headers */
+	em_format_clear_headers (emf);
+	for (ii = 0; ii < G_N_ELEMENTS (default_headers); ii++)
+		em_format_add_header (
+			emf, default_headers[ii].name, NULL,
+			default_headers[ii].flags);
 }
 
-/* ********************************************************************** */
-
-static void
-preserve_charset_in_content_type (CamelMimePart *ipart,
-                                  CamelMimePart *opart)
-{
-	CamelDataWrapper *data_wrapper;
-	CamelContentType *content_type;
-	const gchar *charset;
-
-	g_return_if_fail (ipart != NULL);
-	g_return_if_fail (opart != NULL);
-
-	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
-	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
-
-	if (content_type == NULL)
-		return;
-
-	charset = camel_content_type_param (content_type, "charset");
-
-	if (charset == NULL || *charset == '\0')
-		return;
+/**
+ * em_format_add_header:
+ * @emf:
+ * @name: The name of the header, as it will appear during output.
+ * @value: Value of the header. Can be NULL.
+ * @flags: EM_FORMAT_HEAD_* defines to control display attributes.
+ *
+ * Add a specific header to show.  If any headers are set, they will
+ * be displayed in the order set by this function.  Certain known
+ * headers included in this list will be shown using special
+ * formatting routines.
+ **/
+void
+em_format_add_header (EMFormat *emf,
+                      const gchar *name,
+                      const gchar *value,
+                      guint32 flags)
+{
+	EMFormatHeader *h;
 
-	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
-	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (name && *name);
 
-	camel_content_type_set_param (content_type, "charset", charset);
+	h = em_format_header_new (name, value);
+	h->flags = flags;
+	g_queue_push_tail (&emf->header_list, h);
 }
 
-#ifdef ENABLE_SMIME
-static void
-emf_application_xpkcs7mime (EMFormat *emf,
-                            CamelStream *stream,
-                            CamelMimePart *part,
-                            const EMFormatHandler *info,
-                            GCancellable *cancellable,
-                            gboolean is_fallback)
+void
+em_format_add_header_struct (EMFormat *emf,
+                             EMFormatHeader *header)
 {
-	CamelCipherContext *context;
-	CamelMimePart *opart;
-	CamelCipherValidity *valid;
-	EMFormatCache *emfc;
-	GError *local_error = NULL;
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (header && header->name);
 
-	/* should this perhaps run off a key of ".secured" ? */
-	emfc = g_hash_table_lookup (emf->inline_table, emf->part_id->str);
-	if (emfc && emfc->valid) {
-		em_format_format_secure (
-			emf, stream, emfc->secured,
-			camel_cipher_validity_clone (emfc->valid),
-			cancellable);
-		return;
-	}
+	em_format_add_header (emf, header->name, header->value, header->flags);
+}
 
-	context = camel_smime_context_new (emf->session);
+void
+em_format_remove_header (EMFormat * emf,
+                         const gchar *name,
+                         const gchar *value)
+{
+	GList *iter = NULL;
 
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
-		EM_FORMAT_VALIDITY_FOUND_SMIME;
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (name && *name);
 
-	opart = camel_mime_part_new ();
-	valid = camel_cipher_context_decrypt_sync (
-		context, part, opart, cancellable, &local_error);
-	preserve_charset_in_content_type (part, opart);
-	if (valid == NULL) {
-		em_format_format_error (
-			emf, stream, "%s",
-			local_error->message ? local_error->message :
-			_("Could not parse S/MIME message: Unknown error"));
-		g_clear_error (&local_error);
+	iter = g_queue_peek_head_link (&emf->header_list);
+	while (iter) {
+		EMFormatHeader *header = iter->data;
 
-		em_format_part_as (emf, stream, part, NULL, cancellable);
-	} else {
-		if (emfc == NULL)
-			emfc = emf_insert_cache (emf, emf->part_id->str);
+		if (!header->value || !*header->value) {
+			GList *next = iter->next;
+			if (g_strcmp0 (name, header->name) == 0)
+				g_queue_delete_link (&emf->header_list, iter);
 
-		emfc->valid = camel_cipher_validity_clone (valid);
-		g_object_ref ((emfc->secured = opart));
+			iter = next;
+			continue;
+		}
+
+		if (value && *value) {
+			if ((g_strcmp0 (name, header->name) == 0) &&
+			    (g_strcmp0 (value, header->value) == 0))
+				break;
+		} else {
+			if (g_strcmp0 (name, header->name) == 0)
+				break;
+		}
 
-		add_validity_found (emf, valid);
-		em_format_format_secure (
-			emf, stream, opart, valid, cancellable);
+		iter = iter->next;
 	}
 
-	g_object_unref (opart);
-	g_object_unref (context);
+	if (iter) {
+		em_format_header_free (iter->data);
+		g_queue_delete_link (&emf->header_list, iter);
+	}
 }
-#endif
 
-/* RFC 1740 */
-static void
-emf_multipart_appledouble (EMFormat *emf,
-                           CamelStream *stream,
-                           CamelMimePart *part,
-                           const EMFormatHandler *info,
-                           GCancellable *cancellable,
-                           gboolean is_fallback)
+void
+em_format_remove_header_struct (EMFormat * emf,
+                                const EMFormatHeader * header)
 {
-	CamelMultipart *mp;
-	CamelMimePart *mime_part;
-	gint len;
-
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
-	}
-
-	mime_part = camel_multipart_get_part (mp, 1);
-	if (mime_part) {
-		/* try the data fork for something useful, doubtful but who knows */
-		len = emf->part_id->len;
-		g_string_append_printf(emf->part_id, ".appledouble.1");
-		em_format_part (emf, stream, mime_part, cancellable);
-		g_string_truncate (emf->part_id, len);
-	} else
-		em_format_format_source (emf, stream, part, cancellable);
+	g_return_if_fail (header);
 
+	em_format_remove_header (emf, header->name, header->value);
 }
 
-/* RFC ??? */
-static void
-emf_multipart_mixed (EMFormat *emf,
-                     CamelStream *stream,
-                     CamelMimePart *part,
-                     const EMFormatHandler *info,
-                     GCancellable *cancellable,
-                     gboolean is_fallback)
+void
+em_format_add_puri (EMFormat *emf,
+                    EMFormatPURI *puri)
 {
-	CamelMultipart *mp;
-	gint i, nparts, len;
+	GList *item;
 
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (puri != NULL);
 
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
-	}
+	emf->mail_part_list = g_list_append (emf->mail_part_list, puri);
+	item = g_list_last (emf->mail_part_list);
 
-	len = emf->part_id->len;
-	nparts = camel_multipart_get_number (mp);
-	for (i = 0; i < nparts; i++) {
-		part = camel_multipart_get_part (mp, i);
-		g_string_append_printf(emf->part_id, ".mixed.%d", i);
-		em_format_part (emf, stream, part, cancellable);
-		g_string_truncate (emf->part_id, len);
-	}
-}
+	g_hash_table_insert (emf->mail_part_table,
+			puri->uri, item);
 
-static gboolean	related_display_part_is_attachment
-						(EMFormat *emf,
-						 CamelMimePart *part);
+        d(printf("Added PURI %s\n", puri->uri));
+}
 
-/* RFC 1740 */
-static void
-emf_multipart_alternative (EMFormat *emf,
-                           CamelStream *stream,
-                           CamelMimePart *part,
-                           const EMFormatHandler *info,
-                           GCancellable *cancellable,
-                           gboolean is_fallback)
+EMFormatPURI *
+em_format_find_puri (EMFormat *emf,
+                     const gchar *id)
 {
-	CamelMultipart *mp;
-	gint i, nparts, bestid = 0;
-	CamelMimePart *best = NULL;
-
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+	GList *list_iter;
 
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
-	}
+	/* First handle CIDs... */
+	if (g_str_has_prefix (id, "CID:") || g_str_has_prefix (id, "cid:")) {
+		GHashTableIter iter;
+		gpointer key, value;
 
-	/* as per rfc, find the last part we know how to display */
-	nparts = camel_multipart_get_number (mp);
-	for (i = 0; i < nparts; i++) {
-		CamelDataWrapper *data_wrapper;
-		CamelContentType *type;
-		CamelStream *null_stream;
-		gchar *mime_type;
-		gsize content_size;
+		g_hash_table_iter_init (&iter, emf->mail_part_table);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			EMFormatPURI *puri = ((GList *) value)->data;
+			if (g_strcmp0 (puri->cid, id) == 0)
+				return puri;
+		}
 
-		/* is it correct to use the passed in *part here? */
-		part = camel_multipart_get_part (mp, i);
+		return NULL;
+	}
 
-		if (part == NULL)
-			continue;
+	list_iter = g_hash_table_lookup (emf->mail_part_table, id);
+	if (list_iter)
+		return list_iter->data;
 
-		/* This may block even though the stream does not.
-		 * XXX Pretty inefficient way to test if the MIME part
-		 *     is empty.  Surely there's a quicker way? */
-		null_stream = camel_stream_null_new ();
-		data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (part));
-		camel_data_wrapper_decode_to_stream_sync (
-			data_wrapper, null_stream, cancellable, NULL);
-		content_size = CAMEL_STREAM_NULL (null_stream)->written;
-		g_object_unref (null_stream);
+	return NULL;
+}
 
-		if (content_size == 0)
-			continue;
+void
+em_format_class_add_handler (EMFormatClass *emfc,
+                               EMFormatHandler *handler)
+{
+	EMFormatHandler *old_handler;
 
-		type = camel_mime_part_get_content_type (part);
-		mime_type = camel_content_type_simple (type);
+	g_return_if_fail (EM_IS_FORMAT_CLASS (emfc));
+	g_return_if_fail (handler);
 
-		camel_strdown (mime_type);
+	old_handler = g_hash_table_lookup (
+			emfc->type_handlers, handler->mime_type);
 
-		/*if (want_plain && !strcmp (mime_type, "text/plain"))
-		  return part;*/
+	handler->old = old_handler;
 
-		if (!em_format_is_attachment (emf, part) &&
-		    (!camel_content_type_is (type, "multipart", "related") ||
-		    !related_display_part_is_attachment (emf, part)) &&
-		    (em_format_find_handler (emf, mime_type)
-		    || (best == NULL && em_format_fallback_handler (emf, mime_type)))) {
-			best = part;
-			bestid = i;
-		}
+	/* If parse_func or write_func of the new handler is not set,
+	 * use function from the old handler (if it exists).
+	 * This way we can assign a new write_func for to an existing
+	 * parse_func */
+	if (old_handler && handler->parse_func == NULL) {
+		handler->parse_func = old_handler->parse_func;
+	}
 
-		g_free (mime_type);
+	if (old_handler && handler->write_func == NULL) {
+		handler->write_func = old_handler->write_func;
 	}
 
-	if (best) {
-		gint len = emf->part_id->len;
+	g_hash_table_insert (emfc->type_handlers,
+			handler->mime_type, handler);
+}
 
-		g_string_append_printf(emf->part_id, ".alternative.%d", bestid);
-		em_format_part (emf, stream, best, cancellable);
-		g_string_truncate (emf->part_id, len);
-	} else
-		emf_multipart_mixed (
-			emf, stream, part, info, cancellable, is_fallback);
+void
+em_format_class_remove_handler (EMFormatClass *emfc,
+                                EMFormatHandler *handler)
+{
+	g_return_if_fail (EM_IS_FORMAT_CLASS (emfc));
+	g_return_if_fail (handler);
+
+	g_hash_table_remove (emfc->type_handlers, handler->mime_type);
 }
 
-static void
-emf_multipart_encrypted (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *part,
-                         const EMFormatHandler *info,
-                         GCancellable *cancellable,
-                         gboolean is_fallback)
+const EMFormatHandler *
+em_format_find_handler (EMFormat *emf,
+                        const gchar *mime_type)
 {
-	CamelCipherContext *context;
-	const gchar *protocol;
-	CamelMimePart *opart;
-	CamelCipherValidity *valid;
-	CamelMultipartEncrypted *mpe;
-	EMFormatCache *emfc;
-	GError *local_error = NULL;
+	EMFormatClass *emfc;
+	gchar *s;
+	const EMFormatHandler *handler;
 
-	/* should this perhaps run off a key of ".secured" ? */
-	emfc = g_hash_table_lookup (emf->inline_table, emf->part_id->str);
-	if (emfc && emfc->valid) {
-		em_format_format_secure (
-			emf, stream, emfc->secured,
-			camel_cipher_validity_clone (emfc->valid),
-			cancellable);
-		return;
-	}
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+	g_return_val_if_fail (mime_type && *mime_type, NULL);
 
-	mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part);
-	if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) {
-		em_format_format_error (
-			emf, stream, _("Could not parse MIME message. "
-			"Displaying as source."));
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
-	}
+	emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf);
 
-	/* Currently we only handle RFC2015-style PGP encryption. */
-	protocol = camel_content_type_param (
-		((CamelDataWrapper *)mpe)->mime_type, "protocol");
-	if (protocol == NULL || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) {
-		em_format_format_error (
-			emf, stream, _("Unsupported encryption "
-			"type for multipart/encrypted"));
-		em_format_part_as (
-			emf, stream, part,
-			"multipart/mixed", cancellable);
-		return;
-	}
+	s = g_ascii_strdown (mime_type, -1);
 
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
-		EM_FORMAT_VALIDITY_FOUND_PGP;
+	handler = g_hash_table_lookup (
+			emfc->type_handlers, s);
 
-	context = camel_gpg_context_new (emf->session);
-	opart = camel_mime_part_new ();
-	valid = camel_cipher_context_decrypt_sync (
-		context, part, opart, cancellable, &local_error);
-	preserve_charset_in_content_type (part, opart);
-	if (valid == NULL) {
-		em_format_format_error (
-			emf, stream, local_error->message ?
-			_("Could not parse PGP/MIME message") :
-			_("Could not parse PGP/MIME message: Unknown error"));
-		if (local_error->message != NULL)
-			em_format_format_error (
-				emf, stream, "%s", local_error->message);
-		g_clear_error (&local_error);
+	g_free (s);
 
-		em_format_part_as (
-			emf, stream, part,
-			"multipart/mixed", cancellable);
-	} else {
-		if (emfc == NULL)
-			emfc = emf_insert_cache (emf, emf->part_id->str);
+	return handler;
+}
+
+/**
+ * em_format_fallback_handler:
+ * @emf:
+ * @mime_type:
+ *
+ * Try to find a format handler based on the major type of the @mime_type.
+ *
+ * The subtype is replaced with "*" and a lookup performed.
+ *
+ * Return value:
+ **/
+const EMFormatHandler *
+em_format_fallback_handler (EMFormat *emf,
+                            const gchar *mime_type)
+{
+	gchar *mime, *s;
 
-		emfc->valid = camel_cipher_validity_clone (valid);
-		g_object_ref ((emfc->secured = opart));
+	s = strchr (mime_type, '/');
+	if (s == NULL)
+		mime = (gchar *) mime_type;
+	else {
+		gsize len = (s - mime_type) + 1;
 
-		add_validity_found (emf, valid);
-		em_format_format_secure (
-			emf, stream, opart, valid, cancellable);
+		mime = g_alloca (len + 2);
+		strncpy (mime, mime_type, len);
+		strcpy(mime+len, "*");
 	}
 
-	/* TODO: Make sure when we finalize this part, it is zero'd out */
-	g_object_unref (opart);
-	g_object_unref (context);
+	return em_format_find_handler (emf, mime);
 }
 
-static CamelMimePart *
-get_related_display_part (CamelMimePart *part,
-                          gint *out_displayid)
+void
+em_format_parse (EMFormat *emf,
+                 CamelMimeMessage *message,
+                 CamelFolder *folder,
+                 GCancellable *cancellable)
 {
-	CamelMultipart *mp;
-	CamelMimePart *body_part, *display_part = NULL;
-	CamelContentType *content_type;
-	const gchar *start;
-	gint i, nparts, displayid = 0;
+	GString *part_id;
+	EMFormatPURI *puri;
+	EMFormatParserInfo info = { 0 };
 
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	if (!CAMEL_IS_MULTIPART (mp))
-		return NULL;
+	if (g_cancellable_is_cancelled (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;
+	if (message) {
+		g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
-		/* strip <>'s */
-		len = strlen (start) - 2;
-		start++;
+		if (emf->message)
+			g_object_unref (emf->message);
+		emf->message = g_object_ref (message);
+	}
 
-		for (i = 0; i < nparts; i++) {
-			body_part = camel_multipart_get_part (mp, i);
-			cid = camel_mime_part_get_content_id (body_part);
+	if (folder) {
+		g_return_if_fail (CAMEL_IS_FOLDER  (folder));
 
-			if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
-				display_part = body_part;
-				displayid = i;
-				break;
-			}
-		}
-	} else {
-		display_part = camel_multipart_get_part (mp, 0);
+		if (emf->folder)
+			g_object_unref (emf->folder);
+		emf->folder = g_object_ref (folder);
 	}
 
-	if (out_displayid)
-		*out_displayid = displayid;
+        /* Before the actual parsing starts, let child classes prepare themselves. */
+	if (EM_FORMAT_GET_CLASS (emf)->preparse)
+		EM_FORMAT_GET_CLASS (emf)->preparse (emf);
 
-	return display_part;
+	part_id = g_string_new (".message");
+
+	/* Create a special PURI with entire message */
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI),
+		(CamelMimePart *) emf->message, part_id->str);
+	puri->mime_type = g_strdup ("text/html");
+	em_format_add_puri (emf, puri);
+
+	info.force_handler = TRUE;
+	em_format_parse_part_as (emf, CAMEL_MIME_PART (emf->message), part_id, &info,
+			"x-evolution/message", cancellable);
+
+	g_string_free (part_id, TRUE);
 }
 
-static gboolean
-related_display_part_is_attachment (EMFormat *emf,
-                                    CamelMimePart *part)
+void
+em_format_write (EMFormat *emf,
+                 CamelStream *stream,
+                 EMFormatWriterInfo *info,
+                 GCancellable *cancellable)
 {
-	CamelMimePart *display_part;
+	EMFormatClass *emf_class;
 
-	display_part = get_related_display_part (part, NULL);
-	return display_part && em_format_is_attachment (emf, display_part);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (CAMEL_IS_STREAM (stream));
+
+	emf_class = EM_FORMAT_GET_CLASS (emf);
+	if (emf_class->write)
+		emf_class->write (emf, stream, info, cancellable);
 }
 
 static void
-emf_write_related (EMFormat *emf,
-                   CamelStream *stream,
-                   EMFormatPURI *puri,
-                   GCancellable *cancellable)
+emf_start_async_parser (GSimpleAsyncResult *result,
+                        GObject *object,
+                        GCancellable *cancellable)
 {
-	em_format_format_content (emf, stream, puri->part, cancellable);
-	camel_stream_close (stream, cancellable, NULL);
+	em_format_parse (EM_FORMAT (object), NULL, NULL, cancellable);
 }
 
-/* RFC 2387 */
-static void
-emf_multipart_related (EMFormat *emf,
-                       CamelStream *stream,
-                       CamelMimePart *part,
-                       const EMFormatHandler *info,
+void
+em_format_parse_async (EMFormat *emf,
+                       CamelMimeMessage *message,
+                       CamelFolder *folder,
                        GCancellable *cancellable,
-                       gboolean is_fallback)
+                       GAsyncReadyCallback callback,
+                       gpointer user_data)
 {
-	CamelMultipart *mp;
-	CamelMimePart *body_part, *display_part = NULL;
-	gint i, nparts, partidlen, displayid = 0;
-	gchar *oldpartid;
-	GList *link;
+	GSimpleAsyncResult *result;
 
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
+	if (g_cancellable_is_cancelled (cancellable))
 		return;
-	}
 
-	display_part = get_related_display_part (part, &displayid);
-
-	if (display_part == NULL) {
-		emf_multipart_mixed (
-			emf, stream, part, info, cancellable, is_fallback);
-		return;
-	}
+	if (message) {
+		g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
-	em_format_push_level (emf);
+		if (emf->message)
+			g_object_unref (emf->message);
 
-	oldpartid = g_strdup (emf->part_id->str);
-	partidlen = emf->part_id->len;
+		emf->message = g_object_ref (message);
 
-	/* queue up the parts for possible inclusion */
-	nparts = camel_multipart_get_number (mp);
-	for (i = 0; i < nparts; i++) {
-		body_part = camel_multipart_get_part (mp, i);
-		if (body_part != display_part) {
-			/* set the partid since add_puri uses it */
-			g_string_append_printf(emf->part_id, ".related.%d", i);
-			em_format_add_puri (
-				emf, sizeof (EMFormatPURI), NULL,
-				body_part, emf_write_related);
-			g_string_truncate (emf->part_id, partidlen);
-		}
 	}
 
-	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, NULL, NULL);
+	if (folder) {
+		g_return_if_fail (CAMEL_IS_FOLDER (folder));
 
-	link = g_queue_peek_head_link (emf->pending_uri_level->data);
-
-	while (link && link->next != NULL) {
-		EMFormatPURI *puri = link->data;
+		if (emf->folder)
+			g_object_unref (emf->folder);
 
-		if (puri->use_count == 0) {
-			if (puri->func == emf_write_related) {
-				g_string_printf(emf->part_id, "%s", puri->part_id);
-				em_format_part (
-					emf, stream, puri->part, cancellable);
-			}
-		}
+		emf->folder = g_object_ref (folder);
 
-		link = g_list_next (link);
 	}
 
-	g_string_printf(emf->part_id, "%s", oldpartid);
-	g_free (oldpartid);
-
-	em_format_pull_level (emf);
+	result = g_simple_async_result_new (G_OBJECT (emf), callback,
+					    user_data, em_format_parse_async);
+	g_simple_async_result_run_in_thread (result, emf_start_async_parser,
+					     G_PRIORITY_DEFAULT, cancellable);
 }
 
-static void
-emf_multipart_signed (EMFormat *emf,
-                      CamelStream *stream,
-                      CamelMimePart *part,
-                      const EMFormatHandler *info,
-                      GCancellable *cancellable,
-                      gboolean is_fallback)
+void
+em_format_parse_part_as (EMFormat *emf,
+                         CamelMimePart *part,
+                         GString *part_id,
+                         EMFormatParserInfo *info,
+                         const gchar *mime_type,
+                         GCancellable *cancellable)
 {
-	CamelMimePart *cpart;
-	CamelMultipartSigned *mps;
-	CamelCipherContext *cipher = NULL;
-	EMFormatCache *emfc;
-
-	/* should this perhaps run off a key of ".secured" ? */
-	emfc = g_hash_table_lookup (emf->inline_table, emf->part_id->str);
-	if (emfc && emfc->valid) {
-		em_format_format_secure (
-			emf, stream, emfc->secured,
-			camel_cipher_validity_clone (emfc->valid),
-			cancellable);
-		return;
-	}
+	const EMFormatHandler *handler;
+	const CamelContentDisposition *disposition;
+	EMFormatParserInfo ninfo = {
+		.handler = 0,
+		.validity_type = info ? info->validity_type : 0,
+		.validity = info ? info->validity : 0,
+		.force_handler = 0
+	};
+
+        /* Let everything that claims to be an attachment or inlined part to be parsed 
+         * as an attachment. The parser will decide how to display it. */
+	disposition = camel_mime_part_get_content_disposition (part);
+	if (!info->force_handler && disposition &&
+            (g_strcmp0 (disposition->disposition, "attachment") == 0)) {
+		ninfo.is_attachment = TRUE;
+                handler = em_format_find_handler (emf, "x-evolution/message/attachment");
+		ninfo.handler = handler;
+
+		if (handler && handler->parse_func)
+			handler->parse_func (emf, part, part_id, &ninfo, cancellable);
 
-	mps = (CamelMultipartSigned *) camel_medium_get_content ((CamelMedium *) part);
-	if (!CAMEL_IS_MULTIPART_SIGNED (mps)
-	    || (cpart = camel_multipart_get_part ((CamelMultipart *) mps,
-		CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
-		em_format_format_error (
-			emf, stream, _("Could not parse MIME message. "
-			"Displaying as source."));
-		em_format_format_source (emf, stream, part, cancellable);
 		return;
 	}
 
-	/* FIXME: Should be done via a plugin interface */
-	/* FIXME: duplicated in em-format-html-display.c */
-	if (mps->protocol) {
-#ifdef ENABLE_SMIME
-		if (g_ascii_strcasecmp ("application/x-pkcs7-signature", mps->protocol) == 0
-		    || g_ascii_strcasecmp ("application/pkcs7-signature", mps->protocol) == 0) {
-			cipher = camel_smime_context_new (emf->session);
-			emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SMIME;
-		} else
-#endif
-			if (g_ascii_strcasecmp ("application/pgp-signature", mps->protocol) == 0) {
-				cipher = camel_gpg_context_new (emf->session);
-				emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_PGP;
-			}
-	}
-
-	emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SIGNED;
-
-	if (cipher == NULL) {
-		em_format_format_error(emf, stream, _("Unsupported signature format"));
-		em_format_part_as (
-			emf, stream, part,
-			"multipart/mixed", cancellable);
+	handler = em_format_find_handler (emf, mime_type);
+	if (handler && handler->parse_func) {
+		ninfo.handler = handler;
+		handler->parse_func (emf, part, part_id, &ninfo, cancellable);
 	} else {
-		CamelCipherValidity *valid;
-		GError *local_error = NULL;
+		handler = em_format_find_handler (emf, "x-evolution/message/attachment");
+		ninfo.handler = handler;
 
-		valid = camel_cipher_context_verify_sync (
-			cipher, part, cancellable, &local_error);
-		if (valid == NULL) {
-			em_format_format_error (
-				emf, stream, local_error->message ?
-				_("Error verifying signature") :
-				_("Unknown error verifying signature"));
-			if (local_error->message != NULL)
-				em_format_format_error (
-					emf, stream, "%s",
-					local_error->message);
-			g_clear_error (&local_error);
+                /* When this fails, something is probably very wrong...*/
+		if (handler && handler->parse_func)
+			handler->parse_func (emf, part, part_id, &ninfo, cancellable);
+	}
+}
 
-			em_format_part_as (
-				emf, stream, part,
-				"multipart/mixed", cancellable);
-		} else {
-			if (emfc == NULL)
-				emfc = emf_insert_cache (emf, emf->part_id->str);
+void
+em_format_parse_part (EMFormat *emf,
+                      CamelMimePart *part,
+                      GString *part_id,
+                      EMFormatParserInfo *info,
+                      GCancellable *cancellable)
+{
+	CamelContentType *ct;
+	gchar *mime_type;
 
-			emfc->valid = camel_cipher_validity_clone (valid);
-			g_object_ref ((emfc->secured = cpart));
+	ct = camel_mime_part_get_content_type (part);
+	if (ct) {
+		mime_type = camel_content_type_simple (ct);
+	} else {
+		mime_type = (gchar *) "text/plain";
+	}
 
-			add_validity_found (emf, valid);
-			em_format_format_secure (
-				emf, stream, cpart, valid, cancellable);
-		}
+	em_format_parse_part_as (emf, part, part_id, info, mime_type, cancellable);
 
-		g_object_unref (cipher);
-	}
+	if (ct)
+		g_free (mime_type);
 }
 
-/* RFC 4155 */
-static void
-emf_application_mbox (EMFormat *emf,
-                      CamelStream *stream,
-                      CamelMimePart *mime_part,
-                      const EMFormatHandler *info,
-                      GCancellable *cancellable,
-                      gboolean is_fallback)
+gboolean
+em_format_is_inline (EMFormat *emf,
+                     const gchar *part_id,
+                     CamelMimePart *part,
+                     const EMFormatHandler *handler)
 {
-	const EMFormatHandler *handle;
-	CamelMimeParser *parser;
-	CamelStream *mem_stream;
-	camel_mime_parser_state_t state;
-
-	/* Extract messages from the application/mbox part and
-	 * render them as a flat list of messages. */
-
-	/* XXX If the mbox has multiple messages, maybe render them
-	 *     as a multipart/digest so each message can be expanded
-	 *     or collapsed individually.
-	 *
-	 *     See attachment_handler_mail_x_uid_list() for example. */
+	EMFormatClass *klass;
 
-	/* XXX This is based on em_utils_read_messages_from_stream().
-	 *     Perhaps refactor that function to return an array of
-	 *     messages instead of assuming we want to append them
-	 *     to a folder? */
+	g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
+	g_return_val_if_fail (part_id && *part_id, FALSE);
+	g_return_val_if_fail (CAMEL_IS_MIME_PART (part), FALSE);
+	g_return_val_if_fail (handler, FALSE);
 
-	handle = em_format_find_handler (emf, "x-evolution/message/rfc822");
-	g_return_if_fail (handle != NULL);
+	klass = EM_FORMAT_GET_CLASS (emf);
+	g_return_val_if_fail (klass->is_inline != NULL, FALSE);
 
-	parser = camel_mime_parser_new ();
-	camel_mime_parser_scan_from (parser, TRUE);
+	return klass->is_inline (emf, part_id, part, handler);
 
-	mem_stream = camel_stream_mem_new ();
-	camel_data_wrapper_decode_to_stream_sync (
-		camel_medium_get_content (CAMEL_MEDIUM (mime_part)),
-		mem_stream, NULL, NULL);
-	g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
-	camel_mime_parser_init_with_stream (parser, mem_stream, NULL);
-	g_object_unref (mem_stream);
+}
 
-	/* Extract messages from the mbox. */
-	state = camel_mime_parser_step (parser, NULL, NULL);
-	while (state == CAMEL_MIME_PARSER_STATE_FROM) {
-		CamelMimeMessage *message;
+void
+em_format_format_error (EMFormat *emf,
+                        const gchar *format,
+                        ...)
+{
+	EMFormatPURI *puri;
+	CamelMimePart *part;
+	const EMFormatHandler *handler;
+	gchar *errmsg;
+	gchar *uri;
+	va_list ap;
 
-		message = camel_mime_message_new ();
-		mime_part = CAMEL_MIME_PART (message);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (format != NULL);
 
-		if (!camel_mime_part_construct_from_parser_sync (
-			mime_part, parser, NULL, NULL)) {
-			g_object_unref (message);
-			break;
-		}
+	va_start (ap, format);
+	errmsg = g_strdup_vprintf (format, ap);
 
-		/* Render the message. */
-		handle->handler (
-			emf, stream, mime_part,
-			handle, cancellable, FALSE);
+	part = camel_mime_part_new ();
+	camel_mime_part_set_content (part, errmsg, strlen (errmsg), "text/plain");
+	g_free (errmsg);
+	va_end (ap);
 
-		g_object_unref (message);
+	handler = em_format_find_handler (emf, "x-evolution/error");
 
-		/* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
-		camel_mime_parser_step (parser, NULL, NULL);
+	emf->priv->last_error++;
+	uri = g_strdup_printf (".error.%d", emf->priv->last_error);
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, uri);
+	puri->mime_type = g_strdup ("text/html");
+	if (handler && handler->write_func)
+		puri->write_func = handler->write_func;
+	else
+		puri->write_func = emf_write_error;
 
-		state = camel_mime_parser_step (parser, NULL, NULL);
-	}
+	em_format_add_puri (emf, puri);
 
-	g_object_unref (parser);
+	g_free (uri);
+	g_object_unref (part);
 }
 
-static void
-emf_message_rfc822 (EMFormat *emf,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    const EMFormatHandler *info,
-                    GCancellable *cancellable,
-                    gboolean is_fallback)
+/**
+ * em_format_format_text:
+ * @emf:
+ * @stream: Where to write the converted text
+ * @part: Part whose container is to be formatted
+ * @cancellable: optional #GCancellable object, or %NULL
+ *
+ * Decode/output a part's content to @stream.
+ **/
+void
+em_format_format_text (EMFormat *emf,
+                       CamelStream *stream,
+                       CamelDataWrapper *dw,
+                       GCancellable *cancellable)
 {
-	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
-	const EMFormatHandler *handle;
-	gint len;
-	gchar *parent_message_part_id;
+	CamelStream *filter_stream;
+	CamelMimeFilter *filter;
+	const gchar *charset = NULL;
+	CamelMimeFilterWindows *windows = NULL;
+	CamelStream *mem_stream = NULL;
+	gsize size;
+	gsize max;
+	GSettings *settings;
 
-	if (!CAMEL_IS_MIME_MESSAGE (dw)) {
-		em_format_format_source (emf, stream, part, cancellable);
+	if (g_cancellable_is_cancelled (cancellable))
 		return;
-	}
-
-	parent_message_part_id = emf->current_message_part_id;
-	emf->current_message_part_id = g_strdup (emf->part_id->str);
 
-	len = emf->part_id->len;
-	g_string_append_printf(emf->part_id, ".rfc822");
-
-	handle = em_format_find_handler(emf, "x-evolution/message/rfc822");
-	if (handle)
-		handle->handler (
-			emf, stream, CAMEL_MIME_PART (dw),
-			handle, cancellable, FALSE);
+	if (emf->priv->charset) {
+		charset = emf->priv->charset;
+	} else if (dw->mime_type
+		   && (charset = camel_content_type_param (dw->mime_type, "charset"))
+		   && g_ascii_strncasecmp(charset, "iso-8859-", 9) == 0) {
+		CamelStream *null;
 
-	g_string_truncate (emf->part_id, len);
+		/* Since a few Windows mailers like to claim they sent
+		 * out iso-8859-# encoded text when they really sent
+		 * out windows-cp125#, do some simple sanity checking
+		 * before we move on... */
 
-	g_free (emf->current_message_part_id);
-	emf->current_message_part_id = parent_message_part_id;
-}
+		null = camel_stream_null_new ();
+		filter_stream = camel_stream_filter_new (null);
+		g_object_unref (null);
 
-static void
-emf_message_deliverystatus (EMFormat *emf,
-                            CamelStream *stream,
-                            CamelMimePart *part,
-                            const EMFormatHandler *info,
-                            GCancellable *cancellable,
-                            gboolean is_fallback)
-{
-	em_format_format_text (
-		emf, stream, (CamelDataWrapper *) part, cancellable);
-}
+		windows = (CamelMimeFilterWindows *) camel_mime_filter_windows_new (charset);
+		camel_stream_filter_add (
+			CAMEL_STREAM_FILTER (filter_stream),
+			CAMEL_MIME_FILTER (windows));
 
-static void
-emf_inlinepgp_signed (EMFormat *emf,
-                      CamelStream *stream,
-                      CamelMimePart *ipart,
-                      const EMFormatHandler *info,
-                      GCancellable *cancellable,
-                      gboolean is_fallback)
-{
-	CamelStream *filtered_stream;
-	CamelMimeFilterPgp *pgp_filter;
-	CamelContentType *content_type;
-	CamelCipherContext *cipher;
-	CamelCipherValidity *valid;
-	CamelDataWrapper *dw;
-	CamelMimePart *opart;
-	CamelStream *ostream;
-	gchar *type;
-	GError *local_error = NULL;
+		camel_data_wrapper_decode_to_stream_sync (
+			dw, (CamelStream *) filter_stream, cancellable, NULL);
+		camel_stream_flush ((CamelStream *) filter_stream, cancellable, NULL);
+		g_object_unref (filter_stream);
 
-	if (!ipart) {
-		em_format_format_error(emf, stream, _("Unknown error verifying signature"));
-		return;
+		charset = camel_mime_filter_windows_real_charset (windows);
+	} else if (charset == NULL) {
+		charset = emf->priv->default_charset;
 	}
 
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_SIGNED |
-		EM_FORMAT_VALIDITY_FOUND_PGP;
+	mem_stream = (CamelStream *) camel_stream_mem_new ();
+	filter_stream = camel_stream_filter_new (mem_stream);
 
-	cipher = camel_gpg_context_new (emf->session);
-	/* Verify the signature of the message */
-	valid = camel_cipher_context_verify_sync (
-		cipher, ipart, cancellable, &local_error);
-	if (!valid) {
-		em_format_format_error (
-			emf, stream, local_error->message ?
-			_("Error verifying signature") :
-			_("Unknown error verifying signature"));
-		if (local_error->message)
-			em_format_format_error (
-				emf, stream, "%s", local_error->message);
-		em_format_format_source (emf, stream, ipart, cancellable);
-		/* XXX I think this will loop:
-		 * em_format_part_as(emf, stream, part, "text/plain"); */
-		g_clear_error (&local_error);
-		g_object_unref (cipher);
-		return;
+	if ((filter = camel_mime_filter_charset_new (charset, "UTF-8"))) {
+		camel_stream_filter_add (
+			CAMEL_STREAM_FILTER (filter_stream),
+			CAMEL_MIME_FILTER (filter));
+		g_object_unref (filter);
 	}
 
-	/* Setup output stream */
-	ostream = camel_stream_mem_new ();
-	filtered_stream = camel_stream_filter_new (ostream);
-
-	/* Add PGP header / footer filter */
-	pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new ();
-	camel_stream_filter_add (
-		CAMEL_STREAM_FILTER (filtered_stream),
-		CAMEL_MIME_FILTER (pgp_filter));
-	g_object_unref (pgp_filter);
-
-	/* Pass through the filters that have been setup */
-	dw = camel_medium_get_content ((CamelMedium *) ipart);
-	camel_data_wrapper_decode_to_stream_sync (
-		dw, (CamelStream *) filtered_stream, NULL, NULL);
-	camel_stream_flush ((CamelStream *) filtered_stream, NULL, NULL);
-	g_object_unref (filtered_stream);
+	max = -1;
 
-	/* Create a new text/plain MIME part containing the signed
-	 * content preserving the original part's Content-Type params. */
-	content_type = camel_mime_part_get_content_type (ipart);
-	type = camel_content_type_format (content_type);
-	content_type = camel_content_type_decode (type);
-	g_free (type);
+	settings = g_settings_new ("org.gnome.evolution.mail");
+	if (g_settings_get_boolean (settings, "force-message-limit")) {
+		max = g_settings_get_int (settings, "message-text-part-limit");
+		if (max == 0)
+			max = -1;
+	}
+	g_object_unref (settings);
 
-	g_free (content_type->type);
-	content_type->type = g_strdup ("text");
-	g_free (content_type->subtype);
-	content_type->subtype = g_strdup ("plain");
-	type = camel_content_type_format (content_type);
-	camel_content_type_unref (content_type);
+	size = camel_data_wrapper_decode_to_stream_sync (
+			camel_medium_get_content ((CamelMedium *) dw),
+		(CamelStream *) filter_stream, cancellable, NULL);
+	camel_stream_flush ((CamelStream *) filter_stream, cancellable, NULL);
+	g_object_unref (filter_stream);
 
-	dw = camel_data_wrapper_new ();
-	camel_data_wrapper_construct_from_stream_sync (dw, ostream, NULL, NULL);
-	camel_data_wrapper_set_mime_type (dw, type);
-	g_free (type);
+	g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
 
-	opart = camel_mime_part_new ();
-	camel_medium_set_content ((CamelMedium *) opart, dw);
-	camel_data_wrapper_set_mime_type_field (
-		(CamelDataWrapper *) opart, dw->mime_type);
+	if (max == -1 || size == -1 || size < (max * 1024) || emf->priv->composer) {
+		camel_stream_write_to_stream (
+			mem_stream, (CamelStream *) stream, cancellable, NULL);
+		camel_stream_flush ((CamelStream *) mem_stream, cancellable, NULL);
+	} else {
+		/* Parse it as an attachment */
+		CamelMimePart *part = camel_mime_part_new ();
+		EMFormatParserInfo info = { 0 };
+		GString *part_id = g_string_new (".attachment");
+		camel_medium_set_content ((CamelMedium *) part, dw);
+
+		info.is_attachment = TRUE;
+		em_format_parse_part_as (emf, part, part_id, &info,
+			"x-evolution/message/attachment", cancellable);
+
+		g_string_free (part_id, TRUE);
+		g_object_unref (part);
+	}
 
-	add_validity_found (emf, valid);
-	/* Pass it off to the real formatter */
-	em_format_format_secure (emf, stream, opart, valid, cancellable);
+	if (windows)
+		g_object_unref (windows);
 
-	/* Clean Up */
-	g_object_unref (dw);
-	g_object_unref (opart);
-	g_object_unref (ostream);
-	g_object_unref (cipher);
+	g_object_unref (mem_stream);
 }
 
-static void
-emf_inlinepgp_encrypted (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *ipart,
-                         const EMFormatHandler *info,
-                         GCancellable *cancellable,
-                         gboolean is_fallback)
+/**
+ * em_format_describe_part:
+ * @part:
+ * @mimetype:
+ *
+ * Generate a simple textual description of a part, @mime_type represents
+ * the content.
+ *
+ * Return value:
+ **/
+gchar *
+em_format_describe_part (CamelMimePart *part,
+                         const gchar *mime_type)
 {
-	CamelCipherContext *cipher;
-	CamelCipherValidity *valid;
-	CamelMimePart *opart;
-	CamelDataWrapper *dw;
-	gchar *mime_type;
-	GError *local_error = NULL;
+	GString *stext;
+	const gchar *filename, *description;
+	gchar *content_type, *desc;
 
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
-		EM_FORMAT_VALIDITY_FOUND_PGP;
+	stext = g_string_new("");
+	content_type = g_content_type_from_mime_type (mime_type);
+	desc = g_content_type_get_description (
+		content_type != NULL ? content_type : mime_type);
+	g_free (content_type);
+	g_string_append_printf (
+		stext, _("%s attachment"), desc ? desc : mime_type);
+	g_free (desc);
 
-	cipher = camel_gpg_context_new (emf->session);
-	opart = camel_mime_part_new ();
+	filename = camel_mime_part_get_filename (part);
+	description = camel_mime_part_get_description (part);
 
-	/* Decrypt the message */
-	valid = camel_cipher_context_decrypt_sync (
-		cipher, ipart, opart, cancellable, &local_error);
+	if (!filename || !*filename) {
+		CamelDataWrapper *content;
 
-	if (!valid) {
-		em_format_format_error (
-			emf, stream, _("Could not parse PGP message: "));
-		if (local_error->message != NULL)
-			em_format_format_error (
-				emf, stream, "%s", local_error->message);
-		else
-			em_format_format_error (
-				emf, stream, _("Unknown error"));
-		em_format_format_source (emf, stream, ipart, cancellable);
-		/* XXX I think this will loop:
-		 * em_format_part_as(emf, stream, part, "text/plain"); */
+		content = camel_medium_get_content (CAMEL_MEDIUM (part));
 
-		g_clear_error (&local_error);
-		g_object_unref (cipher);
-		g_object_unref (opart);
-		return;
+		if (CAMEL_IS_MIME_MESSAGE (content))
+			filename = camel_mime_message_get_subject (
+				CAMEL_MIME_MESSAGE (content));
 	}
 
-	dw = camel_medium_get_content ((CamelMedium *) opart);
-	mime_type = camel_data_wrapper_get_mime_type (dw);
-
-	/* this ensures to show the 'opart' as inlined, if possible */
-	if (mime_type != NULL && g_ascii_strcasecmp (
-			mime_type, "application/octet-stream") == 0) {
-		const gchar *snoop = em_format_snoop_type (opart);
-
-		if (snoop)
-			camel_data_wrapper_set_mime_type (dw, snoop);
+	if (filename != NULL && *filename != '\0') {
+		gchar *basename = g_path_get_basename (filename);
+		g_string_append_printf (stext, " (%s)", basename);
+		g_free (basename);
 	}
 
-	preserve_charset_in_content_type (ipart, opart);
-	g_free (mime_type);
-
-	add_validity_found (emf, valid);
-	/* Pass it off to the real formatter */
-	em_format_format_secure (emf, stream, opart, valid, cancellable);
+	if (description != NULL && *description != '\0' &&
+		g_strcmp0 (filename, description) != 0)
+		g_string_append_printf (stext, ", \"%s\"", description);
 
-	/* Clean Up */
-	g_object_unref (opart);
-	g_object_unref (cipher);
+	return g_string_free (stext, FALSE);
 }
 
-static EMFormatHandler type_builtin_table[] = {
-#ifdef ENABLE_SMIME
-	{ (gchar *) "application/x-pkcs7-mime",
-	  emf_application_xpkcs7mime,
-	  EM_FORMAT_HANDLER_INLINE_DISPOSITION },
-#endif
-	{ (gchar *) "application/mbox", emf_application_mbox, EM_FORMAT_HANDLER_INLINE },
-	{ (gchar *) "multipart/alternative", emf_multipart_alternative },
-	{ (gchar *) "multipart/appledouble", emf_multipart_appledouble },
-	{ (gchar *) "multipart/encrypted", emf_multipart_encrypted },
-	{ (gchar *) "multipart/mixed", emf_multipart_mixed },
-	{ (gchar *) "multipart/signed", emf_multipart_signed },
-	{ (gchar *) "multipart/related", emf_multipart_related },
-	{ (gchar *) "multipart/*", emf_multipart_mixed },
-	{ (gchar *) "message/rfc822", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
-	{ (gchar *) "message/news", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
-	{ (gchar *) "message/delivery-status", emf_message_deliverystatus },
-	{ (gchar *) "message/*", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
-
-	/* Insert brokenly-named parts here */
-#ifdef ENABLE_SMIME
-	{ (gchar *) "application/pkcs7-mime",
-	  emf_application_xpkcs7mime,
-	  EM_FORMAT_HANDLER_INLINE_DISPOSITION },
-#endif
-
-	/* internal types */
-	{ (gchar *) "application/x-inlinepgp-signed", emf_inlinepgp_signed },
-	{ (gchar *) "application/x-inlinepgp-encrypted", emf_inlinepgp_encrypted },
-};
-
-static void
-emf_builtin_init (EMFormatClass *class)
+/**
+ * em_format_is_attachment:
+ * @emf:
+ * @part: Part to check.
+ *
+ * Returns true if the part is an attachment.
+ *
+ * A part is not considered an attachment if it is a
+ * multipart, or a text part with no filename.  It is used
+ * to determine if an attachment header should be displayed for
+ * the part.
+ *
+ * Content-Disposition is not checked.
+ *
+ * Return value: TRUE/FALSE
+ **/
+gint
+em_format_is_attachment (EMFormat *emf,
+                         CamelMimePart *part)
 {
-	gint ii;
+	/*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
+	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+
+	if (!dw)
+		return 0;
 
-	for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
-		g_hash_table_insert (
-			class->type_handlers,
-			type_builtin_table[ii].mime_type,
-			&type_builtin_table[ii]);
+	d(printf("checking is attachment %s/%s\n", dw->mime_type->type, dw->mime_type->subtype));
+	return !(camel_content_type_is (dw->mime_type, "multipart", "*")
+		 || camel_content_type_is (
+			dw->mime_type, "application", "x-pkcs7-mime")
+		 || camel_content_type_is (
+			dw->mime_type, "application", "pkcs7-mime")
+		 || camel_content_type_is (
+			dw->mime_type, "application", "x-inlinepgp-signed")
+		 || camel_content_type_is (
+			dw->mime_type, "application", "x-inlinepgp-encrypted")
+		 || camel_content_type_is (
+			dw->mime_type, "x-evolution", "evolution-rss-feed")
+		 || camel_content_type_is (dw->mime_type, "text", "calendar")
+		 || camel_content_type_is (dw->mime_type, "text", "x-calendar")
+		 || (camel_content_type_is (dw->mime_type, "text", "*")
+		     && camel_mime_part_get_filename (part) == NULL));
 }
 
 /**
@@ -2493,5 +2401,237 @@ em_format_snoop_type (CamelMimePart *part)
 	return res;
 
 	/* We used to load parts to check their type, we don't anymore,
-	 * see bug #11778 for some discussion */
+	 * see bug #211778 for some discussion */
+}
+
+/**
+ * Construct a URI for message.
+ *
+ * The URI can contain multiple query parameters. The list of parameters must be
+ * NULL-terminated. Each query must contain name, GType of value and value.
+ *
+ * @param folder Folder wit the message
+ * @param message_uid ID of message within the \p folder
+ * @param first_param_name Name of first query parameter followed by GType of it's value and value.
+ */
+gchar *
+em_format_build_mail_uri (CamelFolder *folder,
+                          const gchar *message_uid,
+                          const gchar *first_param_name,
+                          ...)
+{
+	CamelStore *store;
+	gchar *uri, *tmp;
+	va_list ap;
+	const gchar *name;
+	const gchar *service_uid, *folder_name;
+	gchar separator;
+
+	g_return_val_if_fail (message_uid && *message_uid, NULL);
+
+	if (!folder) {
+                folder_name = "generic";
+                service_uid = "generic";
+	} else {
+		folder_name = camel_folder_get_full_name (folder);
+		store = camel_folder_get_parent_store (folder);
+		if (store)
+			service_uid = camel_service_get_uid (CAMEL_SERVICE (store));
+		else
+                        service_uid = "generic";
+	}
+
+	tmp = g_strdup_printf ("mail://%s/%s/%s",
+			service_uid,
+			folder_name,
+			message_uid);
+
+	va_start (ap, first_param_name);
+	name = first_param_name;
+	separator = '?';
+	while (name) {
+		gchar *tmp2;
+		gint type = va_arg (ap, gint);
+		switch (type) {
+			case G_TYPE_INT:
+			case G_TYPE_BOOLEAN: {
+				gint val = va_arg (ap, gint);
+				tmp2 = g_strdup_printf ("%s%c%s=%d", tmp,
+						separator, name, val);
+				break;
+			}
+			case G_TYPE_FLOAT:
+			case G_TYPE_DOUBLE: {
+				gdouble val = va_arg (ap, double);
+				tmp2 = g_strdup_printf ("%s%c%s=%f", tmp,
+						separator, name, val);
+				break;
+			}
+			case G_TYPE_STRING: {
+				gchar *val = va_arg (ap, gchar *);
+				gchar *escaped = soup_uri_encode (val, NULL);
+				tmp2 = g_strdup_printf ("%s%c%s=%s", tmp,
+						separator, name, escaped);
+				g_free (escaped);
+				break;
+			}
+			default:
+				g_warning ("Invalid param type %s", g_type_name (type));
+				return NULL;
+		}
+
+		g_free (tmp);
+		tmp = tmp2;
+
+		if (separator == '?')
+			separator = '&';
+
+		name = va_arg (ap, gchar *);
+	}
+	va_end (ap);
+
+	uri = tmp;
+
+	/* For some reason, webkit won't accept URL with username, but
+	 * without password (mail://store host/folder/mail), so we
+	 * will replace the '@' symbol by '/' to get URL like
+	 * mail://store/host/folder/mail which is OK
+	 */
+	tmp = strchr (tmp, '@');
+	if (tmp) {
+		tmp[0] = '/';
+	}
+
+	return uri;
+}
+
+void
+em_format_redraw (EMFormat *emf)
+{
+	g_return_if_fail (EM_IS_FORMAT (emf));
+
+	g_signal_emit (emf, signals[REDRAW_REQUESTED], 0);
+}
+
+/**************************************************************************/
+EMFormatPURI *
+em_format_puri_new (EMFormat *emf,
+                    gsize puri_size,
+                    CamelMimePart *part,
+                    const gchar *uri)
+{
+	EMFormatPURI *puri;
+
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+	g_return_val_if_fail (puri_size >= sizeof (EMFormatPURI), NULL);
+
+	puri = (EMFormatPURI *) g_malloc0 (puri_size);
+	puri->emf = emf;
+
+	if (part)
+		puri->part = g_object_ref (part);
+
+	if (uri)
+		puri->uri = g_strdup (uri);
+
+	return puri;
+}
+
+void
+em_format_puri_free (EMFormatPURI *puri)
+{
+	g_return_if_fail (puri);
+
+	if (puri->part)
+		g_object_unref (puri->part);
+
+	if (puri->uri)
+		g_free (puri->uri);
+
+	if (puri->cid)
+		g_free (puri->cid);
+
+	if (puri->mime_type)
+		g_free (puri->mime_type);
+
+	if (puri->validity)
+		camel_cipher_validity_free (puri->validity);
+
+	if (puri->validity_parent)
+		camel_cipher_validity_free (puri->validity_parent);
+
+	if (puri->free)
+		puri->free (puri);
+
+	g_free (puri);
+}
+
+void
+em_format_puri_write (EMFormatPURI *puri,
+                      CamelStream *stream,
+                      EMFormatWriterInfo *info,
+                      GCancellable *cancellable)
+{
+	g_return_if_fail (puri);
+	g_return_if_fail (CAMEL_IS_STREAM (stream));
+
+	if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) {
+		const EMFormatHandler *handler;
+		handler = em_format_find_handler (puri->emf, "x-evolution/message/source");
+		handler->write_func (puri->emf, puri, stream, info, cancellable);
+		return;
+	}
+
+	if (puri->write_func) {
+		puri->write_func (puri->emf, puri, stream, info, cancellable);
+	} else {
+		const EMFormatHandler *handler;
+		const gchar *mime_type;
+
+		if (puri->mime_type) {
+			mime_type = puri->mime_type;
+		} else {
+			mime_type = (gchar *) "plain/text";
+		}
+
+		handler = em_format_find_handler (puri->emf, mime_type);
+		if (handler && handler->write_func) {
+			handler->write_func (puri->emf,
+					puri, stream, info, cancellable);
+		}
+	}
+}
+
+EMFormatHeader *
+em_format_header_new (const gchar *name,
+                      const gchar *value)
+{
+	EMFormatHeader *header;
+
+	g_return_val_if_fail (name && *name, NULL);
+
+	header = g_new0 (EMFormatHeader, 1);
+	header->name = g_strdup (name);
+	if (value && *value)
+		header->value = g_strdup (value);
+
+	return header;
+}
+
+void
+em_format_header_free (EMFormatHeader * header)
+{
+	g_return_if_fail (header != NULL);
+
+	if (header->name) {
+		g_free (header->name);
+		header->name = NULL;
+	}
+
+	if (header->value) {
+		g_free (header->value);
+		header->value = NULL;
+	}
+
+	g_free (header);
 }
diff --git a/em-format/em-format.h b/em-format/em-format.h
index cf214d8..712b41b 100644
--- a/em-format/em-format.h
+++ b/em-format/em-format.h
@@ -21,14 +21,11 @@
  *
  */
 
-/*
-  Abstract class for formatting mime messages
-*/
-
 #ifndef EM_FORMAT_H
 #define EM_FORMAT_H
 
 #include <camel/camel.h>
+#include <gtk/gtk.h>
 
 /* Standard GObject macros */
 #define EM_TYPE_FORMAT \
@@ -51,194 +48,139 @@
 
 G_BEGIN_DECLS
 
+#define EM_FORMAT_HEADER_BOLD (1<<0)
+#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */
+
+#define EM_FORMAT_VALIDITY_FOUND_PGP		(1<<0)
+#define EM_FORMAT_VALIDITY_FOUND_SMIME		(1<<1)
+#define EM_FORMAT_VALIDITY_FOUND_SIGNED		(1<<2)
+#define EM_FORMAT_VALIDITY_FOUND_ENCRYPTED	(1<<3)
+
 typedef struct _EMFormat EMFormat;
 typedef struct _EMFormatClass EMFormatClass;
 typedef struct _EMFormatPrivate EMFormatPrivate;
 
-typedef struct _EMFormatHandler EMFormatHandler;
+typedef struct _EMFormatPURI EMFormatPURI;
 typedef struct _EMFormatHeader EMFormatHeader;
+typedef struct _EMFormatHandler EMFormatHandler;
+typedef struct _EMFormatParserInfo EMFormatParserInfo;
+typedef struct _EMFormatWriterInfo EMFormatWriterInfo;
+typedef struct _WebKitDOMElement WebKitDOMElement;
 
-typedef void	(*EMFormatFunc)			(EMFormat *emf,
+typedef void		(*EMFormatParseFunc)	(EMFormat *emf,
+						 CamelMimePart *part,
+						 GString *part_id,
+						 EMFormatParserInfo *info,
+						 GCancellable *cancellable);
+typedef void		(*EMFormatWriteFunc)	(EMFormat *emf,
+						 EMFormatPURI *puri,
 						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 const EMFormatHandler *info,
-						 GCancellable *cancellable,
-						 gboolean is_fallback);
-
-typedef enum {
-	EM_FORMAT_MODE_NORMAL,
-	EM_FORMAT_MODE_ALLHEADERS,
-	EM_FORMAT_MODE_SOURCE
-} EMFormatMode;
+						 EMFormatWriterInfo *info,
+						 GCancellable *cancellable);
+typedef GtkWidget *	(*EMFormatWidgetFunc)	(EMFormat *emf,
+						 EMFormatPURI *puri,
+						 GCancellable *cancellable);
+typedef void		(*EMailDisplayBindFunc)	(WebKitDOMElement *root,
+						 EMFormatPURI *puri);
 
-/**
- * EMFormatHandlerFlags - Format handler flags.
- *
- * @EM_FORMAT_HANDLER_INLINE: This type should be shown expanded
- * inline by default.
- * @EM_FORMAT_HANDLER_INLINE_DISPOSITION: This type should always be
- * shown inline, despite what the Content-Disposition suggests.
- *
- **/
 typedef enum {
 	EM_FORMAT_HANDLER_INLINE = 1 << 0,
-	EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1 << 1
+	EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1 << 1,
+	EM_FORMAT_HANDLER_COMPOUND_TYPE = 1 << 2
 } EMFormatHandlerFlags;
 
-/**
- * struct _EMFormatHandler - MIME type handler.
- *
- * @mime_type: Type this handler handles.
- * @handler: The handler callback.
- * @flags: Handler flags
- * @old: The last handler set on this type.  Allows overrides to
- * fallback to previous implementation.
- *
- **/
+typedef enum {
+	EM_FORMAT_WRITE_MODE_NORMAL= 1 << 0,
+	EM_FORMAT_WRITE_MODE_ALL_HEADERS = 1 << 1,
+	EM_FORMAT_WRITE_MODE_SOURCE = 1 << 2,
+	EM_FORMAT_WRITE_MODE_PRINTING = 1 << 3,
+	EM_FORMAT_WRITE_MODE_RAW = 1 << 4
+} EMFormatWriteMode;
+
 struct _EMFormatHandler {
 	gchar *mime_type;
-	EMFormatFunc handler;
+	EMFormatParseFunc parse_func;
+	EMFormatWriteFunc write_func;
 	EMFormatHandlerFlags flags;
 
 	EMFormatHandler *old;
 };
 
-typedef struct _EMFormatPURI EMFormatPURI;
-typedef void	(*EMFormatPURIFunc)		(EMFormat *emf,
-						 CamelStream *stream,
-						 EMFormatPURI *puri,
-						 GCancellable *cancellable);
-
 /**
- * struct _EMFormatPURI - Pending URI object.
- *
- * @free: May be set by allocator and will be called when no longer needed.
- * @format:
- * @uri: Calculated URI of the part, if the part has one in its
- * Content-Location field.
- * @cid: The RFC2046 Content-Id of the part.  If none is present, a unique value
- * is calculated from @part_id.
- * @part_id: A unique identifier for each part.
- * @func: Callback for when the URI is requested.  The callback writes
- * its data to the supplied stream.
- * @part:
- * @use_count:
- *
- * This is used for multipart/related, and other formatters which may
- * need to include a reference to out-of-band data in the content
- * stream.
- *
- * This object may be subclassed as a struct.
- **/
-struct _EMFormatPURI {
-	void (*free)(EMFormatPURI *p); /* optional callback for freeing user-fields */
-	EMFormat *format;
+ * Use this struct to pass additional information between
+ * EMFormatParseFunc's.
+ * Much cleaner then setting public property of EMFormat.
+ */
+struct _EMFormatParserInfo {
+	const EMFormatHandler *handler;
 
-	gchar *uri;		/* will be the location of the part, may be empty */
-	gchar *cid;		/* will always be set, a fake one created if needed */
-	gchar *part_id;		/* will always be set, emf->part_id->str for this part */
+	/* EM_FORMAT_VALIDITY_* flags */
+	guint32 validity_type;
+	CamelCipherValidity *validity;
 
-	EMFormatPURIFunc func;
-	CamelMimePart *part;
+        gint is_attachment : 1;
+        gint force_handler: 1;
+};
 
-	guint use_count;	/* used by multipart/related to see if it was accessed */
+struct _EMFormatWriterInfo {
+	EMFormatWriteMode mode;
+	gboolean headers_collapsable;
+	gboolean headers_collapsed;
 };
 
 struct _EMFormatHeader {
 	guint32 flags;		/* E_FORMAT_HEADER_ * */
-	gchar name[1];
+	gchar *name;
+	gchar *value;
 };
 
 #define EM_FORMAT_HEADER_BOLD (1<<0)
 #define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */
 
-#define EM_FORMAT_VALIDITY_FOUND_PGP       (1<<0)
-#define EM_FORMAT_VALIDITY_FOUND_SMIME     (1<<1)
-#define EM_FORMAT_VALIDITY_FOUND_SIGNED    (1<<2)
-#define EM_FORMAT_VALIDITY_FOUND_ENCRYPTED (1<<3)
+struct _EMFormatPURI {
+	CamelMimePart *part;
+
+	EMFormat *emf;
+	EMFormatWriteFunc write_func;
+	EMFormatWidgetFunc widget_func;
+
+	/**
+	 * Called by #EMailDisplay whenever document/frame is reloaded.
+	 * Modules and plugins can create bindings to events of DOM
+	 * objects they created.
+	 */
+	EMailDisplayBindFunc bind_func;
+
+	gchar *uri;
+	gchar *cid;
+	gchar *mime_type;
+
+	/* EM_FORMAT_VALIDITY_* flags */
+	guint32 validity_type;
+	CamelCipherValidity *validity;
+	CamelCipherValidity *validity_parent;
+
+	gboolean is_attachment;
+
+	void (*free)(EMFormatPURI *puri); /* optional callback for freeing user-fields */
+};
 
-/**
- * struct _EMFormat - Mail formatter object.
- *
- * @parent:
- * @priv:
- * @message:
- * @folder:
- * @uid:
- * @part_id:
- * @header_list:
- * @session:
- * @base url:
- * @snoop_mime_type:
- * @valid:
- * @valid_parent:
- * @inline_table:
- * @pending_uri_table:
- * @pending_uri_tree:
- * @pending_uri_level:
- * @mode:
- * @charset:
- * @default_charset:
- *
- * Most fields are private or read-only.
- *
- * This is the base MIME formatter class.  It provides no formatting
- * itself, but drives most of the basic types, including multipart / * types.
- **/
 struct _EMFormat {
 	GObject parent;
 	EMFormatPrivate *priv;
 
-	/* The current message */
 	CamelMimeMessage *message;
-
 	CamelFolder *folder;
-	gchar *uid;
+	gchar *message_uid;
+        gchar *uri_base;
 
-	/* Current part ID prefix for identifying parts directly. */
-	GString *part_id;
-	/* part_id of the currently processing message
-	 * (when the message has message-attachments) */
-	gchar *current_message_part_id;
+	/* Defines order in which parts should be displayed */
+	GList *mail_part_list;
+	/* For quick search for parts by their URI/ID */
+	GHashTable *mail_part_table;
 
 	/* If empty, then all. */
 	GQueue header_list;
-
-	/* Used for authentication when required. */
-	CamelSession *session;
-
-	/* Content-Base header or absolute Content-Location, for any part. */
-	CamelURL *base;
-
-	/* If we snooped an application/octet-stream, what we snooped. */
-	const gchar *snoop_mime_type;
-
-	/* For validity enveloping. */
-	CamelCipherValidity *valid;
-	CamelCipherValidity *valid_parent;
-
-	/* For checking whether we found any signed or encrypted parts. */
-	guint32 validity_found;
-
-	/* For forcing inlining. */
-	GHashTable *inline_table;
-
-	/* Global URI lookup table for message. */
-	GHashTable *pending_uri_table;
-
-	/* This structure is used internally to form a visibility tree of
-	 * parts in the current formatting stream.  This is to implement the
-	 * part resolution rules for RFC2387 to implement multipart/related. */
-	GNode *pending_uri_tree;
-
-	/* The current level to search from. */
-	GNode *pending_uri_level;
-
-	EMFormatMode mode;		/* source/headers/etc */
-	gchar *charset;			/* charset override */
-	gchar *default_charset;		/* charset fallback */
-	gboolean composer;		/* formatting from composer? */
-	gboolean print;			/* formatting for printing? */
 };
 
 struct _EMFormatClass {
@@ -246,187 +188,161 @@ struct _EMFormatClass {
 
 	GHashTable *type_handlers;
 
-	/* lookup handler, default falls back to hashtable above */
-	const EMFormatHandler *
-			(*find_handler)		(EMFormat *emf,
-						 const gchar *mime_type);
-
-	/* start formatting a message */
-	void		(*format_clone)		(EMFormat *emf,
-						 CamelFolder *folder,
-						 const gchar *uid,
-						 CamelMimeMessage *message,
-						 EMFormat *source,
-						 GCancellable *cancellable);
-
-	/* some internel error/inconsistency */
-	void		(*format_error)		(EMFormat *emf,
-						 CamelStream *stream,
-						 const gchar *errmsg);
-
-	/* use for external structured parts */
-	void		(*format_attachment)	(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 const gchar *mime_type,
-						 const EMFormatHandler *info,
-						 GCancellable *cancellable);
+	gboolean	(*is_inline)			(EMFormat *emf,
+							 const gchar *part_id,
+							 CamelMimePart *part,
+							 const EMFormatHandler *handler);
 
-	/* use for unparsable content */
-	void		(*format_source)	(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 GCancellable *cancellable);
-	/* for outputing secure(d) content */
-	void		(*format_secure)	(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 CamelCipherValidity *validity,
-						 GCancellable *cancellable);
+	/* Write the entire message to stream */
+	void		(*write)			(EMFormat *emf,
+							 CamelStream *stream,
+							 EMFormatWriterInfo *info,
+							 GCancellable *cancellable);
 
-	/* returns true if the formatter is still busy with pending stuff */
-	gboolean	(*busy)			(EMFormat *);
-
-	/* Shows optional way to open messages  */
-	void		(*format_optional)	(EMFormat *emf,
-						 CamelStream *filter_stream,
-						 CamelMimePart *mime_part,
-						 CamelStream *mem_stream,
-						 GCancellable *cancellable);
-
-	gboolean	(*is_inline)		(EMFormat *emf,
-						 const gchar *part_id,
-						 CamelMimePart *mime_part,
-						 const EMFormatHandler *handle);
+        void            (*preparse)                     (EMFormat *emf);
 
 	/* signals */
-	/* complete, alternative to polling busy, for asynchronous work */
-	void		(*complete)		(EMFormat *emf);
-};
+	void		(*redraw_requested)		(EMFormat *emf);
 
-void		em_format_set_mode		(EMFormat *emf,
-						 EMFormatMode mode);
-void		em_format_set_charset		(EMFormat *emf,
-						 const gchar *charset);
-void		em_format_set_default_charset	(EMFormat *emf,
-						 const gchar *charset);
-
-/* also indicates to show all headers */
-void		em_format_clear_headers		(EMFormat *emf);
-
-void		em_format_default_headers	(EMFormat *emf);
-void		em_format_add_header		(EMFormat *emf,
-						 const gchar *name,
-						 guint32 flags);
-
-/* FIXME: Need a 'clone' api to copy details about the current view (inlines etc)
- * Or maybe it should live with sub-classes? */
-
-gint		em_format_is_attachment		(EMFormat *emf,
-						 CamelMimePart *part);
-
-gboolean	em_format_is_inline		(EMFormat *emf,
-						 const gchar *part_id,
-						 CamelMimePart *mime_part,
-						 const EMFormatHandler *handle);
-void		em_format_set_inline		(EMFormat *emf,
-						 const gchar *partid,
-						 gint state);
-
-gchar *		em_format_describe_part		(CamelMimePart *part,
-						 const gchar *mime_type);
-
-/* for implementers */
-GType		em_format_get_type		(void);
-
-void		em_format_class_add_handler	(EMFormatClass *emfc,
-						 EMFormatHandler *info);
-void		em_format_class_remove_handler	(EMFormatClass *emfc,
-						 EMFormatHandler *info);
-const EMFormatHandler *
-		em_format_find_handler		(EMFormat *emf,
-						 const gchar *mime_type);
-const EMFormatHandler *
-		em_format_fallback_handler	(EMFormat *emf,
-						 const gchar *mime_type);
-
-/* puri is short for pending uri ... really */
-EMFormatPURI *	em_format_add_puri		(EMFormat *emf,
-						 gsize size,
-						 const gchar *uri,
-						 CamelMimePart *part,
-						 EMFormatPURIFunc func);
-EMFormatPURI *	em_format_find_visible_puri	(EMFormat *emf,
-						 const gchar *uri);
-EMFormatPURI *	em_format_find_puri		(EMFormat *emf,
-						 const gchar *uri);
-void		em_format_clear_puri_tree	(EMFormat *emf);
-void		em_format_push_level		(EMFormat *emf);
-void		em_format_pull_level		(EMFormat *emf);
-
-/* clones inline state/view and format, or use to redraw */
-void		em_format_format_clone		(EMFormat *emf,
-						 CamelFolder *folder,
-						 const gchar *uid,
-						 CamelMimeMessage *message,
-						 EMFormat *source,
-						 GCancellable *cancellable);
-
-/* formats a new message */
-void		em_format_format		(EMFormat *emf,
-						 CamelFolder *folder,
-						 const gchar *uid,
-						 CamelMimeMessage *message,
-						 GCancellable *cancellable);
-void		em_format_queue_redraw		(EMFormat *emf);
-void		em_format_format_attachment	(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 const gchar *mime_type,
-						 const EMFormatHandler *info,
-						 GCancellable *cancellable);
-void		em_format_format_error		(EMFormat *emf,
-						 CamelStream *stream,
-						 const gchar *format,
-						 ...) G_GNUC_PRINTF (3, 4);
-void		em_format_format_secure		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 CamelCipherValidity *valid,
-						 GCancellable *cancellable);
-void		em_format_format_source		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 GCancellable *cancellable);
-
-gboolean	em_format_busy			(EMFormat *emf);
-
-/* raw content only */
-void		em_format_format_content	(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *part,
-						 GCancellable *cancellable);
-
-/* raw content text parts - should this just be checked/done by above? */
-void		em_format_format_text		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelDataWrapper *part,
-						 GCancellable *cancellable);
-
-void		em_format_part_as		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *part,
-						 const gchar *mime_type,
-						 GCancellable *cancellable);
-void		em_format_part			(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 GCancellable *cancellable);
-void		em_format_merge_handler		(EMFormat *new,
-						 EMFormat *old);
-
-const gchar *	em_format_snoop_type		(CamelMimePart *part);
+};
 
-G_END_DECLS
+EMFormat *		em_format_new			(void);
+
+GType			em_format_get_type		(void);
+
+void			em_format_set_charset		(EMFormat *emf,
+							 const gchar *charset);
+const gchar *		em_format_get_charset		(EMFormat *emf);
+
+void			em_format_set_default_charset	(EMFormat *emf,
+							 const gchar *charset);
+const gchar *		em_format_get_default_charset	(EMFormat *emf);
+
+void			em_format_set_composer		(EMFormat *emf,
+							 gboolean composer);
+gboolean		em_format_get_composer		(EMFormat *emf);
+
+void			em_format_set_base_url		(EMFormat *emf,
+							 CamelURL *url);
+void			em_format_set_base_url_string	(EMFormat *emf,
+							 const gchar *url_string);
+CamelURL *		em_format_get_base_url		(EMFormat *emf);
+
+void			em_format_clear_headers		(EMFormat *emf);
+
+void			em_format_default_headers	(EMFormat *emf);
+
+void			em_format_add_header		(EMFormat *emf,
+							 const gchar *name,
+							 const gchar *value,
+							 guint32 flags);
+void			em_format_add_header_struct	(EMFormat *emf,
+							 EMFormatHeader *header);
+void			em_format_remove_header		(EMFormat *emf,
+							 const gchar *name,
+							 const gchar *value);
+void                    em_format_remove_header_struct	(EMFormat *emf,
+							 const EMFormatHeader *header);
+
+void			em_format_add_puri		(EMFormat *emf,
+							 EMFormatPURI *puri);
+EMFormatPURI *		em_format_find_puri		(EMFormat *emf,
+							 const gchar *id);
+
+void			em_format_class_add_handler	(EMFormatClass *emfc,
+							 EMFormatHandler *handler);
+void			em_format_class_remove_handler	(EMFormatClass *emfc,
+							 EMFormatHandler *handler);
+
+const EMFormatHandler *	em_format_find_handler		(EMFormat *emf,
+							 const gchar *mime_type);
+const EMFormatHandler *	em_format_fallback_handler	(EMFormat *emf,
+							 const gchar *mime_type);
+
+void			em_format_parse			(EMFormat *emf,
+							 CamelMimeMessage *message,
+							 CamelFolder *folder,
+							 GCancellable *cancellable);
+
+void			em_format_write			(EMFormat *emf,
+							 CamelStream *stream,
+							 EMFormatWriterInfo *info,
+							 GCancellable *cancellable);
+
+void                    em_format_parse_async           (EMFormat *emf,
+                                                         CamelMimeMessage *message,
+                                                         CamelFolder *folder,
+                                                         GCancellable *cancellable,
+                                                         GAsyncReadyCallback callback,
+                                                         gpointer user_data);
+
+void			em_format_parse_part		(EMFormat *emf,
+							 CamelMimePart *part,
+							 GString *part_id,
+							 EMFormatParserInfo *info,
+							 GCancellable *cancellable);
+void			em_format_parse_part_as		(EMFormat *emf,
+							 CamelMimePart *part,
+							 GString *part_id,
+							 EMFormatParserInfo *info,
+							 const gchar *mime_type,
+							 GCancellable *cancellable);
+gboolean		em_format_is_inline		(EMFormat *emf,
+							 const gchar *part_id,
+							 CamelMimePart *part,
+							 const EMFormatHandler *handler);
+
+gchar *			em_format_get_error_id		(EMFormat *emf);
+
+void			em_format_format_error		(EMFormat *emf,
+							 const gchar *format,
+							 ...) G_GNUC_PRINTF (2, 3);
+void			em_format_format_text		(EMFormat *emf,
+							 CamelStream *stream,
+							 CamelDataWrapper *dw,
+							 GCancellable *cancellable);
+gchar *			em_format_describe_part		(CamelMimePart *part,
+							 const gchar *mime_type);
+gint			em_format_is_attachment	(EMFormat *emf,
+							 CamelMimePart *part);
+const gchar *		em_format_snoop_type		(CamelMimePart *part);
+
+gchar *			em_format_build_mail_uri	(CamelFolder *folder,
+							 const gchar *message_uid,
+							 const gchar *part_uid,
+							 ...) G_GNUC_NULL_TERMINATED;
+
+/* EMFormatParseFunc that does nothing. Use it to disable
+ * parsing of a specific mime type parts  */
+void			em_format_empty_parser		(EMFormat *emf,
+							 CamelMimePart *part,
+							 GString *part_id,
+							 EMFormatParserInfo *info,
+							 GCancellable *cancellable);
+
+/* EMFormatWriteFunc that does nothing. Use it to disable
+ * writing of a specific mime type parts */
+void			em_format_empty_writer		(EMFormat *emf,
+							 EMFormatPURI *puri,
+							 CamelStream *stream,
+							 EMFormatWriterInfo *info,
+							 GCancellable *cancellable);
+
+void			em_format_redraw		(EMFormat *emf);
+
+EMFormatPURI *		em_format_puri_new		(EMFormat *emf,
+							 gsize puri_size,
+							 CamelMimePart *part,
+							 const gchar *uri);
+void			em_format_puri_free		(EMFormatPURI *puri);
+
+void			em_format_puri_write		(EMFormatPURI *puri,
+							 CamelStream *stream,
+							 EMFormatWriterInfo *info,
+							 GCancellable *cancellable);
+
+EMFormatHeader *		em_format_header_new		(const gchar *name,
+							 const gchar *value);
+void			em_format_header_free		(EMFormatHeader *header);
 
 #endif /* EM_FORMAT_H */
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 3a13284..e6eccc8 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -22,7 +22,9 @@ libevolution_mail_la_CPPFLAGS =				\
 	$(CERT_UI_CFLAGS)				\
 	$(CANBERRA_CFLAGS)				\
 	$(CLUTTER_CFLAGS)				\
-	$(GTKHTML_CFLAGS)				\
+	$(GTKHTML_CFLAGS)                               \
+	$(JAVASCRIPTCORE_CFLAGS)			\
+	$(LIBSOUP_CFLAGS)				\
 	-DEVOLUTION_DATADIR=\""$(datadir)"\"		\
 	-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"	\
 	-DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\"	\
@@ -56,11 +58,13 @@ mailinclude_HEADERS =					\
 	e-mail-migrate.h				\
 	e-mail-notebook-view.h				\
 	e-mail-paned-view.h				\
+	e-mail-printer.h				\
 	e-mail-reader-utils.h				\
 	e-mail-reader.h					\
-	e-mail-ui-session.h				\
+	e-mail-request.h				\
 	e-mail-sidebar.h				\
 	e-mail-tag-editor.h				\
+	e-mail-ui-session.h				\
 	e-mail-view.h					\
 	em-account-editor.h				\
 	em-composer-utils.h				\
@@ -81,7 +85,6 @@ mailinclude_HEADERS =					\
 	em-format-html-display.h			\
 	em-format-html-print.h				\
 	em-format-html.h				\
-	em-html-stream.h				\
 	em-search-context.h				\
 	em-subscription-editor.h			\
 	em-sync-stream.h				\
@@ -121,11 +124,13 @@ libevolution_mail_la_SOURCES =				\
 	e-mail-migrate.c				\
 	e-mail-notebook-view.c				\
 	e-mail-paned-view.c				\
+	e-mail-printer.c				\
 	e-mail-reader-utils.c				\
 	e-mail-reader.c					\
-	e-mail-ui-session.c				\
+	e-mail-request.c				\
 	e-mail-sidebar.c				\
 	e-mail-tag-editor.c				\
+	e-mail-ui-session.c				\
 	e-mail-view.c					\
 	em-account-editor.c				\
 	em-composer-utils.c				\
@@ -146,7 +151,6 @@ libevolution_mail_la_SOURCES =				\
 	em-format-html-display.c			\
 	em-format-html-print.c				\
 	em-format-html.c				\
-	em-html-stream.c				\
 	em-search-context.c				\
 	em-subscription-editor.c			\
 	em-sync-stream.c				\
@@ -194,7 +198,11 @@ libevolution_mail_la_LIBADD =				\
 	$(CANBERRA_LIBS)				\
 	$(CLUTTER_LIBS)					\
 	$(GTKHTML_LIBS)					\
-	$(SMIME_LIBS)
+	$(JAVASCRIPTCORE_CFLAGS)			\
+	$(E_WIDGETS_LIBS)				\
+	$(SMIME_LIBS)					\
+	$(LIBSOUP_LIBS)					\
+	$(GNOME_PLATFORM_LIBS)
 
 libevolution_mail_la_LDFLAGS = -avoid-version $(NO_UNDEFINED)
 
diff --git a/mail/e-mail-attachment-bar.c b/mail/e-mail-attachment-bar.c
index 21a298c..7572c66 100644
--- a/mail/e-mail-attachment-bar.c
+++ b/mail/e-mail-attachment-bar.c
@@ -60,7 +60,8 @@ enum {
 	PROP_ACTIVE_VIEW,
 	PROP_DRAGGING,
 	PROP_EDITABLE,
-	PROP_EXPANDED
+	PROP_EXPANDED,
+	PROP_STORE
 };
 
 /* Forward Declarations */
@@ -78,7 +79,6 @@ G_DEFINE_TYPE_WITH_CODE (
 static void
 mail_attachment_bar_update_status (EMailAttachmentBar *bar)
 {
-	EAttachmentView *view;
 	EAttachmentStore *store;
 	GtkActivatable *activatable;
 	GtkAction *action;
@@ -88,8 +88,7 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar)
 	gchar *display_size;
 	gchar *markup;
 
-	view = E_ATTACHMENT_VIEW (bar);
-	store = e_attachment_view_get_store (view);
+	store = E_ATTACHMENT_STORE (bar->priv->model);
 	label = GTK_LABEL (bar->priv->status_label);
 
 	num_attachments = e_attachment_store_get_num_attachments (store);
@@ -120,6 +119,31 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar)
 }
 
 static void
+mail_attachment_bar_set_store (EMailAttachmentBar *bar,
+                               EAttachmentStore *store)
+{
+	g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+	bar->priv->model = g_object_ref (store);
+
+	gtk_icon_view_set_model (GTK_ICON_VIEW (bar->priv->icon_view),
+		bar->priv->model);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (bar->priv->tree_view),
+		bar->priv->model);
+
+	g_signal_connect_swapped (
+		bar->priv->model, "notify::num-attachments",
+		G_CALLBACK (mail_attachment_bar_update_status), bar);
+
+	g_signal_connect_swapped (
+		bar->priv->model, "notify::total-size",
+		G_CALLBACK (mail_attachment_bar_update_status), bar);
+
+	/* Initialize */
+	mail_attachment_bar_update_status (bar);
+}
+
+static void
 mail_attachment_bar_set_property (GObject *object,
                                   guint property_id,
                                   const GValue *value,
@@ -127,7 +151,7 @@ mail_attachment_bar_set_property (GObject *object,
 {
 	switch (property_id) {
 		case PROP_ACTIVE_VIEW:
-			e_mail_attachment_bar_set_active_view (
+				e_mail_attachment_bar_set_active_view (
 				E_MAIL_ATTACHMENT_BAR (object),
 				g_value_get_int (value));
 			return;
@@ -149,6 +173,11 @@ mail_attachment_bar_set_property (GObject *object,
 				E_MAIL_ATTACHMENT_BAR (object),
 				g_value_get_boolean (value));
 			return;
+		case PROP_STORE:
+			mail_attachment_bar_set_store (
+				E_MAIL_ATTACHMENT_BAR (object),
+				g_value_get_object (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -188,6 +217,11 @@ mail_attachment_bar_get_property (GObject *object,
 				e_mail_attachment_bar_get_expanded (
 				E_MAIL_ATTACHMENT_BAR (object)));
 			return;
+		case PROP_STORE:
+			g_value_set_object (
+				value,
+				e_mail_attachment_bar_get_store (
+				E_MAIL_ATTACHMENT_BAR (object)));
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -201,8 +235,6 @@ mail_attachment_bar_dispose (GObject *object)
 	priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object);
 
 	if (priv->model != NULL) {
-		e_attachment_store_remove_all (
-			E_ATTACHMENT_STORE (priv->model));
 		g_object_unref (priv->model);
 		priv->model = NULL;
 	}
@@ -347,17 +379,6 @@ mail_attachment_bar_get_private (EAttachmentView *view)
 	return e_attachment_view_get_private (view);
 }
 
-static EAttachmentStore *
-mail_attachment_bar_get_store (EAttachmentView *view)
-{
-	EMailAttachmentBar *bar;
-
-	bar = E_MAIL_ATTACHMENT_BAR (view);
-	view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
-
-	return e_attachment_view_get_store (view);
-}
-
 static GtkTreePath *
 mail_attachment_bar_get_path_at_pos (EAttachmentView *view,
                                      gint x,
@@ -488,6 +509,17 @@ e_mail_attachment_bar_class_init (EMailAttachmentBarClass *class)
 			G_PARAM_READWRITE |
 			G_PARAM_CONSTRUCT));
 
+	g_object_class_install_property (
+		object_class,
+		PROP_STORE,
+		g_param_spec_object (
+			"store",
+			"Attachment Store",
+			NULL,
+			E_TYPE_ATTACHMENT_STORE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY));
+
 	g_object_class_override_property (
 		object_class, PROP_DRAGGING, "dragging");
 
@@ -499,7 +531,7 @@ static void
 e_mail_attachment_bar_interface_init (EAttachmentViewInterface *interface)
 {
 	interface->get_private = mail_attachment_bar_get_private;
-	interface->get_store = mail_attachment_bar_get_store;
+	interface->get_store = e_mail_attachment_bar_get_store;
 	interface->get_path_at_pos = mail_attachment_bar_get_path_at_pos;
 	interface->get_selected_paths = mail_attachment_bar_get_selected_paths;
 	interface->path_is_selected = mail_attachment_bar_path_is_selected;
@@ -520,7 +552,6 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar)
 	GtkAction *action;
 
 	bar->priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (bar);
-	bar->priv->model = e_attachment_store_new ();
 
 	gtk_box_set_spacing (GTK_BOX (bar), 6);
 
@@ -644,23 +675,18 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar)
 	bar->priv->status_label = g_object_ref (widget);
 	gtk_widget_show (widget);
 
-	g_signal_connect_swapped (
-		bar->priv->model, "notify::num-attachments",
-		G_CALLBACK (mail_attachment_bar_update_status), bar);
-
-	g_signal_connect_swapped (
-		bar->priv->model, "notify::total-size",
-		G_CALLBACK (mail_attachment_bar_update_status), bar);
-
 	g_object_unref (size_group);
 }
 
 GtkWidget *
-e_mail_attachment_bar_new (void)
+e_mail_attachment_bar_new (EAttachmentStore *store)
 {
+	g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
 	return g_object_new (
 		E_TYPE_MAIL_ATTACHMENT_BAR,
-		"editable", FALSE, NULL);
+		"editable", FALSE,
+		"store", store, NULL);
 }
 
 gint
@@ -729,3 +755,11 @@ e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar,
 
 	g_object_notify (G_OBJECT (bar), "expanded");
 }
+
+EAttachmentStore *
+e_mail_attachment_bar_get_store (EMailAttachmentBar *bar)
+{
+	g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), NULL);
+
+	return E_ATTACHMENT_STORE (bar->priv->model);
+}
diff --git a/mail/e-mail-attachment-bar.h b/mail/e-mail-attachment-bar.h
index 93c1b89..b83d973 100644
--- a/mail/e-mail-attachment-bar.h
+++ b/mail/e-mail-attachment-bar.h
@@ -60,7 +60,7 @@ struct _EMailAttachmentBarClass {
 };
 
 GType		e_mail_attachment_bar_get_type	(void);
-GtkWidget *	e_mail_attachment_bar_new	(void);
+GtkWidget *	e_mail_attachment_bar_new	(EAttachmentStore *store);
 gint		e_mail_attachment_bar_get_active_view
 						(EMailAttachmentBar *bar);
 void		e_mail_attachment_bar_set_active_view
@@ -71,6 +71,8 @@ gboolean	e_mail_attachment_bar_get_expanded
 void		e_mail_attachment_bar_set_expanded
 						(EMailAttachmentBar *bar,
 						 gboolean expanded);
+EAttachmentStore *
+		e_mail_attachment_bar_get_store	(EMailAttachmentBar *bar);
 
 G_END_DECLS
 
diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c
index 0dbb3d0..806980d 100644
--- a/mail/e-mail-browser.c
+++ b/mail/e-mail-browser.c
@@ -55,7 +55,8 @@ struct _EMailBrowserPrivate {
 	EMailBackend *backend;
 	GtkUIManager *ui_manager;
 	EFocusTracker *focus_tracker;
-	EMFormatHTMLDisplay *formatter;
+
+	EMFormatWriteMode mode;
 
 	GtkWidget *main_menu;
 	GtkWidget *main_toolbar;
@@ -74,7 +75,8 @@ enum {
 	PROP_GROUP_BY_THREADS,
 	PROP_SHOW_DELETED,
 	PROP_REPLY_STYLE,
-	PROP_UI_MANAGER
+	PROP_UI_MANAGER,
+	PROP_DISPLAY_MODE,
 };
 
 static gpointer parent_class;
@@ -260,11 +262,10 @@ static void
 mail_browser_message_selected_cb (EMailBrowser *browser,
                                   const gchar *uid)
 {
-	EMFormatHTML *formatter;
 	CamelMessageInfo *info;
 	CamelFolder *folder;
 	EMailReader *reader;
-	EWebView *web_view;
+	EMailDisplay *display;
 	const gchar *title;
 	guint32 state;
 
@@ -276,8 +277,7 @@ mail_browser_message_selected_cb (EMailBrowser *browser,
 		return;
 
 	folder = e_mail_reader_get_folder (reader);
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
+	display = e_mail_reader_get_mail_display (reader);
 
 	info = camel_folder_get_message_info (folder, uid);
 
@@ -289,7 +289,7 @@ mail_browser_message_selected_cb (EMailBrowser *browser,
 		title = _("(No Subject)");
 
 	gtk_window_set_title (GTK_WINDOW (browser), title);
-	gtk_widget_grab_focus (GTK_WIDGET (web_view));
+	gtk_widget_grab_focus (GTK_WIDGET (display));
 
 	camel_message_info_set_flags (
 		info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
@@ -319,7 +319,6 @@ mail_browser_popup_event_cb (EMailBrowser *browser,
                              GdkEventButton *event,
                              const gchar *uri)
 {
-	EMFormatHTML *formatter;
 	EMailReader *reader;
 	EWebView *web_view;
 	GtkMenu *menu;
@@ -329,8 +328,7 @@ mail_browser_popup_event_cb (EMailBrowser *browser,
 		return FALSE;
 
 	reader = E_MAIL_READER (browser);
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
+	web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
 
 	if (e_web_view_get_cursor_image (web_view) != NULL)
 		return FALSE;
@@ -415,6 +413,11 @@ mail_browser_set_property (GObject *object,
 				E_MAIL_BROWSER (object),
 				g_value_get_boolean (value));
 			return;
+
+		case PROP_DISPLAY_MODE:
+			E_MAIL_BROWSER (object)->priv->mode =
+				g_value_get_int (value);
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -468,6 +471,11 @@ mail_browser_get_property (GObject *object,
 				value, e_mail_browser_get_ui_manager (
 				E_MAIL_BROWSER (object)));
 			return;
+
+		case PROP_DISPLAY_MODE:
+			g_value_set_int (
+				value, E_MAIL_BROWSER (object)->priv->mode);
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -495,11 +503,6 @@ mail_browser_dispose (GObject *object)
 		priv->focus_tracker = NULL;
 	}
 
-	if (priv->formatter != NULL) {
-		g_object_unref (priv->formatter);
-		priv->formatter = NULL;
-	}
-
 	if (priv->main_menu != NULL) {
 		g_object_unref (priv->main_menu);
 		priv->main_menu = NULL;
@@ -534,14 +537,13 @@ static void
 mail_browser_constructed (GObject *object)
 {
 	EMailBrowser *browser;
-	EMFormatHTML *formatter;
 	EMailReader *reader;
 	EMailBackend *backend;
 	EMailSession *session;
+	EMailDisplay *display;
 	EShellBackend *shell_backend;
 	EShell *shell;
 	EFocusTracker *focus_tracker;
-	ESearchBar *search_bar;
 	GSettings *settings;
 	GtkAccelGroup *accel_group;
 	GtkActionGroup *action_group;
@@ -549,7 +551,6 @@ mail_browser_constructed (GObject *object)
 	GtkUIManager *ui_manager;
 	GtkWidget *container;
 	GtkWidget *widget;
-	EWebView *web_view;
 	const gchar *domain;
 	const gchar *id;
 	guint merge_id;
@@ -558,7 +559,6 @@ mail_browser_constructed (GObject *object)
 	G_OBJECT_CLASS (parent_class)->constructed (object);
 
 	browser = E_MAIL_BROWSER (object);
-
 	reader = E_MAIL_READER (object);
 	backend = e_mail_reader_get_backend (reader);
 	session = e_mail_backend_get_session (backend);
@@ -575,9 +575,6 @@ mail_browser_constructed (GObject *object)
 	gtk_application_add_window (
 		GTK_APPLICATION (shell), GTK_WINDOW (object));
 
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
-
 	/* The message list is a widget, but it is not shown in the browser.
 	 * Unfortunately, the widget is inseparable from its model, and the
 	 * model is all we need. */
@@ -592,15 +589,20 @@ mail_browser_constructed (GObject *object)
 		browser->priv->message_list, "message-list-built",
 		G_CALLBACK (mail_browser_message_list_built_cb), object);
 
+	display = g_object_new (E_TYPE_MAIL_DISPLAY,
+			"mode", E_MAIL_BROWSER (object)->priv->mode, NULL);
+
 	g_signal_connect_swapped (
-		web_view, "popup-event",
+		display, "popup-event",
 		G_CALLBACK (mail_browser_popup_event_cb), object);
 
 	g_signal_connect_swapped (
-		web_view, "status-message",
+		display, "status-message",
 		G_CALLBACK (mail_browser_status_message_cb), object);
 
-	/* Add action groups before initializing the reader interface. */
+	widget = e_preview_pane_new (E_WEB_VIEW (display));
+	browser->priv->preview_pane = g_object_ref (widget);
+	gtk_widget_show (widget);
 
 	action_group = gtk_action_group_new (ACTION_GROUP_STANDARD);
 	gtk_action_group_set_translation_domain (action_group, domain);
@@ -613,6 +615,7 @@ mail_browser_constructed (GObject *object)
 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
 
 	/* For easy access.  Takes ownership of the reference. */
+
 	g_object_set_data_full (
 		object, ACTION_GROUP_STANDARD,
 		action_group, (GDestroyNotify) g_object_unref);
@@ -664,7 +667,6 @@ mail_browser_constructed (GObject *object)
 
 	container = widget;
 
-	/* Create the status bar before connecting proxy widgets. */
 	widget = gtk_statusbar_new ();
 	gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
 	browser->priv->statusbar = g_object_ref (widget);
@@ -682,21 +684,9 @@ mail_browser_constructed (GObject *object)
 
 	gtk_style_context_add_class (
 		gtk_widget_get_style_context (widget),
-		GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
-
-	gtk_widget_show (GTK_WIDGET (web_view));
+				     GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
 
-	widget = e_preview_pane_new (web_view);
-	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
-	browser->priv->preview_pane = g_object_ref (widget);
-	gtk_widget_show (widget);
-
-	search_bar = e_preview_pane_get_search_bar (E_PREVIEW_PANE (widget));
-
-	g_signal_connect_swapped (
-		search_bar, "changed",
-		G_CALLBACK (em_format_queue_redraw),
-		browser->priv->formatter);
+	gtk_container_add (GTK_CONTAINER (container), browser->priv->preview_pane);
 
 	/* Bind GObject properties to GSettings keys. */
 
@@ -713,8 +703,6 @@ mail_browser_constructed (GObject *object)
 	e_plugin_ui_register_manager (ui_manager, id, object);
 	e_plugin_ui_enable_manager (ui_manager, id);
 
-	e_mail_reader_connect_headers (E_MAIL_READER (reader));
-
 	e_extensible_load_extensions (E_EXTENSIBLE (object));
 }
 
@@ -772,14 +760,15 @@ mail_browser_get_hide_deleted (EMailReader *reader)
 	return !e_mail_browser_get_show_deleted (browser);
 }
 
-static EMFormatHTML *
-mail_browser_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_browser_get_mail_display (EMailReader *reader)
 {
-	EMailBrowser *browser;
+	EMailBrowserPrivate *priv;
 
-	browser = E_MAIL_BROWSER (reader);
+	priv = E_MAIL_BROWSER_GET_PRIVATE (E_MAIL_BROWSER (reader));
 
-	return EM_FORMAT_HTML (browser->priv->formatter);
+	return E_MAIL_DISPLAY (e_preview_pane_get_web_view (
+					E_PREVIEW_PANE (priv->preview_pane)));
 }
 
 static GtkWidget *
@@ -916,6 +905,18 @@ e_mail_browser_class_init (EMailBrowserClass *class)
 			"Show deleted messages",
 			FALSE,
 			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DISPLAY_MODE,
+		g_param_spec_int (
+			"display-mode",
+			"Display Mode",
+			NULL,
+			0,
+			G_MAXINT,
+			EM_FORMAT_WRITE_MODE_NORMAL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -923,7 +924,7 @@ e_mail_browser_reader_init (EMailReaderInterface *interface)
 {
 	interface->get_action_group = mail_browser_get_action_group;
 	interface->get_backend = mail_browser_get_backend;
-	interface->get_formatter = mail_browser_get_formatter;
+	interface->get_mail_display = mail_browser_get_mail_display;
 	interface->get_hide_deleted = mail_browser_get_hide_deleted;
 	interface->get_message_list = mail_browser_get_message_list;
 	interface->get_popup_menu = mail_browser_get_popup_menu;
@@ -936,7 +937,6 @@ static void
 e_mail_browser_init (EMailBrowser *browser)
 {
 	browser->priv = E_MAIL_BROWSER_GET_PRIVATE (browser);
-	browser->priv->formatter = em_format_html_display_new ();
 
 	gtk_window_set_title (GTK_WINDOW (browser), _("Evolution"));
 	gtk_window_set_default_size (GTK_WINDOW (browser), 600, 400);
@@ -948,13 +948,22 @@ e_mail_browser_init (EMailBrowser *browser)
 }
 
 GtkWidget *
-e_mail_browser_new (EMailBackend *backend)
+e_mail_browser_new (EMailBackend *backend,
+                    CamelFolder *folder,
+                    const gchar *msg_uid,
+                    EMFormatWriteMode mode)
 {
+	GtkWidget *widget;
+
 	g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
 
-	return g_object_new (
+	widget= g_object_new (
 		E_TYPE_MAIL_BROWSER,
-		"backend", backend, NULL);
+		"backend", backend,
+		"display-mode", mode,
+		NULL);
+
+	return widget;
 }
 
 void
diff --git a/mail/e-mail-browser.h b/mail/e-mail-browser.h
index c09c85b..88f8174 100644
--- a/mail/e-mail-browser.h
+++ b/mail/e-mail-browser.h
@@ -24,6 +24,7 @@
 
 #include <mail/e-mail-backend.h>
 #include <misc/e-focus-tracker.h>
+#include <mail/e-mail-display.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_BROWSER \
@@ -60,7 +61,10 @@ struct _EMailBrowserClass {
 };
 
 GType		e_mail_browser_get_type		(void);
-GtkWidget *	e_mail_browser_new		(EMailBackend *backend);
+GtkWidget *	e_mail_browser_new		(EMailBackend *backend,
+						 CamelFolder *folder,
+						 const gchar *message_uid,
+						 EMFormatWriteMode mode);
 void		e_mail_browser_close		(EMailBrowser *browser);
 gboolean	e_mail_browser_get_show_deleted	(EMailBrowser *browser);
 void		e_mail_browser_set_show_deleted (EMailBrowser *browser,
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 7461e59..07f45ad 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -23,14 +23,33 @@
 #include <config.h>
 #endif
 
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
 #include "e-mail-display.h"
 
 #include <glib/gi18n.h>
+#include <gdk/gdk.h>
 
+#include "e-util/e-marshal.h"
 #include "e-util/e-util.h"
 #include "e-util/e-plugin-ui.h"
 #include "mail/em-composer-utils.h"
 #include "mail/em-utils.h"
+#include "mail/e-mail-request.h"
+#include "mail/em-format-html-display.h"
+#include "mail/e-mail-attachment-bar.h"
+#include "widgets/misc/e-attachment-button.h"
+
+#include <camel/camel.h>
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-requester.h>
+
+#include <JavaScriptCore/JavaScript.h>
+
+#define d(x)
+
+G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW)
 
 #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -38,13 +57,29 @@
 
 struct _EMailDisplayPrivate {
 	EMFormatHTML *formatter;
+
+	EMFormatWriteMode mode;
+	gboolean headers_collapsable;
+	gboolean headers_collapsed;
+
+	GtkActionGroup *mailto_actions;
+        GtkActionGroup *images_actions;
+
+        gint force_image_load: 1;
 };
 
 enum {
 	PROP_0,
-	PROP_FORMATTER
+	PROP_FORMATTER,
+	PROP_MODE,
+	PROP_HEADERS_COLLAPSABLE,
+	PROP_HEADERS_COLLAPSED,
 };
 
+static gpointer parent_class;
+
+static CamelDataCache *emd_global_http_cache = 0;
+
 static const gchar *ui =
 "<ui>"
 "  <popup name='context'>"
@@ -61,6 +96,15 @@ static const gchar *ui =
 "  </popup>"
 "</ui>";
 
+static const gchar *image_ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='custom-actions-2'>"
+"      <menuitem action='image-save'/>"
+"    </placeholder>"
+"  </popup>"
+"</ui>";
+
 static GtkActionEntry mailto_entries[] = {
 
 	{ "add-to-address-book",
@@ -88,7 +132,7 @@ static GtkActionEntry mailto_entries[] = {
 	  NULL,
 	  N_("Send _Reply To..."),
 	  NULL,
-	  N_("Send a reply message to this address"),  
+	  N_("Send a reply message to this address"),
 	  NULL   /* Handled by EMailReader */ },
 
 	/*** Menus ***/
@@ -101,7 +145,54 @@ static GtkActionEntry mailto_entries[] = {
 	  NULL }
 };
 
-G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW)
+static GtkActionEntry image_entries[] = {
+
+        { "image-save",
+        GTK_STOCK_SAVE,
+        N_("Save _Image..."),
+        NULL,
+        N_("Save the image to a file"),
+        NULL    /* Handled by EMailReader */ },
+
+};
+
+static void
+mail_display_webview_update_actions (EWebView *web_view,
+                                     gpointer user_data)
+{
+	const gchar *image_src;
+	gboolean visible;
+	GtkAction *action;
+
+	g_return_if_fail (web_view != NULL);
+
+	image_src = e_web_view_get_cursor_image_src (web_view);
+        visible = image_src && g_str_has_prefix (image_src, "cid:");
+	if (!visible && image_src) {
+		CamelStream *image_stream;
+
+                image_stream = camel_data_cache_get (emd_global_http_cache, "http", image_src, NULL);
+
+		visible = image_stream != NULL;
+
+		if (image_stream)
+			g_object_unref (image_stream);
+	}
+
+        action = e_web_view_get_action (web_view, "image-save");
+	if (action)
+		gtk_action_set_visible (action, visible);
+}
+
+static void
+formatter_image_loading_policy_changed_cb (GObject *object,
+                                           GParamSpec *pspec,
+                                           gpointer user_data)
+{
+	EMailDisplay *display = user_data;
+
+	e_mail_display_load_images (display);
+}
 
 static void
 mail_display_update_formatter_colors (EMailDisplay *display)
@@ -115,6 +206,9 @@ mail_display_update_formatter_colors (EMailDisplay *display)
 	state = gtk_widget_get_state (GTK_WIDGET (display));
 	formatter = display->priv->formatter;
 
+	if (!display->priv->formatter)
+		return;
+
 	style = gtk_widget_get_style (GTK_WIDGET (display));
 	if (style == NULL)
 		return;
@@ -156,6 +250,21 @@ mail_display_set_property (GObject *object,
 				E_MAIL_DISPLAY (object),
 				g_value_get_object (value));
 			return;
+		case PROP_MODE:
+			e_mail_display_set_mode (
+				E_MAIL_DISPLAY (object),
+				g_value_get_int (value));
+			return;
+		case PROP_HEADERS_COLLAPSABLE:
+			e_mail_display_set_headers_collapsable (
+				E_MAIL_DISPLAY (object),
+				g_value_get_boolean (value));
+			return;
+		case PROP_HEADERS_COLLAPSED:
+			e_mail_display_set_headers_collapsed (
+				E_MAIL_DISPLAY (object),
+				g_value_get_boolean (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -173,6 +282,21 @@ mail_display_get_property (GObject *object,
 				value, e_mail_display_get_formatter (
 				E_MAIL_DISPLAY (object)));
 			return;
+		case PROP_MODE:
+			g_value_set_int (
+				value, e_mail_display_get_mode (
+				E_MAIL_DISPLAY (object)));
+			return;
+		case PROP_HEADERS_COLLAPSABLE:
+			g_value_set_boolean (
+				value, e_mail_display_get_headers_collapsable (
+				E_MAIL_DISPLAY (object)));
+			return;
+		case PROP_HEADERS_COLLAPSED:
+			g_value_set_boolean (
+				value, e_mail_display_get_headers_collapsed (
+				E_MAIL_DISPLAY (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -191,14 +315,14 @@ mail_display_dispose (GObject *object)
 	}
 
 	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object);
+	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
 mail_display_realize (GtkWidget *widget)
 {
 	/* Chain up to parent's realize() method. */
-	GTK_WIDGET_CLASS (e_mail_display_parent_class)->realize (widget);
+	GTK_WIDGET_CLASS (parent_class)->realize (widget);
 
 	mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
 }
@@ -207,62 +331,28 @@ static void
 mail_display_style_set (GtkWidget *widget,
                         GtkStyle *previous_style)
 {
-	EMailDisplayPrivate *priv;
+	EMailDisplay *display = E_MAIL_DISPLAY (widget);
 
-	priv = E_MAIL_DISPLAY_GET_PRIVATE (widget);
+	mail_display_update_formatter_colors (display);
 
 	/* Chain up to parent's style_set() method. */
-	GTK_WIDGET_CLASS (e_mail_display_parent_class)->
-		style_set (widget, previous_style);
-
-	mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
-	em_format_queue_redraw (EM_FORMAT (priv->formatter));
-}
-
-static void
-mail_display_load_string (EWebView *web_view,
-                          const gchar *string)
-{
-	EMailDisplayPrivate *priv;
-
-	priv = E_MAIL_DISPLAY_GET_PRIVATE (web_view);
-	g_return_if_fail (priv->formatter != NULL);
-
-	if (em_format_busy (EM_FORMAT (priv->formatter)))
-		return;
-
-	/* Chain up to parent's load_string() method. */
-	E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
-		load_string (web_view, string);
-}
-
-static void
-mail_display_url_requested (GtkHTML *html,
-                            const gchar *uri,
-                            GtkHTMLStream *stream)
-{
-	/* XXX Sadly, we must block the default method
-	 *     until EMFormatHTML is made asynchronous. */
+	GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
 }
 
 static gboolean
 mail_display_process_mailto (EWebView *web_view,
-                             const gchar *mailto_uri)
+                             const gchar *mailto_uri,
+                             gpointer user_data)
 {
-	g_return_val_if_fail (web_view != NULL, FALSE);
+	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
 	g_return_val_if_fail (mailto_uri != NULL, FALSE);
-	g_return_val_if_fail (E_IS_MAIL_DISPLAY (web_view), FALSE);
 
 	if (g_ascii_strncasecmp (mailto_uri, "mailto:";, 7) == 0) {
-		EMailDisplayPrivate *priv;
 		EMFormat *format;
 		CamelFolder *folder = NULL;
 		EShell *shell;
 
-		priv = E_MAIL_DISPLAY_GET_PRIVATE (web_view);
-		g_return_val_if_fail (priv->formatter != NULL, FALSE);
-
-		format = EM_FORMAT (priv->formatter);
+		format = (EMFormat *) E_MAIL_DISPLAY (web_view)->priv->formatter;
 
 		if (format != NULL && format->folder != NULL)
 			folder = format->folder;
@@ -277,79 +367,825 @@ mail_display_process_mailto (EWebView *web_view,
 	return FALSE;
 }
 
-static void
-mail_display_link_clicked (GtkHTML *html,
-                           const gchar *uri)
+static gboolean
+mail_display_link_clicked (WebKitWebView *web_view,
+                           WebKitWebFrame *frame,
+                           WebKitNetworkRequest *request,
+                           WebKitWebNavigationAction *navigation_action,
+                           WebKitWebPolicyDecision *policy_decision,
+                           gpointer user_data)
 {
-	EMailDisplayPrivate *priv;
+	EMailDisplay *display;
+	const gchar *uri = webkit_network_request_get_uri (request);
 
-	priv = E_MAIL_DISPLAY_GET_PRIVATE (html);
-	g_return_if_fail (priv->formatter != NULL);
-
-	if (g_str_has_prefix (uri, "##")) {
-		guint32 flags;
-
-		flags = priv->formatter->header_wrap_flags;
-
-		if (strcmp (uri, "##TO##") == 0) {
-			if (!(flags & EM_FORMAT_HTML_HEADER_TO))
-				flags |= EM_FORMAT_HTML_HEADER_TO;
-			else
-				flags &= ~EM_FORMAT_HTML_HEADER_TO;
-		} else if (strcmp (uri, "##CC##") == 0) {
-			if (!(flags & EM_FORMAT_HTML_HEADER_CC))
-				flags |= EM_FORMAT_HTML_HEADER_CC;
-			else
-				flags &= ~EM_FORMAT_HTML_HEADER_CC;
-		} else if (strcmp (uri, "##BCC##") == 0) {
-			if (!(flags & EM_FORMAT_HTML_HEADER_BCC))
-				flags |= EM_FORMAT_HTML_HEADER_BCC;
-			else
-				flags &= ~EM_FORMAT_HTML_HEADER_BCC;
-		} else if (strcmp (uri, "##HEADERS##") == 0) {
-			EMFormatHTMLHeadersState state;
-
-			state = em_format_html_get_headers_state (
-				priv->formatter);
-
-			if (state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED)
-				state = EM_FORMAT_HTML_HEADERS_STATE_EXPANDED;
-			else
-				state = EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED;
-
-			em_format_html_set_headers_state (
-				priv->formatter, state);
-		}
+	display = E_MAIL_DISPLAY (web_view);
+	if (display->priv->formatter == NULL)
+		return FALSE;
 
-		priv->formatter->header_wrap_flags = flags;
-		em_format_queue_redraw (EM_FORMAT (priv->formatter));
-
-	} else if (mail_display_process_mailto (E_WEB_VIEW (html), uri)) {
+	if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) {
 		/* do nothing, function handled the "mailto:"; uri already */
-	} else if (*uri == '#')
-		gtk_html_jump_to_anchor (html, uri + 1);
+		webkit_web_policy_decision_ignore (policy_decision);
+		return TRUE;
 
-	else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0)
+	} else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
 		/* ignore */ ;
+		webkit_web_policy_decision_ignore (policy_decision);
+		return TRUE;
 
-	else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0)
+	} else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
 		/* ignore */ ;
+		webkit_web_policy_decision_ignore (policy_decision);
+		return TRUE;
+
+	}
+
+	/* Let webkit handle it */
+	return FALSE;
+}
+
+static void
+webkit_request_load_from_file (WebKitNetworkRequest *request,
+                               const gchar *path)
+{
+	gchar *data = NULL;
+	gsize length = 0;
+	gboolean status;
+	gchar *b64, *new_uri;
+	gchar *ct;
+
+	status = g_file_get_contents (path, &data, &length, NULL);
+	if (!status)
+		return;
+
+	b64 = g_base64_encode ((guchar *) data, length);
+	ct = g_content_type_guess (path, NULL, 0, NULL);
+
+	new_uri =  g_strdup_printf ("data:%s;base64,%s", ct, b64);
+	webkit_network_request_set_uri (request, new_uri);
+
+	g_free (b64);
+	g_free (new_uri);
+	g_free (ct);
+	g_free (data);
+}
+
+static void
+mail_display_resource_requested (WebKitWebView *web_view,
+                                 WebKitWebFrame *frame,
+                                 WebKitWebResource *resource,
+                                 WebKitNetworkRequest *request,
+                                 WebKitNetworkResponse *response,
+                                 gpointer user_data)
+{
+	EMailDisplay *display = E_MAIL_DISPLAY (web_view);
+	EMFormat *formatter = EM_FORMAT (display->priv->formatter);
+	const gchar *uri = webkit_network_request_get_uri (request);
+
+	if (!formatter) {
+                webkit_network_request_set_uri (request, "invalid://uri");
+		return;
+	}
+
+        /* Redirect cid:part_id to mail://mail_id/cid:part_id */
+        if (g_str_has_prefix (uri, "cid:")) {
+
+		/* Always write raw content of CID object */
+		gchar *new_uri = em_format_build_mail_uri (formatter->folder,
+			formatter->message_uid,
+			"part_id", G_TYPE_STRING, uri,
+			"mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, NULL);
+
+		webkit_network_request_set_uri (request, new_uri);
+
+		g_free (new_uri);
+
+        /* WebKit won't allow to load a local file when displaing "remote" mail://,
+	   protocol, so we need to handle this manually */
+        } else if (g_str_has_prefix (uri, "file:")) {
+		gchar *path;
+
+		path = g_filename_from_uri (uri, NULL, NULL);
+		if (!path)
+			return;
+
+		webkit_request_load_from_file (request, path);
+
+		g_free (path);
+
+        /* Redirect http(s) request to evo-http(s) protocol. See EMailRequest for
+         * further details about this. */
+        } else if (g_str_has_prefix (uri, "http:") || g_str_has_prefix (uri, "https")) {
+
+		gchar *new_uri, *mail_uri, *enc;
+		SoupURI *soup_uri;
+		GHashTable *query;
+		gchar *uri_md5;
+		CamelStream *stream;
+
+                /* Open Evolution's cache */
+		uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+		stream = camel_data_cache_get (
+                                emd_global_http_cache, "http", uri_md5, NULL);
+		g_free (uri_md5);
+
+                /* If the URI is not cached and we are not allowed to load it
+                 * then redirect to invalid URI, so that webkit would display
+                 * a native placeholder for it. */
+		if (!stream && !display->priv->force_image_load &&
+		    !em_format_html_can_load_images (display->priv->formatter)) {
+                        webkit_network_request_set_uri (request, "invalid://protocol");
+			return;
+		}
+
+                new_uri = g_strconcat ("evo-", uri, NULL);
+		mail_uri = em_format_build_mail_uri (formatter->folder,
+				formatter->message_uid, NULL, NULL);
+
+		soup_uri = soup_uri_new (new_uri);
+		if (soup_uri->query) {
+			query = soup_form_decode (soup_uri->query);
+		} else {
+			query = g_hash_table_new_full (g_str_hash, g_str_equal,
+						       g_free, g_free);
+		}
+		enc = soup_uri_encode (mail_uri, NULL);
+                g_hash_table_insert (query, g_strdup ("__evo-mail"), enc);
+
+		if (display->priv->force_image_load) {
+			g_hash_table_insert (query,
+                                g_strdup ("__evo-load-images"),
+                                             g_strdup ("true"));
+		}
+
+		g_free (mail_uri);
+
+		soup_uri_set_query_from_form (soup_uri, query);
+		g_free (new_uri);
+
+		new_uri = soup_uri_to_string (soup_uri, FALSE);
+		webkit_network_request_set_uri (request, new_uri);
+
+		g_free (new_uri);
+		soup_uri_free (soup_uri);
+		g_hash_table_unref (query);
+	}
+}
+
+static WebKitDOMElement *
+find_element_by_id (WebKitDOMDocument *document,
+                    const gchar *id)
+{
+	WebKitDOMNodeList *frames;
+	WebKitDOMElement *element;
+	gulong i, length;
+
+        /* Try to look up the element in this DOM document */
+	element = webkit_dom_document_get_element_by_id (document, id);
+	if (element)
+		return element;
+
+        /* If the element is not here then recursively scan all frames */
+        frames = webkit_dom_document_get_elements_by_tag_name(document, "iframe");
+	length = webkit_dom_node_list_get_length (frames);
+	for (i = 0; i < length; i++) {
+
+		WebKitDOMHTMLIFrameElement *iframe =
+			WEBKIT_DOM_HTML_IFRAME_ELEMENT (
+				webkit_dom_node_list_item (frames, i));
+
+		WebKitDOMDocument *frame_doc =
+			webkit_dom_html_iframe_element_get_content_document (iframe);
+
+		WebKitDOMElement *el =
+			find_element_by_id (frame_doc, id);
+
+		if (el)
+			return el;
+	}
+
+	return NULL;
+}
+
+static void
+mail_display_plugin_widget_resize (GObject *object,
+                                   gpointer dummy,
+                                   EMailDisplay *display)
+{
+	GtkWidget *widget;
+	WebKitDOMElement *parent_element;
+	gchar *dim;
+	gint height;
+
+	widget = GTK_WIDGET (object);
+	gtk_widget_get_preferred_height (widget, &height, NULL);
+        parent_element = g_object_get_data (object, "parent_element");
+
+	if (!parent_element || !WEBKIT_DOM_IS_ELEMENT (parent_element)) {
+                d(printf("%s: %s does not have (valid) parent element!\n",
+                        G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
+		return;
+	}
+
+        /* Int -> Str */
+	dim = g_strdup_printf ("%d", height);
+
+        /* Set height of the containment <object> to match height of the
+         * GtkWidget it contains */
+	webkit_dom_html_object_element_set_height (
+		WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim);
+	g_free (dim);
+}
+
+static void
+mail_display_plugin_widget_realize_cb (GtkWidget *widget,
+                                       gpointer user_data)
+{
+        /* Initial resize of the <object> element when the widget
+         * is displayed for the first time. */
+	mail_display_plugin_widget_resize (G_OBJECT (widget), NULL, user_data);
+}
+
+static void
+plugin_widget_set_parent_element (GtkWidget *widget,
+                                  EMailDisplay *display)
+{
+	const gchar *uri;
+	WebKitDOMDocument *document;
+	WebKitDOMElement *element;
+
+        uri = g_object_get_data (G_OBJECT (widget), "uri");
+	if (!uri || !*uri)
+		return;
+
+	document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
+	element = find_element_by_id (document, uri);
+
+	if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) {
+                g_warning ("Failed to find parent <object> for '%s' - no ID set?", uri);
+		return;
+	}
+
+        /* Assign the WebKitDOMElement to "parent_element" data of the GtkWidget
+         * and the GtkWidget to "widget" data of the DOM Element */
+        g_object_set_data (G_OBJECT (widget), "parent_element", element);
+        g_object_set_data (G_OBJECT (element), "widget", widget);
+}
+
+static void
+attachment_button_expanded (GObject *object,
+                            GParamSpec *pspec,
+                            gpointer user_data)
+{
+	EAttachmentButton *button = E_ATTACHMENT_BUTTON (object);
+	WebKitDOMElement *attachment = user_data;
+	WebKitDOMCSSStyleDeclaration *css;
+	gboolean expanded;
+
+        d(printf("Attachment button %s (%p) expansion state toggled!\n",
+                (gchar *) g_object_get_data (object, "uri"), object));
+
+	expanded = e_attachment_button_get_expanded (button) &&
+			gtk_widget_get_visible (GTK_WIDGET (button));
+
+	if (!WEBKIT_DOM_IS_ELEMENT (attachment)) {
+                d(printf("%s: Parent element for button %s does not exist!\n",
+                        G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
+		return;
+	}
+
+        /* Show or hide the DIV which contains the attachment (iframe, image...) */
+	css = webkit_dom_element_get_style (attachment);
+	webkit_dom_css_style_declaration_set_property (
+                css, "display", expanded ? "block" : "none", "", NULL);
+}
+
+static void
+constraint_widget_visibility (GObject *object,
+                              GParamSpec *pspec,
+                              gpointer user_data)
+{
+	GtkWidget *widget = GTK_WIDGET (object);
+	EAttachmentButton *button = user_data;
+
+	gboolean can_show = e_attachment_button_get_expanded (button);
+	gboolean is_visible = gtk_widget_get_visible (widget);
+
+	if (is_visible && !can_show)
+		gtk_widget_hide (widget);
+	else if (!is_visible && can_show)
+		gtk_widget_show (widget);
+
+        /* Otherwise it's OK */
+}
+
+static void
+bind_iframe_content_visibility (EAttachmentButton *button,
+                                WebKitDOMElement *iframe)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMNodeList *nodes;
+	gulong i, length;
+
+	if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe))
+		return;
+
+	document = webkit_dom_html_iframe_element_get_content_document (
+			WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
+        nodes = webkit_dom_document_get_elements_by_tag_name (document, "object");
+	length = webkit_dom_node_list_get_length (nodes);
 
-	else {
-		/* Chain up to parent's link_clicked() method. */
-		GTK_HTML_CLASS (e_mail_display_parent_class)->
-			link_clicked (html, uri);
+        d(printf("Found %ld objects within iframe %s\n", length,
+		webkit_dom_html_iframe_element_get_name (
+			WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe))));
+
+        /* Iterate through all <object>s and bind visibility of their widget
+         * with expanded-state of related attachment button */
+	for (i = 0; i < length; i++) {
+
+		WebKitDOMNode *node = webkit_dom_node_list_item (nodes, i);
+		GtkWidget *widget;
+
+                widget = g_object_get_data (G_OBJECT (node), "widget");
+		if (!widget)
+			continue;
+
+                d(printf("Binding visibility of widget %s (%p) with button %s (%p)\n",
+                        (gchar *) g_object_get_data (G_OBJECT (widget), "uri"), widget,
+                        (gchar *) g_object_get_data (G_OBJECT (button), "uri"), button));
+
+		g_object_bind_property (
+                        button, "expanded",
+                        widget, "visible",
+			G_BINDING_SYNC_CREATE);
+
+                /* Ensure that someone won't attempt to _show() the widget when
+                 * it is supposed to be hidden and vice versa. */
+                g_signal_connect (widget, "notify::visible",
+			G_CALLBACK (constraint_widget_visibility), button);
 	}
 }
 
 static void
+bind_attachment_iframe_visibility (GObject *object,
+                                   GParamSpec *pspec,
+                                   gpointer user_data)
+{
+	WebKitWebFrame *webframe;
+	const gchar *frame_name;
+	gchar *button_uri;
+	WebKitDOMDocument *document;
+	WebKitDOMElement *attachment;
+	WebKitDOMElement *button_element;
+	WebKitDOMNodeList *nodes;
+	gulong i, length;
+	GtkWidget *button;
+
+        /* Whenever an <iframe> is loaded, bind visibility of all GtkWidgets
+         * the document within the <iframe> contains with "expanded" property
+         * of the EAttachmentButton */
+
+	webframe = WEBKIT_WEB_FRAME (object);
+	if (webkit_web_frame_get_load_status (webframe) != WEBKIT_LOAD_FINISHED)
+		return;
+
+	frame_name = webkit_web_frame_get_name (webframe);
+
+        d(printf("Rebinding visibility of frame %s because it's URL changed\n",
+		 frame_name));
+
+        /* Get DOMDocument of the main document */
+	document = webkit_web_view_get_dom_document (
+		webkit_web_frame_get_web_view (webframe));
+	if (!document)
+		return;
+
+        /* Find the <DIV> containing the <iframe> and related EAttachmentButton
+         * within the DOM */
+	attachment = find_element_by_id (document, frame_name);
+	if (!attachment)
+		return;
+
+        button_uri = g_strconcat (frame_name, ".attachment_button", NULL);
+	button_element = find_element_by_id (document, button_uri);
+	g_free (button_uri);
+	if (!button_element)
+		return;
+
+        button = g_object_get_data (G_OBJECT (button_element), "widget");
+
+        /* Get <iframe> representing the attachment content */
+        nodes = webkit_dom_element_get_elements_by_tag_name (attachment, "iframe");
+	length = webkit_dom_node_list_get_length (nodes);
+	for (i = 0; i < length; i++) {
+
+		WebKitDOMNode *node =
+			webkit_dom_node_list_item (nodes, i);
+
+		if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node))
+			continue;
+
+                /* Bind visibility of all GtkWidget within the
+                 * iframe with "expanded" property of the button */
+		bind_iframe_content_visibility (
+			E_ATTACHMENT_BUTTON (button),
+			WEBKIT_DOM_ELEMENT (node));
+	}
+}
+
+static GtkWidget *
+mail_display_plugin_widget_requested (WebKitWebView *web_view,
+                                      gchar *mime_type,
+                                      gchar *uri,
+                                      GHashTable *param,
+                                      gpointer user_data)
+{
+	EMFormat *emf;
+	EMailDisplay *display;
+	EMFormatPURI *puri;
+	GtkWidget *widget;
+	gchar *puri_uri;
+
+        puri_uri = g_hash_table_lookup (param, "data");
+        if (!puri_uri || !g_str_has_prefix (uri, "mail://"))
+		return NULL;
+
+	display = E_MAIL_DISPLAY (web_view);
+	emf = (EMFormat *) display->priv->formatter;
+
+	puri = em_format_find_puri (emf, puri_uri);
+	if (!puri) {
+		return NULL;
+	}
+
+	if (puri->widget_func)
+		widget = puri->widget_func (emf, puri, NULL);
+	else
+		widget = NULL;
+
+	if (!widget)
+		return NULL;
+
+	if (E_IS_ATTACHMENT_BUTTON (widget)) {
+                /* Attachment button has URI different then the actual PURI because
+                 * that URI identifies the attachment itself */
+                gchar *button_uri = g_strconcat (puri_uri, ".attachment_button", NULL);
+                g_object_set_data_full (G_OBJECT (widget), "uri",
+			button_uri, (GDestroyNotify) g_free);
+	} else {
+                g_object_set_data_full (G_OBJECT (widget), "uri",
+			g_strdup (puri_uri), (GDestroyNotify) g_free);
+	}
+
+        /* Set widget's <object> container as GObject data "parent_element" */
+	plugin_widget_set_parent_element (widget, display);
+
+        /* Resizing a GtkWidget requires changing size of parent
+         * <object> HTML element in DOM. */
+        g_signal_connect (widget, "realize",
+			  G_CALLBACK (mail_display_plugin_widget_realize_cb), display);
+        g_signal_connect (widget, "size-allocate",
+			  G_CALLBACK (mail_display_plugin_widget_resize), display);
+
+        /* Embed the attachment bar into the GtkBox before we do anything
+         * further with the widget. */
+	if (E_IS_MAIL_ATTACHMENT_BAR (widget)) {
+
+                /* When EMailAttachmentBar is expanded/collapsed it does not
+                 * emit size-allocate signal despite it changes it's height. */
+		GtkWidget *box = NULL;
+
+                /* Only when packed in box, EMailAttachmentBar reports correct 
+                 * height */
+		box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+		gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+
+                g_signal_connect (widget, "notify::expanded",
+			G_CALLBACK (mail_display_plugin_widget_resize), display);
+                g_signal_connect (widget, "notify::active-view",
+			G_CALLBACK (mail_display_plugin_widget_resize), display);
+
+                /* Show the EAttachmentBar but not the containing layout */
+		gtk_widget_show (widget);
+
+		widget = box;
+
+	} else if (E_IS_ATTACHMENT_BUTTON (widget)) {
+
+                /* Bind visibility of DOM element containing related
+                 * attachment with 'expanded' property of this
+                 * attachment button. */
+		WebKitDOMElement *attachment;
+		WebKitDOMDocument *document;
+
+		document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
+		attachment = find_element_by_id (document, puri_uri);
+		if (!attachment) {
+			e_attachment_button_set_expandable (
+				E_ATTACHMENT_BUTTON (widget), FALSE);
+		} else {
+			const CamelContentDisposition *disposition;
+			WebKitDOMNodeList *nodes;
+			gulong i, length;
+
+                        /* Show/hide the attachment when the EAttachmentButton
+                         * is expanded/collapsed or shown/hidden */
+                        g_signal_connect_data (widget, "notify::expanded",
+				G_CALLBACK (attachment_button_expanded),
+				g_object_ref (attachment), (GClosureNotify) g_object_unref, 0);
+                        g_signal_connect_data (widget, "notify::visible",
+				G_CALLBACK (attachment_button_expanded),
+				g_object_ref (attachment), (GClosureNotify) g_object_unref, 0);
+                        /* Initial synchronization */
+			attachment_button_expanded (G_OBJECT (widget),
+				NULL, attachment);
+
+                        /* Find all <iframes> within the attachment and bind
+                         * it's visiblity to expanded state of the attachment btn */
+			nodes = webkit_dom_element_get_elements_by_tag_name (
+                                        attachment, "iframe");
+			length = webkit_dom_node_list_get_length (nodes);
+			for (i = 0; i < length; i++) {
+
+				WebKitDOMNode *node =
+					webkit_dom_node_list_item (nodes, i);
+
+				if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node))
+					continue;
+
+				bind_iframe_content_visibility (
+					E_ATTACHMENT_BUTTON (widget),
+					WEBKIT_DOM_ELEMENT (node));
+			}
+
+                        /* Expand inlined attachments */
+			disposition =
+				camel_mime_part_get_content_disposition (puri->part);
+			if (disposition &&
+			    g_ascii_strncasecmp (
+                                disposition->disposition, "inline", 6) == 0) {
+
+				e_attachment_button_set_expanded (
+					E_ATTACHMENT_BUTTON (widget), TRUE);
+			}
+		}
+	}
+
+        d(printf("Created widget %s (%p)\n", puri_uri, widget));
+	return widget;
+}
+
+static void
+toggle_headers_visibility (WebKitDOMElement *button,
+                           WebKitDOMEvent *event,
+                           WebKitWebView *web_view)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMElement *short_headers, *full_headers;
+	WebKitDOMCSSStyleDeclaration *css_short, *css_full;
+	gboolean expanded;
+	const gchar *path;
+
+	document = webkit_web_view_get_dom_document (web_view);
+
+	short_headers = webkit_dom_document_get_element_by_id (
+                document, "__evo-short-headers");
+	if (!short_headers)
+		return;
+
+	css_short = webkit_dom_element_get_style (short_headers);
+
+	full_headers = webkit_dom_document_get_element_by_id (
+                document, "__evo-full-headers");
+	if (!full_headers)
+		return;
+
+	css_full = webkit_dom_element_get_style (full_headers);
+
+	expanded = (g_strcmp0 (webkit_dom_css_style_declaration_get_property_value (
+                        css_full, "display"), "block") == 0);
+
+        webkit_dom_css_style_declaration_set_property (css_full, "display",
+                expanded ? "none" : "block", "", NULL);
+        webkit_dom_css_style_declaration_set_property (css_short, "display",
+                expanded ? "block" : "none", "", NULL);
+
+	if (expanded)
+                path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
+	else
+                path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+
+	webkit_dom_html_image_element_set_src (
+		WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+
+	e_mail_display_set_headers_collapsed (E_MAIL_DISPLAY (web_view), expanded);
+
+        d(printf("Headers %s!\n", expanded ? "collapsed" : "expanded"));
+}
+
+static const gchar* addresses[] = { "to", "cc", "bcc" };
+
+static void
+toggle_address_visibility (WebKitDOMElement *button,
+                           WebKitDOMEvent *event,
+                           const gchar *address)
+{
+	WebKitDOMElement *full_addr, *ellipsis;
+	WebKitDOMCSSStyleDeclaration *css_full, *css_ellipsis;
+	WebKitDOMDocument *document;
+	gchar *id;
+	const gchar *path;
+	gboolean expanded;
+
+	document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (button));
+
+        id = g_strconcat ("__evo-moreaddr-", address, NULL);
+	full_addr = webkit_dom_document_get_element_by_id (document, id);
+	g_free (id);
+
+	if (!full_addr)
+		return;
+
+	css_full = webkit_dom_element_get_style (full_addr);
+
+        id = g_strconcat ("__evo-moreaddr-ellipsis-", address, NULL);
+	ellipsis = webkit_dom_document_get_element_by_id (document, id);
+	g_free (id);
+
+	if (!ellipsis)
+		return;
+
+	css_ellipsis = webkit_dom_element_get_style (ellipsis);
+
+	expanded = (g_strcmp0 (
+		webkit_dom_css_style_declaration_get_property_value (
+                css_full, "display"), "inline") == 0);
+
+	webkit_dom_css_style_declaration_set_property (
+                css_full, "display", (expanded ? "none" : "inline"), "", NULL);
+	webkit_dom_css_style_declaration_set_property (
+                css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL);
+
+	if (expanded) {
+                path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
+	} else {
+                path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+	}
+
+	if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) {
+                id = g_strconcat ("__evo-moreaddr-img-", address, NULL);
+		button = webkit_dom_document_get_element_by_id (document, id);
+		g_free (id);
+
+		if (!button)
+			return;
+	}
+
+	webkit_dom_html_image_element_set_src (
+		WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+
+}
+
+static void
+setup_DOM_bindings (GObject *object,
+                    GParamSpec *pspec,
+                    gpointer user_data)
+{
+	WebKitWebView *web_view;
+	WebKitWebFrame *frame;
+	WebKitLoadStatus load_status;
+	WebKitDOMDocument *document;
+	WebKitDOMElement *button;
+	gint i = 0;
+
+	frame = WEBKIT_WEB_FRAME (object);
+	load_status = webkit_web_frame_get_load_status (frame);
+	if (load_status != WEBKIT_LOAD_FINISHED)
+		return;
+
+	web_view = webkit_web_frame_get_web_view (frame);
+	document = webkit_web_view_get_dom_document (web_view);
+
+	button = webkit_dom_document_get_element_by_id (
+                        document, "__evo-collapse-headers-img");
+	if (!button)
+		return;
+
+        d(printf("Conntecting to __evo-collapsable-headers-img::click event\n"));
+
+	webkit_dom_event_target_add_event_listener (
+                WEBKIT_DOM_EVENT_TARGET (button), "click",
+		G_CALLBACK (toggle_headers_visibility), FALSE, web_view);
+
+	for (i = 0; i < 3; i++) {
+		gchar *id;
+                id = g_strconcat ("__evo-moreaddr-img-", addresses[i], NULL);
+		button = webkit_dom_document_get_element_by_id (document, id);
+		g_free (id);
+
+		if (!button)
+			continue;
+
+		webkit_dom_event_target_add_event_listener (
+                        WEBKIT_DOM_EVENT_TARGET (button), "click",
+			G_CALLBACK (toggle_address_visibility), FALSE,
+			(gpointer) addresses[i]);
+
+                id = g_strconcat ("__evo-moreaddr-ellipsis-", addresses[i], NULL);
+		button = webkit_dom_document_get_element_by_id (document, id);
+		g_free (id);
+
+		if (!button)
+			continue;
+
+		webkit_dom_event_target_add_event_listener (
+                        WEBKIT_DOM_EVENT_TARGET (button), "click",
+			G_CALLBACK (toggle_address_visibility), FALSE,
+			(gpointer) addresses[i]);
+	}
+}
+
+static void
+puri_bind_dom (GObject *object,
+               GParamSpec *pspec,
+               gpointer user_data)
+{
+	WebKitWebFrame *frame;
+	WebKitLoadStatus load_status;
+	WebKitWebView *web_view;
+	WebKitDOMDocument *document;
+	EMailDisplay *display;
+	GList *iter;
+	EMFormat *emf;
+	const gchar *frame_puri;
+
+	frame = WEBKIT_WEB_FRAME (object);
+	load_status = webkit_web_frame_get_load_status (frame);
+
+	if (load_status != WEBKIT_LOAD_FINISHED)
+		return;
+
+	frame_puri = webkit_web_frame_get_name (frame);
+	web_view = webkit_web_frame_get_web_view (frame);
+	display = E_MAIL_DISPLAY (web_view);
+
+	emf = EM_FORMAT (display->priv->formatter);
+	if (!emf)
+		return;
+
+	iter = g_hash_table_lookup (
+			emf->mail_part_table,
+			webkit_web_frame_get_name (frame));
+
+	document = webkit_web_view_get_dom_document (web_view);
+
+	while (iter) {
+
+		EMFormatPURI *puri = iter->data;
+
+		if (!puri)
+			continue;
+
+		/* Iterate only the PURI rendered in the frame and all it's "subPURIs" */
+		if (!g_str_has_prefix (puri->uri, frame_puri))
+			break;
+
+		if (puri->bind_func) {
+			WebKitDOMElement *el = find_element_by_id (document, puri->uri);
+			if (el) {
+                                d(printf("bind_func for %s\n", puri->uri));
+				puri->bind_func (el, puri);
+			}
+		}
+
+		iter = iter->next;
+	}
+}
+
+static void
+mail_display_frame_created (WebKitWebView *web_view,
+                            WebKitWebFrame *frame,
+                            gpointer user_data)
+{
+        d(printf("Frame %s created!\n", webkit_web_frame_get_name (frame)));
+
+        /* Re-bind visibility of this newly created <iframe> with
+         * related EAttachmentButton whenever content of this <iframe> is
+         * (re)loaded */
+        g_signal_connect (frame, "notify::load-status",
+		G_CALLBACK (bind_attachment_iframe_visibility), NULL);
+
+	/* Call bind_func of all PURIs written in this frame */
+	g_signal_connect (frame, "notify::load-status",
+		G_CALLBACK (puri_bind_dom), NULL);
+}
+
+static void
 e_mail_display_class_init (EMailDisplayClass *class)
 {
 	GObjectClass *object_class;
 	GtkWidgetClass *widget_class;
-	EWebViewClass *web_view_class;
-	GtkHTMLClass *html_class;
 
+	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
@@ -361,14 +1197,6 @@ e_mail_display_class_init (EMailDisplayClass *class)
 	widget_class->realize = mail_display_realize;
 	widget_class->style_set = mail_display_style_set;
 
-	web_view_class = E_WEB_VIEW_CLASS (class);
-	web_view_class->load_string = mail_display_load_string;
-	web_view_class->process_mailto = mail_display_process_mailto;
-
-	html_class = GTK_HTML_CLASS (class);
-	html_class->url_requested = mail_display_url_requested;
-	html_class->link_clicked = mail_display_link_clicked;
-
 	g_object_class_install_property (
 		object_class,
 		PROP_FORMATTER,
@@ -378,38 +1206,122 @@ e_mail_display_class_init (EMailDisplayClass *class)
 			NULL,
 			EM_TYPE_FORMAT_HTML,
 			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_MODE,
+		g_param_spec_int (
+			"mode",
+			"Display Mode",
+			NULL,
+			0,
+			G_MAXINT,
+			EM_FORMAT_WRITE_MODE_NORMAL,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_HEADERS_COLLAPSABLE,
+		g_param_spec_boolean (
+			"headers-collapsable",
+			"Headers Collapsable",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_HEADERS_COLLAPSED,
+		g_param_spec_boolean (
+			"headers-collapsed",
+			"Headers Collapsed",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE));
 }
 
 static void
 e_mail_display_init (EMailDisplay *display)
 {
-	EWebView *web_view;
 	GtkUIManager *ui_manager;
-	GtkActionGroup *action_group;
 	GError *error = NULL;
-
-	web_view = E_WEB_VIEW (display);
+	SoupSession *session;
+	SoupSessionFeature *feature;
+	const gchar *user_cache_dir;
+	WebKitWebSettings *settings;
+	WebKitWebFrame *main_frame;
 
 	display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
 
-	/* EWebView's action groups are added during its instance
-	 * initialization function (like what we're in now), so it
-	 * is safe to fetch them this early in construction. */
-	action_group = e_web_view_get_action_group (web_view, "mailto");
-
-	/* We don't actually handle the actions we're adding.
-	 * EMailReader handles them.  How devious is that? */
-	gtk_action_group_add_actions (
-		action_group, mailto_entries,
-		G_N_ELEMENTS (mailto_entries), display);
-
-	/* Because we are loading from a hard-coded string, there is
-	 * no chance of I/O errors.  Failure here implies a malformed
-	 * UI definition.  Full stop. */
-	ui_manager = e_web_view_get_ui_manager (web_view);
+	display->priv->force_image_load = FALSE;
+	display->priv->mailto_actions = gtk_action_group_new ("mailto");
+	gtk_action_group_add_actions (display->priv->mailto_actions, mailto_entries,
+		G_N_ELEMENTS (mailto_entries), NULL);
+
+        display->priv->images_actions = gtk_action_group_new ("image");
+	gtk_action_group_add_actions (display->priv->images_actions, image_entries,
+		G_N_ELEMENTS (image_entries), NULL);
+
+	webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (display), TRUE);
+
+	settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (display));
+        g_object_set (settings, "enable-frame-flattening", TRUE, NULL);
+
+        g_signal_connect (display, "navigation-policy-decision-requested",
+			  G_CALLBACK (mail_display_link_clicked), NULL);
+        g_signal_connect (display, "resource-request-starting",
+			  G_CALLBACK (mail_display_resource_requested), NULL);
+        g_signal_connect (display, "process-mailto",
+			  G_CALLBACK (mail_display_process_mailto), NULL);
+        g_signal_connect (display, "update-actions",
+			  G_CALLBACK (mail_display_webview_update_actions), NULL);
+        g_signal_connect (display, "create-plugin-widget",
+			  G_CALLBACK (mail_display_plugin_widget_requested), NULL);
+        g_signal_connect (display, "frame-created",
+			  G_CALLBACK (mail_display_frame_created), NULL);
+
+	main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
+        g_signal_connect (main_frame, "notify::load-status",
+		G_CALLBACK (setup_DOM_bindings), NULL);
+	main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
+	g_signal_connect (main_frame, "notify::load-status",
+			  G_CALLBACK (puri_bind_dom), NULL);
+
+        /* Because we are loading from a hard-coded string, there is
+         * no chance of I/O errors.  Failure here implies a malformed
+         * UI definition.  Full stop. */
+	ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display));
+	gtk_ui_manager_insert_action_group (ui_manager, display->priv->mailto_actions, 0);
 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
-	if (error != NULL)
-		g_error ("%s", error->message);
+
+	if (error != NULL) {
+                g_error ("%s", error->message);
+		g_error_free (error);
+	}
+
+	error = NULL;
+	gtk_ui_manager_insert_action_group (ui_manager, display->priv->images_actions, 0);
+	gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, &error);
+
+	if (error != NULL) {
+                g_error ("%s", error->message);
+		g_error_free (error);
+	}
+
+	/* Register our own handler for our own mail:// protocol */
+	session = webkit_get_default_session ();
+	feature = SOUP_SESSION_FEATURE (soup_requester_new ());
+	soup_session_feature_add_feature (feature, E_TYPE_MAIL_REQUEST);
+	soup_session_add_feature (session, feature);
+	g_object_unref (feature);
+
+	/* cache expiry - 2 hour access, 1 day max */
+	user_cache_dir = e_get_user_cache_dir ();
+	emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL);
+	if (emd_global_http_cache) {
+		camel_data_cache_set_expire_age (emd_global_http_cache, 24 * 60 * 60);
+		camel_data_cache_set_expire_access (emd_global_http_cache, 2 * 60 * 60);
+	}
 }
 
 EMFormatHTML *
@@ -427,10 +1339,264 @@ e_mail_display_set_formatter (EMailDisplay *display,
 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
 	g_return_if_fail (EM_IS_FORMAT_HTML (formatter));
 
-	if (display->priv->formatter != NULL)
+	g_object_ref (formatter);
+
+	if (display->priv->formatter != NULL) {
+		/* The formatter might still exist after unrefing it, so 
+		 * we need to stop listening to it's request for redrawing */
+		g_signal_handlers_disconnect_by_func (
+			display->priv->formatter, e_mail_display_reload, display);
 		g_object_unref (display->priv->formatter);
+	}
+
+	display->priv->formatter = formatter;
+
+	mail_display_update_formatter_colors (display);
 
-	display->priv->formatter = g_object_ref (formatter);
+        g_signal_connect (formatter, "notify::image-loading-policy",
+		G_CALLBACK (formatter_image_loading_policy_changed_cb), display);
+	g_signal_connect_swapped (formatter, "redraw-requested",
+		G_CALLBACK (e_mail_display_reload), display);
+        g_signal_connect_swapped (formatter, "notify::charset",
+		G_CALLBACK (e_mail_display_reload), display);
 
 	g_object_notify (G_OBJECT (display), "formatter");
 }
+
+EMFormatWriteMode
+e_mail_display_get_mode (EMailDisplay *display)
+{
+	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display),
+			EM_FORMAT_WRITE_MODE_NORMAL);
+
+	return display->priv->mode;
+}
+
+void
+e_mail_display_set_mode (EMailDisplay *display,
+                         EMFormatWriteMode mode)
+{
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	if (display->priv->mode == mode)
+		return;
+
+	display->priv->mode = mode;
+
+	e_mail_display_reload (display);
+
+	g_object_notify (G_OBJECT (display), "mode");
+}
+
+gboolean
+e_mail_display_get_headers_collapsable (EMailDisplay *display)
+{
+	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
+
+	return display->priv->headers_collapsable;
+}
+
+void
+e_mail_display_set_headers_collapsable (EMailDisplay *display,
+                                        gboolean collapsable)
+{
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	if (display->priv->headers_collapsable == collapsable)
+		return;
+
+	display->priv->headers_collapsable = collapsable;
+	e_mail_display_reload (display);
+
+	g_object_notify (G_OBJECT (display), "headers-collapsable");
+}
+
+gboolean
+e_mail_display_get_headers_collapsed (EMailDisplay *display)
+{
+	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
+
+	if (display->priv->headers_collapsable)
+		return display->priv->headers_collapsed;
+
+	return FALSE;
+}
+
+void
+e_mail_display_set_headers_collapsed (EMailDisplay *display,
+                                      gboolean collapsed)
+{
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	if (display->priv->headers_collapsed == collapsed)
+		return;
+
+	display->priv->headers_collapsed = collapsed;
+
+	g_object_notify (G_OBJECT (display), "headers-collapsed");
+}
+
+void
+e_mail_display_load (EMailDisplay *display,
+                     const gchar *msg_uri)
+{
+	EMFormat *emf;
+	gchar *uri;
+
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	display->priv->force_image_load = FALSE;
+
+	emf = EM_FORMAT (display->priv->formatter);
+
+	uri = em_format_build_mail_uri (emf->folder, emf->message_uid,
+                "mode", G_TYPE_INT, display->priv->mode,
+                "headers_collapsable", G_TYPE_BOOLEAN, display->priv->headers_collapsable,
+                "headers_collapsed", G_TYPE_BOOLEAN, display->priv->headers_collapsed,
+		NULL);
+
+	e_web_view_load_uri (E_WEB_VIEW (display), uri);
+
+	g_free (uri);
+}
+
+void
+e_mail_display_reload (EMailDisplay *display)
+{
+	EWebView *web_view;
+	const gchar *uri;
+	gchar *base;
+	GString *new_uri;
+	GHashTable *table;
+	GHashTableIter table_iter;
+	gpointer key, val;
+	gchar separator;
+
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	web_view = E_WEB_VIEW (display);
+	uri = e_web_view_get_uri (web_view);
+
+	if (!uri || !*uri)
+		return;
+
+	if (strstr(uri, "?") == NULL) {
+		e_web_view_reload (web_view);
+		return;
+	}
+
+	base = g_strndup (uri, strstr (uri, "?") - uri);
+	new_uri = g_string_new (base);
+	g_free (base);
+
+        table = soup_form_decode (strstr (uri, "?") + 1);
+	g_hash_table_insert (table, g_strdup ("mode"), g_strdup_printf ("%d", display->priv->mode));
+	g_hash_table_insert (table, g_strdup ("headers_collapsable"), g_strdup_printf ("%d", display->priv->headers_collapsable));
+	g_hash_table_insert (table, g_strdup ("headers_collapsed"), g_strdup_printf ("%d", display->priv->headers_collapsed));
+
+	g_hash_table_iter_init (&table_iter, table);
+	separator = '?';
+	while (g_hash_table_iter_next (&table_iter, &key, &val)) {
+		g_string_append_printf (new_uri, "%c%s=%s", separator,
+			(gchar *) key, (gchar *) val);
+
+	if (separator == '?')
+		separator = '&';
+	}
+
+	e_web_view_load_uri (web_view, new_uri->str);
+
+	g_string_free (new_uri, TRUE);
+	g_hash_table_destroy (table);
+}
+
+GtkAction *
+e_mail_display_get_action (EMailDisplay *display,
+                           const gchar *action_name)
+{
+	GtkAction *action;
+
+	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+	g_return_val_if_fail (action_name != NULL, NULL);
+
+	action = gtk_action_group_get_action (display->priv->mailto_actions, action_name);
+	if (!action)
+		action = gtk_action_group_get_action (display->priv->images_actions, action_name);
+
+	return action;
+}
+
+void
+e_mail_display_set_status (EMailDisplay *display,
+                           const gchar *status)
+{
+	gchar *str;
+
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	str = g_strdup_printf (
+                "<!DOCTYPE>"
+                "<html>"
+                  "<head><title>Evolution Mail Display</title></head>"
+                  "<body>"
+                    "<table border=\"0\" width=\"100%%\" height=\"100%%\">"
+                      "<tr height=\"100%%\" valign=\"middle\">"
+                        "<td width=\"100%%\" align=\"center\">"
+                          "<strong>%s</strong>"
+                        "</td>"
+                      "</tr>"
+                    "</table>"
+                  "</body>"
+                "</html>", status);
+
+	e_web_view_load_string (E_WEB_VIEW (display), str);
+	g_free (str);
+
+	gtk_widget_show_all (GTK_WIDGET (display));
+}
+
+gchar *
+e_mail_display_get_selection_plain_text (EMailDisplay *display,
+                                         gint *len)
+{
+	EWebView *web_view;
+	WebKitWebFrame *frame;
+	const gchar *frame_name;
+	GValue value = {0};
+	GType type;
+	const gchar *str;
+
+	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+
+	web_view = E_WEB_VIEW (display);
+	frame = webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (web_view));
+	frame_name = webkit_web_frame_get_name (frame);
+
+	type = e_web_view_frame_exec_script (web_view, frame_name, "window.getSelection().toString()", &value);
+	g_return_val_if_fail (type == G_TYPE_STRING, NULL);
+
+	str = g_value_get_string (&value);
+
+	if (len)
+		*len = strlen (str);
+
+	return g_strdup (str);
+}
+
+void
+e_mail_display_load_images (EMailDisplay *display)
+{
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	display->priv->force_image_load = TRUE;
+	e_web_view_reload (E_WEB_VIEW (display));
+}
+
+void
+e_mail_display_set_force_load_images (EMailDisplay *display,
+                                      gboolean force_load_images)
+{
+	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+	display->priv->force_image_load = force_load_images;
+}
diff --git a/mail/e-mail-display.h b/mail/e-mail-display.h
index 1b71a9d..cbac1e3 100644
--- a/mail/e-mail-display.h
+++ b/mail/e-mail-display.h
@@ -22,8 +22,9 @@
 #ifndef E_MAIL_DISPLAY_H
 #define E_MAIL_DISPLAY_H
 
-#include <mail/em-format-html.h>
-#include <misc/e-web-view.h>
+#include <widgets/misc/e-web-view.h>
+#include <widgets/misc/e-search-bar.h>
+#include "em-format-html.h"
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_DISPLAY \
@@ -51,18 +52,53 @@ typedef struct _EMailDisplayClass EMailDisplayClass;
 typedef struct _EMailDisplayPrivate EMailDisplayPrivate;
 
 struct _EMailDisplay {
-	EWebView parent;
+	EWebView web_view;
 	EMailDisplayPrivate *priv;
 };
 
 struct _EMailDisplayClass {
 	EWebViewClass parent_class;
+
 };
 
-GType		e_mail_display_get_type		(void);
-EMFormatHTML *	e_mail_display_get_formatter	(EMailDisplay *display);
-void		e_mail_display_set_formatter	(EMailDisplay *display,
-						 EMFormatHTML *formatter);
+GType			e_mail_display_get_type		(void);
+EMFormatHTML *		e_mail_display_get_formatter	(EMailDisplay *display);
+void			e_mail_display_set_formatter	(EMailDisplay *display,
+							 EMFormatHTML *formatter);
+
+void			e_mail_display_set_mode		(EMailDisplay *display,
+							 EMFormatWriteMode mode);
+EMFormatWriteMode	e_mail_display_get_mode		(EMailDisplay *display);
+void			e_mail_display_set_headers_collapsable
+							(EMailDisplay *display,
+							 gboolean collapsable);
+gboolean		e_mail_display_get_headers_collapsable
+							(EMailDisplay *display);
+void			e_mail_display_set_headers_collapsed
+							(EMailDisplay *display,
+							 gboolean collapsed);
+gboolean		e_mail_display_get_headers_collapsed
+							(EMailDisplay *display);
+
+void			e_mail_display_load		(EMailDisplay *display,
+							 const gchar *msg_uri);
+void			e_mail_display_reload		(EMailDisplay *display);
+
+GtkAction *		e_mail_display_get_action	(EMailDisplay *display,
+							 const gchar *action_name);
+
+void			e_mail_display_set_status	(EMailDisplay *display,
+							 const gchar *status);
+
+gchar *			e_mail_display_get_selection_plain_text
+							(EMailDisplay *display,
+							 gint *len);
+
+void                    e_mail_display_load_images      (EMailDisplay *display);
+
+void                    e_mail_display_set_force_load_images
+                                                        (EMailDisplay *display,
+                                                         gboolean force_load_images);
 
 G_END_DECLS
 
diff --git a/mail/e-mail-notebook-view.c b/mail/e-mail-notebook-view.c
index 64995bc..86b47f6 100644
--- a/mail/e-mail-notebook-view.c
+++ b/mail/e-mail-notebook-view.c
@@ -900,21 +900,17 @@ mail_notebook_view_get_backend (EMailReader *reader)
 	return E_MAIL_BACKEND (shell_backend);
 }
 
-static EMFormatHTML *
-mail_notebook_view_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_notebook_view_get_mail_display (EMailReader *reader)
 {
-	EMailNotebookView *notebook_view;
-	EMailView *current_view;
+	EMailNotebookViewPrivate *priv;
 
-	notebook_view = E_MAIL_NOTEBOOK_VIEW (reader);
-	current_view = notebook_view->priv->current_view;
+	priv = E_MAIL_NOTEBOOK_VIEW (reader)->priv;
 
-	if (current_view == NULL)
+	if (priv->current_view == NULL)
 		return NULL;
 
-	reader = E_MAIL_READER (current_view);
-
-	return e_mail_reader_get_formatter (reader);
+	return e_mail_reader_get_mail_display (E_MAIL_READER (priv->current_view));
 }
 
 static gboolean
@@ -1458,7 +1454,7 @@ e_mail_notebook_view_reader_init (EMailReaderInterface *interface)
 	interface->get_action_group = mail_notebook_view_get_action_group;
 	interface->get_alert_sink = mail_notebook_view_get_alert_sink;
 	interface->get_backend = mail_notebook_view_get_backend;
-	interface->get_formatter = mail_notebook_view_get_formatter;
+	interface->get_mail_display = mail_notebook_view_get_mail_display;
 	interface->get_hide_deleted = mail_notebook_view_get_hide_deleted;
 	interface->get_message_list = mail_notebook_view_get_message_list;
 	interface->get_popup_menu = mail_notebook_view_get_popup_menu;
diff --git a/mail/e-mail-paned-view.c b/mail/e-mail-paned-view.c
index e1779a8..5c7b356 100644
--- a/mail/e-mail-paned-view.c
+++ b/mail/e-mail-paned-view.c
@@ -59,8 +59,9 @@ struct _EMailPanedViewPrivate {
 	GtkWidget *scrolled_window;
 	GtkWidget *message_list;
 	GtkWidget *preview_pane;
+	GtkWidget *search_bar;
 
-	EMFormatHTMLDisplay *formatter;
+	EMailDisplay *display;
 	GalViewInstance *view_instance;
 
 	/* ETable scrolling hack */
@@ -358,11 +359,6 @@ mail_paned_view_dispose (GObject *object)
 		priv->preview_pane = NULL;
 	}
 
-	if (priv->formatter != NULL) {
-		g_object_unref (priv->formatter);
-		priv->formatter = NULL;
-	}
-
 	if (priv->view_instance != NULL) {
 		g_object_unref (priv->view_instance);
 		priv->view_instance = NULL;
@@ -427,14 +423,14 @@ mail_paned_view_get_backend (EMailReader *reader)
 	return E_MAIL_BACKEND (shell_backend);
 }
 
-static EMFormatHTML *
-mail_paned_view_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_paned_view_get_mail_display (EMailReader *reader)
 {
-	EMailPanedView *paned_view;
+	EMailPanedViewPrivate *priv;
 
-	paned_view = E_MAIL_PANED_VIEW (reader);
+	priv = E_MAIL_PANED_VIEW (reader)->priv;
 
-	return EM_FORMAT_HTML (paned_view->priv->formatter);
+	return priv->display;
 }
 
 static gboolean
@@ -622,7 +618,6 @@ mail_paned_view_constructed (GObject *object)
 	EShellView *shell_view;
 	EShell *shell;
 	EShellSettings *shell_settings;
-	ESearchBar *search_bar;
 	EMailReader *reader;
 	EMailBackend *backend;
 	EMailSession *session;
@@ -630,10 +625,11 @@ mail_paned_view_constructed (GObject *object)
 	GtkWidget *message_list;
 	GtkWidget *container;
 	GtkWidget *widget;
-	EWebView *web_view;
 
 	priv = E_MAIL_PANED_VIEW_GET_PRIVATE (object);
-	priv->formatter = em_format_html_display_new ();
+
+	priv->display = g_object_new (E_TYPE_MAIL_DISPLAY,
+		"headers-collapsable", TRUE, NULL);
 
 	view = E_MAIL_VIEW (object);
 	shell_view = e_mail_view_get_shell_view (view);
@@ -645,17 +641,9 @@ mail_paned_view_constructed (GObject *object)
 	backend = E_MAIL_BACKEND (shell_backend);
 	session = e_mail_backend_get_session (backend);
 
-	/* Make headers collapsable and store state of headers in config file */
-	em_format_html_set_headers_collapsable (
-		EM_FORMAT_HTML (priv->formatter), TRUE);
-	g_object_bind_property (
-		shell_settings, "paned-view-headers-state",
-		priv->formatter, "headers-state",
-		G_BINDING_BIDIRECTIONAL |
-		G_BINDING_SYNC_CREATE);
-
-	web_view = em_format_html_get_web_view (
-		EM_FORMAT_HTML (priv->formatter));
+	g_object_bind_property (shell_settings, "paned-view-headers-state",
+				priv->display, "headers-collapsed",
+				G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
 	/* Build content widgets. */
 
@@ -692,11 +680,10 @@ mail_paned_view_constructed (GObject *object)
 
 	container = priv->paned;
 
-	gtk_widget_show (GTK_WIDGET (web_view));
-
-	widget = e_preview_pane_new (web_view);
+	widget = e_preview_pane_new (E_WEB_VIEW (priv->display));
 	gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE);
 	priv->preview_pane = g_object_ref (widget);
+	gtk_widget_show (GTK_WIDGET (priv->display));
 	gtk_widget_show (widget);
 
 	g_object_bind_property (
@@ -704,12 +691,6 @@ mail_paned_view_constructed (GObject *object)
 		widget, "visible",
 		G_BINDING_SYNC_CREATE);
 
-	search_bar = e_preview_pane_get_search_bar (E_PREVIEW_PANE (widget));
-
-	g_signal_connect_swapped (
-		search_bar, "changed",
-		G_CALLBACK (em_format_queue_redraw), priv->formatter);
-
 	/* Load the view instance. */
 
 	e_mail_view_update_view_instance (E_MAIL_VIEW (object));
@@ -733,8 +714,6 @@ mail_paned_view_constructed (GObject *object)
 		G_CALLBACK (mail_paned_view_restore_state_cb),
 		object);
 
-	e_mail_reader_connect_headers (reader);
-
 	/* Do this after creating the message list.  Our
 	 * set_preview_visible() method relies on it. */
 	e_mail_view_set_preview_visible (view, TRUE);
@@ -749,26 +728,23 @@ static void
 mail_paned_view_set_search_strings (EMailView *view,
                                     GSList *search_strings)
 {
+	EMailDisplay *display;
+	EWebView *web_view;
 	EMailReader *reader;
-	EPreviewPane *preview_pane;
-	ESearchBar *search_bar;
-	ESearchingTokenizer *tokenizer;
 
 	reader = E_MAIL_READER (view);
-	preview_pane = e_mail_reader_get_preview_pane (reader);
-	search_bar = e_preview_pane_get_search_bar (preview_pane);
-	tokenizer = e_search_bar_get_tokenizer (search_bar);
+	display = e_mail_reader_get_mail_display (reader);
+	if (!display)
+		return;
+
+	web_view = E_WEB_VIEW (display);
 
-	e_searching_tokenizer_set_secondary_case_sensitivity (tokenizer, FALSE);
-	e_searching_tokenizer_set_secondary_search_string (tokenizer, NULL);
+	e_web_view_clear_highlights (web_view);
 
 	while (search_strings != NULL) {
-		e_searching_tokenizer_add_secondary_search_string (
-			tokenizer, search_strings->data);
+		e_web_view_add_highlight (web_view, search_strings->data);
 		search_strings = g_slist_next (search_strings);
 	}
-
-	e_search_bar_changed (search_bar);
 }
 
 static GalViewInstance *
@@ -1047,7 +1023,7 @@ e_mail_paned_view_reader_init (EMailReaderInterface *interface)
 	interface->get_action_group = mail_paned_view_get_action_group;
 	interface->get_alert_sink = mail_paned_view_get_alert_sink;
 	interface->get_backend = mail_paned_view_get_backend;
-	interface->get_formatter = mail_paned_view_get_formatter;
+	interface->get_mail_display = mail_paned_view_get_mail_display;
 	interface->get_hide_deleted = mail_paned_view_get_hide_deleted;
 	interface->get_message_list = mail_paned_view_get_message_list;
 	interface->get_popup_menu = mail_paned_view_get_popup_menu;
@@ -1092,6 +1068,14 @@ e_mail_paned_view_hide_message_list_pane (EMailPanedView *view,
 		gtk_widget_hide (view->priv->scrolled_window);
 }
 
+GtkWidget *
+e_mail_paned_view_get_preview (EMailPanedView *view)
+{
+	g_return_val_if_fail (E_IS_MAIL_PANED_VIEW (view), NULL);
+
+	return GTK_WIDGET (mail_paned_view_get_mail_display (E_MAIL_READER (view)));
+}
+
 void
 e_mail_paned_view_set_enable_show_folder (EMailPanedView *view,
                                           gboolean set)
diff --git a/mail/e-mail-paned-view.h b/mail/e-mail-paned-view.h
index 3226b39..5e6879a 100644
--- a/mail/e-mail-paned-view.h
+++ b/mail/e-mail-paned-view.h
@@ -71,6 +71,7 @@ GtkWidget *	e_mail_paned_view_new		(EShellView *shell_view);
 void		e_mail_paned_view_hide_message_list_pane
 						(EMailPanedView *view,
 						 gboolean visible);
+GtkWidget *	e_mail_paned_view_get_preview	(EMailPanedView *view);
 void		e_mail_paned_view_set_enable_show_folder
 						(EMailPanedView *view,
 						 gboolean set);
diff --git a/mail/e-mail-printer.c b/mail/e-mail-printer.c
new file mode 100644
index 0000000..f0fb841
--- /dev/null
+++ b/mail/e-mail-printer.c
@@ -0,0 +1,859 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 2011 Dan Vratil <dvratil redhat com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-print.h>
+#include <e-util/e-marshal.h>
+
+#include <webkit/webkitdom.h>
+
+#include "e-mail-printer.h"
+#include "em-format-html-print.h"
+#include "e-mail-display.h"
+
+static gpointer parent_class = NULL;
+
+enum {
+        BUTTON_SELECT_ALL,
+        BUTTON_SELECT_NONE,
+        BUTTON_TOP,
+        BUTTON_UP,
+        BUTTON_DOWN,
+        BUTTON_BOTTOM,
+        BUTTONS_COUNT
+};
+
+#define w(x)
+
+struct _EMailPrinterPrivate {
+	EMFormatHTMLPrint *efhp;
+
+        gboolean export_mode;
+
+	GtkListStore *headers;
+
+	WebKitWebView *webview; /* WebView to print from */
+	gchar *uri;
+	GtkWidget *buttons[BUTTONS_COUNT];
+	GtkWidget *treeview;
+
+	GtkPrintOperation *operation;
+};
+
+G_DEFINE_TYPE (
+	EMailPrinter,
+	e_mail_printer,
+	G_TYPE_OBJECT);
+
+enum {
+	PROP_0,
+	PROP_PRINT_FORMATTER
+};
+
+enum {
+	SIGNAL_DONE,
+	LAST_SIGNAL
+};
+
+enum {
+	COLUMN_ACTIVE,
+	COLUMN_HEADER_NAME,
+	COLUMN_HEADER_VALUE,
+	COLUMN_HEADER_STRUCT,
+	LAST_COLUMN
+};
+
+static guint signals[LAST_SIGNAL];
+
+static gint
+emp_header_name_equal (const EMFormatHeader *h1,
+                       const EMFormatHeader *h2)
+{
+	if ((h2->value == NULL) || (h1->value == NULL)) {
+		return g_strcmp0 (h1->name, h2->name);
+	} else {
+		if ((g_strcmp0 (h1->name, h2->name) == 0) &&
+		    (g_strcmp0 (h1->value, h2->value) == 0))
+			return 0;
+		else
+			return 1;
+	}
+}
+
+static void
+emp_draw_footer (GtkPrintOperation *operation,
+                 GtkPrintContext *context,
+                 gint page_nr)
+{
+	PangoFontDescription *desc;
+	PangoLayout *layout;
+	gint n_pages;
+	gdouble width, height;
+	gchar *text;
+	cairo_t *cr;
+
+	cr = gtk_print_context_get_cairo_context (context);
+	width = gtk_print_context_get_width (context);
+	height = gtk_print_context_get_height (context);
+
+	g_object_get (operation, "n-pages", &n_pages, NULL);
+	text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
+
+	cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
+	cairo_fill (cr);
+
+	desc = pango_font_description_from_string ("Sans Regular 10");
+	layout = gtk_print_context_create_pango_layout (context);
+	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+	pango_layout_set_font_description (layout, desc);
+	pango_layout_set_text (layout, text, -1);
+	pango_layout_set_width (layout, width * PANGO_SCALE);
+	pango_font_description_free (desc);
+
+	cairo_move_to (cr, 0, height + 5);
+	pango_cairo_show_layout (cr, layout);
+
+	g_object_unref (layout);
+	g_free (text);
+}
+
+static void
+emp_printing_done (GtkPrintOperation *operation,
+                   GtkPrintOperationResult result,
+                   gpointer user_data)
+{
+	EMailPrinter *emp = user_data;
+
+	g_signal_emit (emp, signals[SIGNAL_DONE], 0, operation, result);
+}
+
+static void
+emp_start_printing (GObject *object,
+                    GParamSpec *pspec,
+                    gpointer user_data)
+{
+	WebKitWebView *web_view;
+	WebKitWebFrame *frame;
+	WebKitLoadStatus load_status;
+	EMailPrinter *emp = user_data;
+
+	web_view = WEBKIT_WEB_VIEW (object);
+	load_status = webkit_web_view_get_load_status (web_view);
+
+	if (load_status != WEBKIT_LOAD_FINISHED)
+		return;
+
+	frame = webkit_web_view_get_main_frame (web_view);
+
+	if (emp->priv->export_mode) {
+		gtk_print_operation_set_export_filename (
+			emp->priv->operation,
+			emp->priv->efhp->export_filename);
+		webkit_web_frame_print_full (
+			frame, emp->priv->operation,
+			GTK_PRINT_OPERATION_ACTION_EXPORT, NULL);
+	} else {
+		webkit_web_frame_print_full
+		(frame, emp->priv->operation,
+		 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL);
+	}
+
+}
+
+static void
+emp_run_print_operation (EMailPrinter *emp)
+{
+	EMFormat *emf;
+	SoupSession *session;
+	GHashTable *formatters;
+	gchar *mail_uri;
+
+	emf = EM_FORMAT (emp->priv->efhp);
+	mail_uri = em_format_build_mail_uri (emf->folder, emf->message_uid, NULL, NULL);
+
+	/* It's safe to assume that session exists and contains formatters table,
+	 * because at least the message we are about to print now must be already
+	 * there */
+	session = webkit_get_default_session ();
+	formatters = g_object_get_data (G_OBJECT (session), "formatters");
+	g_hash_table_insert (formatters, g_strdup (mail_uri), emp->priv->efhp);
+
+	/* Print_layout is a special EMPart created by EMFormatHTMLPrint */
+	if (emp->priv->uri)
+		g_free (emp->priv->uri);
+
+        emp->priv->uri = g_strconcat (mail_uri, "?part_id=print_layout&__evo-load-images=1", NULL);
+
+	if (emp->priv->webview == NULL) {
+		emp->priv->webview = g_object_new (E_TYPE_MAIL_DISPLAY, NULL);
+		e_web_view_set_enable_frame_flattening (E_WEB_VIEW (emp->priv->webview), FALSE);
+		e_mail_display_set_force_load_images (
+				E_MAIL_DISPLAY (emp->priv->webview), TRUE);
+		g_object_ref_sink (emp->priv->webview);
+                g_signal_connect (emp->priv->webview, "notify::load-status",
+			G_CALLBACK (emp_start_printing), emp);
+
+		w ({
+			GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+			GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
+			gtk_container_add (GTK_CONTAINER (window), sw);
+			gtk_container_add (GTK_CONTAINER (sw),
+					   GTK_WIDGET (emp->priv->webview));
+			gtk_widget_show_all (window);
+		});
+	}
+
+	e_mail_display_set_formatter (E_MAIL_DISPLAY (emp->priv->webview),
+				      (EMFormatHTML *) emp->priv->efhp);
+
+	webkit_web_view_load_uri (emp->priv->webview, emp->priv->uri);
+
+	g_free (mail_uri);
+}
+
+static void
+set_header_visible (EMailPrinter *emp,
+                    EMFormatHeader *header,
+                    gint index,
+                    gboolean visible)
+{
+	WebKitDOMDocument *document;
+	WebKitDOMNodeList *headers;
+	WebKitDOMElement *element;
+	WebKitDOMCSSStyleDeclaration *style;
+
+	document = webkit_web_view_get_dom_document (emp->priv->webview);
+        headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
+
+	g_return_if_fail (index < webkit_dom_node_list_get_length (headers));
+
+	element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (headers, index));
+	style = webkit_dom_element_get_style (element);
+	webkit_dom_css_style_declaration_set_property (style,
+                "display", (visible ? "table-row" : "none"), "", NULL);
+}
+
+static void
+header_active_renderer_toggled_cb (GtkCellRendererToggle *renderer,
+                                   gchar *path,
+                                   EMailPrinter *emp)
+{
+	GtkTreeIter iter;
+	GtkTreePath *p;
+	gboolean active;
+	EMFormatHeader *header;
+	gint *indices;
+
+	gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (emp->priv->headers),
+		&iter, path);
+
+	gtk_tree_model_get (GTK_TREE_MODEL (emp->priv->headers), &iter,
+		COLUMN_ACTIVE, &active, -1);
+	gtk_tree_model_get (GTK_TREE_MODEL (emp->priv->headers), &iter,
+		COLUMN_HEADER_STRUCT, &header, -1);
+	gtk_list_store_set (GTK_LIST_STORE (emp->priv->headers), &iter,
+		COLUMN_ACTIVE, !active, -1);
+
+	p = gtk_tree_path_new_from_string (path);
+	indices = gtk_tree_path_get_indices (p);
+	set_header_visible (emp, header, indices[0], !active);
+	gtk_tree_path_free (p);
+}
+
+static void
+emp_headers_tab_toggle_selection (GtkWidget *button,
+                                  gpointer user_data)
+{
+	EMailPrinter *emp = user_data;
+	GtkTreeIter iter;
+	gboolean select;
+
+	if (button == emp->priv->buttons[BUTTON_SELECT_ALL])
+		select = TRUE;
+	else if (button == emp->priv->buttons[BUTTON_SELECT_NONE])
+		select = FALSE;
+	else
+		return;
+
+	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (emp->priv->headers), &iter))
+		return;
+
+	do {
+		EMFormatHeader *header;
+		GtkTreePath *path;
+		gint *indices;
+
+		gtk_tree_model_get (GTK_TREE_MODEL (emp->priv->headers), &iter,
+			COLUMN_HEADER_STRUCT, &header, -1);
+		gtk_list_store_set (GTK_LIST_STORE (emp->priv->headers), &iter,
+			COLUMN_ACTIVE, select, -1);
+
+		path = gtk_tree_model_get_path (GTK_TREE_MODEL (emp->priv->headers), &iter);
+		indices = gtk_tree_path_get_indices (path);
+		set_header_visible (emp, header, indices[0], select);
+		gtk_tree_path_free (path);
+
+	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (emp->priv->headers), &iter));
+}
+
+static void
+emp_headers_tab_selection_changed (GtkTreeSelection *selection,
+                                   gpointer user_data)
+{
+	EMailPrinter *emp = user_data;
+	gboolean enabled;
+	GList *selected_rows;
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+
+	if (gtk_tree_selection_count_selected_rows (selection) == 0) {
+		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], FALSE);
+		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], FALSE);
+		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], FALSE);
+		gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], FALSE);
+
+		return;
+	}
+
+	model = GTK_TREE_MODEL (emp->priv->headers);
+	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+	path = gtk_tree_path_copy (selected_rows->data);
+	enabled = gtk_tree_path_prev (path);
+	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], enabled);
+	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], enabled);
+
+	gtk_tree_model_get_iter (model, &iter, g_list_last (selected_rows)->data);
+	enabled = gtk_tree_model_iter_next (model, &iter);
+	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], enabled);
+	gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], enabled);
+
+	g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (selected_rows);
+	gtk_tree_path_free (path);
+}
+
+static void
+emp_headers_tab_move (GtkWidget *button,
+                      gpointer user_data)
+{
+	EMailPrinter *emp = user_data;
+	GtkTreeSelection *selection;
+	GList *selected_rows, *references, *l;
+	GtkTreePath *path;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GtkTreeRowReference *selection_middle;
+	gint *indices;
+
+	WebKitDOMDocument *document;
+	WebKitDOMNodeList *headers;
+	WebKitDOMNode *header, *parent;
+
+	model = GTK_TREE_MODEL (emp->priv->headers);
+	selection = gtk_tree_view_get_selection  (GTK_TREE_VIEW (emp->priv->treeview));
+	selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+        /* The order of header rows in the HMTL document should be in sync with
+	   order of headers in the listview and in efhp->headers_list */
+	document = webkit_web_view_get_dom_document (emp->priv->webview);
+        headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
+
+	l = g_list_nth (selected_rows, g_list_length (selected_rows) / 2);
+	selection_middle = gtk_tree_row_reference_new (model, l->data);
+
+	references = NULL;
+	for (l = selected_rows; l; l = l->next) {
+		references = g_list_prepend (references,
+			gtk_tree_row_reference_new (model, l->data));
+	}
+
+	if (button == emp->priv->buttons[BUTTON_TOP]) {
+
+		for (l = references; l; l = l->next) {
+                        /* Move the rows in the view  */
+			path = gtk_tree_row_reference_get_path (l->data);
+			gtk_tree_model_get_iter (model, &iter, path);
+			gtk_list_store_move_after (emp->priv->headers, &iter, NULL);
+
+                        /* Move the header row in HTML document */
+			indices = gtk_tree_path_get_indices (path);
+			header = webkit_dom_node_list_item (headers, indices[0]);
+			parent = webkit_dom_node_get_parent_node (header);
+			webkit_dom_node_remove_child (parent, header, NULL);
+			webkit_dom_node_insert_before (parent, header,
+				webkit_dom_node_get_first_child (parent), NULL);
+
+			gtk_tree_path_free (path);
+		}
+
+       } else if (button == emp->priv->buttons[BUTTON_UP]) {
+
+		GtkTreeIter *iter_prev;
+		WebKitDOMNode *node2;
+
+		references = g_list_reverse (references);
+
+		for (l = references; l; l = l->next) {
+
+			path = gtk_tree_row_reference_get_path (l->data);
+			gtk_tree_model_get_iter (model, &iter, path);
+			iter_prev = gtk_tree_iter_copy (&iter);
+			gtk_tree_model_iter_previous (model, iter_prev);
+
+			gtk_list_store_move_before (emp->priv->headers, &iter, iter_prev);
+
+			indices = gtk_tree_path_get_indices (path);
+			header = webkit_dom_node_list_item (headers, indices[0]);
+			node2 = webkit_dom_node_get_previous_sibling (header);
+			parent = webkit_dom_node_get_parent_node (header);
+
+			webkit_dom_node_remove_child (parent, header, NULL);
+			webkit_dom_node_insert_before (parent, header, node2, NULL);
+
+			gtk_tree_path_free (path);
+			gtk_tree_iter_free (iter_prev);
+		}
+
+	} else if (button == emp->priv->buttons[BUTTON_DOWN]) {
+
+		GtkTreeIter *iter_next;
+		WebKitDOMNode *node2;
+
+		for (l = references; l; l = l->next) {
+
+			path = gtk_tree_row_reference_get_path (l->data);
+			gtk_tree_model_get_iter (model, &iter, path);
+			iter_next = gtk_tree_iter_copy (&iter);
+			gtk_tree_model_iter_next (model, iter_next);
+
+			gtk_list_store_move_after (emp->priv->headers, &iter, iter_next);
+
+			indices = gtk_tree_path_get_indices (path);
+			header = webkit_dom_node_list_item (headers, indices[0]);
+			node2 = webkit_dom_node_get_next_sibling (header);
+			parent = webkit_dom_node_get_parent_node (header);
+
+			webkit_dom_node_remove_child (parent, header, NULL);
+			webkit_dom_node_insert_before (parent, header,
+				webkit_dom_node_get_next_sibling (node2), NULL);
+
+			gtk_tree_path_free (path);
+			gtk_tree_iter_free (iter_next);
+		}
+
+	} else if (button == emp->priv->buttons[BUTTON_BOTTOM]) {
+
+		references = g_list_reverse (references);
+
+		for (l = references; l; l = l->next) {
+			path = gtk_tree_row_reference_get_path (l->data);
+			gtk_tree_model_get_iter (model, &iter, path);
+			gtk_list_store_move_before (emp->priv->headers, &iter, NULL);
+
+                        /* Move the header row in HTML document */
+			indices = gtk_tree_path_get_indices (path);
+			header = webkit_dom_node_list_item (headers, indices[0]);
+			parent = webkit_dom_node_get_parent_node (header);
+			webkit_dom_node_remove_child (parent, header, NULL);
+			webkit_dom_node_append_child (parent, header, NULL);
+
+			gtk_tree_path_free (path);
+		}
+	};
+
+	g_list_foreach (references, (GFunc) gtk_tree_row_reference_free, NULL);
+	g_list_free (references);
+
+        /* Keep the selection in middle of the screen */
+	path = gtk_tree_row_reference_get_path (selection_middle);
+	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (emp->priv->treeview),
+		path, COLUMN_ACTIVE, TRUE, 0.5, 0.5);
+	gtk_tree_path_free (path);
+	gtk_tree_row_reference_free (selection_middle);
+
+	g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (selected_rows);
+
+	emp_headers_tab_selection_changed (selection, user_data);
+}
+
+static GtkWidget *
+emp_create_headers_tab (GtkPrintOperation *operation,
+                        EMailPrinter *emp)
+{
+	GtkWidget *vbox, *hbox, *scw, *button;
+	GtkTreeView *view;
+	GtkTreeSelection *selection;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
+	gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
+
+	emp->priv->treeview = gtk_tree_view_new_with_model (
+		GTK_TREE_MODEL (emp->priv->headers));
+	view = GTK_TREE_VIEW (emp->priv->treeview);
+	selection = gtk_tree_view_get_selection (view);
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+        g_signal_connect (selection, "changed",
+		G_CALLBACK (emp_headers_tab_selection_changed), emp);
+
+	renderer = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (renderer, "toggled",
+		G_CALLBACK (header_active_renderer_toggled_cb), emp);
+	column = gtk_tree_view_column_new_with_attributes (
+		_("Print"), renderer, 
+		"active", COLUMN_ACTIVE, NULL);
+	gtk_tree_view_append_column (view, column);
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (
+		_("Header Name"), renderer, 
+		"text", COLUMN_HEADER_NAME, NULL);
+	gtk_tree_view_append_column (view, column);
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (
+		_("Header Value"), renderer,
+		"text", COLUMN_HEADER_VALUE, NULL);
+	gtk_tree_view_append_column (view, column);
+
+	scw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_container_add (GTK_CONTAINER (scw), GTK_WIDGET (view));
+	gtk_box_pack_start (GTK_BOX (hbox), scw, TRUE, TRUE, 0);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_SELECT_ALL);
+	emp->priv->buttons[BUTTON_SELECT_ALL] = button;
+        g_signal_connect (button, "clicked",
+		G_CALLBACK (emp_headers_tab_toggle_selection), emp);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
+	emp->priv->buttons[BUTTON_SELECT_NONE] = button;
+        g_signal_connect (button, "clicked",
+		G_CALLBACK (emp_headers_tab_toggle_selection), emp);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_GOTO_TOP);
+	emp->priv->buttons[BUTTON_TOP] = button;
+	gtk_widget_set_sensitive (button, FALSE);
+        g_signal_connect (button, "clicked",
+		G_CALLBACK (emp_headers_tab_move), emp);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
+	emp->priv->buttons[BUTTON_UP] = button;
+	gtk_widget_set_sensitive (button, FALSE);
+        g_signal_connect (button, "clicked",
+		G_CALLBACK (emp_headers_tab_move), emp);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
+	emp->priv->buttons[BUTTON_DOWN] = button;
+	gtk_widget_set_sensitive (button, FALSE);
+        g_signal_connect (button, "clicked",
+		G_CALLBACK (emp_headers_tab_move), emp);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_GOTO_BOTTOM);
+	emp->priv->buttons[BUTTON_BOTTOM] = button;
+	gtk_widget_set_sensitive (button, FALSE);
+        g_signal_connect (button, "clicked",
+		G_CALLBACK (emp_headers_tab_move), emp);
+	gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+	gtk_print_operation_set_custom_tab_label (operation, _("Headers"));
+	gtk_widget_show_all (hbox);
+
+	return hbox;
+}
+
+static void
+emp_set_formatter (EMailPrinter *emp,
+                   EMFormatHTMLPrint *formatter)
+{
+	EMFormat *emf = (EMFormat *) formatter;
+	CamelMediumHeader *header;
+	GArray *headers;
+	gint i;
+	GtkTreeIter last_known;
+
+	g_return_if_fail (EM_IS_FORMAT_HTML_PRINT (formatter));
+
+	g_object_ref (formatter);
+
+	if (emp->priv->efhp)
+		g_object_unref (emp->priv->efhp);
+
+	emp->priv->efhp = formatter;
+
+	if (emp->priv->headers)
+		g_object_unref (emp->priv->headers);
+	emp->priv->headers = gtk_list_store_new (5,
+		G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
+
+	headers = camel_medium_get_headers (CAMEL_MEDIUM (emf->message));
+	if (!headers)
+		return;
+
+	for (i = 0; i < headers->len; i++) {
+		GtkTreeIter iter;
+		GList *found_header;
+		EMFormatHeader *emfh;
+
+		header = &g_array_index (headers, CamelMediumHeader, i);
+		emfh = em_format_header_new (header->name, header->value);
+
+		found_header = g_queue_find_custom (&EM_FORMAT (formatter)->header_list,
+				emfh, (GCompareFunc) emp_header_name_equal);
+
+		if (!found_header) {
+			emfh->flags |= EM_FORMAT_HTML_HEADER_HIDDEN;
+			em_format_add_header_struct (EM_FORMAT (formatter), emfh);
+			gtk_list_store_append (emp->priv->headers, &iter);
+		} else {
+			if (gtk_list_store_iter_is_valid (emp->priv->headers, &last_known))
+				gtk_list_store_insert_after (emp->priv->headers, &iter, &last_known);
+			else
+				gtk_list_store_insert_after (emp->priv->headers, &iter, NULL);
+
+			last_known = iter;
+		}
+
+		gtk_list_store_set (emp->priv->headers, &iter,
+			COLUMN_ACTIVE, (found_header != NULL),
+			COLUMN_HEADER_NAME, emfh->name,
+			COLUMN_HEADER_VALUE, emfh->value,
+			COLUMN_HEADER_STRUCT, emfh, -1);
+	}
+
+	camel_medium_free_headers (CAMEL_MEDIUM (emf->message), headers);
+}
+
+static void
+emp_set_property (GObject *object,
+                  guint property_id,
+                  const GValue *value,
+                  GParamSpec *pspec)
+{
+	EMailPrinter *emp = E_MAIL_PRINTER (object);
+
+	switch (property_id) {
+
+		case PROP_PRINT_FORMATTER:
+			emp_set_formatter (emp, g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emp_get_property (GObject *object,
+                  guint property_id,
+                  GValue *value,
+                  GParamSpec *pspec)
+{
+	EMailPrinter *emp = E_MAIL_PRINTER (object);
+
+	switch (property_id) {
+
+		case PROP_PRINT_FORMATTER:
+			g_value_set_object (value,
+				e_mail_printer_get_print_formatter (emp));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emp_finalize (GObject *object)
+{
+	EMailPrinterPrivate *priv = E_MAIL_PRINTER (object)->priv;
+
+	if (priv->efhp) {
+		g_object_unref (priv->efhp);
+		priv->efhp = NULL;
+	}
+
+	if (priv->headers) {
+		GtkTreeIter iter;
+
+		if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->headers), &iter)) {
+			do {
+				EMFormatHeader *header = NULL;
+				gtk_tree_model_get (GTK_TREE_MODEL (priv->headers), &iter,
+					COLUMN_HEADER_STRUCT, &header, -1);
+				em_format_header_free (header);
+			} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->headers), &iter));
+		}
+		g_object_unref (priv->headers);
+		priv->headers = NULL;
+	}
+
+	if (priv->webview) {
+		g_object_unref (priv->webview);
+		priv->webview = NULL;
+	}
+
+	if (priv->uri) {
+		g_free (priv->uri);
+		priv->uri = NULL;
+	}
+
+	if (priv->operation) {
+		g_object_unref (priv->operation);
+		priv->operation = NULL;
+	}
+
+        /* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+e_mail_printer_class_init (EMailPrinterClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (EMailPrinterPrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->set_property = emp_set_property;
+	object_class->get_property = emp_get_property;
+	object_class->finalize = emp_finalize;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PRINT_FORMATTER,
+		g_param_spec_object (
+			"print-formatter",
+			NULL,
+			NULL,
+			EM_TYPE_FORMAT_HTML_PRINT,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	signals[SIGNAL_DONE] =	g_signal_new ("done",
+		G_TYPE_FROM_CLASS (klass),
+		G_SIGNAL_RUN_FIRST,
+		G_STRUCT_OFFSET (EMailPrinterClass, done),
+		NULL, NULL,
+		e_marshal_VOID__OBJECT_INT,
+		G_TYPE_NONE, 2,
+		GTK_TYPE_PRINT_OPERATION, G_TYPE_INT);
+}
+
+static void
+e_mail_printer_init (EMailPrinter *emp)
+{
+	emp->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		emp, E_TYPE_MAIL_PRINTER, EMailPrinterPrivate);
+
+	emp->priv->efhp = NULL;
+	emp->priv->headers = NULL;
+	emp->priv->webview = NULL;
+}
+
+EMailPrinter *
+e_mail_printer_new (EMFormatHTML * source)
+{
+	EMailPrinter *emp;
+	EMFormatHTMLPrint *efhp;
+
+	efhp = em_format_html_print_new (source);
+
+	emp = g_object_new (E_TYPE_MAIL_PRINTER,
+		"print-formatter", efhp, NULL);
+
+	g_object_unref (efhp);
+
+	return emp;
+}
+
+void
+e_mail_printer_print (EMailPrinter *emp,
+                      gboolean export_mode,
+                      GCancellable *cancellable)
+{
+	g_return_if_fail (E_IS_MAIL_PRINTER (emp));
+
+	if (emp->priv->operation)
+		g_object_unref (emp->priv->operation);
+	emp->priv->operation = e_print_operation_new ();
+	gtk_print_operation_set_unit (emp->priv->operation, GTK_UNIT_PIXEL);
+
+	gtk_print_operation_set_show_progress (emp->priv->operation, TRUE);
+	g_signal_connect (emp->priv->operation, "create-custom-widget",
+		G_CALLBACK (emp_create_headers_tab), emp);
+	g_signal_connect (emp->priv->operation, "done",
+		G_CALLBACK (emp_printing_done), emp);
+        g_signal_connect (emp->priv->operation, "draw-page",
+		G_CALLBACK (emp_draw_footer), NULL);
+
+	emp->priv->export_mode = export_mode;
+
+	if (cancellable)
+                g_signal_connect_swapped (cancellable, "cancelled",
+			G_CALLBACK (gtk_print_operation_cancel), emp->priv->operation);
+
+	emp_run_print_operation (emp);
+}
+
+const gchar *
+e_mail_printer_get_export_filename (EMailPrinter *printer)
+{
+	g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
+
+	if (!printer->priv->efhp)
+	  return NULL;
+
+	return printer->priv->efhp->export_filename;
+}
+
+void
+e_mail_printer_set_export_filename (EMailPrinter *printer,
+                                    const gchar *filename)
+{
+	g_return_if_fail (E_IS_MAIL_PRINTER (printer));
+	g_return_if_fail (printer->priv->efhp != NULL);
+
+	if (printer->priv->efhp->export_filename && *printer->priv->efhp->export_filename)
+	  g_free (printer->priv->efhp->export_filename);
+
+	printer->priv->efhp->export_filename = g_strdup (filename);
+}
+
+EMFormatHTMLPrint *
+e_mail_printer_get_print_formatter (EMailPrinter *emp)
+{
+	g_return_val_if_fail (E_IS_MAIL_PRINTER (emp), NULL);
+
+	return emp->priv->efhp;
+}
+
diff --git a/mail/e-mail-printer.h b/mail/e-mail-printer.h
new file mode 100644
index 0000000..fcd163e
--- /dev/null
+++ b/mail/e-mail-printer.h
@@ -0,0 +1,85 @@
+/*
+ * Class for printing emails
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 2011 Dan Vratil <dvratil redhat com>
+ */
+
+#ifndef E_MAIL_PRINTER_H
+#define E_MAIL_PRINTER_H
+
+#include "mail/em-format-html-print.h"
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_PRINTER \
+	(e_mail_printer_get_type ())
+#define E_MAIL_PRINTER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_PRINTER, EMailPrinter))
+#define E_MAIL_PRINTER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_PRINTER, EMailPrinterClass))
+#define E_IS_MAIL_PRINTER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_PRINTER))
+#define E_IS_MAIL_PRINTER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_MAIL_PRINTER_CLASS))
+#define E_MAIL_PRINTER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_PRINTER, EMailPrinterClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailPrinter EMailPrinter;
+typedef struct _EMailPrinterClass EMailPrinterClass;
+typedef struct _EMailPrinterPrivate EMailPrinterPrivate;
+
+struct _EMailPrinter {
+	GObject parent;
+	EMailPrinterPrivate *priv;
+};
+
+struct _EMailPrinterClass {
+	GObjectClass parent_class;
+
+	void	(*done)			(EMailPrinter *printer,
+					 GtkPrintOperation *operation,
+					 GtkPrintOperationResult *result,
+					 gpointer user_data);
+};
+
+GType		e_mail_printer_get_type	(void);
+
+EMailPrinter *  e_mail_printer_new	(EMFormatHTML *source);
+
+void		e_mail_printer_print	(EMailPrinter *printer,
+					 gboolean export,
+					 GCancellable *cancellable);
+
+void            e_mail_printer_set_export_filename
+                                        (EMailPrinter *printer,
+                                         const gchar *filename);
+
+const gchar *    e_mail_printer_get_export_filename
+                                        (EMailPrinter *printer);
+
+EMFormatHTMLPrint *
+		e_mail_printer_get_print_formatter
+					(EMailPrinter *printer);
+
+G_END_DECLS
+
+#endif /* E_MAIL_PRINTER_H */
diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c
index bc87295..5956ab2 100644
--- a/mail/e-mail-reader-utils.c
+++ b/mail/e-mail-reader-utils.c
@@ -44,6 +44,7 @@
 
 #include "mail/e-mail-backend.h"
 #include "mail/e-mail-browser.h"
+#include "mail/e-mail-printer.h"
 #include "mail/em-composer-utils.h"
 #include "mail/em-format-html-print.h"
 #include "mail/em-utils.h"
@@ -412,7 +413,8 @@ e_mail_reader_open_selected (EMailReader *reader)
 		const gchar *uid = views->pdata[ii];
 		GtkWidget *browser;
 
-		browser = e_mail_browser_new (backend);
+		browser = e_mail_browser_new (backend, folder, uid,
+				EM_FORMAT_WRITE_MODE_NORMAL);
 		e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
 		e_mail_reader_set_message (E_MAIL_READER (browser), uid);
 		copy_tree_state (reader, E_MAIL_READER (browser));
@@ -430,93 +432,74 @@ e_mail_reader_open_selected (EMailReader *reader)
 	return ii;
 }
 
-/* Helper for e_mail_reader_print() */
-static void
-mail_reader_print_cb (CamelFolder *folder,
-                      GAsyncResult *result,
-                      AsyncContext *context)
+static gboolean
+destroy_printing_activity (EActivity *activity)
 {
-	EAlertSink *alert_sink;
-	CamelMimeMessage *message;
-	EMFormatHTML *formatter;
-	EMFormatHTMLPrint *html_print;
-	GError *error = NULL;
-
-	alert_sink = e_activity_get_alert_sink (context->activity);
+	g_object_unref (activity);
 
-	message = camel_folder_get_message_finish (folder, result, &error);
+	return FALSE;
+}
 
-	if (e_activity_handle_cancellation (context->activity, error)) {
-		g_warn_if_fail (message == NULL);
-		async_context_free (context);
-		g_error_free (error);
-		return;
+static void
+printing_done_cb (EMailPrinter *printer,
+                  GtkPrintOperation *operation,
+                  GtkPrintOperationResult result,
+                  gpointer user_data)
+{
+	EActivity *activity = user_data;
 
-	} else if (error != NULL) {
-		g_warn_if_fail (message == NULL);
-		e_alert_submit (
-			alert_sink, "mail:no-retrieve-message",
-			error->message, NULL);
-		async_context_free (context);
-		g_error_free (error);
-		return;
-	}
+	if (result == GTK_PRINT_OPERATION_RESULT_ERROR) {
 
-	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+		EAlertSink *alert_sink;
+		GError *error = NULL;
 
-	formatter = e_mail_reader_get_formatter (context->reader);
+		alert_sink = e_activity_get_alert_sink (activity);
+		gtk_print_operation_get_error (operation, &error);
 
-	html_print = em_format_html_print_new (
-		formatter, context->print_action);
-	em_format_merge_handler (
-		EM_FORMAT (html_print), EM_FORMAT (formatter));
-	em_format_html_print_message (
-		html_print, message, folder, context->message_uid);
-	g_object_unref (html_print);
+		if (error != NULL) {
+			e_alert_submit (alert_sink, "mail:printing-failed",
+				error->message, NULL);
+			g_error_free (error);
+		}
 
-	g_object_unref (message);
+		g_object_unref (activity);
+		g_object_unref (printer);
+		return;
+	}
 
-	e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
+	/* Set activity as completed, and keep it displayed for a few seconds
+	 * so that user can actually see the the printing was sucesfully finished. */
+	e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
+	g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 3,
+		(GSourceFunc) destroy_printing_activity, activity, NULL);
 
-	async_context_free (context);
+	g_object_unref (printer);
 }
 
 void
 e_mail_reader_print (EMailReader *reader,
                      GtkPrintOperationAction action)
 {
+	EMailDisplay *display;
+	EMailPrinter *printer;
+	EMFormatHTML *formatter;
 	EActivity *activity;
-	AsyncContext *context;
 	GCancellable *cancellable;
-	CamelFolder *folder;
-	GPtrArray *uids;
-	const gchar *message_uid;
 
 	g_return_if_fail (E_IS_MAIL_READER (reader));
 
-	folder = e_mail_reader_get_folder (reader);
-	g_return_if_fail (CAMEL_IS_FOLDER (folder));
-
-	/* XXX Learn to handle len > 1. */
-	uids = e_mail_reader_get_selected_uids (reader);
-	g_return_if_fail (uids != NULL && uids->len == 1);
-	message_uid = g_ptr_array_index (uids, 0);
+	display = e_mail_reader_get_mail_display (reader);
+	formatter = e_mail_display_get_formatter (display);
 
 	activity = e_mail_reader_new_activity (reader);
+	e_activity_set_text (activity, _("Printing"));
+	e_activity_set_state (activity, E_ACTIVITY_RUNNING);
 	cancellable = e_activity_get_cancellable (activity);
 
-	context = g_slice_new0 (AsyncContext);
-	context->activity = activity;
-	context->reader = g_object_ref (reader);
-	context->message_uid = g_strdup (message_uid);
-	context->print_action = action;
-
-	camel_folder_get_message (
-		folder, message_uid, G_PRIORITY_DEFAULT,
-		cancellable, (GAsyncReadyCallback)
-		mail_reader_print_cb, context);
-
-	em_utils_uids_free (uids);
+	printer = e_mail_printer_new (formatter);
+	g_signal_connect (printer, "done",
+		G_CALLBACK (printing_done_cb), activity);
+	e_mail_printer_print (printer, FALSE, cancellable);
 }
 
 static void
@@ -763,6 +746,7 @@ mail_reader_get_message_ready_cb (CamelFolder *folder,
 	EMailBackend *backend;
 	EAlertSink *alert_sink;
 	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	CamelMimeMessage *message;
 	GError *error = NULL;
 
@@ -790,8 +774,8 @@ mail_reader_get_message_ready_cb (CamelFolder *folder,
 
 	backend = e_mail_reader_get_backend (context->reader);
 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
-
-	formatter = e_mail_reader_get_formatter (context->reader);
+	display = e_mail_reader_get_mail_display (context->reader);
+	formatter = e_mail_display_get_formatter (display);
 
 	em_utils_reply_to_message (
 		shell, message,
@@ -814,6 +798,7 @@ e_mail_reader_reply_to_message (EMailReader *reader,
 	EShell *shell;
 	EMailBackend *backend;
 	EShellBackend *shell_backend;
+	EMailDisplay *display;
 	EMFormatHTML *formatter;
 	GtkWidget *message_list;
 	CamelMimeMessage *new_message;
@@ -834,14 +819,15 @@ e_mail_reader_reply_to_message (EMailReader *reader,
 
 	backend = e_mail_reader_get_backend (reader);
 	folder = e_mail_reader_get_folder (reader);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
+	formatter = e_mail_display_get_formatter (display);
 	message_list = e_mail_reader_get_message_list (reader);
 	reply_style = e_mail_reader_get_reply_style (reader);
 
 	shell_backend = E_SHELL_BACKEND (backend);
 	shell = e_shell_backend_get_shell (shell_backend);
 
-	web_view = em_format_html_get_web_view (formatter);
+	web_view = E_WEB_VIEW (display);
 
 	if (reply_type == E_MAIL_REPLY_TO_RECIPIENT) {
 		const gchar *uri;
@@ -885,7 +871,8 @@ e_mail_reader_reply_to_message (EMailReader *reader,
 	if (!e_web_view_is_selection_active (web_view))
 		goto whole_message;
 
-	selection = gtk_html_get_selection_html (GTK_HTML (web_view), &length);
+	selection = e_web_view_get_selection_html (web_view);
+	length = strlen (selection);
 	if (selection == NULL || *selection == '\0')
 		goto whole_message;
 
@@ -1397,20 +1384,18 @@ static void
 headers_changed_cb (GConfClient *client,
                     guint cnxn_id,
                     GConfEntry *entry,
-                    EMailReader *reader)
+                    EMFormat *emf)
 {
-	EMFormatHTML *formatter;
 	GSList *header_config_list, *p;
 
 	g_return_if_fail (client != NULL);
-	g_return_if_fail (reader != NULL);
-
-	formatter = e_mail_reader_get_formatter (reader);
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
 	header_config_list = gconf_client_get_list (
 		client, "/apps/evolution/mail/display/headers",
 		GCONF_VALUE_STRING, NULL);
-	em_format_clear_headers (EM_FORMAT (formatter));
+
+	em_format_clear_headers (emf);
 	for (p = header_config_list; p; p = g_slist_next (p)) {
 		EMailReaderHeader *h;
 		gchar *xml = (gchar *) p->data;
@@ -1418,21 +1403,20 @@ headers_changed_cb (GConfClient *client,
 		h = e_mail_reader_header_from_xml (xml);
 		if (h && h->enabled)
 			em_format_add_header (
-				EM_FORMAT (formatter),
-				h->name, EM_FORMAT_HEADER_BOLD);
+				emf, h->name, NULL, EM_FORMAT_HEADER_BOLD);
 
 		e_mail_reader_header_free (h);
 	}
 
 	if (!header_config_list)
-		em_format_default_headers (EM_FORMAT (formatter));
+		em_format_default_headers (emf);
 
 	g_slist_foreach (header_config_list, (GFunc) g_free, NULL);
 	g_slist_free (header_config_list);
 
 	/* force a redraw */
-	if (EM_FORMAT (formatter)->message)
-		em_format_queue_redraw (EM_FORMAT (formatter));
+	if (emf->message)
+		em_format_redraw (emf);
 }
 
 static void
@@ -1458,7 +1442,8 @@ remove_header_notify_cb (gpointer data)
  * updates the EMFormat whenever it changes and on this call too.
  **/
 void
-e_mail_reader_connect_headers (EMailReader *reader)
+e_mail_reader_connect_headers (EMailReader *reader,
+                               EMFormat *emf)
 {
 	GConfClient *client;
 	guint notify_id;
@@ -1471,13 +1456,13 @@ e_mail_reader_connect_headers (EMailReader *reader)
 	notify_id = gconf_client_notify_add (
 		client, "/apps/evolution/mail/display/headers",
 		(GConfClientNotifyFunc) headers_changed_cb,
-		reader, NULL, NULL);
+		emf, NULL, NULL);
 
 	g_object_set_data_full (
-		G_OBJECT (reader), "reader-header-notify-id",
+		G_OBJECT (emf), "reader-header-notify-id",
 		GINT_TO_POINTER (notify_id), remove_header_notify_cb);
 
-	headers_changed_cb (client, 0, NULL, reader);
+	headers_changed_cb (client, 0, NULL, emf);
 
 	g_object_unref (client);
 }
diff --git a/mail/e-mail-reader-utils.h b/mail/e-mail-reader-utils.h
index bb4671b..6913d09 100644
--- a/mail/e-mail-reader-utils.h
+++ b/mail/e-mail-reader-utils.h
@@ -67,7 +67,8 @@ EMailReaderHeader *
 gchar *		e_mail_reader_header_to_xml	(EMailReaderHeader *header);
 void		e_mail_reader_header_free	(EMailReaderHeader *header);
 
-void		e_mail_reader_connect_headers	(EMailReader *reader);
+void		e_mail_reader_connect_headers	(EMailReader *reader,
+						 EMFormat *emf);
 
 G_END_DECLS
 
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 53d4c31..0106b6e 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -48,13 +48,13 @@
 
 #include "mail/e-mail-backend.h"
 #include "mail/e-mail-browser.h"
-#include "mail/e-mail-display.h"
 #include "mail/e-mail-reader-utils.h"
 #include "mail/e-mail-view.h"
 #include "mail/em-composer-utils.h"
 #include "mail/em-event.h"
 #include "mail/em-folder-selector.h"
 #include "mail/em-folder-tree.h"
+#include "mail/em-format-html-display.h"
 #include "mail/em-utils.h"
 #include "mail/mail-autofilter.h"
 #include "mail/mail-vfolder-ui.h"
@@ -70,6 +70,8 @@
 	((EMailReaderPrivate *) g_object_get_qdata \
 	(G_OBJECT (obj), quark_private))
 
+#define d(x)
+
 typedef struct _EMailReaderClosure EMailReaderClosure;
 typedef struct _EMailReaderPrivate EMailReaderPrivate;
 
@@ -125,6 +127,13 @@ static guint signals[LAST_SIGNAL];
 G_DEFINE_INTERFACE (EMailReader, e_mail_reader, G_TYPE_INITIALLY_UNOWNED)
 
 static void
+mail_reader_set_display_formatter_for_message (EMailReader *reader,
+                                               EMailDisplay *display,
+                                               const gchar *message_uid,
+                                               CamelMimeMessage *message,
+                                               CamelFolder *folder);
+
+static void
 mail_reader_closure_free (EMailReaderClosure *closure)
 {
 	if (closure->reader != NULL)
@@ -201,7 +210,6 @@ action_add_to_address_book_cb (GtkAction *action,
 	EShell *shell;
 	EMailBackend *backend;
 	EShellBackend *shell_backend;
-	EMFormatHTML *formatter;
 	CamelInternetAddress *cia;
 	EWebView *web_view;
 	CamelURL *curl;
@@ -211,9 +219,10 @@ action_add_to_address_book_cb (GtkAction *action,
 	/* This action is defined in EMailDisplay. */
 
 	backend = e_mail_reader_get_backend (reader);
-	formatter = e_mail_reader_get_formatter (reader);
 
-	web_view = em_format_html_get_web_view (formatter);
+	web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
+	if (!web_view)
+		return;
 
 	uri = e_web_view_get_selected_uri (web_view);
 	g_return_if_fail (uri != NULL);
@@ -247,21 +256,136 @@ exit:
 }
 
 static void
+attachment_load_finish (EAttachment *attachment,
+                        GAsyncResult *result,
+                        GFile *file)
+{
+	EShell *shell;
+	GtkWindow *parent;
+
+	e_attachment_load_finish (attachment, result, NULL);
+
+	shell = e_shell_get_default ();
+	parent = e_shell_get_active_window (shell);
+
+	e_attachment_save_async (
+		attachment, file, (GAsyncReadyCallback)
+		e_attachment_save_handle_error, parent);
+
+	g_object_unref (file);
+}
+
+static void
+action_mail_image_save_cb (GtkAction *action,
+                           EMailReader *reader)
+{
+	EMailDisplay *display;
+	EWebView *web_view;
+	EMFormat *emf;
+	const gchar *image_src;
+	CamelMimePart *part;
+	EAttachment *attachment;
+	GFile *file;
+
+	display = e_mail_reader_get_mail_display (reader);
+	web_view = E_WEB_VIEW (display);
+
+	if (!E_IS_WEB_VIEW (web_view))
+		return;
+
+	image_src = e_web_view_get_cursor_image_src (web_view);
+	if (!image_src)
+		return;
+
+	emf = EM_FORMAT (e_mail_display_get_formatter (display));
+	g_return_if_fail (emf != NULL);
+	g_return_if_fail (emf->message != NULL);
+
+        if (g_str_has_prefix (image_src, "cid:")) {
+		part = camel_mime_message_get_part_by_content_id (
+			emf->message, image_src + 4);
+		g_return_if_fail (part != NULL);
+
+		g_object_ref (part);
+	} else {
+		CamelStream *image_stream;
+		CamelDataWrapper *dw;
+		CamelDataCache *cache;
+		const gchar *filename;
+		const gchar *user_cache_dir;
+
+                /* Open cache and find the file there */
+		user_cache_dir = e_get_user_cache_dir ();
+		cache = camel_data_cache_new (user_cache_dir, NULL);
+                image_stream = camel_data_cache_get (cache, "http", image_src, NULL);
+		if (!image_stream) {
+			g_object_unref (cache);
+			return;
+		}
+
+		filename = strrchr (image_src, '/');
+		if (filename && strchr (filename, '?'))
+			filename = NULL;
+		else if (filename)
+			filename = filename + 1;
+
+		part = camel_mime_part_new ();
+		if (filename)
+			camel_mime_part_set_filename (part, filename);
+
+		dw = camel_data_wrapper_new ();
+		camel_data_wrapper_set_mime_type (
+                        dw, "application/octet-stream");
+		camel_data_wrapper_construct_from_stream_sync (
+			dw, image_stream, NULL, NULL);
+		camel_medium_set_content (CAMEL_MEDIUM (part), dw);
+		g_object_unref (dw);
+
+		camel_mime_part_set_encoding (
+			part, CAMEL_TRANSFER_ENCODING_BASE64);
+
+		g_object_unref (image_stream);
+		g_object_unref (cache);
+	}
+
+	file = e_shell_run_save_dialog (
+		e_shell_get_default (),
+                _("Save Image"), camel_mime_part_get_filename (part),
+		NULL, NULL, NULL);
+	if (file == NULL) {
+		g_object_unref (part);
+		return;
+	}
+
+	attachment = e_attachment_new ();
+	e_attachment_set_mime_part (attachment, part);
+
+	e_attachment_load_async (
+		attachment, (GAsyncReadyCallback)
+		attachment_load_finish, file);
+
+	g_object_unref (part);
+}
+
+static void
 action_mail_charset_cb (GtkRadioAction *action,
                         GtkRadioAction *current,
                         EMailReader *reader)
 {
+	EMailDisplay *display;
 	EMFormatHTML *formatter;
 	const gchar *charset;
 
 	if (action != current)
 		return;
 
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
+	formatter = e_mail_display_get_formatter (display);
 	charset = g_object_get_data (G_OBJECT (action), "charset");
 
 	/* Charset for "Default" action will be NULL. */
-	em_format_set_charset (EM_FORMAT (formatter), charset);
+	if (formatter)
+		em_format_set_charset (EM_FORMAT (formatter), charset);
 }
 
 static void
@@ -438,38 +562,38 @@ static void
 action_mail_flag_clear_cb (GtkAction *action,
                            EMailReader *reader)
 {
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	CamelFolder *folder;
 	GtkWindow *window;
 	GPtrArray *uids;
 
 	folder = e_mail_reader_get_folder (reader);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	uids = e_mail_reader_get_selected_uids (reader);
 	window = e_mail_reader_get_window (reader);
 
 	em_utils_flag_for_followup_clear (window, folder, uids);
 
-	em_format_queue_redraw (EM_FORMAT (formatter));
+	e_mail_display_reload (display);
 }
 
 static void
 action_mail_flag_completed_cb (GtkAction *action,
                                EMailReader *reader)
 {
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	CamelFolder *folder;
 	GtkWindow *window;
 	GPtrArray *uids;
 
 	folder = e_mail_reader_get_folder (reader);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	uids = e_mail_reader_get_selected_uids (reader);
 	window = e_mail_reader_get_window (reader);
 
 	em_utils_flag_for_followup_completed (window, folder, uids);
 
-	em_format_queue_redraw (EM_FORMAT (formatter));
+	e_mail_display_reload (display);
 }
 
 static void
@@ -663,11 +787,11 @@ static void
 action_mail_load_images_cb (GtkAction *action,
                             EMailReader *reader)
 {
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 
-	em_format_html_load_images (formatter);
+	e_mail_display_load_images (display);
 }
 
 static void
@@ -1576,20 +1700,14 @@ static void
 action_mail_show_all_headers_cb (GtkToggleAction *action,
                                  EMailReader *reader)
 {
-	EMFormatHTML *formatter;
-	EMFormatMode mode;
+	EMailDisplay *display;
 
-	formatter = e_mail_reader_get_formatter (reader);
-
-	if (!formatter)
-		return;
+	display = e_mail_reader_get_mail_display (reader);
 
 	if (gtk_toggle_action_get_active (action))
-		mode = EM_FORMAT_MODE_ALLHEADERS;
+		e_mail_display_set_mode (display, EM_FORMAT_WRITE_MODE_ALL_HEADERS);
 	else
-		mode = EM_FORMAT_MODE_NORMAL;
-
-	em_format_set_mode (EM_FORMAT (formatter), mode);
+		e_mail_display_set_mode (display, EM_FORMAT_WRITE_MODE_NORMAL);
 }
 
 static void
@@ -1597,8 +1715,9 @@ action_mail_show_source_cb (GtkAction *action,
                             EMailReader *reader)
 {
 	EMailBackend *backend;
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	CamelFolder *folder;
+	CamelMimeMessage *message;
 	GtkWidget *browser;
 	GPtrArray *uids;
 	const gchar *message_uid;
@@ -1610,16 +1729,16 @@ action_mail_show_source_cb (GtkAction *action,
 	g_return_if_fail (uids != NULL && uids->len == 1);
 	message_uid = g_ptr_array_index (uids, 0);
 
-	browser = e_mail_browser_new (backend);
-	reader = E_MAIL_READER (browser);
-	formatter = e_mail_reader_get_formatter (reader);
+	message = camel_folder_get_message_sync (folder, message_uid, NULL, NULL);
 
-	if (formatter != NULL)
-		em_format_set_mode (
-			EM_FORMAT (formatter), EM_FORMAT_MODE_SOURCE);
+	browser = e_mail_browser_new (backend, NULL, NULL, EM_FORMAT_WRITE_MODE_SOURCE);
+	e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
+	e_mail_reader_set_message (E_MAIL_READER (browser), message_uid);
+
+	display = e_mail_reader_get_mail_display (E_MAIL_READER (browser));
+	mail_reader_set_display_formatter_for_message (
+		E_MAIL_READER (browser), display, message_uid, message, folder);
 
-	e_mail_reader_set_folder (reader, folder);
-	e_mail_reader_set_message (reader, message_uid);
 	gtk_widget_show (browser);
 
 	em_utils_uids_free (uids);
@@ -1671,39 +1790,33 @@ static void
 action_mail_zoom_100_cb (GtkAction *action,
                          EMailReader *reader)
 {
-	EMFormatHTML *formatter;
-	EWebView *web_view;
+	EMailDisplay *display;
 
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
+	display = e_mail_reader_get_mail_display (reader);
 
-	e_web_view_zoom_100 (web_view);
+	webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (display), 1.0);
 }
 
 static void
 action_mail_zoom_in_cb (GtkAction *action,
                         EMailReader *reader)
 {
-	EMFormatHTML *formatter;
-	EWebView *web_view;
+	EMailDisplay *display;
 
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
+	display = e_mail_reader_get_mail_display (reader);
 
-	e_web_view_zoom_in (web_view);
+	webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (display));
 }
 
 static void
 action_mail_zoom_out_cb (GtkAction *action,
                          EMailReader *reader)
 {
-	EMFormatHTML *formatter;
-	EWebView *web_view;
+	EMailDisplay *display;
 
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
+	display = e_mail_reader_get_mail_display (reader);
 
-	e_web_view_zoom_out (web_view);
+	webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (display));
 }
 
 static void
@@ -1712,7 +1825,6 @@ action_search_folder_recipient_cb (GtkAction *action,
 {
 	EMailBackend *backend;
 	EMailSession *session;
-	EMFormatHTML *formatter;
 	EWebView *web_view;
 	CamelFolder *folder;
 	CamelURL *curl;
@@ -1721,9 +1833,7 @@ action_search_folder_recipient_cb (GtkAction *action,
 	/* This action is defined in EMailDisplay. */
 
 	folder = e_mail_reader_get_folder (reader);
-	formatter = e_mail_reader_get_formatter (reader);
-
-	web_view = em_format_html_get_web_view (formatter);
+	web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
 
 	uri = e_web_view_get_selected_uri (web_view);
 	g_return_if_fail (uri != NULL);
@@ -1753,7 +1863,6 @@ action_search_folder_sender_cb (GtkAction *action,
 {
 	EMailBackend *backend;
 	EMailSession *session;
-	EMFormatHTML *formatter;
 	EWebView *web_view;
 	CamelFolder *folder;
 	CamelURL *curl;
@@ -1762,9 +1871,7 @@ action_search_folder_sender_cb (GtkAction *action,
 	/* This action is defined in EMailDisplay. */
 
 	folder = e_mail_reader_get_folder (reader);
-	formatter = e_mail_reader_get_formatter (reader);
-
-	web_view = em_format_html_get_web_view (formatter);
+	web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
 
 	uri = e_web_view_get_selected_uri (web_view);
 	g_return_if_fail (uri != NULL);
@@ -2494,6 +2601,7 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure)
 	EMailReader *reader;
 	GtkWidget *message_list;
 	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	CamelMimeMessage *message;
 	const gchar *current_uid;
 	const gchar *message_uid;
@@ -2502,19 +2610,20 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure)
 	reader = closure->reader;
 	message_uid = closure->message_uid;
 
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
+	formatter = e_mail_display_get_formatter (display);
 	message_list = e_mail_reader_get_message_list (reader);
 
 	if (e_tree_is_dragging (E_TREE (message_list)))
 		return FALSE;
 
-	current_uid = EM_FORMAT (formatter)->uid;
-	uid_is_current &= (g_strcmp0 (current_uid, message_uid) == 0);
-
 	current_uid = MESSAGE_LIST (message_list)->cursor_uid;
 	uid_is_current &= (g_strcmp0 (current_uid, message_uid) == 0);
 
-	message = EM_FORMAT (formatter)->message;
+	if (formatter)
+		message = EM_FORMAT (formatter)->message;
+	else
+		message = NULL;
 
 	if (uid_is_current && message != NULL)
 		g_signal_emit (
@@ -2535,7 +2644,6 @@ schedule_timeout_mark_seen (EMailReader *reader)
 	gboolean schedule_timeout;
 	gint timeout_interval;
 	const gchar *message_uid;
-
 	backend = e_mail_reader_get_backend (reader);
 	message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
 	shell_backend = E_SHELL_BACKEND (backend);
@@ -2669,56 +2777,47 @@ static gboolean
 mail_reader_message_selected_timeout_cb (EMailReader *reader)
 {
 	EMailReaderPrivate *priv;
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	GtkWidget *message_list;
-	EPreviewPane *preview_pane;
 	CamelFolder *folder;
 	const gchar *cursor_uid;
 	const gchar *format_uid;
+	EMFormat *formatter;
 
 	priv = E_MAIL_READER_GET_PRIVATE (reader);
 
 	folder = e_mail_reader_get_folder (reader);
 
-	formatter = e_mail_reader_get_formatter (reader);
 	message_list = e_mail_reader_get_message_list (reader);
-	preview_pane = e_mail_reader_get_preview_pane (reader);
+	display = e_mail_reader_get_mail_display (reader);
+	formatter = EM_FORMAT (e_mail_display_get_formatter (display));
 
 	cursor_uid = MESSAGE_LIST (message_list)->cursor_uid;
-	format_uid = EM_FORMAT (formatter)->uid;
-
-	e_preview_pane_clear_alerts (preview_pane);
+	format_uid = formatter ? formatter->message_uid : NULL;
 
 	if (MESSAGE_LIST (message_list)->last_sel_single) {
-		gboolean preview_visible;
+		GtkWidget *widget;
+		gboolean display_visible;
 		gboolean selected_uid_changed;
 
 		/* Decide whether to download the full message now. */
+		widget = GTK_WIDGET (display);
+		display_visible = gtk_widget_get_mapped (widget);
 
-		preview_visible =
-			gtk_widget_get_mapped (GTK_WIDGET (preview_pane));
-		selected_uid_changed = g_strcmp0 (cursor_uid, format_uid);
+		selected_uid_changed = (g_strcmp0 (cursor_uid, format_uid) != 0);
 
-		if (preview_visible && selected_uid_changed) {
+		if (display_visible && selected_uid_changed) {
 			EMailReaderClosure *closure;
 			GCancellable *cancellable;
 			EActivity *activity;
-			EWebView *web_view;
 			gchar *string;
 
-			web_view = e_preview_pane_get_web_view (preview_pane);
-
-			string = g_strdup_printf (
-				_("Retrieving message '%s'"), cursor_uid);
-#if HAVE_CLUTTER
-			if (!e_shell_get_express_mode (e_shell_get_default ()))
-				e_web_view_load_string (web_view, string);
-#else
-			e_web_view_load_string (web_view, string);
-#endif
+			string = g_strdup_printf (_("Retrieving message '%s'"), cursor_uid);
+			e_mail_display_set_status (display, string);
 			g_free (string);
 
 			activity = e_mail_reader_new_activity (reader);
+                        e_activity_set_text (activity, _("Retrieving message"));
 			cancellable = e_activity_get_cancellable (activity);
 
 			closure = g_slice_new0 (EMailReaderClosure);
@@ -2736,9 +2835,6 @@ mail_reader_message_selected_timeout_cb (EMailReader *reader)
 			priv->retrieving_message = g_object_ref (cancellable);
 		}
 	} else {
-		/* FIXME Need to pass a GCancellable. */
-		em_format_format (
-			EM_FORMAT (formatter), NULL, NULL, NULL, NULL);
 		priv->restoring_message_selection = FALSE;
 	}
 
@@ -2869,7 +2965,7 @@ mail_reader_set_folder (EMailReader *reader,
                         CamelFolder *folder)
 {
 	EMailReaderPrivate *priv;
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	CamelFolder *previous_folder;
 	GtkWidget *message_list;
 	EMailBackend *backend;
@@ -2879,7 +2975,7 @@ mail_reader_set_folder (EMailReader *reader,
 	priv = E_MAIL_READER_GET_PRIVATE (reader);
 
 	backend = e_mail_reader_get_backend (reader);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
 	previous_folder = e_mail_reader_get_folder (reader);
@@ -2899,8 +2995,7 @@ mail_reader_set_folder (EMailReader *reader,
 		em_utils_folder_is_outbox (folder) ||
 		em_utils_folder_is_sent (folder));
 
-	/* FIXME Need to pass a GCancellable. */
-	em_format_format (EM_FORMAT (formatter), NULL, NULL, NULL, NULL);
+	e_web_view_clear (E_WEB_VIEW (display));
 
 	priv->folder_was_just_selected = (folder != NULL);
 
@@ -2931,18 +3026,128 @@ mail_reader_folder_loaded (EMailReader *reader)
 	e_mail_reader_update_actions (reader, state);
 }
 
+struct _formatter_weak_ref_closure {
+	GHashTable *formatters;
+	gchar *mail_uri;
+};
+
+static void
+formatter_weak_ref_cb (struct _formatter_weak_ref_closure *data,
+                       EMFormat *formatter)
+{
+	/* When this callback is called, the formatter is being finalized
+	 * so we only remove it from the formatters table. */
+	g_hash_table_remove (data->formatters,
+		data->mail_uri);
+
+        d(printf("Destroying formatter %p (%s)\n", formatter, data->mail_uri));
+
+	/* Destroying the formatter will prevent this callback
+	 * being called, so we can remove the closure data as well */
+	g_hash_table_unref (data->formatters);
+	g_free (data->mail_uri);
+	g_free (data);
+}
+
+struct format_parser_async_closure_ {
+        EMailDisplay *display;
+        EActivity *activity;
+};
+
+static void
+format_parser_async_done_cb (GObject *source,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+	EMFormat *emf = EM_FORMAT (source);
+	struct format_parser_async_closure_ *closure = user_data;
+
+	e_mail_display_set_formatter (closure->display, EM_FORMAT_HTML (emf));
+	e_mail_display_load (closure->display, emf->uri_base);
+
+	g_object_unref (closure->activity);
+	g_object_unref (closure->display);
+	g_free (closure);
+
+	g_object_unref (result);
+
+        /* Remove the reference added when formatter was created,
+         * so that only owners are EMailDisplays */
+	g_object_unref (emf);
+}
+
+static void
+mail_reader_set_display_formatter_for_message (EMailReader *reader,
+                                               EMailDisplay *display,
+                                               const gchar *message_uid,
+                                               CamelMimeMessage *message,
+                                               CamelFolder *folder)
+{
+	SoupSession *session;
+	GHashTable *formatters;
+	EMFormat *formatter;
+	gchar *mail_uri;
+
+	mail_uri = em_format_build_mail_uri (folder, message_uid, NULL, NULL);
+
+	session = webkit_get_default_session ();
+	formatters = g_object_get_data (G_OBJECT (session), "formatters");
+	if (!formatters) {
+		formatters = g_hash_table_new_full (g_str_hash, g_str_equal,
+				(GDestroyNotify) g_free, NULL);
+		g_object_set_data (G_OBJECT (session), "formatters", formatters);
+	}
+
+	if ((formatter = g_hash_table_lookup (formatters, mail_uri)) == NULL) {
+		struct _formatter_weak_ref_closure *formatter_data =
+				g_new0 (struct _formatter_weak_ref_closure, 1);
+
+		struct format_parser_async_closure_ *closure;
+
+		formatter_data->formatters = g_hash_table_ref (formatters);
+		formatter_data->mail_uri = g_strdup (mail_uri);
+
+		formatter = EM_FORMAT (em_format_html_display_new ());
+
+		/* When no EMailDisplay holds reference to the formatter, then
+		 * the formatter can be destroyed. */
+		g_object_weak_ref (G_OBJECT (formatter),
+			(GWeakNotify) formatter_weak_ref_cb, formatter_data);
+
+		formatter->message_uid = g_strdup (message_uid);
+		formatter->uri_base = g_strdup (mail_uri);
+
+		e_mail_reader_connect_headers (reader, formatter);
+
+		closure = g_new0 (struct format_parser_async_closure_, 1);
+		closure->activity = e_mail_reader_new_activity (reader);
+		e_activity_set_text (closure->activity, _("Parsing message"));
+		closure->display = g_object_ref (display);
+
+		em_format_parse_async (formatter, message, folder,
+			e_activity_get_cancellable (closure->activity),
+			format_parser_async_done_cb, closure);
+
+		/* Don't free the mail_uri!! */
+		g_hash_table_insert (formatters, mail_uri, formatter);
+	} else {
+		e_mail_display_set_formatter (display, EM_FORMAT_HTML (formatter));
+		e_mail_display_load (display, formatter->uri_base);
+
+		g_free (mail_uri);
+	}
+}
+
 static void
 mail_reader_message_loaded (EMailReader *reader,
                             const gchar *message_uid,
                             CamelMimeMessage *message)
 {
 	EMailReaderPrivate *priv;
-	EMFormatHTML *formatter;
 	GtkWidget *message_list;
 	EMailBackend *backend;
 	CamelFolder *folder;
-	EWebView *web_view;
-	EPreviewPane *preview_pane;
+	EMailDisplay *display;
 	EShellBackend *shell_backend;
 	EShell *shell;
 	EMEvent *event;
@@ -2953,15 +3158,12 @@ mail_reader_message_loaded (EMailReader *reader,
 
 	folder = e_mail_reader_get_folder (reader);
 	backend = e_mail_reader_get_backend (reader);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
-	preview_pane = e_mail_reader_get_preview_pane (reader);
 
 	shell_backend = E_SHELL_BACKEND (backend);
 	shell = e_shell_backend_get_shell (shell_backend);
 
-	web_view = e_preview_pane_get_web_view (preview_pane);
-
 	/** @Event: message.reading
 	 * @Title: Viewing a message
 	 * @Target: EMEventTargetMessage
@@ -2975,10 +3177,8 @@ mail_reader_message_loaded (EMailReader *reader,
 		(EEvent *) event, "message.reading",
 		(EEventTarget *) target);
 
-	/* FIXME Need to pass a GCancellable. */
-	em_format_format (
-		EM_FORMAT (formatter), folder,
-		message_uid, message, NULL);
+	mail_reader_set_display_formatter_for_message (
+		reader, display, message_uid, message, folder);
 
 	/* Reset the shell view icon. */
 	e_shell_event (shell, "mail-icon", (gpointer) "evolution-mail");
@@ -2996,7 +3196,7 @@ mail_reader_message_loaded (EMailReader *reader,
 		g_clear_error (&error);
 	} else if (error != NULL) {
 		e_alert_submit (
-			E_ALERT_SINK (web_view),
+			E_ALERT_SINK (display),
 			"mail:no-retrieve-message",
 			error->message, NULL);
 		g_error_free (error);
@@ -3597,14 +3797,13 @@ e_mail_reader_init (EMailReader *reader,
                     gboolean init_actions,
                     gboolean connect_signals)
 {
-	EMFormatHTML *formatter;
 	EMenuToolAction *menu_tool_action;
-	EWebView *web_view;
 	GtkActionGroup *action_group;
 	GtkWidget *message_list;
 	GtkAction *action;
 	gboolean sensitive;
 	const gchar *action_name;
+	EMailDisplay *display;
 
 #ifndef G_OS_WIN32
 	GSettings *settings;
@@ -3612,10 +3811,8 @@ e_mail_reader_init (EMailReader *reader,
 
 	g_return_if_fail (E_IS_MAIL_READER (reader));
 
-	formatter = e_mail_reader_get_formatter (reader);
 	message_list = e_mail_reader_get_message_list (reader);
-
-	web_view = em_format_html_get_web_view (formatter);
+	display = e_mail_reader_get_mail_display (reader);
 
 	if (!init_actions)
 		goto connect_signals;
@@ -3755,30 +3952,38 @@ e_mail_reader_init (EMailReader *reader,
 	gtk_action_set_is_important (action, TRUE);
 	gtk_action_set_short_label (action, _("Reply"));
 
+	display = e_mail_reader_get_mail_display (reader);
+
 	action_name = "add-to-address-book";
-	action = e_web_view_get_action (web_view, action_name);
+	action = e_mail_display_get_action (display, action_name);
 	g_signal_connect (
 		action, "activate",
 		G_CALLBACK (action_add_to_address_book_cb), reader);
 
 	action_name = "send-reply";
-	action = e_web_view_get_action (web_view, action_name);
+	action = e_mail_display_get_action (display, action_name);
 	g_signal_connect (
 		action, "activate",
 		G_CALLBACK (action_mail_reply_recipient_cb), reader);
 
 	action_name = "search-folder-recipient";
-	action = e_web_view_get_action (web_view, action_name);
+	action = e_mail_display_get_action (display, action_name);
 	g_signal_connect (
 		action, "activate",
 		G_CALLBACK (action_search_folder_recipient_cb), reader);
 
 	action_name = "search-folder-sender";
-	action = e_web_view_get_action (web_view, action_name);
+	action = e_mail_display_get_action (display, action_name);
 	g_signal_connect (
 		action, "activate",
 		G_CALLBACK (action_search_folder_sender_cb), reader);
 
+        action_name = "image-save";
+	action = e_mail_display_get_action (display, action_name);
+	g_signal_connect (
+                action, "activate",
+		G_CALLBACK (action_mail_image_save_cb), reader);
+
 #ifndef G_OS_WIN32
 	/* Lockdown integration. */
 
@@ -3821,7 +4026,7 @@ e_mail_reader_init (EMailReader *reader,
 
 	g_object_bind_property (
 		action, "active",
-		web_view, "caret-mode",
+		display, "caret-mode",
 		G_BINDING_BIDIRECTIONAL |
 		G_BINDING_SYNC_CREATE);
 
@@ -3832,7 +4037,7 @@ connect_signals:
 
 	/* Connect signals. */
 	g_signal_connect_swapped (
-		web_view, "key-press-event",
+		display, "key-press-event",
 		G_CALLBACK (mail_reader_key_press_event_cb), reader);
 
 	g_signal_connect_swapped (
@@ -4157,17 +4362,17 @@ e_mail_reader_get_backend (EMailReader *reader)
 	return interface->get_backend (reader);
 }
 
-EMFormatHTML *
-e_mail_reader_get_formatter (EMailReader *reader)
+EMailDisplay *
+e_mail_reader_get_mail_display (EMailReader *reader)
 {
 	EMailReaderInterface *interface;
 
 	g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
 
 	interface = E_MAIL_READER_GET_INTERFACE (reader);
-	g_return_val_if_fail (interface->get_formatter != NULL, NULL);
+	g_return_val_if_fail (interface->get_mail_display != NULL, NULL);
 
-	return interface->get_formatter (reader);
+	return interface->get_mail_display (reader);
 }
 
 gboolean
diff --git a/mail/e-mail-reader.h b/mail/e-mail-reader.h
index 233e52b..6f03fc0 100644
--- a/mail/e-mail-reader.h
+++ b/mail/e-mail-reader.h
@@ -31,7 +31,7 @@
 #include <camel/camel.h>
 #include <libevolution-utils/e-alert-sink.h>
 #include <mail/e-mail-backend.h>
-#include <mail/em-format-html.h>
+#include <mail/e-mail-display.h>
 #include <misc/e-preview-pane.h>
 
 /* Standard GObject macros */
@@ -96,7 +96,7 @@ struct _EMailReaderInterface {
 						 EMailReaderActionGroup group);
 	EAlertSink *	(*get_alert_sink)	(EMailReader *reader);
 	EMailBackend *	(*get_backend)		(EMailReader *reader);
-	EMFormatHTML *	(*get_formatter)	(EMailReader *reader);
+	EMailDisplay *	(*get_mail_display)	(EMailReader *reader);
 	gboolean	(*get_hide_deleted)	(EMailReader *reader);
 	GtkWidget *	(*get_message_list)	(EMailReader *reader);
 	GtkMenu *	(*get_popup_menu)	(EMailReader *reader);
@@ -141,7 +141,7 @@ GtkActionGroup *
 						 EMailReaderActionGroup group);
 EAlertSink *	e_mail_reader_get_alert_sink	(EMailReader *reader);
 EMailBackend *	e_mail_reader_get_backend	(EMailReader *reader);
-EMFormatHTML *	e_mail_reader_get_formatter	(EMailReader *reader);
+EMailDisplay *	e_mail_reader_get_mail_display	(EMailReader *reader);
 gboolean	e_mail_reader_get_hide_deleted	(EMailReader *reader);
 GtkWidget *	e_mail_reader_get_message_list	(EMailReader *reader);
 guint		e_mail_reader_open_selected_mail
diff --git a/mail/e-mail-request.c b/mail/e-mail-request.c
new file mode 100644
index 0000000..34f1845
--- /dev/null
+++ b/mail/e-mail-request.c
@@ -0,0 +1,771 @@
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "e-mail-request.h"
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-requester.h>
+#include <libsoup/soup-request-http.h>
+
+#include <glib/gi18n.h>
+#include <camel/camel.h>
+
+#include "em-format-html.h"
+
+#include <e-util/e-icon-factory.h>
+#include <e-util/e-util.h>
+
+#define d(x)
+#define dd(x)
+
+G_DEFINE_TYPE (EMailRequest, e_mail_request, SOUP_TYPE_REQUEST)
+
+struct _EMailRequestPrivate {
+	EMFormatHTML *efh;
+
+	CamelStream *output_stream;
+	EMFormatPURI *puri;
+	gchar *mime_type;
+
+	gint content_length;
+
+	GHashTable *uri_query;
+
+        gchar *ret_mime_type;
+};
+
+static void
+handle_mail_request (GSimpleAsyncResult *res,
+                     GObject *object,
+                     GCancellable *cancellable)
+{
+	EMailRequest *request = E_MAIL_REQUEST (object);
+	EMFormatHTML *efh = request->priv->efh;
+	EMFormat *emf = EM_FORMAT (efh);
+	GInputStream *stream;
+	GByteArray *ba;
+	gchar *part_id;
+	EMFormatWriterInfo info = {0};
+	gchar *val;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	if (request->priv->output_stream != NULL) {
+		g_object_unref (request->priv->output_stream);
+	}
+
+	request->priv->output_stream = camel_stream_mem_new ();
+
+	val = g_hash_table_lookup (request->priv->uri_query, "headers_collapsed");
+	if (val)
+		info.headers_collapsed = atoi (val);
+
+	val = g_hash_table_lookup (request->priv->uri_query, "headers_collapsable");
+	if (val)
+		info.headers_collapsable = atoi (val);
+
+	val = g_hash_table_lookup (request->priv->uri_query, "mode");
+	if (val)
+		info.mode = atoi (val);
+
+	part_id = g_hash_table_lookup (request->priv->uri_query, "part_id");
+	if (part_id) {
+                /* original part_id is owned by the GHashTable */
+		part_id = soup_uri_decode (part_id);
+		request->priv->puri = em_format_find_puri (emf, part_id);
+
+		if (request->priv->puri) {
+			em_format_puri_write (request->priv->puri,
+				request->priv->output_stream, &info, NULL);
+		} else {
+			g_warning ("Failed to lookup requested part '%s' - this should not happen!", part_id);
+		}
+
+		g_free (part_id);
+	} else {
+
+		em_format_write (emf, request->priv->output_stream, &info, NULL);
+	}
+
+	/* Convert the GString to GInputStream and send it back to WebKit */
+	ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (request->priv->output_stream));
+	if (!ba->data) {
+		gchar *data = g_strdup_printf(_("Failed to load part '%s'"), part_id);
+		dd(printf("%s", data));
+		g_byte_array_append (ba, (guchar *) data, strlen (data));
+		g_free (data);
+	} else {
+		dd ({
+			gchar *d = g_strndup ((gchar *) ba->data, ba->len);
+                        printf("%s", d);
+			g_free (d);
+		});
+	}
+
+	stream = g_memory_input_stream_new_from_data (
+			(gchar *) ba->data, ba->len, NULL);
+	g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+}
+
+static void
+handle_file_request (GSimpleAsyncResult *res,
+                     GObject *object,
+                     GCancellable *cancellable)
+{
+	EMailRequest *request = E_MAIL_REQUEST (object);
+	SoupURI *uri;
+	GInputStream *stream;
+	gchar *contents;
+	gsize length;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	uri = soup_request_get_uri (SOUP_REQUEST (request));
+
+	if (g_file_get_contents (uri->path, &contents, &length, NULL)) {
+
+		request->priv->mime_type = g_content_type_guess (uri->path, NULL, 0, NULL);
+		request->priv->content_length = length;
+
+		stream = g_memory_input_stream_new_from_data (
+				contents, length, (GDestroyNotify) g_free);
+		g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+	}
+}
+
+struct http_request_async_data {
+        GMainLoop *loop;
+        GCancellable *cancellable;
+        CamelDataCache *cache;
+        gchar *cache_key;
+
+        GInputStream *stream;
+        CamelStream *cache_stream;
+        gchar *content_type;
+        goffset content_length;
+
+        gchar *buff;
+};
+
+static void
+http_request_write_to_cache (GInputStream *stream,
+                             GAsyncResult *res,
+                             struct http_request_async_data *data)
+{
+	GError *error;
+	gssize len;
+
+	error = NULL;
+	len = g_input_stream_read_finish (stream, res, &error);
+
+        /* Error while reading data */
+	if (len == -1) {
+                g_message ("Error while reading input stream: %s",
+                        error ? error->message : "Unknown error");
+		g_clear_error (&error);
+
+		g_main_loop_quit (data->loop);
+
+		if (data->buff)
+			g_free (data->buff);
+
+                /* Don't keep broken data in cache */
+                camel_data_cache_remove (data->cache, "http", data->cache_key, NULL);
+		return;
+	}
+
+        /* EOF */
+	if (len == 0) {
+		camel_stream_close (data->cache_stream, data->cancellable, NULL);
+
+		if (data->buff)
+			g_free (data->buff);
+
+		g_main_loop_quit (data->loop);
+		return;
+	}
+
+	if (!data->cache_stream) {
+
+		if (data->buff)
+			g_free (data->buff);
+
+		g_main_loop_quit (data->loop);
+		return;
+	}
+
+        /* Write chunk to cache and read another block of data. */
+	camel_stream_write (data->cache_stream, data->buff, len,
+		data->cancellable, NULL);
+
+	g_input_stream_read_async (stream, data->buff, 4096,
+		G_PRIORITY_DEFAULT, data->cancellable,
+		(GAsyncReadyCallback) http_request_write_to_cache, data);
+}
+
+static void
+http_request_finished (SoupRequest *request,
+                       GAsyncResult *res,
+                       struct http_request_async_data *data)
+{
+	GError *error;
+	SoupMessage *message;
+
+	error = NULL;
+	data->stream = soup_request_send_finish (request, res, &error);
+
+	if (!data->stream) {
+                g_warning("HTTP request failed: %s", error ? error->message: "Unknown error");
+		g_clear_error (&error);
+		g_main_loop_quit (data->loop);
+		return;
+	}
+
+	message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+	if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
+                g_warning ("HTTP request failed: HTTP code %d", message->status_code);
+		g_main_loop_quit (data->loop);
+		g_object_unref (message);
+		return;
+	}
+
+	g_object_unref (message);
+
+	data->content_length = soup_request_get_content_length (request);
+	data->content_type = g_strdup (soup_request_get_content_type (request));
+
+	if (!data->cache_stream) {
+		g_main_loop_quit (data->loop);
+		return;
+	}
+
+	data->buff = g_malloc (4096);
+	g_input_stream_read_async (data->stream, data->buff, 4096,
+		G_PRIORITY_DEFAULT, data->cancellable,
+		(GAsyncReadyCallback) http_request_write_to_cache, data);
+
+}
+
+static void
+handle_http_request (GSimpleAsyncResult *res,
+                     GObject *object,
+                     GCancellable *cancellable)
+{
+	EMailRequest *request = E_MAIL_REQUEST (object);
+	SoupURI *soup_uri;
+	gchar *evo_uri, *uri;
+	GInputStream *stream;
+	gboolean force_load_images = FALSE;
+	gchar *uri_md5;
+
+	const gchar *user_cache_dir;
+	CamelDataCache *cache;
+	CamelStream *cache_stream;
+
+	gssize len;
+	gchar *buff;
+
+	GHashTable *query;
+
+        /* Remove the __evo-mail query */
+	soup_uri = soup_request_get_uri (SOUP_REQUEST (request));
+	query = soup_form_decode (soup_uri->query);
+        g_hash_table_remove (query, "__evo-mail");
+
+        /* Remove __evo-load-images if present (and in such case set
+         * force_load_images to TRUE) */
+        force_load_images = g_hash_table_remove (query, "__evo-load-images");
+
+	soup_uri_set_query_from_form (soup_uri, query);
+	g_hash_table_unref (query);
+
+	evo_uri = soup_uri_to_string (soup_uri, FALSE);
+
+        /* Remove the "evo-" prefix from scheme */
+	if (evo_uri && (strlen (evo_uri) > 5)) {
+		uri = g_strdup (&evo_uri[4]);
+		g_free (evo_uri);
+	}
+
+	g_return_if_fail (uri && *uri);
+
+	/* Use MD5 hash of the URI as a filname of the resourec cache file.
+	 * We were previously using the URI as a filename but the URI is
+	 * sometimes too long for a filename. */
+	uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+
+        /* Open Evolution's cache */
+	user_cache_dir = e_get_user_cache_dir ();
+	cache = camel_data_cache_new (user_cache_dir, NULL);
+	if (cache) {
+		camel_data_cache_set_expire_age (cache, 24 * 60 * 60);
+		camel_data_cache_set_expire_access (cache, 2 * 60 * 60);
+	}
+
+        /* Found item in cache! */
+        cache_stream = camel_data_cache_get (cache, "http", uri_md5, NULL);
+	if (cache_stream) {
+
+		stream = g_memory_input_stream_new ();
+
+		request->priv->content_length = 0;
+
+		buff = g_malloc (4096);
+		while ((len = camel_stream_read (cache_stream, buff, 4096,
+				cancellable, NULL)) > 0) {
+
+			g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
+				buff, len, g_free);
+			request->priv->content_length += len;
+
+			buff = g_malloc (4096);
+		}
+
+		g_object_unref (cache_stream);
+
+                /* When succesfully read some data from cache then
+                 * get mimetype and return the stream to WebKit.
+                 * Otherwise try to fetch the resource again from the network. */
+		if ((len != -1) && (request->priv->content_length > 0)) {
+			GFile *file;
+			GFileInfo *info;
+			gchar *path;
+
+                        path = camel_data_cache_get_filename (cache, "http", uri_md5, NULL);
+			file = g_file_new_for_path (path);
+			info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+					0, cancellable, NULL);
+
+			request->priv->mime_type = g_strdup (
+				g_file_info_get_content_type (info));
+
+                        d(printf ("'%s' found in cache (%d bytes, %s)\n",
+				uri, request->priv->content_length,
+				request->priv->mime_type));
+
+			g_object_unref (info);
+			g_object_unref (file);
+			g_free (path);
+
+                        /* Set result and quit the thread */
+			g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+
+			goto cleanup;
+		} else {
+                        d(printf("Failed to load '%s' from cache.\n", uri));
+		}
+	}
+
+        /* Item not found in cache, but image loading policy allows us to fetch
+         * it from the interwebs */
+	if (force_load_images || em_format_html_can_load_images (request->priv->efh)) {
+
+		SoupRequester *requester;
+		SoupRequest *http_request;
+		SoupSession *session;
+		GMainContext *context;
+		GError *error;
+
+		struct http_request_async_data data = { 0 };
+
+		context = g_main_context_get_thread_default ();
+		session = soup_session_async_new_with_options (
+					SOUP_SESSION_ASYNC_CONTEXT, context, NULL);
+
+		requester = soup_requester_new ();
+		soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+
+		http_request = soup_requester_request (requester, uri, NULL);
+
+		error = NULL;
+		data.loop = g_main_loop_new (context, TRUE);
+		data.cancellable = cancellable;
+		data.cache = cache;
+		data.cache_key = uri_md5;
+                data.cache_stream = camel_data_cache_add (cache, "http", uri_md5, &error);
+
+		if (!data.cache_stream) {
+			g_warning ("Failed to create cache file for '%s': %s",
+				uri, error ? error->message : "Unknown error");
+			g_clear_error (&error);
+		}
+
+                /* Send the request and waint in mainloop until it's finished
+                 * and copied to cache */
+                d(printf(" '%s' not in cache, sending HTTP request\n", uri));
+		soup_request_send_async (http_request, cancellable,
+			(GAsyncReadyCallback) http_request_finished, &data);
+
+		g_main_loop_run (data.loop);
+                d(printf (" '%s' fetched from internet and (hopefully) stored in"
+                          " cache\n", uri));
+
+		g_main_loop_unref (data.loop);
+
+		g_object_unref (session);
+
+		g_object_unref (http_request);
+		g_object_unref (requester);
+
+		stream = data.stream;
+		if (!stream)
+			goto cleanup;
+
+		request->priv->content_length = data.content_length;
+		request->priv->mime_type = data.content_type;
+
+		g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+
+		goto cleanup;
+
+	}
+
+cleanup:
+	g_free (uri);
+	g_free (uri_md5);
+}
+
+static void
+handle_stock_request (GSimpleAsyncResult *res,
+                      GObject *object,
+                      GCancellable *cancellable)
+{
+	EMailRequest *request;
+	SoupURI *uri;
+	GtkIconTheme *icon_theme;
+	GtkIconInfo *icon_info;
+	const gchar *file;
+	gchar *a_size;
+	gssize size;
+	gchar *buffer;
+	gsize buff_len;
+	GtkStyleContext *context;
+	GtkWidgetPath *path;
+	GtkIconSet *set;
+
+	request = E_MAIL_REQUEST (object);
+	uri = soup_request_get_uri (SOUP_REQUEST (object));
+
+	if (request->priv->uri_query) {
+                a_size = g_hash_table_lookup (request->priv->uri_query, "size");
+	} else {
+		a_size = NULL;
+	}
+
+	if (!a_size) {
+		size = GTK_ICON_SIZE_BUTTON;
+	} else {
+		size = atoi (a_size);
+	}
+
+        /* Try style context first */
+	context = gtk_style_context_new ();
+	path = gtk_widget_path_new ();
+	gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
+	gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
+	gtk_style_context_set_path (context, path);
+
+	set = gtk_style_context_lookup_icon_set (context, uri->host);
+	if (!set) {
+                /* Fallback to icon theme */
+		icon_theme = gtk_icon_theme_get_default ();
+		icon_info = gtk_icon_theme_lookup_icon (
+				icon_theme, uri->host, size,
+				GTK_ICON_LOOKUP_USE_BUILTIN);
+		if (!icon_info) {
+			gtk_widget_path_free (path);
+			g_object_unref (context);
+			return;
+		}
+
+		file = gtk_icon_info_get_filename (icon_info);
+		buffer = NULL;
+		if (file) {
+			if (g_file_get_contents (file, &buffer, &buff_len, NULL)) {
+
+				request->priv->mime_type =
+					g_content_type_guess (file, NULL, 0, NULL);
+				request->priv->content_length = buff_len;
+			}
+
+		} else {
+			GdkPixbuf *pixbuf;
+
+			pixbuf = gtk_icon_info_get_builtin_pixbuf (icon_info);
+			if (pixbuf) {
+				gdk_pixbuf_save_to_buffer (
+					pixbuf, &buffer,
+                                        &buff_len, "png", NULL, NULL);
+
+                                request->priv->mime_type = g_strdup("image/png");
+				request->priv->content_length = buff_len;
+
+				g_object_unref (pixbuf);
+			}
+		}
+
+		gtk_icon_info_free (icon_info);
+
+	} else {
+		GdkPixbuf *pixbuf;
+
+		pixbuf = gtk_icon_set_render_icon_pixbuf (set, context, size);
+				gdk_pixbuf_save_to_buffer (
+					pixbuf, &buffer,
+                                        &buff_len, "png", NULL, NULL);
+
+                request->priv->mime_type = g_strdup("image/png");
+		request->priv->content_length = buff_len;
+
+		g_object_unref (pixbuf);
+	}
+
+	if (buffer) {
+		GInputStream *stream;
+		stream = g_memory_input_stream_new_from_data (
+				buffer, buff_len, (GDestroyNotify) g_free);
+		g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+	}
+
+	gtk_widget_path_free (path);
+	g_object_unref (context);
+
+}
+
+static void
+e_mail_request_init (EMailRequest *request)
+{
+	request->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		request, E_TYPE_MAIL_REQUEST, EMailRequestPrivate);
+
+	request->priv->efh = NULL;
+	request->priv->output_stream = NULL;
+	request->priv->uri_query = NULL;
+	request->priv->puri = NULL;
+	request->priv->mime_type = NULL;
+	request->priv->content_length = 0;
+}
+
+static void
+mail_request_finalize (GObject *object)
+{
+	EMailRequest *request = E_MAIL_REQUEST (object);
+
+	if (request->priv->output_stream) {
+		g_object_unref (request->priv->output_stream);
+		request->priv->output_stream = NULL;
+	}
+
+	if (request->priv->mime_type) {
+		g_free (request->priv->mime_type);
+		request->priv->mime_type = NULL;
+	}
+
+	if (request->priv->uri_query) {
+		g_hash_table_destroy (request->priv->uri_query);
+		request->priv->uri_query = NULL;
+	}
+
+	if (request->priv->ret_mime_type) {
+		g_free (request->priv->ret_mime_type);
+		request->priv->ret_mime_type = NULL;
+	}
+
+	G_OBJECT_CLASS (e_mail_request_parent_class)->finalize (object);
+}
+
+static gboolean
+mail_request_check_uri (SoupRequest *request,
+		       SoupURI *uri,
+		       GError **error)
+{
+	return ((strcmp (uri->scheme, "mail") == 0) ||
+		(strcmp (uri->scheme, "evo-file") == 0) ||
+                (strcmp (uri->scheme, "evo-http") == 0) ||
+                (strcmp (uri->scheme, "evo-https") == 0) ||
+                (strcmp (uri->scheme, "gtk-stock") == 0));
+}
+
+static void
+mail_request_send_async (SoupRequest *request,
+                         GCancellable *cancellable,
+                         GAsyncReadyCallback callback,
+                         gpointer user_data)
+{
+	SoupSession *session;
+	EMailRequest *emr = E_MAIL_REQUEST (request);
+	GSimpleAsyncResult *result;
+	SoupURI *uri;
+	GHashTable *formatters;
+
+	session = soup_request_get_session (request);
+	uri = soup_request_get_uri (request);
+
+	d(printf("received request for %s\n", soup_uri_to_string (uri, FALSE)));
+
+        /* WebKit won't allow us to load data through local file:// protocol
+         * when using "remote" mail:// protocol, so we have evo-file://
+         * which WebKit thinks it's remote, but in fact it behaves like
+         * oridnary file:// */
+        if (g_strcmp0 (uri->scheme, "evo-file") == 0) {
+
+		result = g_simple_async_result_new (G_OBJECT (request), callback,
+				user_data, mail_request_send_async);
+		g_simple_async_result_run_in_thread (result, handle_file_request,
+				G_PRIORITY_DEFAULT, cancellable);
+
+		return;
+	}
+
+	if (uri->query) {
+		emr->priv->uri_query = soup_form_decode (uri->query);
+	} else {
+		emr->priv->uri_query = NULL;
+	}
+
+        formatters = g_object_get_data (G_OBJECT (session), "formatters");
+					g_return_if_fail (formatters != NULL);
+
+        /* Get HTML content of given PURI part */
+        if (g_strcmp0 (uri->scheme, "mail") == 0) {
+		gchar *uri_str;
+
+                uri_str = g_strdup_printf ("%s://%s%s", uri->scheme, uri->host, uri->path);
+		emr->priv->efh = g_hash_table_lookup (formatters, uri_str);
+		g_free (uri_str);
+
+		g_return_if_fail (emr->priv->efh);
+
+		result = g_simple_async_result_new (G_OBJECT (request), callback,
+				user_data, mail_request_send_async);
+		g_simple_async_result_run_in_thread (result, handle_mail_request,
+				G_PRIORITY_DEFAULT, cancellable);
+
+		return;
+
+        /* For http and https requests we have this evo-http(s) protocol.
+	 * We first try to lookup the data in local cache and when not found,
+	 * we send standard  http(s) request to fetch them. But only when image 
+	 * loading policy allows us. */
+	} else if ((g_strcmp0 (uri->scheme, "evo-http") == 0) ||
+                   (g_strcmp0 (uri->scheme, "evo-https") == 0)) {
+
+		gchar *mail_uri;
+		const gchar *enc = g_hash_table_lookup (emr->priv->uri_query,
+                                        "__evo-mail");
+
+		g_return_if_fail (enc && *enc);
+
+		mail_uri = soup_uri_decode (enc);
+
+		emr->priv->efh = g_hash_table_lookup (formatters, mail_uri);
+		g_free (mail_uri);
+
+		g_return_if_fail (emr->priv->efh);
+
+		result = g_simple_async_result_new (G_OBJECT (request), callback,
+				user_data, mail_request_send_async);
+		g_simple_async_result_run_in_thread (result, handle_http_request,
+				G_PRIORITY_DEFAULT, cancellable);
+
+		return;
+
+        } else if ((g_strcmp0 (uri->scheme, "gtk-stock") == 0)) {
+
+		result = g_simple_async_result_new (G_OBJECT (request), callback,
+				user_data, mail_request_send_async);
+		g_simple_async_result_run_in_thread (result, handle_stock_request,
+				G_PRIORITY_DEFAULT, cancellable);
+
+		return;
+	}
+}
+
+static GInputStream *
+mail_request_send_finish (SoupRequest *request,
+                          GAsyncResult *result,
+                          GError **error)
+{
+	GInputStream *stream;
+
+	stream = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+	g_object_unref (result);
+
+        /* Reset the stream before passing it back to webkit */
+	if (stream && G_IS_SEEKABLE (stream))
+		g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
+	else /* We must always return something */
+		stream = g_memory_input_stream_new ();
+
+	return stream;
+}
+
+static goffset
+mail_request_get_content_length (SoupRequest *request)
+{
+	EMailRequest *emr = E_MAIL_REQUEST (request);
+	GByteArray *ba;
+	gint content_length = 0;
+
+	if (emr->priv->content_length > 0)
+		content_length = emr->priv->content_length;
+	else if (emr->priv->output_stream) {
+		ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (emr->priv->output_stream));
+		if (ba) {
+			content_length = ba->len;
+		}
+	}
+
+	d(printf("Content-Length: %d bytes\n", content_length));
+	return content_length;
+}
+
+static const gchar *
+mail_request_get_content_type (SoupRequest *request)
+{
+	EMailRequest *emr = E_MAIL_REQUEST (request);
+	gchar *mime_type;
+
+	if (emr->priv->mime_type) {
+		mime_type = g_strdup (emr->priv->mime_type);
+	} else if (!emr->priv->puri) {
+		mime_type = g_strdup ("text/html");
+	} else if (!emr->priv->puri->mime_type) {
+		CamelContentType *ct = camel_mime_part_get_content_type (emr->priv->puri->part);
+		mime_type = camel_content_type_simple (ct);
+	} else {
+		mime_type = g_strdup (emr->priv->puri->mime_type);
+	}
+
+	if (g_strcmp0 (mime_type, "text/html") == 0) {
+                emr->priv->ret_mime_type = g_strconcat (mime_type, "; charset=\"UTF-8\"", NULL);
+		g_free (mime_type);
+	} else {
+		emr->priv->ret_mime_type = mime_type;
+	}
+
+        d(printf("Content-Type: %s\n", emr->priv->ret_mime_type));
+
+	return emr->priv->ret_mime_type;
+}
+
+static const char *data_schemes[] = { "mail", "evo-file", "evo-http", "evo-https", "gtk-stock", NULL };
+
+static void
+e_mail_request_class_init (EMailRequestClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	SoupRequestClass *request_class = SOUP_REQUEST_CLASS (class);
+
+	g_type_class_add_private (class, sizeof (EMailRequestPrivate));
+
+	object_class->finalize = mail_request_finalize;
+
+	request_class->schemes = data_schemes;
+	request_class->send_async = mail_request_send_async;
+	request_class->send_finish = mail_request_send_finish;
+	request_class->get_content_type = mail_request_get_content_type;
+	request_class->get_content_length = mail_request_get_content_length;
+	request_class->check_uri = mail_request_check_uri;
+}
diff --git a/mail/e-mail-request.h b/mail/e-mail-request.h
new file mode 100644
index 0000000..9233621
--- /dev/null
+++ b/mail/e-mail-request.h
@@ -0,0 +1,36 @@
+#ifndef E_MAIL_REQUEST_H
+#define E_MAIL_REQUEST_H
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-request.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_MAIL_REQUEST            (e_mail_request_get_type ())
+#define E_MAIL_REQUEST(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), E_TYPE_MAIL_REQUEST, EMailRequest))
+#define E_MAIL_REQUEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_MAIL_REQUEST, EMailRequestClass))
+#define E_IS_MAIL_REQUEST(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), E_TYPE_MAIL_REQUEST))
+#define E_IS_MAIL_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_MAIL_REQUEST))
+#define E_MAIL_REQUEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_MAIL_REQUEST, EMailRequestClass))
+
+typedef struct _EMailRequest EMailRequest;
+typedef struct _EMailRequestClass EMailRequestClass;
+typedef struct _EMailRequestPrivate EMailRequestPrivate;
+
+struct _EMailRequest {
+	SoupRequest parent;
+
+	EMailRequestPrivate *priv;
+};
+
+struct _EMailRequestClass {
+	SoupRequestClass parent;
+};
+
+GType e_mail_request_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_REQUEST_H */
diff --git a/mail/em-account-editor.c b/mail/em-account-editor.c
index d53289d..74a3d5b 100644
--- a/mail/em-account-editor.c
+++ b/mail/em-account-editor.c
@@ -2523,9 +2523,9 @@ emae_refresh_providers (EMAccountEditor *emae,
 		GTK_COMBO_BOX (combo_box), service->protocol);
 
 	/* make sure at least something is selected;
-	   this applies for cases when user changed from provider which was
-	   store and transport together, to a store provider only (like from
-	   exchange to imap provider), which left unselected transport type
+	 * this applies for cases when user changed from provider which was
+	 * store and transport together, to a store provider only (like from
+	 * exchange to imap provider), which left unselected transport type
 	*/
 	if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) == -1)
 		gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index 745609f..f4b8560 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -51,12 +51,14 @@
 #include <composer/e-composer-actions.h>
 #include <composer/e-composer-post-header.h>
 
+#include "e-mail-printer.h"
 #include "em-utils.h"
 #include "em-composer-utils.h"
 #include "em-folder-selector.h"
 #include "em-folder-tree.h"
 #include "em-format-html.h"
 #include "em-format-html-print.h"
+#include "em-format-html-display.h"
 #include "em-format-quote.h"
 #include "em-event.h"
 #include "mail-send-recv.h"
@@ -929,17 +931,38 @@ em_utils_composer_save_to_outbox_cb (EMsgComposer *composer,
 }
 
 static void
+composer_print_done_cb (EMailPrinter *emp,
+                        GtkPrintOperation *operation,
+                        GtkPrintOperationResult result,
+                        gpointer user_data)
+{
+	EMFormat *emf = user_data;
+	g_object_unref (emf);
+	g_object_unref (emp);
+}
+
+static void
 em_utils_composer_print_cb (EMsgComposer *composer,
                             GtkPrintOperationAction action,
                             CamelMimeMessage *message,
                             EActivity *activity,
                             EMailSession *session)
 {
-	EMFormatHTMLPrint *efhp;
+	EMailPrinter *emp;
+	EMFormatHTMLDisplay *efhd;
+
+	efhd = em_format_html_display_new ();
+	((EMFormat *) efhd)->message_uid = g_strdup (camel_mime_message_get_message_id (message));
+
+        /* Parse the message */
+	em_format_parse ((EMFormat *) efhd, message, NULL, NULL);
+
+        /* Use EMailPrinter and WebKit to print the message */
+	emp = e_mail_printer_new ((EMFormatHTML *) efhd);
+        g_signal_connect (emp, "done",
+		G_CALLBACK (composer_print_done_cb), efhd);
 
-	efhp = em_format_html_print_new (NULL, action);
-	em_format_html_print_message (efhp, message, NULL, NULL);
-	g_object_unref (efhp);
+	e_mail_printer_print (emp, FALSE, NULL);
 }
 
 /* Composing messages... */
diff --git a/mail/em-format-hook.c b/mail/em-format-hook.c
index 2338055..7ceeb20 100644
--- a/mail/em-format-hook.c
+++ b/mail/em-format-hook.c
@@ -61,24 +61,23 @@ static const EPluginHookTargetKey emfh_flag_map[] = {
 G_DEFINE_TYPE (EMFormatHook, em_format_hook, E_TYPE_PLUGIN_HOOK)
 
 static void
-emfh_format_format (EMFormat *md,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    const EMFormatHandler *info,
-                    GCancellable *cancellable,
-                    gboolean is_fallback)
+emfh_parse_part (EMFormat *emf,
+                 CamelMimePart *part,
+                 GString *part_id,
+                 EMFormatParserInfo *info,
+                 GCancellable *cancellable)
 {
-	struct _EMFormatHookItem *item = (EMFormatHookItem *) info;
+	struct _EMFormatHookItem *item = (EMFormatHookItem *) info->handler;
 
 	if (item->hook->hook.plugin->enabled) {
 		EMFormatHookTarget target = {
-			md, stream, part, item
+			emf, part, part_id, info
 		};
 
 		e_plugin_invoke (item->hook->hook.plugin, item->format, &target);
-	} else if (info->old) {
-		info->old->handler (
-			md, stream, part, info->old, cancellable, FALSE);
+	} else if (info->handler->old) {
+		info->handler->old->parse_func (
+			emf, part, part_id, info, cancellable);
 	}
 }
 
@@ -116,7 +115,7 @@ emfh_construct_item (EPluginHook *eph,
 	item->handler.flags = e_plugin_hook_mask(root, emfh_flag_map, "flags");
 	item->format = e_plugin_xml_prop(root, "format");
 
-	item->handler.handler = emfh_format_format;
+	item->handler.parse_func = emfh_parse_part;
 	item->hook = emfh;
 
 	if (item->handler.mime_type == NULL || item->format == NULL)
diff --git a/mail/em-format-hook.h b/mail/em-format-hook.h
index ad0745b..09076c2 100644
--- a/mail/em-format-hook.h
+++ b/mail/em-format-hook.h
@@ -40,9 +40,9 @@ typedef void (*EMFormatHookFunc)(struct _EPlugin *plugin, EMFormatHookTarget *da
 
 struct _EMFormatHookTarget {
 	struct _EMFormat *format;
-	CamelStream *stream;
 	CamelMimePart *part;
-	struct _EMFormatHookItem *item;
+	GString *part_id;
+	EMFormatParserInfo *info;
 };
 
 struct _EMFormatHookItem {
diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c
index 013e1ac..8f20169 100644
--- a/mail/em-format-html-display.c
+++ b/mail/em-format-html-display.c
@@ -38,9 +38,6 @@
 #undef interface
 #endif
 
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-embedded.h>
-
 #include <glib/gi18n.h>
 
 #include <e-util/e-util.h>
@@ -53,6 +50,8 @@
 #include <shell/e-shell.h>
 #include <shell/e-shell-utils.h>
 
+#include <libedataserver/e-flag.h>
+
 #if defined (HAVE_NSS) && defined (ENABLE_SMIME)
 #include "certificate-viewer.h"
 #include "e-cert-db.h"
@@ -65,6 +64,8 @@
 #include "widgets/misc/e-attachment.h"
 #include "widgets/misc/e-attachment-button.h"
 #include "widgets/misc/e-attachment-view.h"
+#include "shell/e-shell.h"
+#include "shell/e-shell-window.h"
 
 #define EM_FORMAT_HTML_DISPLAY_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -77,16 +78,9 @@
 	((obj), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplayPrivate))
 
 struct _EMFormatHTMLDisplayPrivate {
-	GHashTable *attachment_views;  /* weak reference; message_part_id->EAttachmentView */
-	gboolean attachment_expanded;
-};
 
-struct _smime_pobject {
-	EMFormatHTMLPObject object;
+        EAttachmentView *last_view;
 
-	gint signature;
-	CamelCipherValidity *valid;
-	GtkWidget *widget;
 };
 
 /* TODO: move the dialogue elsehwere */
@@ -111,78 +105,100 @@ static const struct {
 	{ "stock_lock-ok", N_("Encrypted, strong"), N_("This message is encrypted, with a strong encryption algorithm. It would be very difficult for an outsider to view the content of this message in a practical amount of time.") },
 };
 
-static const gchar *smime_sign_colour[5] = {
-	"", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\"",""
+static const GdkRGBA smime_sign_colour[5] = {
+	{ 0 }, { 0.53, 0.73, 0.53, 1 }, { 0.73, 0.53, 0.53, 1 }, { 0.91, 0.82, 0.13, 1 }, { 0 },
 };
 
-static void efhd_attachment_frame (EMFormat *emf, CamelStream *stream, EMFormatPURI *puri, GCancellable *cancellable);
-static void efhd_message_add_bar (EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
-static gboolean efhd_attachment_button (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject);
-static gboolean efhd_attachment_optional (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *object);
+static void efhd_message_prefix	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_message_add_bar	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_parse_attachment	(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_parse_secure		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_parse_optional		(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+
+static GtkWidget * efhd_attachment_bar		(EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable);
+static GtkWidget * efhd_attachment_button	(EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable);
+static GtkWidget * efhd_attachment_optional	(EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable);
+
+static void efhd_write_attachment_bar   (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhd_write_attachment       (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhd_write_secure_button    (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
 static void efhd_free_attach_puri_data (EMFormatPURI *puri);
 
-struct _attach_puri {
-	EMFormatPURI puri;
+static void efhd_builtin_init (EMFormatHTMLDisplayClass *efhc);
+
+static gpointer parent_class;
 
-	const EMFormatHandler *handle;
+static EAttachmentStore *
+find_parent_attachment_store (EMFormatHTMLDisplay *efhd,
+                              const gchar *part_id)
+{
+	EMFormat *emf = (EMFormat *) efhd;
+	EMFormatAttachmentBarPURI *abp;
+	gchar *tmp, *pos;
+	GList *item;
 
-	const gchar *snoop_mime_type;
+	tmp = g_strdup (part_id);
 
-	/* for the > and V buttons */
-	GtkWidget *forward, *down;
-	/* currently no way to correlate this data to the frame :( */
-	GtkHTML *frame;
-	guint shown : 1;
+	do {
+		gchar *id;
 
-	/* Embedded Frame */
-	GtkHTMLEmbedded *html;
+		pos = g_strrstr (tmp, ".");
+		if (!pos)
+			break;
 
-	/* Attachment */
-	EAttachment *attachment;
-	gchar *attachment_view_part_id;
+		g_free (tmp);
+		tmp = g_strndup (part_id, pos - tmp);
+		id = g_strdup_printf ("%s.attachment-bar", tmp);
 
-	/* image stuff */
-	gint fit_width;
-	gint fit_height;
-	GtkImage *image;
-	GtkWidget *event_box;
+		item = g_hash_table_lookup (emf->mail_part_table, id);
 
-	/* Optional Text Mem Stream */
-	CamelStreamMem *mstream;
+		g_free (id);
 
-	/* Signed / Encrypted */
-        camel_cipher_validity_sign_t sign;
-        camel_cipher_validity_encrypt_t encrypt;
-};
+	} while (pos && !item);
 
-static void	efhd_message_prefix		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *part,
-						 const EMFormatHandler *info,
-						 GCancellable *cancellable,
-						 gboolean is_fallback);
+	g_free (tmp);
 
-static void efhd_builtin_init (EMFormatHTMLDisplayClass *efhc);
+	abp = (EMFormatAttachmentBarPURI *) item->data;
 
-G_DEFINE_TYPE (
-	EMFormatHTMLDisplay,
-	em_format_html_display,
-	EM_TYPE_FORMAT_HTML)
+	if (abp)
+		return abp->store;
+	else
+		return NULL;
+}
 
 static void
-efhd_xpkcs7mime_free (EMFormatHTMLPObject *o)
+efhd_attachment_bar_puri_free (EMFormatPURI *puri)
 {
-	struct _smime_pobject *po = (struct _smime_pobject *) o;
+	EMFormatAttachmentBarPURI *abp;
 
-	if (po->widget)
-		gtk_widget_destroy (po->widget);
-	camel_cipher_validity_free (po->valid);
+	abp = (EMFormatAttachmentBarPURI *) puri;
+
+	if (abp->store) {
+		g_object_unref (abp->store);
+		abp->store = NULL;
+	}
+}
+
+static void
+efhd_xpkcs7mime_free (EMFormatPURI *puri)
+{
+	EMFormatSMIMEPURI *sp = (EMFormatSMIMEPURI *) puri;
+
+	if (sp->widget)
+		gtk_widget_destroy (sp->widget);
+
+	if (sp->description)
+		g_free (sp->description);
+
+	if (sp->valid)
+		camel_cipher_validity_free (sp->valid);
 }
 
 static void
 efhd_xpkcs7mime_info_response (GtkWidget *widget,
                                guint button,
-                               struct _smime_pobject *po)
+                               EMFormatSMIMEPURI *po)
 {
 	gtk_widget_destroy (widget);
 	po->widget = NULL;
@@ -191,7 +207,7 @@ efhd_xpkcs7mime_info_response (GtkWidget *widget,
 #if defined (HAVE_NSS) && defined (ENABLE_SMIME)
 static void
 efhd_xpkcs7mime_viewcert_clicked (GtkWidget *button,
-                                  struct _smime_pobject *po)
+                                  EMFormatSMIMEPURI *po)
 {
 	CamelCipherCertInfo *info = g_object_get_data((GObject *)button, "e-cert-info");
 	ECert *ec = NULL;
@@ -221,7 +237,7 @@ efhd_xpkcs7mime_viewcert_clicked (GtkWidget *button,
 static void
 efhd_xpkcs7mime_add_cert_table (GtkWidget *grid,
                                 GQueue *certlist,
-                                struct _smime_pobject *po)
+                                EMFormatSMIMEPURI *po)
 {
 	GList *head, *link;
 	GtkTable *table;
@@ -283,9 +299,9 @@ efhd_xpkcs7mime_add_cert_table (GtkWidget *grid,
 
 static void
 efhd_xpkcs7mime_validity_clicked (GtkWidget *button,
-                                  EMFormatHTMLPObject *pobject)
+                                  EMFormatPURI *puri)
 {
-	struct _smime_pobject *po = (struct _smime_pobject *) pobject;
+	EMFormatSMIMEPURI *po = (EMFormatSMIMEPURI *) puri;
 	GtkBuilder *builder;
 	GtkWidget *grid, *w;
 
@@ -367,20 +383,19 @@ efhd_xpkcs7mime_validity_clicked (GtkWidget *button,
 	g_object_unref (builder);
 
 	g_signal_connect (
-		po->widget, "response",
+	        po->widget, "response",
 		G_CALLBACK (efhd_xpkcs7mime_info_response), po);
 
 	gtk_widget_show (po->widget);
 }
 
-static gboolean
-efhd_xpkcs7mime_button (EMFormatHTML *efh,
-                        GtkHTMLEmbedded *eb,
-                        EMFormatHTMLPObject *pobject)
+static GtkWidget *
+efhd_xpkcs7mime_button (EMFormat *emf,
+                        EMFormatPURI *puri,
+                        GCancellable *cancellable)
 {
-	GtkWidget *container;
-	GtkWidget *widget;
-	struct _smime_pobject *po = (struct _smime_pobject *) pobject;
+	GtkWidget *box, *button, *layout, *widget;
+	EMFormatSMIMEPURI *po = (EMFormatSMIMEPURI *) puri;
 	const gchar *icon_name;
 
 	/* FIXME: need to have it based on encryption and signing too */
@@ -389,450 +404,436 @@ efhd_xpkcs7mime_button (EMFormatHTML *efh,
 	else
 		icon_name = smime_encrypt_table[po->valid->encrypt.status].icon;
 
-	container = GTK_WIDGET (eb);
+	box = gtk_event_box_new ();
+	if (po->valid->sign.status != 0)
+		gtk_widget_override_background_color (box, GTK_STATE_FLAG_NORMAL,
+			&smime_sign_colour[po->valid->sign.status]);
 
-	widget = gtk_button_new ();
-	g_signal_connect (
-		widget, "clicked",
-		G_CALLBACK (efhd_xpkcs7mime_validity_clicked), pobject);
-	gtk_container_add (GTK_CONTAINER (container), widget);
-	gtk_widget_show (widget);
+	layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+	gtk_container_add (GTK_CONTAINER (box), layout);
 
-	container = widget;
+	button = gtk_button_new ();
+	gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0);
+	g_signal_connect (button, "clicked",
+		G_CALLBACK (efhd_xpkcs7mime_validity_clicked), puri);
 
 	widget = gtk_image_new_from_icon_name (
-		icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
-	gtk_container_add (GTK_CONTAINER (container), widget);
-	gtk_widget_show (widget);
+			icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+	gtk_button_set_image (GTK_BUTTON (button), widget);
 
-	return TRUE;
-}
+	widget = gtk_label_new (po->description);
+	gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
 
-static gboolean
-remove_attachment_view_cb (gpointer message_part_id,
-                           gpointer attachment_view,
-                           gpointer gone_attachment_view)
-{
-	return attachment_view == gone_attachment_view;
+	gtk_widget_show_all (box);
+
+	return box;
 }
 
+struct attachment_load_data {
+	EAttachment *attachment;
+	EFlag *flag;
+};
+
 static void
-efhd_attachment_view_gone_cb (gpointer efh,
-                              GObject *gone_attachment_view)
+attachment_loaded (EAttachment *attachment,
+                   GAsyncResult *res,
+                   gpointer user_data)
 {
-	EMFormatHTMLDisplay *efhd = EM_FORMAT_HTML_DISPLAY (efh);
+	struct attachment_load_data *data = user_data;
+	EShell *shell;
+	GtkWindow *window;
 
-	g_return_if_fail (efhd != NULL);
+	shell = e_shell_get_default ();
+	window = e_shell_get_active_window (shell);
+	if (!E_IS_SHELL_WINDOW (window))
+		window = NULL;
 
-	g_hash_table_foreach_remove (
-		efhd->priv->attachment_views,
-		remove_attachment_view_cb,
-		gone_attachment_view);
-}
+	e_attachment_load_handle_error (data->attachment, res, window);
 
-static void
-weak_unref_attachment_view_cb (gpointer message_part_id,
-                               gpointer attachment_view,
-                               gpointer efh)
-{
-	g_object_weak_unref (
-		G_OBJECT (attachment_view),
-		efhd_attachment_view_gone_cb, efh);
+	e_flag_set (data->flag);
 }
 
-static void
-efhd_format_clone (EMFormat *emf,
-                   CamelFolder *folder,
-                   const gchar *uid,
-                   CamelMimeMessage *msg,
-                   EMFormat *src,
-                   GCancellable *cancellable)
+/* Idle callback */
+static gboolean
+load_attachment_idle (struct attachment_load_data *data)
 {
-	EMFormatHTMLDisplay *efhd;
-
-	efhd = EM_FORMAT_HTML_DISPLAY (emf);
-	g_return_if_fail (efhd != NULL);
+	e_attachment_load_async (data->attachment,
+		(GAsyncReadyCallback) attachment_loaded, data);
 
-	g_hash_table_foreach (efhd->priv->attachment_views, weak_unref_attachment_view_cb, efhd);
-	g_hash_table_remove_all (efhd->priv->attachment_views);
-
-	if (emf != src)
-		EM_FORMAT_HTML (emf)->header_wrap_flags = 0;
-
-	/* Chain up to parent's format_clone() method. */
-	EM_FORMAT_CLASS (em_format_html_display_parent_class)->
-		format_clone (emf, folder, uid, msg, src, cancellable);
+	return FALSE;
 }
 
 static void
-efhd_format_attachment (EMFormat *emf,
-                        CamelStream *stream,
-                        CamelMimePart *part,
-                        const gchar *mime_type,
-                        const EMFormatHandler *handle,
-                        GCancellable *cancellable)
+efhd_parse_attachment (EMFormat *emf,
+                       CamelMimePart *part,
+                       GString *part_id,
+                       EMFormatParserInfo *info,
+                       GCancellable *cancellable)
 {
-	GString *buffer;
-	gchar *classid, *text, *html;
-	struct _attach_puri *info;
-
-	classid = g_strdup_printf ("attachment%s", emf->part_id->str);
-	info = (struct _attach_puri *) em_format_add_puri (
-		emf, sizeof (*info), classid, part, efhd_attachment_frame);
-	info->puri.free = efhd_free_attach_puri_data;
-	info->attachment_view_part_id = g_strdup (emf->current_message_part_id);
-	em_format_html_add_pobject (
-		EM_FORMAT_HTML (emf), sizeof (EMFormatHTMLPObject),
-		classid, part, efhd_attachment_button);
-	info->handle = handle;
-	info->shown = em_format_is_inline (
-		emf, info->puri.part_id, info->puri.part, handle);
-	info->snoop_mime_type = emf->snoop_mime_type;
-	info->attachment = e_attachment_new ();
-	e_attachment_set_mime_part (info->attachment, info->puri.part);
-
-	if (emf->valid) {
-		info->sign = emf->valid->sign.status;
-		info->encrypt = emf->valid->encrypt.status;
+	gchar *text, *html;
+	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
+	EMFormatAttachmentPURI *puri;
+	EAttachmentStore *store;
+	const EMFormatHandler *handler;
+	CamelContentType *ct;
+	gchar *mime_type;
+	gint len;
+	const gchar *cid;
+	guint32 size;
+	struct attachment_load_data *load_data;
+	gboolean can_show = FALSE;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	len = part_id->len;
+	g_string_append (part_id, ".attachment");
+
+        /* Try to find handler for the mime part */
+	ct = camel_mime_part_get_content_type (part);
+	if (ct) {
+		mime_type = camel_content_type_simple (ct);
+		handler = em_format_find_handler (emf, mime_type);
 	}
 
-	buffer = g_string_sized_new (1024);
-
-	g_string_append_printf (
-		buffer, EM_FORMAT_HTML_VPAD
-		"<table cellspacing=0 cellpadding=0>"
-		"<tr><td>"
-		"<table width=10 cellspacing=0 cellpadding=0>"
-		"<tr><td></td><tr>"
-		"</table>"
-		"</td>"
-		"<td><object classid=\"%s\"></object></td>"
-		"<td><table width=3 cellspacing=0 cellpadding=0>"
-		"<tr><td></td></tr>"
-		"</table></td>"
-		"<td><font size=-1>",
-		classid);
-
-	/* output some info about it */
 	/* FIXME: should we look up mime_type from object again? */
 	text = em_format_describe_part (part, mime_type);
 	html = camel_text_to_html (
 		text, EM_FORMAT_HTML (emf)->text_html_flags &
 		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
-	g_string_append (buffer, html);
-	g_free (html);
 	g_free (text);
+	g_free (mime_type);
+
+	puri = (EMFormatAttachmentPURI *) em_format_puri_new (
+			emf, sizeof (EMFormatAttachmentPURI), part, part_id->str);
+	puri->puri.free = efhd_free_attach_puri_data;
+	puri->puri.write_func = efhd_write_attachment;
+	puri->puri.widget_func = efhd_attachment_button;
+	puri->shown = (handler && em_format_is_inline (emf, part_id->str, part, handler));
+	puri->snoop_mime_type = em_format_snoop_type (part);
+	puri->attachment = e_attachment_new ();
+	puri->attachment_view_part_id = NULL;
+	puri->description = html;
+	puri->handle = handler;
+	if (info->validity)
+		puri->puri.validity = camel_cipher_validity_clone (info->validity);
+
+	cid = camel_mime_part_get_content_id (part);
+	if (cid)
+		puri->puri.cid = g_strdup_printf ("cid:%s", cid);
+
+	if (handler) {
+		CamelContentType *ct;
+
+                /* This mime_type is important for WebKit to determine content type.
+                 * We have converted text/ * to text/html, other (binary) formats remained
+                 * untouched. */
+		ct = camel_content_type_decode (handler->mime_type);
+                if (g_strcmp0 (ct->type, "text") == 0)
+                        puri->puri.mime_type = g_strdup ("text/html");
+		else
+			puri->puri.mime_type = camel_content_type_simple (ct);
+		camel_content_type_unref (ct);
+	}
 
-	g_string_append (
-		buffer,
-		"</font></td>"
-		"</tr><tr></table>\n"
-		EM_FORMAT_HTML_VPAD);
-
-	camel_stream_write (
-		stream, buffer->str, buffer->len, cancellable, NULL);
-
-	g_string_free (buffer, TRUE);
+	em_format_add_puri (emf, (EMFormatPURI *) puri);
+
+        /* Though it is an attachment, we still might be able to parse it and
+         * so discover some parts that we might be even able to display. */
+	if (handler && handler->parse_func && (handler->parse_func != efhd_parse_attachment) &&
+	    ((handler->flags & EM_FORMAT_HANDLER_COMPOUND_TYPE) ||
+	     (handler->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION))) {
+		GList *i;
+		EMFormatParserInfo attachment_info = { .handler = handler,
+						       .is_attachment = TRUE };
+		handler->parse_func (emf, puri->puri.part, part_id, &attachment_info, cancellable);
+
+		i = g_hash_table_lookup (emf->mail_part_table, part_id->str);
+		if (i->next && i->next->data) {
+			EMFormatPURI *p = i->next->data;
+			puri->attachment_view_part_id = g_strdup (p->uri);
+			can_show = TRUE;
+		}
+	}
 
-	if (handle && info->shown)
-		handle->handler (
-			emf, stream, part, handle, cancellable, FALSE);
+	e_attachment_set_mime_part (puri->attachment, part);
+	e_attachment_set_shown (puri->attachment, puri->shown);
+	if (puri->puri.validity) {
+		e_attachment_set_signed (puri->attachment, puri->puri.validity->sign.status);
+		e_attachment_set_encrypted (puri->attachment, puri->puri.validity->encrypt.status);
+	}
+	e_attachment_set_can_show (puri->attachment,
+		can_show || (puri->handle && puri->handle->write_func));
 
-	g_free (classid);
-}
+	store = find_parent_attachment_store (efhd, part_id->str);
+	e_attachment_store_add_attachment (store, puri->attachment);
 
-static void
-efhd_format_optional (EMFormat *emf,
-                      CamelStream *fstream,
-                      CamelMimePart *part,
-                      CamelStream *mstream,
-                      GCancellable *cancellable)
-{
-	gchar *classid, *html;
-	struct _attach_puri *info;
-	CamelStream *stream = NULL;
-	GString *buffer;
+	if (emf->folder && emf->folder->summary && emf->message_uid) {
+		CamelDataWrapper *dw = camel_medium_get_content (CAMEL_MEDIUM (puri->puri.part));
+		GByteArray *ba;
+		ba = camel_data_wrapper_get_byte_array (dw);
+		if (ba) {
+			size = ba->len;
 
-	if (CAMEL_IS_STREAM_FILTER (fstream))
-		stream = camel_stream_filter_get_source (
-			CAMEL_STREAM_FILTER (fstream));
-	if (stream == NULL)
-		stream = fstream;
-
-	classid = g_strdup_printf ("optional%s", emf->part_id->str);
-	info = (struct _attach_puri *) em_format_add_puri (
-		emf, sizeof (*info), classid, part, efhd_attachment_frame);
-	info->puri.free = efhd_free_attach_puri_data;
-	info->attachment_view_part_id = g_strdup (emf->current_message_part_id);
-	em_format_html_add_pobject (
-		EM_FORMAT_HTML (emf), sizeof (EMFormatHTMLPObject),
-		classid, part, efhd_attachment_optional);
-	info->handle = em_format_find_handler (emf, "text/plain");
-	info->shown = FALSE;
-	info->snoop_mime_type = "text/plain";
-	info->attachment = e_attachment_new ();
-	e_attachment_set_mime_part (info->attachment, info->puri.part);
-	info->mstream = (CamelStreamMem *) g_object_ref (mstream);
-	if (emf->valid) {
-		info->sign = emf->valid->sign.status;
-		info->encrypt = emf->valid->encrypt.status;
+			if (camel_mime_part_get_encoding (puri->puri.part) == CAMEL_TRANSFER_ENCODING_BASE64)
+				size = size / 1.37;
+		}
 	}
 
-	buffer = g_string_sized_new (1024);
+	load_data = g_new0 (struct attachment_load_data, 1);
+	load_data->attachment = g_object_ref (puri->attachment);
+	load_data->flag = e_flag_new ();
 
-	g_string_append (
-		buffer, EM_FORMAT_HTML_VPAD
-		"<table cellspacing=0 cellpadding=0><tr><td>"
-		"<h3><font size=-1 color=red>");
+	e_flag_clear (load_data->flag);
 
-	html = camel_text_to_html (
-		_("Evolution cannot render this email as it is too "
-		  "large to process. You can view it unformatted or "
-		  "with an external text editor."),
-		EM_FORMAT_HTML (emf)->text_html_flags &
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
-	g_string_append (buffer, html);
-	g_free (html);
+	/* e_attachment_load_async must be called from main thread */
+	g_idle_add ((GSourceFunc) load_attachment_idle, load_data);
 
-	g_string_append_printf (
-		buffer,
-		"</font></h3></td></tr></table>\n"
-		"<table cellspacing=0 cellpadding=0><tr>"
-		"<td><object classid=\"%s\"></object>"
-		"</td></tr></table>" EM_FORMAT_HTML_VPAD,
-		classid);
+	e_flag_wait (load_data->flag);
 
-	camel_stream_write (
-		stream, buffer->str, buffer->len, cancellable, NULL);
+	e_flag_free (load_data->flag);
+	g_object_unref (load_data->attachment);
+	g_free (load_data);
 
-	g_string_free (buffer, TRUE);
+	if (size != 0) {
+		GFileInfo *fileinfo;
+
+		fileinfo = e_attachment_get_file_info (puri->attachment);
+		g_file_info_set_size (fileinfo, size);
+		e_attachment_set_file_info (puri->attachment, fileinfo);
+	}
 
-	g_free (classid);
+	g_string_truncate (part_id, len);
 }
 
 static void
-efhd_format_secure (EMFormat *emf,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    CamelCipherValidity *valid,
-                    GCancellable *cancellable)
+efhd_parse_optional (EMFormat *emf,
+                     CamelMimePart *part,
+                     GString *part_id,
+                     EMFormatParserInfo *info,
+                     GCancellable *cancellable)
 {
-	EMFormatClass *format_class;
+	EMFormatAttachmentPURI *puri;
+	gint len;
+
+	len = part_id->len;
+        g_string_append (part_id, ".optional");
+
+	puri = (EMFormatAttachmentPURI *) em_format_puri_new (
+			emf, sizeof (EMFormatAttachmentPURI), part, part_id->str);
+	puri->puri.free = efhd_free_attach_puri_data;
+	puri->puri.write_func = efhd_write_attachment;
+	puri->puri.widget_func = efhd_attachment_optional;
+	puri->attachment_view_part_id = g_strdup (part_id->str);
+	puri->handle = em_format_find_handler (emf, "text/plain");
+	puri->shown = FALSE;
+	puri->snoop_mime_type = "text/plain";
+	puri->attachment = e_attachment_new ();
+	e_attachment_set_mime_part (puri->attachment, puri->puri.part);
+	puri->description = g_strdup(_("Evolution cannot render this email as it is too "
+				       "large to process. You can view it unformatted or "
+				       "with an external text editor."));
+
+	puri->mstream = CAMEL_STREAM_MEM (camel_stream_mem_new ());
+	camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) part,
+		(CamelStream *) puri->mstream, cancellable, NULL);
+
+	if (info->validity) {
+		puri->puri.validity = camel_cipher_validity_clone (info->validity);
+	}
 
-	format_class = g_type_class_peek (EM_TYPE_FORMAT);
-	format_class->format_secure (emf, stream, part, valid, cancellable);
+	em_format_add_puri (emf, (EMFormatPURI *) puri);
 
-	if (emf->valid == valid
-	    && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
-		|| valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
+	g_string_truncate (part_id, len);
+}
+
+static void
+efhd_parse_secure (EMFormat *emf,
+                   CamelMimePart *part,
+                   GString *part_id,
+                   EMFormatParserInfo *info,
+                   GCancellable *cancellable)
+{
+	if (info->validity
+	    && (info->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
+		|| info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
 		GString *buffer;
-		gchar *classid;
-		struct _smime_pobject *pobj;
+		EMFormatSMIMEPURI *pobj;
 
-		buffer = g_string_sized_new (1024);
+		pobj = (EMFormatSMIMEPURI *) em_format_puri_new (
+				emf, sizeof (EMFormatSMIMEPURI), part, part_id->str);
+		pobj->puri.free = efhd_xpkcs7mime_free;
+		pobj->valid = camel_cipher_validity_clone (info->validity);
+		pobj->puri.widget_func = efhd_xpkcs7mime_button;
+		pobj->puri.write_func = efhd_write_secure_button;
 
-		g_string_append_printf (
-			buffer,
-			"<table border=0 width=\"100%%\" "
-			"cellpadding=3 cellspacing=0%s><tr>",
-			smime_sign_colour[valid->sign.status]);
+		em_format_add_puri (emf, (EMFormatPURI *) pobj);
 
-		classid = g_strdup_printf (
-			"smime:///em-format-html/%s/icon/signed",
-			emf->part_id->str);
-		pobj = (struct _smime_pobject *) em_format_html_add_pobject (
-			EM_FORMAT_HTML (emf), sizeof (*pobj),
-			classid, part, efhd_xpkcs7mime_button);
-		pobj->valid = camel_cipher_validity_clone (valid);
-		pobj->object.free = efhd_xpkcs7mime_free;
-		g_string_append_printf (
-			buffer,
-			"<td valign=center><object classid=\"%s\">"
-			"</object></td><td width=100%% valign=center>",
-			classid);
-		g_free (classid);
+		buffer = g_string_new ("");
 
-		if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
+		if (info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
 			const gchar *desc;
 			gint status;
 
-			status = valid->sign.status;
+			status = info->validity->sign.status;
 			desc = smime_sign_table[status].shortdesc;
 
 			g_string_append (buffer, gettext (desc));
 
 			em_format_html_format_cert_infos (
-				&valid->sign.signers, buffer);
+				&info->validity->sign.signers, buffer);
 		}
 
-		if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
+		if (info->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
 			const gchar *desc;
 			gint status;
 
-			if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
-				g_string_append (buffer, "<br>");
+			if (info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
+				g_string_append (buffer, "\n");
 
-			status = valid->encrypt.status;
+			status = info->validity->encrypt.status;
 			desc = smime_encrypt_table[status].shortdesc;
 			g_string_append (buffer, gettext (desc));
 		}
 
-		g_string_append (buffer, "</td></tr></table>");
-
-		camel_stream_write (
-			stream, buffer->str, buffer->len, cancellable, NULL);
-
-		g_string_free (buffer, TRUE);
+		pobj->description = g_string_free (buffer, FALSE);
 	}
 }
 
+/******************************************************************************/
 static void
-attachment_load_finish (EAttachment *attachment,
-                        GAsyncResult *result,
-                        GFile *file)
+efhd_write_attachment_bar (EMFormat *emf,
+                           EMFormatPURI *puri,
+                           CamelStream *stream,
+                           EMFormatWriterInfo *info,
+                           GCancellable *cancellable)
 {
-	EShell *shell;
-	GtkWindow *parent;
+	EMFormatAttachmentBarPURI *efab = (EMFormatAttachmentBarPURI *) puri;
+	gchar *str;
 
-	e_attachment_load_finish (attachment, result, NULL);
+	if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING)
+		return;
 
-	shell = e_shell_get_default ();
-	parent = e_shell_get_active_window (shell);
+	if (e_attachment_store_get_num_attachments (efab->store) == 0)
+		return;
 
-	e_attachment_save_async (
-		attachment, file, (GAsyncReadyCallback)
-		e_attachment_save_handle_error, parent);
+	str = g_strdup_printf (
+                "<object type=\"application/x-attachment-bar\" "
+			"height=\"20\" width=\"100%%\" "
+                        "id=\"%s\"data=\"%s\"></object>", puri->uri, puri->uri);
 
-	g_object_unref (file);
+	camel_stream_write_string (stream, str, cancellable, NULL);
+
+	g_free (str);
 }
 
 static void
-action_image_save_cb (GtkAction *action,
-                      EMFormatHTMLDisplay *efhd)
+efhd_write_attachment (EMFormat *emf,
+                       EMFormatPURI *puri,
+                       CamelStream *stream,
+                       EMFormatWriterInfo *info,
+                       GCancellable *cancellable)
 {
-	EWebView *web_view;
-	EMFormat *emf;
-	const gchar *image_src;
-	CamelMimePart *part;
-	EAttachment *attachment;
-	GFile *file;
+	gchar *str, *desc;
+	const gchar *mime_type;
+	gchar *button_id;
 
-	web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhd));
-	g_return_if_fail (web_view != NULL);
+	EMFormatAttachmentPURI *efa = (EMFormatAttachmentPURI *) puri;
 
-	image_src = e_web_view_get_cursor_image_src (web_view);
-	if (!image_src)
-		return;
+        /* If the attachment is requested as RAW, then call the handler directly
+         * and do not append any other code. */
+	if ((info->mode == EM_FORMAT_WRITE_MODE_RAW) &&
+	    efa->handle && efa->handle->write_func) {
 
-	emf = EM_FORMAT (efhd);
-	g_return_if_fail (emf != NULL);
-	g_return_if_fail (emf->message != NULL);
+		efa->handle->write_func (emf, puri, stream, info, cancellable);
+		return;
+	}
 
-	if (g_str_has_prefix (image_src, "cid:")) {
-		part = camel_mime_message_get_part_by_content_id (
-			emf->message, image_src + 4);
-		g_return_if_fail (part != NULL);
+	if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) {
 
-		g_object_ref (part);
-	} else {
-		CamelStream *image_stream;
-		CamelDataWrapper *dw;
-		const gchar *filename;
-
-		image_stream = em_format_html_get_cached_image (
-			EM_FORMAT_HTML (efhd), image_src);
-		if (!image_stream)
-			return;
-
-		filename = strrchr (image_src, '/');
-		if (filename && strchr (filename, '?'))
-			filename = NULL;
-		else if (filename)
-			filename = filename + 1;
-
-		part = camel_mime_part_new ();
-		if (filename)
-			camel_mime_part_set_filename (part, filename);
-
-		dw = camel_data_wrapper_new ();
-		camel_data_wrapper_set_mime_type (
-			dw, "application/octet-stream");
-		camel_data_wrapper_construct_from_stream_sync (
-			dw, image_stream, NULL, NULL);
-		camel_medium_set_content (CAMEL_MEDIUM (part), dw);
-		g_object_unref (dw);
-
-		camel_mime_part_set_encoding (
-			part, CAMEL_TRANSFER_ENCODING_BASE64);
-
-		g_object_unref (image_stream);
-	}
+		if (efa->handle && efa->handle->write_func)
+			efa->handle->write_func (emf, puri, stream, info, cancellable);
 
-	file = e_shell_run_save_dialog (
-		e_shell_get_default (),
-		_("Save Image"), camel_mime_part_get_filename (part),
-		NULL, NULL, NULL);
-	if (file == NULL) {
-		g_object_unref (part);
 		return;
 	}
 
-	attachment = e_attachment_new ();
-	e_attachment_set_mime_part (attachment, part);
+	if (efa->handle)
+		mime_type = efa->handle->mime_type;
+	else
+		mime_type = efa->snoop_mime_type;
+
+        button_id = g_strconcat (puri->uri, ".attachment_button", NULL);
+
+	desc = em_format_describe_part (puri->part, mime_type);
+	str = g_strdup_printf (
+                "<div class=\"attachment\">"
+                "<table width=\"100%%\" border=\"0\">"
+                "<tr valign=\"middle\">"
+                "<td align=\"left\" width=\"100\">"
+                "<object type=\"application/x-attachment-button\" "
+		"height=\"20\" width=\"100\" data=\"%s\" id=\"%s\"></object>"
+                "</td>"
+                "<td align=\"left\">%s</td>"
+                "</tr>", puri->uri, button_id, desc);
+
+	camel_stream_write_string (stream, str, cancellable, NULL);
+	g_free (desc);
+	g_free (button_id);
+	g_free (str);
+
+        /* If we know how to write the attachment, then do it */
+	if ((efa->handle && efa->handle->write_func) ||
+	    (efa->attachment_view_part_id)) {
+
+		str = g_strdup_printf (
+                        "<tr><td colspan=\"2\">"
+                        "<div class=\"attachment-wrapper\" id=\"%s\">",
+			puri->uri);
+
+		camel_stream_write_string (stream, str, cancellable, NULL);
+		g_free (str);
+
+		if (efa->handle->write_func) {
+			efa->handle->write_func (
+				emf, puri, stream, info, cancellable);
+		} else if (efa->attachment_view_part_id) {
+			EMFormatPURI *p;
+
+			p = em_format_find_puri (
+				emf, efa->attachment_view_part_id);
+			if (p && p->write_func)
+				p->write_func (emf, p, stream, info, cancellable);
+		}
 
-	e_attachment_load_async (
-		attachment, (GAsyncReadyCallback)
-		attachment_load_finish, file);
+                camel_stream_write_string (stream, "</div></td></tr>", cancellable, NULL);
+	}
 
-	g_object_unref (part);
+        camel_stream_write_string (stream, "</table></div>", cancellable, NULL);
 }
 
 static void
-efhd_web_view_update_actions_cb (EWebView *web_view,
-                                 EMFormatHTMLDisplay *efhd)
+efhd_write_secure_button (EMFormat *emf,
+                          EMFormatPURI *puri,
+                          CamelStream *stream,
+                          EMFormatWriterInfo *info,
+                          GCancellable *cancellable)
 {
-	const gchar *image_src;
-	gboolean visible;
-	GtkAction *action;
-
-	g_return_if_fail (web_view != NULL);
+	gchar *str;
 
-	image_src = e_web_view_get_cursor_image_src (web_view);
-	visible = image_src && g_str_has_prefix (image_src, "cid:");
-	if (!visible && image_src) {
-		CamelStream *image_stream;
+	if ((info->mode != EM_FORMAT_WRITE_MODE_NORMAL) &&
+	    (info->mode != EM_FORMAT_WRITE_MODE_RAW))
+		return;
 
-		image_stream = em_format_html_get_cached_image (
-			EM_FORMAT_HTML (efhd), image_src);
-		visible = image_stream != NULL;
+	str = g_strdup_printf (
+                "<object type=\"application/x-secure-button\" "
+                "height=\"20\" width=\"100%%\" "
+                "data=\"%s\" id=\"%s\"></object>", puri->uri, puri->uri);
 
-		if (image_stream)
-			g_object_unref (image_stream);
-	}
+	camel_stream_write_string (stream, str, cancellable, NULL);
 
-	action = e_web_view_get_action (web_view, "efhd-image-save");
-	if (action)
-		gtk_action_set_visible (action, visible);
+	g_free (str);
 }
 
-static GtkActionEntry image_entries[] = {
-	{ "efhd-image-save",
-	  GTK_STOCK_SAVE,
-	  N_("Save _Image..."),
-	  NULL,
-	  N_("Save the image to a file"),
-	  G_CALLBACK (action_image_save_cb) }
-};
-
-static const gchar *image_ui =
-	"<ui>"
-	"  <popup name='context'>"
-	"    <placeholder name='custom-actions-2'>"
-	"      <menuitem action='efhd-image-save'/>"
-	"    </placeholder>"
-	"  </popup>"
-	"</ui>";
-
 static void
 efhd_finalize (GObject *object)
 {
@@ -841,84 +842,77 @@ efhd_finalize (GObject *object)
 	efhd = EM_FORMAT_HTML_DISPLAY (object);
 	g_return_if_fail (efhd != NULL);
 
-	if (efhd->priv->attachment_views) {
-		g_hash_table_foreach (
-			efhd->priv->attachment_views,
-			weak_unref_attachment_view_cb, efhd);
-		g_hash_table_destroy (efhd->priv->attachment_views);
-		efhd->priv->attachment_views = NULL;
-	}
-
 	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (em_format_html_display_parent_class)->
-		finalize (object);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
-em_format_html_display_class_init (EMFormatHTMLDisplayClass *class)
+efhd_preparse (EMFormat *emf)
+{
+	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
+
+	efhd->priv->last_view = NULL;
+}
+
+static void
+efhd_class_init (EMFormatHTMLDisplayClass *class)
 {
 	GObjectClass *object_class;
-	EMFormatClass *format_class;
 	EMFormatHTMLClass *format_html_class;
+	EMFormatClass *format_class;
 
+	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (EMFormatHTMLDisplayPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
 	object_class->finalize = efhd_finalize;
 
-	format_class = EM_FORMAT_CLASS (class);
-	format_class->format_clone = efhd_format_clone;
-	format_class->format_attachment = efhd_format_attachment;
-	format_class->format_optional = efhd_format_optional;
-	format_class->format_secure = efhd_format_secure;
-
 	format_html_class = EM_FORMAT_HTML_CLASS (class);
 	format_html_class->html_widget_type = E_TYPE_MAIL_DISPLAY;
 
+	format_class = EM_FORMAT_CLASS (class);
+	format_class->preparse = efhd_preparse;
+
 	efhd_builtin_init (class);
 }
 
 static void
-em_format_html_display_init (EMFormatHTMLDisplay *efhd)
+efhd_init (EMFormatHTMLDisplay *efhd)
 {
-	EWebView *web_view;
-	GtkActionGroup *image_actions;
-	GtkUIManager *ui_manager;
-	GError *error = NULL;
-
-	web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhd));
-
 	efhd->priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efhd);
-	efhd->priv->attachment_views = g_hash_table_new_full (
-		g_str_hash, g_str_equal, g_free, NULL);
-	efhd->priv->attachment_expanded = FALSE;
-
-	e_mail_display_set_formatter (
-		E_MAIL_DISPLAY (web_view), EM_FORMAT_HTML (efhd));
 
-	/* we want to convert url's etc */
+        /* we want to convert url's etc */
 	EM_FORMAT_HTML (efhd)->text_html_flags |=
 		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
 		CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
 
-	image_actions = e_web_view_get_action_group (web_view, "image");
-	g_return_if_fail (image_actions != NULL);
-
-	gtk_action_group_add_actions (
-		image_actions, image_entries,
-		G_N_ELEMENTS (image_entries), efhd);
+}
 
-	/* Because we are loading from a hard-coded string, there is
-	 * no chance of I/O errors.  Failure here implies a malformed
-	 * UI definition.  Full stop. */
-	ui_manager = e_web_view_get_ui_manager (web_view);
-	gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, &error);
-	if (error != NULL)
-		g_error ("%s", error->message);
+GType
+em_format_html_display_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EMFormatHTMLDisplayClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) efhd_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EMFormatHTMLDisplay),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) efhd_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			EM_TYPE_FORMAT_HTML, "EMFormatHTMLDisplay",
+			&type_info, 0);
+	}
 
-	g_signal_connect (
-		web_view, "update-actions",
-		G_CALLBACK (efhd_web_view_update_actions_cb), efhd);
+	return type;
 }
 
 EMFormatHTMLDisplay *
@@ -930,8 +924,11 @@ em_format_html_display_new (void)
 /* ********************************************************************** */
 
 static EMFormatHandler type_builtin_table[] = {
-	{ (gchar *) "x-evolution/message/prefix", efhd_message_prefix },
-	{ (gchar *) "x-evolution/message/post-header", (EMFormatFunc)efhd_message_add_bar }
+	{ (gchar *) "x-evolution/message/prefix", efhd_message_prefix, },
+	{ (gchar *) "x-evolution/message/attachment-bar", (EMFormatParseFunc) efhd_message_add_bar, efhd_write_attachment_bar, },
+	{ (gchar *) "x-evolution/message/attachment", efhd_parse_attachment, efhd_write_attachment, },
+	{ (gchar *) "x-evolution/message/x-secure-button", efhd_parse_secure, efhd_write_secure_button, },
+	{ (gchar *) "x-evolution/message/optional", efhd_parse_optional, },
 };
 
 static void
@@ -939,81 +936,51 @@ efhd_builtin_init (EMFormatHTMLDisplayClass *efhc)
 {
 	gint i;
 
-	for (i = 0; i < G_N_ELEMENTS (type_builtin_table); i++)
-		em_format_class_add_handler ((EMFormatClass *) efhc, &type_builtin_table[i]);
-}
-
-static void
-efhd_write_image (EMFormat *emf,
-                  CamelStream *stream,
-                  EMFormatPURI *puri,
-                  GCancellable *cancellable)
-{
-	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
+	EMFormatClass *emfc = (EMFormatClass *) efhc;
 
-	/* TODO: identical to efh_write_image */
-	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);
+	for (i = 0; i < G_N_ELEMENTS (type_builtin_table); i++)
+		em_format_class_add_handler (emfc, &type_builtin_table[i]);
 }
 
 static void
 efhd_message_prefix (EMFormat *emf,
-                     CamelStream *stream,
                      CamelMimePart *part,
-                     const EMFormatHandler *info,
-                     GCancellable *cancellable,
-                     gboolean is_fallback)
+                     GString *part_id,
+                     EMFormatParserInfo *info,
+                     GCancellable *cancellable)
 {
 	const gchar *flag, *comp, *due;
 	time_t date;
 	gchar *iconpath, *due_date_str;
 	GString *buffer;
+	EMFormatAttachmentPURI *puri;
 
-	if (emf->folder == NULL || emf->uid == NULL
-	    || (flag = camel_folder_get_message_user_tag(emf->folder, emf->uid, "follow-up")) == NULL
+	if (emf->folder == NULL || emf->message_uid == NULL
+	    || (flag = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "follow-up")) == NULL
 	    || flag[0] == 0)
 		return;
 
-	buffer = g_string_sized_new (1024);
+	puri = (EMFormatAttachmentPURI *) em_format_puri_new (
+			emf, sizeof (EMFormatAttachmentPURI), part, ".message_prefix");
 
-	/* header displayed for message-flags in mail display */
-	g_string_append (
-		buffer,
-		"<table border=1 width=\"100%%\" "
-		"cellspacing=2 cellpadding=2><tr>");
+	puri->attachment_view_part_id = g_strdup (part_id->str);
 
-	comp = camel_folder_get_message_user_tag(emf->folder, emf->uid, "completed-on");
+	comp = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "completed-on");
 	iconpath = e_icon_factory_get_icon_filename (comp && comp[0] ? "stock_mail-flag-for-followup-done" : "stock_mail-flag-for-followup", GTK_ICON_SIZE_MENU);
 	if (iconpath) {
-		CamelMimePart *iconpart;
-
-		iconpart = em_format_html_file_part (
-			(EMFormatHTML *)emf, "image/png",
-			iconpath, cancellable);
-		g_free (iconpath);
-		if (iconpart) {
-			gchar *classid;
-
-			classid = g_strdup_printf (
-				"icon:///em-format-html-display/%s/%s",
-				emf->part_id->str,
-				comp && comp[0] ? "comp" : "uncomp");
-			g_string_append_printf (
-				buffer,
-				"<td align=\"left\">"
-				"<img src=\"%s\"></td>",
-				classid);
-			(void) em_format_add_puri (
-				emf, sizeof (EMFormatPURI),
-				classid, iconpart, efhd_write_image);
-			g_free (classid);
-			g_object_unref (iconpart);
-		}
+		gchar *classid;
+
+		classid = g_strdup_printf (
+			"icon:///em-format-html-display/%s/%s",
+			part_id->str,
+			comp && comp[0] ? "comp" : "uncomp");
+
+		puri->puri.uri = classid;
+
+		g_free (classid);
 	}
 
-	g_string_append (buffer, "<td align=\"left\" width=\"100%%\">");
+	buffer = g_string_new ("");
 
 	if (comp && comp[0]) {
 		date = camel_header_decode_date (comp, NULL);
@@ -1024,7 +991,7 @@ efhd_message_prefix (EMFormat *emf,
 			flag, _("Completed on"),
 			due_date_str ? due_date_str : "???");
 		g_free (due_date_str);
-	} else if ((due = camel_folder_get_message_user_tag(emf->folder, emf->uid, "due-by")) != NULL && due[0]) {
+	} else if ((due = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "due-by")) != NULL && due[0]) {
 		time_t now;
 
 		date = camel_header_decode_date (due, NULL);
@@ -1032,7 +999,7 @@ efhd_message_prefix (EMFormat *emf,
 		if (now > date)
 			g_string_append_printf (
 				buffer,
-				"<b>%s</b>&nbsp;",
+				"<b>%s</b> ",
 				_("Overdue:"));
 
 		due_date_str = e_datetime_format_format (
@@ -1048,270 +1015,84 @@ efhd_message_prefix (EMFormat *emf,
 		g_string_append (buffer, flag);
 	}
 
-	g_string_append (buffer, "</td></tr></table>");
-
-	camel_stream_write (
-		stream, buffer->str, buffer->len, cancellable, NULL);
-
-	g_string_free (buffer, TRUE);
-}
-
-/* ********************************************************************** */
-
-static void
-efhd_attachment_button_expanded (EAttachmentButton *button,
-                                 GParamSpec *pspec,
-                                 struct _attach_puri *info)
-{
-	EMFormatHTML *efh;
-	EMFormatHTMLDisplay *efhd;
-
-	/* FIXME The PURI struct seems to have some lifecycle issues,
-	 *       because casting info->puri.format to an EMFormatHTML
-	 *       can lead to crashes.  So we hack around it. */
-	efh = g_object_get_data (G_OBJECT (button), "efh");
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
-	if (efh->state == EM_FORMAT_HTML_STATE_RENDERING)
-		return;
-
-	info->shown = e_attachment_button_get_expanded (button);
-
-	em_format_set_inline (
-		info->puri.format, info->puri.part_id, info->shown);
-
-	efhd = (EMFormatHTMLDisplay *) efh;
-	g_return_if_fail (EM_IS_FORMAT_HTML_DISPLAY (efhd));
-
-	efhd->priv->attachment_expanded = TRUE;
-}
-
-/* ********************************************************************** */
-
-static void
-attachment_button_realized (GtkWidget *widget)
-{
-	EMFormatHTML *efh = g_object_get_data (G_OBJECT (widget), "efh");
-	EMFormatHTMLDisplay *efhd;
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
-	efhd = (EMFormatHTMLDisplay *) efh;
-	g_return_if_fail (EM_IS_FORMAT_HTML_DISPLAY (efhd));
-
-	gtk_widget_grab_focus (widget);
-	efhd->priv->attachment_expanded = FALSE;
+	puri->description = g_string_free (buffer, FALSE);
 }
 
 /* ********************************************************************** */
 
 /* attachment button callback */
-static gboolean
-efhd_attachment_button (EMFormatHTML *efh,
-                        GtkHTMLEmbedded *eb,
-                        EMFormatHTMLPObject *pobject)
+static GtkWidget *
+efhd_attachment_button (EMFormat *emf,
+                        EMFormatPURI *puri,
+                        GCancellable *cancellable)
 {
-	EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) efh;
-	struct _attach_puri *info;
-	EAttachmentView *view;
-	EAttachmentStore *store;
-	EAttachment *attachment;
-	EWebView *web_view;
+	EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri;
 	GtkWidget *widget;
-	gpointer parent;
-	EMFormat *emf = (EMFormat *) efh;
-	guint32 size = 0;
 
 	/* FIXME: handle default shown case */
 	d(printf("adding attachment button/content\n"));
 
-	if (emf->folder && emf->folder->summary && emf->uid) {
-		CamelMessageInfo *mi;
-
-		mi = camel_folder_summary_get (emf->folder->summary, emf->uid);
-		if (mi) {
-			const CamelMessageContentInfo *ci;
-
-			ci = camel_folder_summary_guess_content_info (mi, camel_mime_part_get_content_type (pobject->part));
-			if (ci) {
-				size = ci->size;
-				/* what if its not encoded in base64 ? is it a case to consider? */
-				if (ci->encoding && !g_ascii_strcasecmp (ci->encoding, "base64"))
-					size = size / 1.37;
-			}
-			camel_message_info_free (mi);
-		}
-	}
-
-	info = (struct _attach_puri *) em_format_find_puri ((EMFormat *) efh, pobject->classid);
+	if (g_cancellable_is_cancelled (cancellable))
+		return NULL;
 
 	if (!info || info->forward) {
 		g_warning ("unable to expand the attachment\n");
-		return TRUE;
+		return NULL;
 	}
 
-	attachment = info->attachment;
-	e_attachment_set_shown (attachment, info->shown);
-	e_attachment_set_signed (attachment, info->sign);
-	e_attachment_set_encrypted (attachment, info->encrypt);
-	e_attachment_set_can_show (attachment, info->handle != NULL);
-
-	web_view = em_format_html_get_web_view (efh);
-	g_return_val_if_fail (web_view != NULL, TRUE);
-	parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
-	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
-	view = em_format_html_display_get_attachment_view (efhd, info->attachment_view_part_id);
-	g_return_val_if_fail (view != NULL, TRUE);
-	gtk_widget_show (GTK_WIDGET (view));
-
-	store = e_attachment_view_get_store (view);
-	e_attachment_store_add_attachment (store, info->attachment);
-
-	e_attachment_load_async (
-		info->attachment, (GAsyncReadyCallback)
-		e_attachment_load_handle_error, parent);
-	if (size != 0) {
-		GFileInfo *fileinfo;
-
-		fileinfo = e_attachment_get_file_info (info->attachment);
-		g_file_info_set_size (fileinfo, size);
-		e_attachment_set_file_info (info->attachment, fileinfo);
-	}
-
-	widget = e_attachment_button_new (view);
+	widget = e_attachment_button_new ();
+        g_object_set_data (G_OBJECT (widget), "uri", puri->uri);
 	e_attachment_button_set_attachment (
-		E_ATTACHMENT_BUTTON (widget), attachment);
+		E_ATTACHMENT_BUTTON (widget), info->attachment);
+	e_attachment_button_set_view (
+		E_ATTACHMENT_BUTTON (widget),
+		EM_FORMAT_HTML_DISPLAY (emf)->priv->last_view);
+
 	gtk_widget_set_can_focus (widget, TRUE);
-	gtk_container_add (GTK_CONTAINER (eb), widget);
 	gtk_widget_show (widget);
 
-	/* FIXME Not sure why the expanded callback can't just use
-	 *       info->puri.format, but there seems to be lifecycle
-	 *       issues with the PURI struct.  Maybe it should have
-	 *       a reference count? */
-	g_object_set_data (G_OBJECT (widget), "efh", efh);
-
-	g_signal_connect (
-		widget, "notify::expanded",
-		G_CALLBACK (efhd_attachment_button_expanded), info);
-
-	/* If the button is created, then give it focus after
-	 * it is realized, so that user can use arrow keys to scroll
-	 * message */
-	if (efhd->priv->attachment_expanded) {
-		g_signal_connect (
-			widget, "realize",
-			G_CALLBACK (attachment_button_realized), NULL);
-	}
-
-	return TRUE;
-}
-
-static void
-efhd_attachment_frame (EMFormat *emf,
-                       CamelStream *stream,
-                       EMFormatPURI *puri,
-                       GCancellable *cancellable)
-{
-	struct _attach_puri *info = (struct _attach_puri *) puri;
-
-	if (info->shown)
-		info->handle->handler (
-			emf, stream, info->puri.part,
-			info->handle, cancellable, FALSE);
-
-	camel_stream_close (stream, cancellable, NULL);
-}
-
-static void
-set_size_request_cb (gpointer message_part_id,
-                     gpointer widget,
-                     gpointer width)
-{
-	gtk_widget_set_size_request (widget, GPOINTER_TO_INT (width), -1);
+	return widget;
 }
 
-static void
-efhd_bar_resize (EMFormatHTML *efh,
-                 GtkAllocation *event)
+static GtkWidget *
+efhd_attachment_bar (EMFormat *emf,
+                     EMFormatPURI *puri,
+                     GCancellable *cancellable)
 {
-	EMFormatHTMLDisplayPrivate *priv;
-	GtkAllocation allocation;
-	EWebView *web_view;
+	EMFormatAttachmentBarPURI *abp = (EMFormatAttachmentBarPURI *) puri;
 	GtkWidget *widget;
-	gint width;
 
-	priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efh);
+	widget = e_mail_attachment_bar_new (abp->store);
+	EM_FORMAT_HTML_DISPLAY (emf)->priv->last_view = (EAttachmentView *) widget;
 
-	web_view = em_format_html_get_web_view (efh);
-
-	widget = GTK_WIDGET (web_view);
-	gtk_widget_get_allocation (widget, &allocation);
-	width = allocation.width - 12;
-
-	if (width > 0) {
-		g_hash_table_foreach (priv->attachment_views, set_size_request_cb, GINT_TO_POINTER (width));
-	}
+	return widget;
 }
 
-static gboolean
-efhd_add_bar (EMFormatHTML *efh,
-              GtkHTMLEmbedded *eb,
-              EMFormatHTMLPObject *pobject)
+static void
+efhd_message_add_bar (EMFormat *emf,
+                      CamelMimePart *part,
+                      GString *part_id,
+                      EMFormatParserInfo *info,
+                      GCancellable *cancellable)
 {
-	EMFormatHTMLDisplayPrivate *priv;
-	GtkWidget *widget;
-
-	/* XXX See note in efhd_message_add_bar(). */
-	if (!EM_IS_FORMAT_HTML_DISPLAY (efh))
-		return FALSE;
-
-	g_return_val_if_fail (pobject != NULL && pobject->classid != NULL, FALSE);
-	g_return_val_if_fail (g_str_has_prefix (pobject->classid, "attachment-bar:"), FALSE);
-
-	priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efh);
+	EMFormatAttachmentBarPURI *puri;
+	gint len;
 
-	widget = e_mail_attachment_bar_new ();
-	gtk_container_add (GTK_CONTAINER (eb), widget);
-
-	g_hash_table_insert (priv->attachment_views, g_strdup (strchr (pobject->classid, ':') + 1), widget);
-	g_object_weak_ref (G_OBJECT (widget), efhd_attachment_view_gone_cb, efh);
-	gtk_widget_hide (widget);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	g_signal_connect_swapped (
-		eb, "size-allocate",
-		G_CALLBACK (efhd_bar_resize), efh);
+	len = part_id->len;
+	g_string_append (part_id, ".attachment-bar");
+	puri = (EMFormatAttachmentBarPURI *) em_format_puri_new (
+			emf, sizeof (EMFormatAttachmentBarPURI), part, part_id->str);
+	puri->puri.write_func = efhd_write_attachment_bar;
+	puri->puri.widget_func = efhd_attachment_bar;
+	puri->puri.free = efhd_attachment_bar_puri_free;
+	puri->store = E_ATTACHMENT_STORE (e_attachment_store_new ());
 
-	return TRUE;
-}
+	em_format_add_puri (emf, (EMFormatPURI *) puri);
 
-static void
-efhd_message_add_bar (EMFormat *emf,
-                      CamelStream *stream,
-                      CamelMimePart *part,
-                      const EMFormatHandler *info)
-{
-	gchar *classid;
-	gchar *content;
-
-	classid = g_strdup_printf (
-		"attachment-bar:%s", emf->current_message_part_id);
-
-	/* XXX Apparently this installs the callback for -all-
-	 *     EMFormatHTML subclasses, not just this subclass.
-	 *     Bad idea.  So we have to filter out other types
-	 *     in the callback. */
-	em_format_html_add_pobject (
-		EM_FORMAT_HTML (emf),
-		sizeof (EMFormatHTMLPObject),
-		classid, part, efhd_add_bar);
-
-	content = g_strdup_printf (
-		"<td><object classid=\"%s\"></object></td>", classid);
-	camel_stream_write_string (stream, content, NULL, NULL);
-	g_free (content);
-
-	g_free (classid);
+	g_string_truncate (part_id, len);
 }
 
 static void
@@ -1329,41 +1110,28 @@ efhd_optional_button_show (GtkWidget *widget,
 	}
 }
 
-static void
-efhd_resize (GtkWidget *w,
-             GtkAllocation *event,
-             EMFormatHTML *efh)
-{
-	EWebView *web_view;
-	GtkAllocation allocation;
-
-	web_view = em_format_html_get_web_view (efh);
-	gtk_widget_get_allocation (GTK_WIDGET (web_view), &allocation);
-	gtk_widget_set_size_request (w, allocation.width - 48, 250);
-}
-
 /* optional render attachment button callback */
-static gboolean
-efhd_attachment_optional (EMFormatHTML *efh,
-                          GtkHTMLEmbedded *eb,
-                          EMFormatHTMLPObject *pobject)
+static GtkWidget *
+efhd_attachment_optional (EMFormat *efh,
+                          EMFormatPURI *puri,
+                          GCancellable *cancellable)
 {
-	struct _attach_puri *info;
 	GtkWidget *hbox, *vbox, *button, *mainbox, *scroll, *label, *img;
 	AtkObject *a11y;
 	GtkWidget *view;
-	GtkAllocation allocation;
 	GtkTextBuffer *buffer;
 	GByteArray *byte_array;
-	EWebView *web_view;
+	EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return NULL;
 
 	/* FIXME: handle default shown case */
 	d(printf("adding attachment button/content for optional rendering\n"));
 
-	info = (struct _attach_puri *) em_format_find_puri ((EMFormat *) efh, pobject->classid);
 	if (!info || info->forward) {
 		g_warning ("unable to expand the attachment\n");
-		return TRUE;
+		return NULL;
 	}
 
 	scroll = gtk_scrolled_window_new (NULL, NULL);
@@ -1428,28 +1196,19 @@ efhd_attachment_optional (EMFormatHTML *efh,
 	gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 6);
 	gtk_widget_show (GTK_WIDGET (view));
 
-	web_view = em_format_html_get_web_view (efh);
-	gtk_widget_get_allocation (GTK_WIDGET (web_view), &allocation);
-	gtk_widget_set_size_request (scroll, allocation.width - 48, 250);
-	g_signal_connect (
-		scroll, "size_allocate",
-		G_CALLBACK (efhd_resize), efh);
-	gtk_widget_show (scroll);
-
 	if (!info->shown)
 		gtk_widget_hide (scroll);
 
 	gtk_widget_show (vbox);
-	gtk_container_add (GTK_CONTAINER (eb), vbox);
 	info->handle = NULL;
 
-	return TRUE;
+	return view;
 }
 
 static void
 efhd_free_attach_puri_data (EMFormatPURI *puri)
 {
-	struct _attach_puri *info = (struct _attach_puri *) puri;
+	EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri;
 
 	g_return_if_fail (puri != NULL);
 
@@ -1458,23 +1217,18 @@ efhd_free_attach_puri_data (EMFormatPURI *puri)
 		info->attachment = NULL;
 	}
 
-	g_free (info->attachment_view_part_id);
-	info->attachment_view_part_id = NULL;
-}
-
-/* returned object owned by html_display, thus do not unref it */
-EAttachmentView *
-em_format_html_display_get_attachment_view (EMFormatHTMLDisplay *html_display,
-                                            const gchar *message_part_id)
-{
-	gpointer aview;
-
-	g_return_val_if_fail (EM_IS_FORMAT_HTML_DISPLAY (html_display), NULL);
-	g_return_val_if_fail (message_part_id != NULL, NULL);
+	if (info->description) {
+		g_free (info->description);
+		info->description = NULL;
+	}
 
-	/* it should be added in efhd_add_bar() with this message_part_id */
-	aview = g_hash_table_lookup (html_display->priv->attachment_views, message_part_id);
-	g_return_val_if_fail (aview != NULL, NULL);
+	if (info->attachment_view_part_id) {
+		g_free (info->attachment_view_part_id);
+		info->attachment_view_part_id = NULL;
+	}
 
-	return E_ATTACHMENT_VIEW (aview);
+	if (info->mstream) {
+		g_object_unref (info->mstream);
+		info->mstream = NULL;
+	}
 }
diff --git a/mail/em-format-html-display.h b/mail/em-format-html-display.h
index ec29698..c1b22ee 100644
--- a/mail/em-format-html-display.h
+++ b/mail/em-format-html-display.h
@@ -52,6 +52,51 @@ G_BEGIN_DECLS
 typedef struct _EMFormatHTMLDisplay EMFormatHTMLDisplay;
 typedef struct _EMFormatHTMLDisplayClass EMFormatHTMLDisplayClass;
 typedef struct _EMFormatHTMLDisplayPrivate EMFormatHTMLDisplayPrivate;
+typedef struct _EMFormatAttachmentBarPURI EMFormatAttachmentBarPURI;
+typedef struct _EMFormatAttachmentPURI EMFormatAttachmentPURI;
+typedef struct _EMFormatSMIMEPURI EMFormatSMIMEPURI;
+
+struct _EMFormatAttachmentBarPURI {
+	EMFormatPURI puri;
+
+	EAttachmentStore *store;
+};
+
+struct _EMFormatAttachmentPURI {
+	EMFormatPURI puri;
+
+	const EMFormatHandler *handle;
+
+	const gchar *snoop_mime_type;
+
+	/* for the > and V buttons */
+	GtkWidget *forward, *down;
+	guint shown : 1;
+
+	/* Attachment */
+	EAttachment *attachment;
+	gchar *attachment_view_part_id;
+	gchar *description;
+
+	/* image stuff */
+	gint fit_width;
+	gint fit_height;
+	GtkImage *image;
+	GtkWidget *event_box;
+
+	/* Optional Text Mem Stream */
+	CamelStreamMem *mstream;
+};
+
+struct _EMFormatSMIMEPURI {
+	EMFormatPURI puri;
+
+	gchar *description;
+
+	gint signature;
+	CamelCipherValidity *valid;
+	GtkWidget *widget;
+};
 
 struct _EMFormatHTMLDisplay {
 	EMFormatHTML parent;
@@ -67,10 +112,6 @@ struct _EMFormatHTMLDisplayClass {
 GType		em_format_html_display_get_type	(void);
 EMFormatHTMLDisplay *
 		em_format_html_display_new	(void);
-EAttachmentView *
-		em_format_html_display_get_attachment_view
-						(EMFormatHTMLDisplay *html_display,
-						 const gchar *message_part_id);
 G_END_DECLS
 
 #endif /* EM_FORMAT_HTML_DISPLAY_H */
diff --git a/mail/em-format-html-print.c b/mail/em-format-html-print.c
index 0de9e23..2f9e556 100644
--- a/mail/em-format-html-print.c
+++ b/mail/em-format-html-print.c
@@ -28,33 +28,411 @@
 #include <string.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml.h>
 
+#include "em-format-html-print.h"
+#include "em-format-html-display.h"
+#include "e-mail-attachment-bar.h"
 #include <e-util/e-print.h>
-
-#include <libemail-utils/mail-mt.h>
+#include <e-util/e-util.h>
+#include <widgets/misc/e-attachment-store.h>
 #include <libemail-engine/mail-ops.h>
 
 #include "em-format-html-print.h"
 
-G_DEFINE_TYPE (
-	EMFormatHTMLPrint,
-	em_format_html_print,
-	EM_TYPE_FORMAT_HTML)
+#define d(x)
+
+static gpointer parent_class = NULL;
+
+struct _EMFormatHTMLPrintPrivate {
+
+	EMFormatHTML *original_formatter;
+	EMFormatPURI *top_level_puri;
+
+        /* List of attachment PURIs */
+        GList *attachments;
+
+};
+
+enum {
+	PROP_0,
+	PROP_ORIGINAL_FORMATTER
+};
+
+static void efhp_write_print_layout	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhp_write_headers		(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhp_write_inline_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
+static void
+efhp_write_attachments_list (EMFormatHTMLPrint *efhp,
+                             CamelStream *stream,
+                             EMFormatWriterInfo *info,
+                             GCancellable *cancellable)
+{
+	GString *str;
+	GList *iter;
+
+	if (!efhp->priv->attachments)
+		return;
+
+	str = g_string_new (
+                "<table border=\"0\" cellspacing=\"5\" cellpadding=\"0\" "
+                       "class=\"attachments-list\" >\n");
+	g_string_append_printf (str,
+                "<tr><th colspan=\"2\"><h1>%s</h1></td></tr>\n"
+                "<tr><th>%s</th><th>%s</th></tr>\n",
+                _("Attachments"), _("Name"), _("Size"));
+
+	for (iter = efhp->priv->attachments; iter; iter = iter->next) {
+		EMFormatPURI *puri = iter->data;
+		EAttachment *attachment;
+		GFileInfo *fi;
+		gchar *name, *size;
+		GByteArray *ba;
+		CamelDataWrapper *dw;
+
+		attachment = ((EMFormatAttachmentPURI *) puri)->attachment;
+		fi = e_attachment_get_file_info (attachment);
+		if (!fi)
+			continue;
+
+		if (e_attachment_get_description (attachment) &&
+                    *e_attachment_get_description (attachment)) {
+                        name = g_strdup_printf ("%s (%s)",
+				e_attachment_get_description (attachment),
+				g_file_info_get_display_name (fi));
+		} else {
+			name = g_strdup (g_file_info_get_display_name (fi));
+		}
+
+		dw = camel_medium_get_content ((CamelMedium *) puri->part);
+		ba = camel_data_wrapper_get_byte_array (dw);
+		size = g_format_size (ba->len);
+
+                g_string_append_printf (str, "<tr><td>%s</td><td>%s</td></tr>\n",
+			name, size);
+
+		g_free (name);
+		g_free (size);
+	}
+
+        g_string_append (str, "</table>\n");
+
+	camel_stream_write_string (stream, str->str, cancellable, NULL);
+	g_string_free (str, TRUE);
+}
+
+static void
+efhp_write_headers (EMFormat *emf,
+                    EMFormatPURI *puri,
+                    CamelStream *stream,
+                    EMFormatWriterInfo *info,
+                    GCancellable *cancellable)
+{
+	struct _camel_header_raw raw_header;
+	GString *str, *tmp;
+	gchar *subject;
+	const gchar *buf;
+	EMFormatPURI *p;
+	GList *iter;
+	gint attachments_count;
+	gchar *puri_prefix;
+
+	buf = camel_medium_get_header (CAMEL_MEDIUM (puri->part), "subject");
+	subject = camel_header_decode_string (buf, "UTF-8");
+	str = g_string_new ("<table border=\"0\" cellspacing=\"5\" " \
+                            "cellpadding=\"0\" class=\"printing-header\">\n");
+	g_string_append_printf (
+		str,
+                "<tr class=\"header-item\">"
+                "<td colspan=\"2\"><h1>%s</h1></td>"
+                "</tr>\n",
+		subject);
+	g_free (subject);
+
+	for (iter = g_queue_peek_head_link (&emf->header_list); iter; iter = iter->next) {
+
+		EMFormatHeader *header = iter->data;
+		raw_header.name = header->name;
+
+		/* Skip 'Subject' header, it's already displayed. */
+		if (g_ascii_strncasecmp (header->name, "Subject", 7) == 0)
+			continue;
+
+		if (header->value && *header->value) {
+			raw_header.value = header->value;
+			em_format_html_format_header (emf, str,
+				CAMEL_MEDIUM (puri->part), &raw_header,
+				header->flags | EM_FORMAT_HTML_HEADER_NOLINKS,
+				"UTF-8");
+		} else {
+			raw_header.value = g_strdup (camel_medium_get_header (
+				CAMEL_MEDIUM (emf->message), header->name));
+
+			if (raw_header.value && *raw_header.value) {
+				em_format_html_format_header (emf, str,
+					CAMEL_MEDIUM (puri->part), &raw_header,
+					header->flags | EM_FORMAT_HTML_HEADER_NOLINKS,
+					"UTF-8");
+			}
+
+			if (raw_header.value)
+				g_free (raw_header.value);
+		}
+	}
+
+        /* Get prefix of this PURI */
+        puri_prefix = g_strndup (puri->uri, g_strrstr (puri->uri, ".") - puri->uri);
+
+	/* Add encryption/signature header */
+	raw_header.name = _("Security");
+	tmp = g_string_new ("");
+	/* Find first secured part. */
+	for (iter = emf->mail_part_list, puri; iter; iter = iter->next) {
+
+		p = iter->data;
+
+		if (p->validity_type == 0)
+			continue;
+
+		if (!g_str_has_prefix (p->uri, puri_prefix))
+			continue;
+
+		if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) &&
+		    (p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) {
+			g_string_append (tmp, _("GPG signed"));
+		}
+		if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) &&
+		    (p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) {
+			if (tmp->len > 0) g_string_append (tmp, ", ");
+			g_string_append (tmp, _("GPG encrpyted"));
+		}
+		if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) &&
+		    (p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) {
+
+			if (tmp->len > 0) g_string_append (tmp, ", ");
+			g_string_append (tmp, _("S/MIME signed"));
+		}
+		if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) &&
+		    (p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) {
+
+			if (tmp->len > 0) g_string_append (tmp, ", ");
+			g_string_append (tmp, _("S/MIME encrpyted"));
+		}
+
+		break;
+	}
+
+	if (tmp->len > 0) {
+		raw_header.value = tmp->str;
+		em_format_html_format_header (emf, str, CAMEL_MEDIUM (p->part),
+			&raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8");
+	}
+	g_string_free (tmp, TRUE);
+
+	/* Count attachments and display the number as a header */
+	attachments_count = 0;
+
+	for (iter = emf->mail_part_list; iter; iter = iter ? iter->next : iter) {
+
+		p = iter->data;
+
+		if (!g_str_has_prefix (p->uri, puri_prefix))
+			continue;
+
+		if ((p->is_attachment || g_str_has_suffix(p->uri, ".attachment")) &&
+		    (!p->cid)) {
+			attachments_count++;
+                        /* EFHD sometimes creates two PURIs per attachment! */
+			if (iter->next && iter->next->data) {
+				EMFormatPURI *p2 = iter->next->data;
+				if (g_str_has_prefix (p2->uri, p->uri))
+					iter = iter->next;
+			}
+		}
+	}
+	if (attachments_count > 0) {
+		raw_header.name = _("Attachments");
+		raw_header.value = g_strdup_printf ("%d", attachments_count);
+		em_format_html_format_header (emf, str, CAMEL_MEDIUM (puri->part),
+			&raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8");
+		g_free (raw_header.value);
+	}
+
+	g_string_append (str, "</table>");
+
+	camel_stream_write_string (stream, str->str, cancellable, NULL);
+	g_string_free (str, TRUE);
+	g_free (puri_prefix);
+}
+
+static void
+efhp_write_inline_attachment (EMFormat *emf,
+                              EMFormatPURI *puri,
+                              CamelStream *stream,
+                              EMFormatWriterInfo *info,
+                              GCancellable *cancellable)
+{
+	gchar *name;
+	EMFormatAttachmentPURI *att_puri = (EMFormatAttachmentPURI *) puri;
+	EAttachment *attachment;
+	GFileInfo *fi;
+
+	attachment = att_puri->attachment;
+	fi = e_attachment_get_file_info (attachment);
+
+	if (e_attachment_get_description (attachment) &&
+            *e_attachment_get_description (attachment)) {
+                name = g_strdup_printf ("<h2>Attachment: %s (%s)</h2>\n",
+			e_attachment_get_description (attachment),
+			g_file_info_get_display_name (fi));
+	} else {
+                name = g_strdup_printf ("<h2>Attachment: %s</h2>\n",
+			g_file_info_get_display_name (fi));
+	}
+
+	camel_stream_write_string (stream, name, cancellable, NULL);
+	g_free (name);
+}
+
+static void
+efhp_write_print_layout (EMFormat *emf,
+                         EMFormatPURI *puri,
+                         CamelStream *stream,
+                         EMFormatWriterInfo *info,
+                         GCancellable *cancellable)
+{
+	GList *iter;
+	EMFormatWriterInfo print_info = {
+		EM_FORMAT_WRITE_MODE_PRINTING, FALSE, FALSE };
+	EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (emf);
+
+	g_list_free (efhp->priv->attachments);
+	efhp->priv->attachments = NULL;
+
+	camel_stream_write_string (stream,
+		"<!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\" media=\"print\" "
+                      "href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview-print.css\" />\n"
+		"</head>\n"
+		"<body style=\"background: #FFF; color: #000;\">",
+		cancellable, NULL);
+
+	for (iter = emf->mail_part_list; iter != NULL; iter = iter ? iter->next : iter) {
+
+		EMFormatPURI *puri = iter->data;
+
+		if (g_str_has_suffix (puri->uri, "print_layout"))
+			continue;
+
+		/* To late to change .headers writer_func, do it manually. */
+		if (g_str_has_suffix (puri->uri, ".headers")) {
+			efhp_write_headers (emf, puri, stream, &print_info, cancellable);
+			continue;
+		}
+
+		if (g_str_has_suffix (puri->uri, ".rfc822")) {
+
+			puri->write_func (emf, puri, stream, &print_info, cancellable);
+
+                        while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) {
+
+				iter = iter->next;
+				if (iter)
+					puri = iter->data;
+			}
+
+			if (!iter)
+				break;
+
+			continue;
+
+		}
+
+		if (puri->is_attachment || g_str_has_suffix (puri->uri, ".attachment")) {
+			const EMFormatHandler *handler;
+			CamelContentType *ct;
+			gchar *mime_type;
+
+                        if (puri->cid && g_ascii_strncasecmp (puri->cid, "cid:", 4) == 0)
+				continue;
+
+			ct = camel_mime_part_get_content_type (puri->part);
+			mime_type = camel_content_type_simple (ct);
+
+			handler = em_format_find_handler (puri->emf, mime_type);
+                        d(printf("Handler for PURI %s (%s): %s\n", puri->uri, mime_type,
+                                 handler ? handler->mime_type : "(null)"));
+			g_free (mime_type);
+
+			efhp->priv->attachments =
+				g_list_append (efhp->priv->attachments, puri);
+
+			/* If we can't inline this attachment, skip it */
+			if (handler && puri->write_func) {
+				efhp_write_inline_attachment (puri->emf, puri,
+					stream, &print_info, cancellable);
+
+				if (iter->next && iter->next->data) {
+					EMFormatPURI *p;
+					p = iter->next->data;
+
+                                        /* Has the next PURI the same prefix? */
+					if (p->write_func &&
+					    g_str_has_prefix (p->uri, puri->uri)) {
+						p->write_func (emf, p, stream,
+						       &print_info, cancellable);
+						iter = iter->next;
+					} else {
+						if (puri->write_func) {
+							puri->write_func (emf, puri,
+								stream, &print_info,
+								cancellable);
+						}
+					}
+				}
+			}
+
+			continue;
+		}
+
+		/* Ignore widget parts and unwritable non-attachment parts */
+		if (puri->write_func == NULL)
+			continue;
+
+                /* Passed all tests, probably a regular part - display it */
+		puri->write_func (puri->emf, puri, stream, &print_info, cancellable);
+
+	}
+
+	efhp_write_attachments_list (efhp, stream, &print_info, cancellable);
+
+	camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
+}
 
 static void
 efhp_finalize (GObject *object)
 {
-	EMFormatHTMLPrint *efhp = (EMFormatHTMLPrint *) object;
+	EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (object);
 
-	g_free (efhp->export_filename);
-	efhp->export_filename = NULL;
-	gtk_widget_destroy (efhp->window);
-	if (efhp->source != NULL)
-		g_object_unref (efhp->source);
+	if (efhp->priv->original_formatter) {
+		g_object_unref (efhp->priv->original_formatter);
+		efhp->priv->original_formatter = NULL;
+	}
+
+	if (efhp->priv->top_level_puri) {
+		em_format_puri_free (efhp->priv->top_level_puri);
+		efhp->priv->top_level_puri = NULL;
+	}
+
+	if (efhp->priv->attachments) {
+		g_list_free (efhp->priv->attachments);
+		efhp->priv->attachments = NULL;
+	}
 
 	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (em_format_html_print_parent_class)->finalize (object);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static gboolean
@@ -68,173 +446,182 @@ efhp_is_inline (EMFormat *emf,
 }
 
 static void
-em_format_html_print_class_init (EMFormatHTMLPrintClass *class)
+efhp_set_orig_formatter (EMFormatHTMLPrint *efhp,
+                             EMFormat *formatter)
 {
-	GObjectClass *object_class;
-	EMFormatClass *format_class;
-
-	object_class = G_OBJECT_CLASS (class);
-	object_class->finalize = efhp_finalize;
-
-	format_class = EM_FORMAT_CLASS (class);
-	format_class->is_inline = efhp_is_inline;
+	EMFormat *emfp, *emfs;
+	EMFormatPURI *puri;
+	GHashTableIter iter;
+	gpointer key, value;
+
+	efhp->priv->original_formatter = g_object_ref (formatter);
+
+	emfp = EM_FORMAT (efhp);
+	emfs = EM_FORMAT (formatter);
+
+	emfp->mail_part_list = g_list_copy (emfs->mail_part_list);
+
+	/* Make a shallow copy of the table. This table will NOT destroy
+	 * the PURIs when free'd! */
+	if (emfp->mail_part_table)
+		g_hash_table_unref (emfp->mail_part_table);
+
+	emfp->mail_part_table = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_iter_init (&iter, emfs->mail_part_table);
+	while (g_hash_table_iter_next (&iter, &key, &value))
+		g_hash_table_insert (emfp->mail_part_table, key, value);
+
+	if (emfs->folder)
+		emfp->folder = g_object_ref (emfs->folder);
+	emfp->message_uid = g_strdup (emfs->message_uid);
+	emfp->message = g_object_ref (emfs->message);
+
+	/* Add a generic PURI that will write a HTML layout
+	 * for all the parts */
+	puri = em_format_puri_new (EM_FORMAT (efhp),
+		sizeof (EMFormatPURI), NULL, "print_layout");
+	puri->write_func = efhp_write_print_layout;
+	puri->mime_type = g_strdup ("text/html");
+	em_format_add_puri (EM_FORMAT (efhp), puri);
+	efhp->priv->top_level_puri = puri;
 }
 
+static EMFormatHandler type_builtin_table[] = {
+        { (gchar *) "x-evolution/message/headers", 0, efhp_write_headers, },
+};
+
 static void
-em_format_html_print_init (EMFormatHTMLPrint *efhp)
+efhp_builtin_init (EMFormatHTMLPrintClass *efhc)
 {
-	EWebView *web_view;
+	EMFormatClass *emfc;
+	gint ii;
 
-	web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhp));
+	emfc = (EMFormatClass *) efhc;
 
-	/* gtk widgets don't like to be realized outside top level widget
-	 * so we put new html widget into gtk window */
-	efhp->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-	gtk_container_add (GTK_CONTAINER (efhp->window), GTK_WIDGET (web_view));
-	gtk_widget_realize (GTK_WIDGET (web_view));
-	efhp->parent.show_icon = FALSE;
-	((EMFormat *) efhp)->print = TRUE;
-
-	efhp->export_filename = NULL;
-	efhp->async = TRUE;
+	for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
+		em_format_class_add_handler (
+			emfc, &type_builtin_table[ii]);
 }
 
-EMFormatHTMLPrint *
-em_format_html_print_new (EMFormatHTML *source,
-                          GtkPrintOperationAction action)
+static void
+efhp_set_property (GObject *object,
+                   guint prop_id,
+                   const GValue *value,
+                   GParamSpec *pspec)
 {
-	EMFormatHTMLPrint *efhp;
+	switch (prop_id) {
 
-	efhp = g_object_new (EM_TYPE_FORMAT_HTML_PRINT, NULL);
-	if (source != NULL)
-		efhp->source = g_object_ref (source);
-	efhp->action = action;
+		case PROP_ORIGINAL_FORMATTER:
+			efhp_set_orig_formatter (
+				EM_FORMAT_HTML_PRINT (object),
+				(EMFormat *) g_value_get_object (value));
+			return;
+	}
 
-	return efhp;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
-static gint
-efhp_calc_footer_height (GtkHTML *html,
-                         GtkPrintOperation *operation,
-                         GtkPrintContext *context)
+static void
+efhp_get_property (GObject *object,
+                   guint prop_id,
+                   GValue *value,
+                   GParamSpec *pspec)
 {
-	PangoContext *pango_context;
-	PangoFontDescription *desc;
-	PangoFontMetrics *metrics;
-	gint footer_height;
+	EMFormatHTMLPrintPrivate *priv;
 
-	pango_context = gtk_print_context_create_pango_context (context);
-	desc = pango_font_description_from_string ("Sans Regular 10");
+	priv = EM_FORMAT_HTML_PRINT (object)->priv;
 
-	metrics = pango_context_get_metrics (
-		pango_context, desc, pango_language_get_default ());
-	footer_height =
-		pango_font_metrics_get_ascent (metrics) +
-		pango_font_metrics_get_descent (metrics);
-	pango_font_metrics_unref (metrics);
+	switch (prop_id) {
 
-	pango_font_description_free (desc);
-	g_object_unref (pango_context);
+		case PROP_ORIGINAL_FORMATTER:
+			g_value_set_pointer (value,
+				priv->original_formatter);
+			return;
+	}
 
-	return footer_height;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
 static void
-efhp_draw_footer (GtkHTML *html,
-                  GtkPrintOperation *operation,
-                  GtkPrintContext *context,
-                  gint page_nr,
-                  PangoRectangle *rec)
+em_format_html_print_base_init (EMFormatHTMLPrintClass *klass)
 {
-	PangoFontDescription *desc;
-	PangoLayout *layout;
-	gdouble x, y;
-	gint n_pages;
-	gchar *text;
-	cairo_t *cr;
-
-	g_object_get (operation, "n-pages", &n_pages, NULL);
-	text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
-
-	desc = pango_font_description_from_string ("Sans Regular 10");
-	layout = gtk_print_context_create_pango_layout (context);
-	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
-	pango_layout_set_font_description (layout, desc);
-	pango_layout_set_text (layout, text, -1);
-	pango_layout_set_width (layout, rec->width);
-
-	x = pango_units_to_double (rec->x);
-	y = pango_units_to_double (rec->y);
-
-	cr = gtk_print_context_get_cairo_context (context);
-
-	cairo_save (cr);
-	cairo_set_source_rgb (cr, .0, .0, .0);
-	cairo_move_to (cr, x, y);
-	pango_cairo_show_layout (cr, layout);
-	cairo_restore (cr);
-
-	g_object_unref (layout);
-	pango_font_description_free (desc);
-
-	g_free (text);
+	efhp_builtin_init (klass);
 }
 
 static void
-emfhp_complete (EMFormatHTMLPrint *efhp)
+em_format_html_print_class_init (EMFormatHTMLPrintClass *klass)
 {
-	GtkPrintOperation *operation;
-	EWebView *web_view;
-	GError *error = NULL;
+	GObjectClass *object_class;
+	EMFormatClass *format_class;
 
-	web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhp));
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (EMFormatHTMLPrintPrivate));
 
-	operation = e_print_operation_new ();
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = efhp_finalize;
+	object_class->set_property = efhp_set_property;
+	object_class->get_property = efhp_get_property;
 
-	if (efhp->action == GTK_PRINT_OPERATION_ACTION_EXPORT)
-		gtk_print_operation_set_export_filename (operation, efhp->export_filename);
+	format_class = EM_FORMAT_CLASS (klass);
+	format_class->is_inline = efhp_is_inline;
 
-	gtk_html_print_operation_run (
-		GTK_HTML (web_view),
-		operation, efhp->action, NULL,
-		(GtkHTMLPrintCalcHeight) NULL,
-		(GtkHTMLPrintCalcHeight) efhp_calc_footer_height,
-		(GtkHTMLPrintDrawFunc) NULL,
-		(GtkHTMLPrintDrawFunc) efhp_draw_footer,
-		NULL, &error);
+	g_object_class_install_property (
+		object_class,
+		PROP_ORIGINAL_FORMATTER,
+		g_param_spec_object (
+			"original-formatter",
+			NULL,
+			NULL,
+			EM_TYPE_FORMAT,
+			G_PARAM_WRITABLE |
+			G_PARAM_CONSTRUCT_ONLY));
+}
 
-	g_object_unref (operation);
+static void
+em_format_html_print_init (EMFormatHTMLPrint *efhp)
+{
+	efhp->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		efhp, EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrintPrivate);
+
+	efhp->priv->attachments = NULL;
+	efhp->export_filename = NULL;
 }
 
-void
-em_format_html_print_message (EMFormatHTMLPrint *efhp,
-                              CamelMimeMessage *message,
-                              CamelFolder *folder,
-                              const gchar *message_uid)
+GType
+em_format_html_print_get_type (void)
 {
-	g_return_if_fail (EM_IS_FORMAT_HTML_PRINT (efhp));
-	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
-
-	/* Wrap flags to display all entries by default.*/
-	EM_FORMAT_HTML (efhp)->header_wrap_flags |=
-		EM_FORMAT_HTML_HEADER_TO |
-		EM_FORMAT_HTML_HEADER_CC |
-		EM_FORMAT_HTML_HEADER_BCC;
-
-	if (efhp->async) {
-		g_signal_connect (
-			efhp, "complete", G_CALLBACK (emfhp_complete), efhp);
-
-		/* FIXME Not passing a GCancellable here. */
-		em_format_format_clone (
-			(EMFormat *) efhp,
-			folder, message_uid, message,
-			(EMFormat *) efhp->source, NULL);
-	} else {
-		em_format_html_clone_sync (
-			folder, message_uid, message,
-			(EMFormatHTML *) efhp,
-			(EMFormat *) efhp->source);
-		emfhp_complete (efhp);
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EMFormatHTMLPrintClass),
+			(GBaseInitFunc) em_format_html_print_base_init,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) em_format_html_print_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EMFormatHTMLPrint),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) em_format_html_print_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			em_format_html_get_type(), "EMFormatHTMLPrint",
+			&type_info, 0);
 	}
+
+	return type;
+}
+
+EMFormatHTMLPrint *
+em_format_html_print_new (EMFormatHTML *source)
+{
+	EMFormatHTMLPrint *efhp;
+
+	efhp = g_object_new (EM_TYPE_FORMAT_HTML_PRINT,
+		"original-formatter", source,
+		NULL);
+
+	return efhp;
 }
diff --git a/mail/em-format-html-print.h b/mail/em-format-html-print.h
index 5f08b6e..d9fe1ff 100644
--- a/mail/em-format-html-print.h
+++ b/mail/em-format-html-print.h
@@ -45,20 +45,12 @@ G_BEGIN_DECLS
 
 typedef struct _EMFormatHTMLPrint EMFormatHTMLPrint;
 typedef struct _EMFormatHTMLPrintClass EMFormatHTMLPrintClass;
+typedef struct _EMFormatHTMLPrintPrivate EMFormatHTMLPrintPrivate;
 
 struct _EMFormatHTMLPrint {
 	EMFormatHTML parent;
-
-	/* Used to realize the gtkhtml in a toplevel. */
-	GtkWidget *window;
-
-	/* Used for print_message. */
-	EMFormatHTML *source;
-
-	GtkPrintOperationAction action;
+	EMFormatHTMLPrintPrivate *priv;
 	gchar *export_filename;
-
-	gboolean async;
 };
 
 struct _EMFormatHTMLPrintClass {
@@ -67,12 +59,7 @@ struct _EMFormatHTMLPrintClass {
 
 GType		em_format_html_print_get_type	(void);
 EMFormatHTMLPrint *
-		em_format_html_print_new	(EMFormatHTML *source,
-						 GtkPrintOperationAction action);
-void		em_format_html_print_message	(EMFormatHTMLPrint *efhp,
-						 CamelMimeMessage *message,
-						 CamelFolder *folder,
-						 const gchar *uid);
+		em_format_html_print_new	(EMFormatHTML *source);
 
 G_END_DECLS
 
diff --git a/mail/em-format-html.c b/mail/em-format-html.c
index 3c130e6..c481693 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,675 +101,912 @@ 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_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_message		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *part,
-						 const EMFormatHandler *info,
-						 GCancellable *cancellable,
-						 gboolean is_fallback);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-static void	efh_format_secure		(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *part,
-						 CamelCipherValidity *valid,
-						 GCancellable *cancellable);
+	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_builtin_init		(EMFormatHTMLClass *efhc);
+	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 void	efh_write_image			(EMFormat *emf,
-						 CamelStream *stream,
-						 EMFormatPURI *puri,
-						 GCancellable *cancellable);
+	em_format_add_puri (emf, puri);
+	g_string_truncate (part_id, len);
+}
 
-static gpointer parent_class;
-static CamelDataCache *emfh_http_cache;
+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;
 
-#define EMFH_HTTP_CACHE_PATH "http"
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-/* Sigh, this is so we have a cancellable, async rendering thread */
-struct _format_msg {
-	MailMsg base;
+	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);
+	}
 
-	EMFormatHTML *format;
-	EMFormat *format_source;
-	EMHTMLStream *estream;
-	CamelFolder *folder;
-	gchar *uid;
-	CamelMimeMessage *message;
-	gboolean cancelled;
-};
+	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;
 
-static gchar *
-efh_format_desc (struct _format_msg *m)
-{
-	return g_strdup(_("Formatting message"));
+	em_format_add_puri (emf, puri);
+	g_string_truncate (part_id, len);
 }
 
 static void
-efh_format_exec (struct _format_msg *m,
-                 GCancellable *cancellable,
-                 GError **error)
+efh_parse_text_plain (EMFormat *emf,
+                      CamelMimePart *part,
+                      GString *part_id,
+                      EMFormatParserInfo *info,
+                      GCancellable *cancellable)
 {
-	EMFormat *format;
-	CamelStream *stream;
-	struct _EMFormatHTMLJob *job;
-	GNode *puri_level;
-	CamelURL *base;
-	gchar *content;
+	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 (m->format->priv->web_view == NULL) {
-		m->cancelled = TRUE;
+	if (g_cancellable_is_cancelled (cancellable))
 		return;
-	}
 
-	format = EM_FORMAT (m->format);
-	stream = CAMEL_STREAM (m->estream);
+	dw = camel_medium_get_content ((CamelMedium *) part);
+	if (!dw)
+		return;
 
-	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);
-	g_free (content);
+	/* This scans the text part for inline-encoded data, creates
+	 * a multipart of all the parts inside it. */
 
-	/* <insert top-header stuff here> */
+	/* FIXME: We should discard this multipart if it only contains
+	 * the original text, but it makes this hash lookup more complex */
 
-	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);
+	/* 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);
 	}
 
-	camel_stream_flush (stream, cancellable, NULL);
+	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;
+	}
 
-	puri_level = format->pending_uri_level;
-	base = format->base;
+	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);
 
-	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;
-		}
+	mp = em_inline_filter_get_multipart (inline_filter);
 
-	} while (!g_queue_is_empty (&m->format->priv->pending_jobs));
+	if (charset_added) {
+		camel_content_type_set_param (type, "charset", NULL);
+	}
 
-	d(printf("out of jobs, done\n"));
+	g_object_unref (inline_filter);
+	camel_content_type_unref (type);
 
-	format->pending_uri_level = puri_level;
-	m->cancelled = m->cancelled || g_cancellable_is_cancelled (cancellable);
-}
+	/* 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);
 
-static void
-efh_format_done (struct _format_msg *m)
-{
-	d(printf("formatting finished\n"));
+		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);
+		}
+	}
 
-	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");
+	g_object_unref (mp);
 }
 
 static void
-efh_format_free (struct _format_msg *m)
+efh_parse_text_html (EMFormat *emf,
+                     CamelMimePart *part,
+                     GString *part_id,
+                     EMFormatParserInfo *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);
+	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);
+		}
 	}
-	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);
-}
 
-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
-};
+	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);
 
-static gboolean
-efh_format_helper (struct _format_msg *m,
-                   gboolean async)
+	if (cid)
+		g_free (cid);
+}
+
+static void
+efh_parse_message_external (EMFormat *emf,
+                            CamelMimePart *part,
+                            GString *part_id,
+                            EMFormatParserInfo *info,
+                            GCancellable *cancellable)
 {
-	GtkHTMLStream *hstream;
-	EMFormatHTML *efh = m->format;
-	struct _EMFormatHTMLPrivate *p = efh->priv;
-	EWebView *web_view;
+	EMFormatPURI *puri;
+	CamelMimePart *newpart;
+	CamelContentType *type;
+	const gchar *access_type;
+	gchar *url = NULL, *desc = NULL;
+	gchar *content;
+	gint len;
 
-	web_view = em_format_html_get_web_view (m->format);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	if (web_view == NULL) {
-		mail_msg_unref (m);
-		return FALSE;
-	}
+	newpart = camel_mime_part_new ();
 
-	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;
-		}
+	/* 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;
 	}
 
-	g_return_val_if_fail (g_queue_is_empty (&p->pending_jobs), FALSE);
+	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];
 
-	d(printf(" ready to go, firing off format thread\n"));
+		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;
 
-	/* 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);
+		/* 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);
 
-	/* FIXME: method off EMFormat? */
-	if (((EMFormat *) efh)->valid) {
-		camel_cipher_validity_free (((EMFormat *) efh)->valid);
-		((EMFormat *) efh)->valid = NULL;
-		((EMFormat *) efh)->valid_parent = NULL;
-	}
+		if (mode && *mode)
+			sprintf(ftype, ";type=%c",  *mode);
+		else
+			ftype[0] = 0;
 
-	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);
-		}
+		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;
 
-		hstream = NULL;
-		m->estream = (EMHTMLStream *) em_html_stream_new (
-			GTK_HTML (web_view), hstream);
+		name = camel_content_type_param (type, "name");
+		site = camel_content_type_param (type, "site");
+		if (name == NULL)
+			goto fail;
 
-		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);
+		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;
 
-			p->last_part = m->message;
+		/* 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;
 
-		efh->priv->format_id = m->base.seq;
+	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);
 
-		if (async) {
-			mail_msg_unordered_push (m);
-		} else {
-			efh_format_exec (m, NULL, NULL);
-		}
-	}
+	g_free (url);
+	g_free (desc);
+
+fail:
+	content = g_strdup_printf (
+		_("Pointer to unknown external data (\"%s\" type)"),
+		access_type);
+	camel_mime_part_set_content (newpart, content, strlen (content), "text/plain");
+	g_free (content);
 
-	efh->priv->format_timeout_id = 0;
-	efh->priv->format_timeout_msg = NULL;
+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");
 
-	return FALSE;
+	em_format_add_puri (emf, puri);
+	g_string_truncate (part_id, len);
 }
 
 static void
-efh_free_cache (struct _EMFormatHTMLCache *efhc)
+efh_parse_message_deliverystatus (EMFormat *emf,
+                                  CamelMimePart *part,
+                                  GString *part_id,
+                                  EMFormatParserInfo *info,
+                                  GCancellable *cancellable)
 {
-	if (efhc->textmp)
-		g_object_unref (efhc->textmp);
-	g_free (efhc);
+	EMFormatPURI *puri;
+	gint len;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	len = part_id->len;
+	g_string_append (part_id, ".deliverystatus");
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = efh_write_source;
+	puri->mime_type = g_strdup ("text/html");
+	puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+	puri->validity_type = info->validity_type;
+	puri->is_attachment = info->is_attachment;
+
+	em_format_add_puri (emf, puri);
+	g_string_truncate (part_id, len);
 }
 
 static void
-efh_gtkhtml_destroy (GtkHTML *html,
-                     EMFormatHTML *efh)
+efh_parse_message_rfc822 (EMFormat *emf,
+                          CamelMimePart *part,
+                          GString *part_id,
+                          EMFormatParserInfo *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;
-	}
+	CamelDataWrapper *dw;
+	CamelMimePart *opart;
+	CamelStream *stream;
+	CamelMimeParser *parser;
+	gint len;
+	EMFormatParserInfo oinfo = *info;
+	EMFormatPURI *puri;
 
-	/* This probably works ... */
-	if (efh->priv->format_id != -1)
-		mail_msg_cancel (efh->priv->format_id);
+	len = part_id->len;
+	g_string_append (part_id, ".rfc822");
 
-	if (efh->priv->web_view != NULL) {
-		g_object_unref (efh->priv->web_view);
-		efh->priv->web_view = NULL;
-	}
-}
+        /* 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);
 
-static struct _EMFormatHTMLCache *
-efh_insert_cache (EMFormatHTML *efh,
-                  const gchar *partid)
-{
-	struct _EMFormatHTMLCache *efhc;
+        /* 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);
 
-	efhc = g_malloc0 (sizeof (*efh) + strlen (partid));
-	strcpy (efhc->partid, partid);
-	g_hash_table_insert (efh->priv->text_inline_parts, efhc->partid, efhc);
+	parser = camel_mime_parser_new ();
+	camel_mime_parser_init_with_stream (parser, stream, NULL);
 
-	return efhc;
+	opart = camel_mime_part_new ();
+	camel_mime_part_construct_from_parser_sync (opart, parser, cancellable, NULL);
+
+	em_format_parse_part_as (emf, opart, part_id, &oinfo,
+		"x-evolution/message", cancellable);
+
+        /* Add another generic PURI that represents end of the RFC message.
+         * The em_format_write() function will skip all PURIs between the ".rfc822" 
+         * PURI and ".rfc822.end" PURI as they will be rendered in an <iframe> */
+        g_string_append (part_id, ".end");
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), NULL, part_id->str);
+	em_format_add_puri (emf, puri);
+
+	g_string_truncate (part_id, len);
+
+	g_object_unref (opart);
+	g_object_unref (parser);
+	g_object_unref (stream);
 }
 
+/*****************************************************************************/
+
 static void
-efh_set_property (GObject *object,
-                  guint property_id,
-                  const GValue *value,
-                  GParamSpec *pspec)
+efh_write_image (EMFormat *emf,
+                 EMFormatPURI *puri,
+                 CamelStream *stream,
+                  EMFormatWriterInfo *info,
+                 GCancellable *cancellable)
 {
-	switch (property_id) {
-		case PROP_BODY_COLOR:
-			em_format_html_set_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_BODY,
-				g_value_get_boxed (value));
-			return;
+	gchar *content;
+	EMFormatHTML *efh;
+	CamelDataWrapper *dw;
+	GByteArray *ba;
 
-		case PROP_CITATION_COLOR:
-			em_format_html_set_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_CITATION,
-				g_value_get_boxed (value));
-			return;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-		case PROP_CONTENT_COLOR:
-			em_format_html_set_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_CONTENT,
-				g_value_get_boxed (value));
-			return;
+	efh = (EMFormatHTML *) emf;
 
-		case PROP_FRAME_COLOR:
-			em_format_html_set_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_FRAME,
-				g_value_get_boxed (value));
-			return;
+	dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part));
+	g_return_if_fail (dw);
 
-		case PROP_HEADER_COLOR:
-			em_format_html_set_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_HEADER,
-				g_value_get_boxed (value));
-			return;
+	ba = camel_data_wrapper_get_byte_array (dw);
 
-		case PROP_IMAGE_LOADING_POLICY:
-			em_format_html_set_image_loading_policy (
-				EM_FORMAT_HTML (object),
-				g_value_get_enum (value));
-			return;
+	if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
 
-		case PROP_MARK_CITATIONS:
-			em_format_html_set_mark_citations (
-				EM_FORMAT_HTML (object),
-				g_value_get_boolean (value));
-			return;
+		if (!efh->priv->animate_images) {
 
-		case PROP_ONLY_LOCAL_PHOTOS:
-			em_format_html_set_only_local_photos (
-				EM_FORMAT_HTML (object),
-				g_value_get_boolean (value));
-			return;
+			gchar *buff;
+			gsize len;
+			gchar *data;
+			GByteArray anim;
 
-		case PROP_SHOW_SENDER_PHOTO:
-			em_format_html_set_show_sender_photo (
-				EM_FORMAT_HTML (object),
-				g_value_get_boolean (value));
-			return;
+			data = g_strndup ((gchar *) ba->data, (gsize) ba->len);
+			anim.data = g_base64_decode (data, (gsize *) &(anim.len));
+			g_free (data);
 
-		case PROP_SHOW_REAL_DATE:
-			em_format_html_set_show_real_date (
-				EM_FORMAT_HTML (object),
-				g_value_get_boolean (value));
-			return;
+			em_format_html_animation_extract_frame (&anim, &buff, &len);
 
-		case PROP_TEXT_COLOR:
-			em_format_html_set_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_TEXT,
-				g_value_get_boxed (value));
-			return;
-		case PROP_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 (
-				EM_FORMAT_HTML (object),
-				g_value_get_boolean (value));
-	}
+			camel_stream_write (stream, buff, len, cancellable, NULL);
 
-	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
+			g_free (buff);
+			g_free (anim.data);
 
-static void
-efh_get_property (GObject *object,
-                  guint property_id,
-                  GValue *value,
-                  GParamSpec *pspec)
-{
-	GdkColor color;
+		} 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);
+		}
 
-	switch (property_id) {
-		case PROP_BODY_COLOR:
-			em_format_html_get_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_BODY,
-				&color);
-			g_value_set_boxed (value, &color);
-			return;
+	} else {
 
-		case PROP_CITATION_COLOR:
-			em_format_html_get_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_CITATION,
-				&color);
-			g_value_set_boxed (value, &color);
-			return;
+		gchar *buffer;
 
-		case PROP_CONTENT_COLOR:
-			em_format_html_get_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_CONTENT,
-				&color);
-			g_value_set_boxed (value, &color);
-			return;
+		if (!efh->priv->animate_images) {
 
-		case PROP_FRAME_COLOR:
-			em_format_html_get_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_FRAME,
-				&color);
-			g_value_set_boxed (value, &color);
-			return;
+			gchar *buff;
+			gsize len;
+			gchar *data;
+			GByteArray raw_data;
 
-		case PROP_HEADER_COLOR:
-			em_format_html_get_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_HEADER,
-				&color);
-			g_value_set_boxed (value, &color);
-			return;
+			data = g_strndup ((gchar *) ba->data, ba->len);
+			raw_data.data =  (guint8 *) g_base64_decode (
+						data, (gsize *) &(raw_data.len));
+			g_free (data);
 
-		case PROP_IMAGE_LOADING_POLICY:
-			g_value_set_enum (
-				value,
-				em_format_html_get_image_loading_policy (
-				EM_FORMAT_HTML (object)));
-			return;
+			em_format_html_animation_extract_frame (&raw_data, &buff, &len);
 
-		case PROP_MARK_CITATIONS:
-			g_value_set_boolean (
-				value, em_format_html_get_mark_citations (
-				EM_FORMAT_HTML (object)));
-			return;
+			content = g_base64_encode ((guchar *) buff, len);
+			g_free (buff);
+			g_free (raw_data.data);
 
-		case PROP_ONLY_LOCAL_PHOTOS:
-			g_value_set_boolean (
-				value, em_format_html_get_only_local_photos (
-				EM_FORMAT_HTML (object)));
-			return;
+		} else {
+			content = g_strndup ((gchar *) ba->data, ba->len);
+		}
 
-		case PROP_SHOW_SENDER_PHOTO:
-			g_value_set_boolean (
-				value, em_format_html_get_show_sender_photo (
-				EM_FORMAT_HTML (object)));
-			return;
+		/* 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);
 
-		case PROP_SHOW_REAL_DATE:
-			g_value_set_boolean (
-				value, em_format_html_get_show_real_date (
-				EM_FORMAT_HTML (object)));
-			return;
+		camel_stream_write_string (stream, buffer, cancellable, NULL);
+		g_free (buffer);
+		g_free (content);
+	}
+}
 
-		case PROP_TEXT_COLOR:
-			em_format_html_get_color (
-				EM_FORMAT_HTML (object),
-				EM_FORMAT_HTML_COLOR_TEXT,
-				&color);
-			g_value_set_boxed (value, &color);
-			return;
+static void
+efh_write_text_enriched (EMFormat *emf,
+                         EMFormatPURI *puri,
+                         CamelStream *stream,
+                         EMFormatWriterInfo *info,
+                         GCancellable *cancellable)
+{
+	EMFormatHTML *efh = EM_FORMAT_HTML (emf);
+	CamelStream *filtered_stream;
+	CamelMimeFilter *enriched;
+	guint32 flags = 0;
+	GString *buffer;
+	CamelContentType *ct;
+	gchar *mime_type = NULL;
 
-		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:
-			g_value_set_boolean (
-				value, em_format_html_get_headers_collapsable (
-				EM_FORMAT_HTML (object)));
+	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);
 	}
 
-	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	if (!g_strcmp0(mime_type, "text/richtext")) {
+		flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
+		camel_stream_write_string (
+			stream, "\n<!-- text/richtext -->\n",
+			cancellable, NULL);
+	} else {
+		camel_stream_write_string (
+			stream, "\n<!-- text/enriched -->\n",
+			cancellable, NULL);
+	}
+
+	if (mime_type)
+		g_free (mime_type);
+
+	enriched = camel_mime_filter_enriched_new (flags);
+	filtered_stream = camel_stream_filter_new (stream);
+	camel_stream_filter_add (
+		CAMEL_STREAM_FILTER (filtered_stream), enriched);
+	g_object_unref (enriched);
+
+	buffer = g_string_new ("");
+
+	g_string_append_printf (buffer,
+		"<div class=\"part-container\" style=\"border-color: #%06x; "
+		"background-color: #%06x; color: #%06x;\">"
+		"<div class=\"part-container-inner-margin\">\n",
+		e_color_to_value (&efh->priv->colors[
+			EM_FORMAT_HTML_COLOR_FRAME]),
+		e_color_to_value (&efh->priv->colors[
+			EM_FORMAT_HTML_COLOR_CONTENT]),
+		e_color_to_value (&efh->priv->colors[
+			EM_FORMAT_HTML_COLOR_TEXT]));
+
+	camel_stream_write_string (stream, buffer->str, cancellable, NULL);
+	g_string_free (buffer, TRUE);
+
+	em_format_format_text (
+		emf, (CamelStream *) filtered_stream,
+		(CamelDataWrapper *) puri->part, cancellable);
+
+	g_object_unref (filtered_stream);
+	camel_stream_write_string (stream, "</div></div>", cancellable, NULL);
 }
 
 static void
-efh_finalize (GObject *object)
+efh_write_text_plain (EMFormat *emf,
+                      EMFormatPURI *puri,
+                      CamelStream *stream,
+                       EMFormatWriterInfo *info,
+                      GCancellable *cancellable)
 {
-	EMFormatHTML *efh = EM_FORMAT_HTML (object);
+	CamelDataWrapper *dw;
+	CamelStream *filtered_stream;
+	CamelMimeFilter *html_filter;
+	EMFormatHTML *efh = (EMFormatHTML *) emf;
+	gchar *content;
+	const gchar *format;
+	guint32 flags;
+	guint32 rgb;
 
-	em_format_html_clear_pobject (efh);
-	efh_gtkhtml_destroy (GTK_HTML (efh->priv->web_view), efh);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	g_hash_table_destroy (efh->priv->text_inline_parts);
+	flags = efh->text_html_flags;
 
-	g_mutex_free (efh->priv->lock);
+	dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part));
 
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (parent_class)->finalize (object);
-}
+	/* 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;
 
-static gboolean
-efh_format_timeout (struct _format_msg *m)
-{
-	return efh_format_helper (m, TRUE);
-}
+	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);
 
-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);
+	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);
+
+	camel_stream_write_string (stream, "</div></div>\n", cancellable, NULL);
 }
 
-static void
-efh_format_clone (EMFormat *emf,
-                  CamelFolder *folder,
-                  const gchar *uid,
-                  CamelMimeMessage *msg,
-                  EMFormat *emfsource,
-                  GCancellable *cancellable)
+static gchar *
+get_tag (const gchar *tag_name,
+         gchar *opening,
+         gchar *closing)
 {
-	EMFormatHTML *efh = EM_FORMAT_HTML (emf);
-	struct _format_msg *m;
+	gchar *t;
+	gboolean has_end;
 
-	/* How to sub-class ?  Might need to adjust api ... */
+	for (t = closing - 1; t != opening; t--) {
+		if (*t != ' ')
+			break;
+	}
 
-	if (efh->priv->web_view == NULL)
-		return;
+	/* Not a pair tag */
+	if (*t == '/')
+		return g_strndup (opening, closing - opening + 1);
 
-	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;
+	for (t = closing; t && *t; t++) {
+		if (*t == '<')
+			break;
 	}
 
-	if (emfsource != NULL)
-		g_object_ref (emfsource);
+	do {
+		if (*t == '/') {
+			has_end = TRUE;
+			break;
+		}
 
-	if (folder != NULL)
-		g_object_ref (folder);
+		if (*t == '>') {
+			has_end = FALSE;
+			break;
+		}
 
-	if (msg != NULL)
-		g_object_ref (msg);
+		t++;
 
-	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;
+	} while (t && *t);
 
-	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);
-	}
-}
+	/* Broken HTML? */
+	if (!has_end)
+		return g_strndup (opening, closing - opening + 1);
 
-static void
-efh_format_error (EMFormat *emf,
-                  CamelStream *stream,
-                  const gchar *txt)
-{
-	GString *buffer;
-	gchar *html;
+	do {
+		if ((*t != ' ') && (*t != '/'))
+			break;
 
-	buffer = g_string_new ("<em><font color=\"red\">");
+		t++;
+	} while (t && *t);
 
-	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);
+	if (g_strncasecmp (t, tag_name, strlen (tag_name)) == 0) {
 
-	g_string_append (buffer, "</font></em><br>");
+		closing = strstr (t, ">");
 
-	camel_stream_write (stream, buffer->str, buffer->len, NULL, NULL);
+		return g_strndup (opening, closing - opening + strlen (tag_name));
+	}
 
-	g_string_free (buffer, TRUE);
+	/* Broken HTML? */
+	return g_strndup (opening, closing - opening + 1);
 }
 
 static void
-efh_format_source (EMFormat *emf,
-                   CamelStream *stream,
-                   CamelMimePart *part,
-                   GCancellable *cancellable)
+efh_write_text_html (EMFormat *emf,
+                     EMFormatPURI *puri,
+                     CamelStream *stream,
+                     EMFormatWriterInfo *info,
+                     GCancellable *cancellable)
+{
+	EMFormatHTML *efh = (EMFormatHTML *) emf;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
+		em_format_format_text (emf, stream,
+			(CamelDataWrapper *) puri->part, cancellable);
+
+	} else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) {
+		GString *string;
+		GByteArray *ba;
+		gchar *pos;
+		GList *tags, *iter;
+		gboolean valid;
+		gchar *tag;
+		const gchar *document_end;
+		gint length;
+		gint i;
+		CamelStream *decoded_stream;
+
+		decoded_stream = camel_stream_mem_new ();
+		em_format_format_text (emf, decoded_stream,
+			(CamelDataWrapper *) puri->part, cancellable);
+		g_seekable_seek (G_SEEKABLE (decoded_stream), 0, G_SEEK_SET, cancellable, NULL);
+
+		ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (decoded_stream));
+		string = g_string_new_len ((gchar *) ba->data, ba->len);
+
+		g_object_unref (decoded_stream);
+
+		tags = NULL;
+		pos = string->str;
+		valid = FALSE;
+		do {
+			gchar *closing;
+			gchar *opening;
+
+			pos = strstr (pos + 1, "<");
+			if (!pos)
+				break;
+
+			opening = pos;
+			closing = strstr (pos, ">");
+
+			/* Find where the actual tag name begins */
+			for (tag = pos + 1; tag && *tag; tag++) {
+				if (*tag != ' ')
+					break;
+			}
+
+			if (g_ascii_strncasecmp (tag, "style", 5) == 0) {
+				tags = g_list_append (
+					tags,
+					get_tag ("style", opening, closing));
+			} else if (g_ascii_strncasecmp (tag, "script", 6) == 0) {
+				tags = g_list_append (
+					tags,
+                                        get_tag ("script", opening, closing));
+			} else if (g_ascii_strncasecmp (tag, "link", 4) == 0) {
+				tags = g_list_append (
+					tags,
+                                        get_tag ("link", opening, closing));
+			} else if (g_ascii_strncasecmp (tag, "body", 4) == 0) {
+				valid = TRUE;
+				break;
+			}
+
+		} while (TRUE);
+
+		/* Something's wrong, let's write the entire HTML and hope
+		 * that WebKit can handle it */
+		if (!valid) {
+			EMFormatWriterInfo i = *info;
+			i.mode = EM_FORMAT_WRITE_MODE_RAW;
+			efh_write_text_html (emf, puri, stream, &i, cancellable);
+			return;
+		}
+
+		/*	        include the "body" as well -----v */
+		g_string_erase (string, 0, tag - string->str + 4);
+		g_string_prepend (string, "<div ");
+
+		for (iter = tags; iter; iter = iter->next) {
+			g_string_prepend (string, iter->data);
+		}
+
+		g_list_free_full (tags, g_free);
+
+		/* that's reversed </body></html>... */
+		document_end = ">lmth/<>ydob/<";
+		length = strlen (document_end);
+		tag = string->str + string->len - 1;
+		i = 0;
+		valid = FALSE;
+		while (i < length - 1) {
+			gchar c;
+
+			if (g_ascii_isspace (*tag)) {
+				tag--;
+				continue;
+			}
+
+			if ((*tag >= 'A') && (*tag <= 'Z'))
+				c = *tag + 32;
+			else
+				c = *tag;
+
+			if (c == document_end[i]) {
+				tag--;
+				i++;
+				valid = TRUE;
+				continue;
+			}
+
+			valid = FALSE;
+		}
+
+		if (valid)
+			g_string_truncate (string, tag - string->str);
+
+		camel_stream_write_string (stream, string->str, cancellable, NULL);
+
+		g_string_free (string, TRUE);
+	} else {
+		gchar *str;
+		gchar *uri;
+
+		uri = em_format_build_mail_uri (
+				emf->folder, emf->message_uid,
+				"part_id", G_TYPE_STRING, puri->uri,
+				"mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW,
+				NULL);
+
+		str = g_strdup_printf (
+			"<div class=\"part-container\" 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_write_source (EMFormat *emf,
+                  EMFormatPURI *puri,
+                  CamelStream *stream,
+                  EMFormatWriterInfo *info,
+                  GCancellable *cancellable)
 {
+	EMFormatHTML *efh = (EMFormatHTML *) emf;
+	GString *buffer;
 	CamelStream *filtered_stream;
 	CamelMimeFilter *filter;
-	CamelDataWrapper *dw = (CamelDataWrapper *) part;
+	CamelDataWrapper *dw = (CamelDataWrapper *) puri->part;
 
 	filtered_stream = camel_stream_filter_new (stream);
 
@@ -805,1786 +1018,1153 @@ efh_format_source (EMFormat *emf,
 		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, "<table><tr><td><tt>", cancellable, NULL);
-	em_format_format_text (emf, filtered_stream, dw, cancellable);
+		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, "</tt></td></tr></table>", cancellable, NULL);
+		stream, "</code>", cancellable, NULL);
 
 	g_object_unref (filtered_stream);
+	g_string_free (buffer, TRUE);
 }
 
 static void
-efh_format_attachment (EMFormat *emf,
-                       CamelStream *stream,
-                       CamelMimePart *part,
-                       const gchar *mime_type,
-                       const EMFormatHandler *handle,
-                       GCancellable *cancellable)
+efh_write_headers (EMFormat *emf,
+                   EMFormatPURI *puri,
+                   CamelStream *stream,
+                   EMFormatWriterInfo *info,
+                   GCancellable *cancellable)
 {
-	gchar *text, *html;
+	GString *buffer;
+	EMFormatHTML *efh = (EMFormatHTML *) emf;
+	gint bg_color;
 
-	/* we display all inlined attachments only */
+	if (!puri->part)
+		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);
+	buffer = g_string_new ("");
 
-	/* 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);
+	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]);
+	}
 
-	camel_stream_write_string (
-		stream, "</font></td></tr><tr></table>", cancellable, NULL);
+	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);
+	}
 
-	if (handle && em_format_is_inline (emf, emf->part_id->str, part, handle))
-		handle->handler (emf, stream, part, handle, cancellable, FALSE);
-}
+	efh_format_full_headers (efh, buffer, (CamelMedium *) puri->part,
+		(info->mode == EM_FORMAT_WRITE_MODE_ALL_HEADERS),
+		!info->headers_collapsed,
+		cancellable);
 
-static gboolean
-efh_busy (EMFormat *emf)
-{
-	EMFormatHTMLPrivate *priv;
+	g_string_append (buffer, "</td></tr></table></div>");
 
-	priv = EM_FORMAT_HTML_GET_PRIVATE (emf);
+	camel_stream_write_string (stream, buffer->str, cancellable, NULL);
 
-	return (priv->format_id != -1);
+	g_string_free (buffer, true);
 }
+
 static void
-efh_base_init (EMFormatHTMLClass *class)
+efh_write_error (EMFormat *emf,
+                 EMFormatPURI *puri,
+                 CamelStream *stream,
+                 EMFormatWriterInfo *info,
+                 GCancellable *cancellable)
 {
-	efh_builtin_init (class);
+	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_class_init (EMFormatHTMLClass *class)
+efh_write_message_rfc822 (EMFormat *emf,
+                          EMFormatPURI *puri,
+                          CamelStream *stream,
+                          EMFormatWriterInfo *info,
+                          GCancellable *cancellable)
 {
-	GObjectClass *object_class;
-	EMFormatClass *format_class;
-	const gchar *user_cache_dir;
+	if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
 
-	parent_class = g_type_class_peek_parent (class);
-	g_type_class_add_private (class, sizeof (EMFormatHTMLPrivate));
+		GList *puris;
+		GList *iter;
 
-	object_class = G_OBJECT_CLASS (class);
-	object_class->set_property = efh_set_property;
-	object_class->get_property = efh_get_property;
-	object_class->finalize = efh_finalize;
+                /* 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;
 
-	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;
+		iter = iter->next;
+		puris = NULL;
+		while (iter) {
 
-	class->html_widget_type = E_TYPE_WEB_VIEW;
+			EMFormatPURI *p;
+			p = iter->data;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_BODY_COLOR,
-		g_param_spec_boxed (
-			"body-color",
-			"Body Color",
-			NULL,
-			GDK_TYPE_COLOR,
-			G_PARAM_READWRITE));
+                        if (g_str_has_suffix (p->uri, ".rfc822.end"))
+				break;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_CITATION_COLOR,
-		g_param_spec_boxed (
-			"citation-color",
-			"Citation Color",
-			NULL,
-			GDK_TYPE_COLOR,
-			G_PARAM_READWRITE));
+			puris = g_list_append (puris, p);
+			iter = iter->next;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_CONTENT_COLOR,
-		g_param_spec_boxed (
-			"content-color",
-			"Content Color",
-			NULL,
-			GDK_TYPE_COLOR,
-			G_PARAM_READWRITE));
+		};
 
-	g_object_class_install_property (
-		object_class,
-		PROP_FRAME_COLOR,
-		g_param_spec_boxed (
-			"frame-color",
-			"Frame Color",
-			NULL,
-			GDK_TYPE_COLOR,
-			G_PARAM_READWRITE));
+		efh_write_message (emf, puris, stream, info, cancellable);
 
-	g_object_class_install_property (
-		object_class,
-		PROP_HEADER_COLOR,
-		g_param_spec_boxed (
-			"header-color",
-			"Header Color",
-			NULL,
-			GDK_TYPE_COLOR,
-			G_PARAM_READWRITE));
+		g_list_free (puris);
 
-	/* FIXME Make this a proper enum property. */
-	g_object_class_install_property (
-		object_class,
-		PROP_IMAGE_LOADING_POLICY,
-		g_param_spec_enum (
-			"image-loading-policy",
-			"Image Loading Policy",
-			NULL,
-			E_TYPE_MAIL_IMAGE_LOADING_POLICY,
-			E_MAIL_IMAGE_LOADING_POLICY_ALWAYS,
-			G_PARAM_READWRITE));
+	} else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) {
 
-	g_object_class_install_property (
-		object_class,
-		PROP_MARK_CITATIONS,
-		g_param_spec_boolean (
-			"mark-citations",
-			"Mark Citations",
-			NULL,
-			TRUE,
-			G_PARAM_READWRITE));
+		GList *iter;
+		gboolean can_write = FALSE;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_ONLY_LOCAL_PHOTOS,
-		g_param_spec_boolean (
-			"only-local-photos",
-			"Only Local Photos",
-			NULL,
-			TRUE,
-			G_PARAM_READWRITE |
-			G_PARAM_CONSTRUCT));
+		iter = g_hash_table_lookup (emf->mail_part_table, puri->uri);
+		if (!iter || !iter->next)
+			return;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_SHOW_SENDER_PHOTO,
-		g_param_spec_boolean (
-			"show-sender-photo",
-			"Show Sender Photo",
-			NULL,
-			TRUE,
-			G_PARAM_READWRITE |
-			G_PARAM_CONSTRUCT));
+                /* Skip everything before attachment bar, inclusive */\
+		iter = iter->next;
+		while (iter) {
 
-	g_object_class_install_property (
-		object_class,
-		PROP_SHOW_REAL_DATE,
-		g_param_spec_boolean (
-			"show-real-date",
-			"Show real Date header value",
-			NULL,
-			TRUE,
-			G_PARAM_READWRITE |
-			G_PARAM_CONSTRUCT));
+			EMFormatPURI *p = iter->data;
 
-	g_object_class_install_property (
-		object_class,
-		PROP_TEXT_COLOR,
-		g_param_spec_boxed (
-			"text-color",
-			"Text Color",
-			NULL,
-			GDK_TYPE_COLOR,
-			G_PARAM_READWRITE));
+                        /* EMFormatHTMLPrint has registered a special writer
+                         * for headers, try to find it and use it. */
+                        if (g_str_has_suffix (p->uri, ".headers")) {
 
-	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));
+				const EMFormatHandler *handler;
 
-	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));
+				handler = em_format_find_handler (
+                                        emf, "x-evolution/message/headers");
+				if (handler && handler->write_func)
+					handler->write_func (emf, p, stream, info, cancellable);
 
-	g_object_class_install_property (
-		object_class,
-		PROP_HEADERS_STATE,
-		g_param_spec_boolean (
-			"headers-collapsable",
-			NULL,
-			NULL,
-			FALSE,
-			G_PARAM_READWRITE));
+				iter = iter->next;
+				continue;
+			}
 
-	/* 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);
-	}
-}
+                        if (g_str_has_suffix (p->uri, ".rfc822.end"))
+				break;
 
-static void
-efh_init (EMFormatHTML *efh,
-          EMFormatHTMLClass *class)
-{
-	EWebView *web_view;
-	GdkColor *color;
+                        if (g_str_has_suffix (p->uri, ".attachment-bar"))
+				can_write = TRUE;
 
-	efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh);
+			if (can_write && p->write_func) {
+				p->write_func (
+					emf, p, stream, info, cancellable);
+			}
 
-	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);
+			iter = iter->next;
+		}
 
-	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY];
-	gdk_color_parse ("#eeeeee", color);
+	} else {
+		gchar *str;
+		gchar *uri;
 
-	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT];
-	gdk_color_parse ("#ffffff", color);
+		EMFormatHTML *efh = (EMFormatHTML *) emf;
+		EMFormatPURI *p;
+		GList *iter;
 
-	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME];
-	gdk_color_parse ("#3f3f3f", color);
+		iter = g_hash_table_lookup (emf->mail_part_table, puri->uri);
+		if (!iter || !iter->next)
+			return;
 
-	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER];
-	gdk_color_parse ("#eeeeee", color);
+		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);
+	}
 
-	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_TEXT];
-	gdk_color_parse ("#000000", color);
-
-	efh->text_html_flags =
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
-		CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
-		CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
-	efh->show_icon = TRUE;
-	efh->state = EM_FORMAT_HTML_STATE_NONE;
-
-	e_extensible_load_extensions (E_EXTENSIBLE (efh));
-}
-
-GType
-em_format_html_get_type (void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY (type == 0)) {
-		static const GTypeInfo type_info = {
-			sizeof (EMFormatHTMLClass),
-			(GBaseInitFunc) efh_base_init,
-			(GBaseFinalizeFunc) NULL,
-			(GClassInitFunc) efh_class_init,
-			(GClassFinalizeFunc) NULL,
-			NULL,  /* class_data */
-			sizeof (EMFormatHTML),
-			0,     /* n_preallocs */
-			(GInstanceInitFunc) efh_init,
-			NULL   /* value_table */
-		};
-
-		static const GInterfaceInfo extensible_info = {
-			(GInterfaceInitFunc) NULL,
-			(GInterfaceFinalizeFunc) NULL,
-			NULL   /* interface_data */
-		};
-
-		type = g_type_register_static (
-			em_format_get_type(), "EMFormatHTML",
-			&type_info, G_TYPE_FLAG_ABSTRACT);
-
-		g_type_add_interface_static (
-			type, E_TYPE_EXTENSIBLE, &extensible_info);
-	}
-
-	return type;
-}
-
-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,
-                          GdkColor *color)
-{
-	GdkColor *format_color;
-
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-	g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES);
-	g_return_if_fail (color != NULL);
-
-	format_color = &efh->priv->colors[type];
-
-	color->red   = format_color->red;
-	color->green = format_color->green;
-	color->blue  = format_color->blue;
-}
-
-void
-em_format_html_set_color (EMFormatHTML *efh,
-                          EMFormatHTMLColorType type,
-                          const GdkColor *color)
-{
-	GdkColor *format_color;
-	const gchar *property_name;
-
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-	g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES);
-	g_return_if_fail (color != NULL);
-
-	format_color = &efh->priv->colors[type];
-
-	if (gdk_color_equal (color, format_color))
-		return;
-
-	format_color->red   = color->red;
-	format_color->green = color->green;
-	format_color->blue  = color->blue;
-
-	switch (type) {
-		case EM_FORMAT_HTML_COLOR_BODY:
-			property_name = "body-color";
-			break;
-		case EM_FORMAT_HTML_COLOR_CITATION:
-			property_name = "citation-color";
-			break;
-		case EM_FORMAT_HTML_COLOR_CONTENT:
-			property_name = "content-color";
-			break;
-		case EM_FORMAT_HTML_COLOR_FRAME:
-			property_name = "frame-color";
-			break;
-		case EM_FORMAT_HTML_COLOR_HEADER:
-			property_name = "header-color";
-			break;
-		case EM_FORMAT_HTML_COLOR_TEXT:
-			property_name = "text-color";
-			break;
-		default:
-			g_return_if_reached ();
-	}
-
-	g_object_notify (G_OBJECT (efh), property_name);
-}
-
-EMailImageLoadingPolicy
-em_format_html_get_image_loading_policy (EMFormatHTML *efh)
-{
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0);
-
-	return efh->priv->image_loading_policy;
-}
-
-void
-em_format_html_set_image_loading_policy (EMFormatHTML *efh,
-                                         EMailImageLoadingPolicy policy)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
-	if (policy == efh->priv->image_loading_policy)
-		return;
-
-	efh->priv->image_loading_policy = policy;
-
-	g_object_notify (G_OBJECT (efh), "image-loading-policy");
-}
-
-gboolean
-em_format_html_get_mark_citations (EMFormatHTML *efh)
-{
-	guint32 flags;
-
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
-
-	flags = efh->text_html_flags;
-
-	return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0);
-}
-
-void
-em_format_html_set_mark_citations (EMFormatHTML *efh,
-                                   gboolean mark_citations)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
-	if (mark_citations)
-		efh->text_html_flags |=
-			CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
-	else
-		efh->text_html_flags &=
-			~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
-
-	g_object_notify (G_OBJECT (efh), "mark-citations");
-}
-
-gboolean
-em_format_html_get_only_local_photos (EMFormatHTML *efh)
-{
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
-
-	return efh->priv->only_local_photos;
 }
 
-void
-em_format_html_set_only_local_photos (EMFormatHTML *efh,
-                                      gboolean only_local_photos)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
-	efh->priv->only_local_photos = only_local_photos;
-
-	g_object_notify (G_OBJECT (efh), "only-local-photos");
-}
+/*****************************************************************************/
 
-gboolean
-em_format_html_get_show_sender_photo (EMFormatHTML *efh)
-{
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
-
-	return efh->priv->show_sender_photo;
-}
-
-void
-em_format_html_set_show_sender_photo (EMFormatHTML *efh,
-                                      gboolean show_sender_photo)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
-	efh->priv->show_sender_photo = show_sender_photo;
-
-	g_object_notify (G_OBJECT (efh), "show-sender-photo");
-}
+/* 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 },
 
-gboolean
-em_format_html_get_show_real_date (EMFormatHTML *efh)
-{
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
+	/* 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, },
 
-	return efh->priv->show_real_date;
-}
+	/* 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, },
+};
 
-void
-em_format_html_set_show_real_date (EMFormatHTML *efh,
-                                   gboolean show_real_date)
+static void
+efh_builtin_init (EMFormatHTMLClass *efhc)
 {
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
+	EMFormatClass *emfc;
+	gint ii;
 
-	efh->priv->show_real_date = show_real_date;
+	emfc = (EMFormatClass *) efhc;
 
-	g_object_notify (G_OBJECT (efh), "show-real-date");
+	for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
+		em_format_class_add_handler (
+			emfc, &type_builtin_table[ii]);
 }
 
-EMFormatHTMLHeadersState
-em_format_html_get_headers_state (EMFormatHTML *efh)
+static void
+efh_set_property (GObject *object,
+                  guint property_id,
+                  const GValue *value,
+                  GParamSpec *pspec)
 {
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), EM_FORMAT_HTML_HEADERS_STATE_EXPANDED);
-
-	return efh->priv->headers_state;
-}
+	switch (property_id) {
+		case PROP_BODY_COLOR:
+			em_format_html_set_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_BODY,
+				g_value_get_boxed (value));
+			return;
 
-void
-em_format_html_set_headers_state (EMFormatHTML *efh,
-                                  EMFormatHTMLHeadersState state)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
+		case PROP_CITATION_COLOR:
+			em_format_html_set_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_CITATION,
+				g_value_get_boxed (value));
+			return;
 
-	efh->priv->headers_state = state;
+		case PROP_CONTENT_COLOR:
+			em_format_html_set_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_CONTENT,
+				g_value_get_boxed (value));
+			return;
 
-	g_object_notify (G_OBJECT (efh), "headers-state");
-}
+		case PROP_FRAME_COLOR:
+			em_format_html_set_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_FRAME,
+				g_value_get_boxed (value));
+			return;
 
-gboolean
-em_format_html_get_headers_collapsable (EMFormatHTML *efh)
-{
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
+		case PROP_HEADER_COLOR:
+			em_format_html_set_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_HEADER,
+				g_value_get_boxed (value));
+			return;
 
-	return efh->priv->headers_collapsable;
-}
+		case PROP_IMAGE_LOADING_POLICY:
+			em_format_html_set_image_loading_policy (
+				EM_FORMAT_HTML (object),
+				g_value_get_enum (value));
+			return;
 
-void
-em_format_html_set_headers_collapsable (EMFormatHTML *efh,
-                                        gboolean collapsable)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
+		case PROP_MARK_CITATIONS:
+			em_format_html_set_mark_citations (
+				EM_FORMAT_HTML (object),
+				g_value_get_boolean (value));
+			return;
 
-	efh->priv->headers_collapsable = collapsable;
+		case PROP_ONLY_LOCAL_PHOTOS:
+			em_format_html_set_only_local_photos (
+				EM_FORMAT_HTML (object),
+				g_value_get_boolean (value));
+			return;
 
-	g_object_notify (G_OBJECT (efh), "headers-collapsable");
-}
+		case PROP_SHOW_SENDER_PHOTO:
+			em_format_html_set_show_sender_photo (
+				EM_FORMAT_HTML (object),
+				g_value_get_boolean (value));
+			return;
 
-CamelMimePart *
-em_format_html_file_part (EMFormatHTML *efh,
-                          const gchar *mime_type,
-                          const gchar *filename,
-                          GCancellable *cancellable)
-{
-	CamelMimePart *part;
-	CamelStream *stream;
-	CamelDataWrapper *dw;
-	gchar *basename;
+		case PROP_SHOW_REAL_DATE:
+			em_format_html_set_show_real_date (
+				EM_FORMAT_HTML (object),
+				g_value_get_boolean (value));
+			return;
 
-	stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL);
-	if (stream == NULL)
-		return NULL;
+		case PROP_TEXT_COLOR:
+			em_format_html_set_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_TEXT,
+				g_value_get_boxed (value));
+			return;
 
-	dw = camel_data_wrapper_new ();
-	camel_data_wrapper_construct_from_stream_sync (
-		dw, stream, cancellable, NULL);
-	g_object_unref (stream);
-	if (mime_type)
-		camel_data_wrapper_set_mime_type (dw, mime_type);
-	part = camel_mime_part_new ();
-	camel_medium_set_content ((CamelMedium *) part, dw);
-	g_object_unref (dw);
-	basename = g_path_get_basename (filename);
-	camel_mime_part_set_filename (part, basename);
-	g_free (basename);
+		case PROP_ANIMATE_IMAGES:
+			em_format_html_set_animate_images (
+				EM_FORMAT_HTML (object),
+				g_value_get_boolean (value));
+			return;
+	}
 
-	return part;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
-/* 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)
+static void
+efh_get_property (GObject *object,
+                  guint property_id,
+                  GValue *value,
+                  GParamSpec *pspec)
 {
-	EMFormatHTMLPObject *pobj;
+	GdkColor color;
 
-	if (size < sizeof (EMFormatHTMLPObject)) {
-		g_warning ("size is less than the size of EMFormatHTMLPObject\n");
-		size = sizeof (EMFormatHTMLPObject);
-	}
+	switch (property_id) {
+		case PROP_BODY_COLOR:
+			em_format_html_get_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_BODY,
+				&color);
+			g_value_set_boxed (value, &color);
+			return;
 
-	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);
+		case PROP_CITATION_COLOR:
+			em_format_html_get_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_CITATION,
+				&color);
+			g_value_set_boxed (value, &color);
+			return;
 
-	pobj->format = efh;
-	pobj->func = func;
-	pobj->part = part;
+		case PROP_CONTENT_COLOR:
+			em_format_html_get_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_CONTENT,
+				&color);
+			g_value_set_boxed (value, &color);
+			return;
 
-	g_queue_push_tail (&efh->pending_object_list, pobj);
+		case PROP_FRAME_COLOR:
+			em_format_html_get_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_FRAME,
+				&color);
+			g_value_set_boxed (value, &color);
+			return;
 
-	return pobj;
-}
+		case PROP_HEADER_COLOR:
+			em_format_html_get_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_HEADER,
+				&color);
+			g_value_set_boxed (value, &color);
+			return;
 
-EMFormatHTMLPObject *
-em_format_html_find_pobject (EMFormatHTML *emf,
-                             const gchar *classid)
-{
-	GList *link;
+		case PROP_IMAGE_LOADING_POLICY:
+			g_value_set_enum (
+				value,
+				em_format_html_get_image_loading_policy (
+				EM_FORMAT_HTML (object)));
+			return;
 
-	g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL);
-	g_return_val_if_fail (classid != NULL, NULL);
+		case PROP_MARK_CITATIONS:
+			g_value_set_boolean (
+				value, em_format_html_get_mark_citations (
+				EM_FORMAT_HTML (object)));
+			return;
 
-	link = g_queue_peek_head_link (&emf->pending_object_list);
+		case PROP_ONLY_LOCAL_PHOTOS:
+			g_value_set_boolean (
+				value, em_format_html_get_only_local_photos (
+				EM_FORMAT_HTML (object)));
+			return;
 
-	while (link != NULL) {
-		EMFormatHTMLPObject *pw = link->data;
+		case PROP_SHOW_SENDER_PHOTO:
+			g_value_set_boolean (
+				value, em_format_html_get_show_sender_photo (
+				EM_FORMAT_HTML (object)));
+			return;
 
-		if (!strcmp (pw->classid, classid))
-			return pw;
+		case PROP_SHOW_REAL_DATE:
+			g_value_set_boolean (
+				value, em_format_html_get_show_real_date (
+				EM_FORMAT_HTML (object)));
+			return;
 
-		link = g_list_next (link);
+		case PROP_TEXT_COLOR:
+			em_format_html_get_color (
+				EM_FORMAT_HTML (object),
+				EM_FORMAT_HTML_COLOR_TEXT,
+				&color);
+			g_value_set_boxed (value, &color);
+			return;
+		case PROP_ANIMATE_IMAGES:
+			g_value_set_boolean (
+				value, em_format_html_get_animate_images (
+				EM_FORMAT_HTML (object)));
+			return;
 	}
 
-	return NULL;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
-EMFormatHTMLPObject *
-em_format_html_find_pobject_func (EMFormatHTML *emf,
-                                  CamelMimePart *part,
-                                  EMFormatHTMLPObjectFunc func)
+static void
+efh_finalize (GObject *object)
 {
-	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;
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-void
-em_format_html_remove_pobject (EMFormatHTML *emf,
-                               EMFormatHTMLPObject *pobject)
+static void
+efh_write_attachment (EMFormat *emf,
+                                          EMFormatPURI *puri,
+                                          CamelStream *stream,
+                                          EMFormatWriterInfo *info,
+                      GCancellable *cancellable)
 {
-	g_return_if_fail (EM_IS_FORMAT_HTML (emf));
-	g_return_if_fail (pobject != NULL);
+	gchar *text, *html;
+	CamelContentType *ct;
+	gchar *mime_type;
+	const EMFormatHandler *handler;
 
-	g_queue_remove (&emf->pending_object_list, pobject);
+	/* we display all inlined attachments only */
 
-	if (pobject->free != NULL)
-		pobject->free (pobject);
+	/* 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);
 
-	g_free (pobject->classid);
-	g_free (pobject);
-}
+	ct = camel_mime_part_get_content_type (puri->part);
+	mime_type = camel_content_type_simple (ct);
 
-void
-em_format_html_clear_pobject (EMFormatHTML *emf)
-{
-	g_return_if_fail (EM_IS_FORMAT_HTML (emf));
+	/* 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);
 
-	while (!g_queue_is_empty (&emf->pending_object_list)) {
-		EMFormatHTMLPObject *pobj;
+	camel_stream_write_string (
+		stream, "</font></td></tr><tr></table>", cancellable, NULL);
 
-		pobj = g_queue_pop_head (&emf->pending_object_list);
-		em_format_html_remove_pobject (emf, pobj);
+	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);
 	}
-}
-
-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;
+	g_free (mime_type);
 }
 
-void
-em_format_html_job_queue (EMFormatHTML *emfh,
-                          struct _EMFormatHTMLJob *job)
+static void
+efh_preparse (EMFormat *emf)
 {
-	g_mutex_lock (emfh->priv->lock);
-	g_queue_push_tail (&emfh->priv->pending_jobs, job);
-	g_mutex_unlock (emfh->priv->lock);
-}
+	CamelInternetAddress *addr;
 
-/* ********************************************************************** */
+	EMFormatHTML *efh = EM_FORMAT_HTML (emf);
 
-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);
+	if (!emf->message) {
+		efh->priv->can_load_images = FALSE;
+		return;
+	}
+
+	addr = camel_mime_message_get_from (emf->message);
+	efh->priv->can_load_images = em_utils_in_addressbook (addr, FALSE);
 }
 
 static void
-emfh_configure_stream_for_proxy (CamelHttpStream *stream,
-                                 const gchar *uri)
+efh_write_message (EMFormat *emf,
+                   GList *puris,
+                   CamelStream *stream,
+                   EMFormatWriterInfo *info,
+                   GCancellable *cancellable)
 {
-	EProxy *proxy;
-	SoupURI *proxy_uri;
-	gchar *basic;
-	gchar *basic64;
-	const gchar *user = "";
-	const gchar *password = "";
-
-	proxy = em_utils_get_proxy ();
+	GList *iter;
+	EMFormatHTML *efh;
+	gchar *header;
 
-	if (!e_proxy_require_proxy_for_uri (proxy, uri))
-		return;
+	efh = (EMFormatHTML *) emf;
 
-	proxy_uri = e_proxy_peek_uri_for (proxy, uri);
+	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]));
 
-	if (proxy_uri == NULL)
-		return;
+	camel_stream_write_string (stream, header, cancellable, NULL);
+	g_free (header);
 
-	if (proxy_uri->user != NULL)
-		user = proxy_uri->user;
+	if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) {
 
-	if (proxy_uri->password != NULL)
-		password = proxy_uri->password;
+		efh_write_source (emf, emf->mail_part_list->data,
+				  stream, info, cancellable);
 
-	if (*user == '\0' && *password == '\0')
+                camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
 		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);
-}
+	for (iter = puris; iter; iter = iter->next) {
 
-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;
-		}
+		EMFormatPURI *puri = iter->data;
 
-		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);
+		if (!puri)
+			continue;
+
+                /* If current PURI has suffix .rfc822 then iterate through all
+                 * subsequent PURIs until PURI with suffix .rfc822.end is found.
+                 * These skipped PURIs contain entire RFC message which will
+                 * be written in <iframe> as attachment.
+                 */
+                if (g_str_has_suffix (puri->uri, ".rfc822")) {
+
+                        /* If the PURI is not an attachment, then we must
+                         * inline it here otherwise it would not be displayed. */
+			if (!puri->is_attachment && puri->write_func) {
+                                /* efh_write_message_rfc822 starts parsing _after_
+                                 * the passed PURI, so we must give it previous PURI here */
+				EMFormatPURI *p;
+				if (!iter->prev)
+					continue;
 
-	camel_url_free (url);
+				p = iter->prev->data;
+				puri->write_func (emf, p, stream, info, cancellable);
+			}
 
-	if (instream == NULL)
-		goto done;
+                        while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) {
 
-	if (emfh_http_cache != NULL && cistream == NULL)
-		costream = camel_data_cache_add (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+				iter = iter->next;
+				if (iter)
+					puri = iter->data;
 
-	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(".rfc822 - skipping %s\n", puri->uri));
 			}
-			d(printf("  read %d bytes\n", n));
-			if (costream && camel_stream_write (costream, buffer, n, cancellable, NULL) == -1) {
-				n = -1;
+
+                        /* Skip the .rfc822.end PURI as well. */
+			if (!iter)
 				break;
-			}
 
-			camel_stream_write (job->stream, buffer, n, cancellable, NULL);
+			continue;
 		}
-	} 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);
+		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));
+		}
 	}
 
-	g_object_unref (instream);
-done:
-	camel_operation_pop_message (cancellable);
-badurl:
-	g_free (job->u.uri);
+        camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
 }
 
-/* ********************************************************************** */
-
 static void
-efh_url_requested (GtkHTML *html,
-                   const gchar *url,
-                   GtkHTMLStream *handle,
-                   EMFormatHTML *efh)
+efh_write (EMFormat *emf,
+           CamelStream *stream,
+           EMFormatWriterInfo *info,
+           GCancellable *cancellable)
 {
-	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);
-	}
+	efh_write_message (emf, emf->mail_part_list, stream, info, cancellable);
+}
 
-	g_signal_stop_emission_by_name (html, "url-requested");
+static void
+efh_base_init (EMFormatHTMLClass *klass)
+{
+	efh_builtin_init (klass);
 }
 
-static gboolean
-efh_object_requested (GtkHTML *html,
-                      GtkHTMLEmbedded *eb,
-                      EMFormatHTML *efh)
+static void
+efh_class_init (EMFormatHTMLClass *klass)
 {
-	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));
-	}
+	GObjectClass *object_class;
+	EMFormatClass *emf_class;
 
-	return res;
-}
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (EMFormatHTMLPrivate));
 
-/* ********************************************************************** */
-#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") },
-};
+	emf_class = EM_FORMAT_CLASS (klass);
+	emf_class->preparse = efh_preparse;
+	emf_class->write = efh_write;
 
-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") },
-};
+	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;
 
-static const gchar *smime_sign_colour[4] = {
-	"", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\""
-};
+	g_object_class_install_property (
+		object_class,
+		PROP_BODY_COLOR,
+		g_param_spec_boxed (
+			"body-color",
+			"Body Color",
+			NULL,
+			GDK_TYPE_COLOR,
+			G_PARAM_READWRITE));
 
-/* 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_object_class_install_property (
+		object_class,
+		PROP_CITATION_COLOR,
+		g_param_spec_boxed (
+			"citation-color",
+			"Citation Color",
+			NULL,
+			GDK_TYPE_COLOR,
+			G_PARAM_READWRITE));
 
-		g_string_append_printf (
-			buffer,
-			"<table border=0 width=\"100%%\" "
-			"cellpadding=3 cellspacing=0%s><tr>",
-			smime_sign_colour[valid->sign.status]);
+	g_object_class_install_property (
+		object_class,
+		PROP_CONTENT_COLOR,
+		g_param_spec_boxed (
+			"content-color",
+			"Content Color",
+			NULL,
+			GDK_TYPE_COLOR,
+			G_PARAM_READWRITE));
 
-		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);
+	g_object_class_install_property (
+		object_class,
+		PROP_FRAME_COLOR,
+		g_param_spec_boxed (
+			"frame-color",
+			"Frame Color",
+			NULL,
+			GDK_TYPE_COLOR,
+			G_PARAM_READWRITE));
 
-		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);
+	g_object_class_install_property (
+		object_class,
+		PROP_HEADER_COLOR,
+		g_param_spec_boxed (
+			"header-color",
+			"Header Color",
+			NULL,
+			GDK_TYPE_COLOR,
+			G_PARAM_READWRITE));
 
-		if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
-			g_string_append (
-				buffer, _(smime_sign_table[valid->sign.status].shortdesc));
+	/* FIXME Make this a proper enum property. */
+	g_object_class_install_property (
+		object_class,
+		PROP_IMAGE_LOADING_POLICY,
+		g_param_spec_enum (
+			"image-loading-policy",
+			"Image Loading Policy",
+			NULL,
+			E_TYPE_MAIL_IMAGE_LOADING_POLICY,
+			E_MAIL_IMAGE_LOADING_POLICY_ALWAYS,
+			G_PARAM_READWRITE));
 
-			em_format_html_format_cert_infos (
-				&valid->sign.signers, buffer);
-		}
+	g_object_class_install_property (
+		object_class,
+		PROP_MARK_CITATIONS,
+		g_param_spec_boolean (
+			"mark-citations",
+			"Mark Citations",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE));
 
-		if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
-			if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
-				g_string_append (buffer, "<br>");
+	g_object_class_install_property (
+		object_class,
+		PROP_ONLY_LOCAL_PHOTOS,
+		g_param_spec_boolean (
+			"only-local-photos",
+			"Only Local Photos",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
 
-			g_string_append (
-				buffer, _(smime_encrypt_table[valid->encrypt.status].shortdesc));
-		}
+	g_object_class_install_property (
+		object_class,
+		PROP_SHOW_SENDER_PHOTO,
+		g_param_spec_boolean (
+			"show-sender-photo",
+			"Show Sender Photo",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
 
-		g_string_append (buffer, "</td></tr></table>");
+	g_object_class_install_property (
+		object_class,
+		PROP_SHOW_REAL_DATE,
+		g_param_spec_boolean (
+			"show-real-date",
+			"Show real Date header value",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT));
 
-		camel_stream_write (
-			stream, buffer->str,
-			buffer->len, cancellable, NULL);
+	g_object_class_install_property (
+		object_class,
+		PROP_TEXT_COLOR,
+		g_param_spec_boxed (
+			"text-color",
+			"Text Color",
+			NULL,
+			GDK_TYPE_COLOR,
+			G_PARAM_READWRITE));
 
-		g_string_free (buffer, TRUE);
-	}
+	g_object_class_install_property (
+		object_class,
+		PROP_ANIMATE_IMAGES,
+		g_param_spec_boolean (
+                        "animate-images",
+                        "Animate images",
+			NULL,
+			FALSE,
+			G_PARAM_READWRITE));
 }
 
 static void
-efh_text_plain (EMFormat *emf,
-                CamelStream *stream,
-                CamelMimePart *part,
-                const EMFormatHandler *info,
-                GCancellable *cancellable,
-                gboolean is_fallback)
+efh_init (EMFormatHTML *efh,
+          EMFormatHTMLClass *klass)
 {
-	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;
+	GdkColor *color;
 
-	dw = camel_medium_get_content ((CamelMedium *) part);
-	if (!dw)
-		return;
+	efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh);
 
-	/* 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;
+	g_queue_init (&efh->pending_object_list);
 
-	/* This scans the text part for inline-encoded data, creates
-	 * a multipart of all the parts inside it. */
+	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY];
+	gdk_color_parse ("#eeeeee", color);
 
-	/* FIXME: We should discard this multipart if it only contains
-	 * the original text, but it makes this hash lookup more complex */
+	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT];
+	gdk_color_parse ("#ffffff", color);
 
-	/* 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 ... */
+	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME];
+	gdk_color_parse ("#3f3f3f", color);
 
-	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);
-		}
+	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER];
+	gdk_color_parse ("#eeeeee", color);
 
-		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;
-		}
+	color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_TEXT];
+	gdk_color_parse ("#000000", color);
 
-		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);
-		}
+	efh->text_html_flags =
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
+		CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+	efh->show_icon = TRUE;
 
-		g_object_unref (inline_filter);
-		camel_content_type_unref (ct);
-	}
+	e_extensible_load_extensions (E_EXTENSIBLE (efh));
+}
 
-	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);
+GType
+em_format_html_get_type (void)
+{
+	static GType type = 0;
 
-	/* We handle our made-up multipart here, so we don't recursively call ourselves */
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EMFormatHTMLClass),
+			(GBaseInitFunc) efh_base_init,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) efh_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EMFormatHTML),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) efh_init,
+			NULL   /* value_table */
+		};
 
-	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);
+		static const GInterfaceInfo extensible_info = {
+			(GInterfaceInitFunc) NULL,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
 
-		if (!newpart)
-			continue;
+		type = g_type_register_static (
+			em_format_get_type(), "EMFormatHTML",
+			&type_info, G_TYPE_FLAG_ABSTRACT);
 
-		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_type_add_interface_static (
+			type, E_TYPE_EXTENSIBLE, &extensible_info);
 	}
 
-	g_object_unref (filtered_stream);
+	return type;
 }
 
-static void
-efh_text_enriched (EMFormat *emf,
-                   CamelStream *stream,
-                   CamelMimePart *part,
-                   const EMFormatHandler *info,
-                   GCancellable *cancellable,
-                   gboolean is_fallback)
+/*****************************************************************************/
+void
+em_format_html_get_color (EMFormatHTML *efh,
+                          EMFormatHTMLColorType type,
+                          GdkColor *color)
 {
-	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);
+	GdkColor *format_color;
 
-	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);
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
+	g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES);
+	g_return_if_fail (color != NULL);
 
-	em_format_format_text (
-		emf, (CamelStream *) filtered_stream,
-		(CamelDataWrapper *) part, cancellable);
+	format_color = &efh->priv->colors[type];
 
-	g_object_unref (filtered_stream);
-	camel_stream_write_string (stream, "</div>", cancellable, NULL);
+	color->red   = format_color->red;
+	color->green = format_color->green;
+	color->blue  = format_color->blue;
 }
 
-static void
-efh_write_text_html (EMFormat *emf,
-                     CamelStream *stream,
-                     EMFormatPURI *puri,
-                     GCancellable *cancellable)
+void
+em_format_html_set_color (EMFormatHTML *efh,
+                          EMFormatHTMLColorType type,
+                          const GdkColor *color)
 {
-#if d(!)0
-	CamelStream *out;
-	gint fd;
-	CamelDataWrapper *dw;
+	GdkColor *format_color;
+	const gchar *property_name;
 
-	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);
-}
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
+	g_return_if_fail (type < EM_FORMAT_HTML_NUM_COLOR_TYPES);
+	g_return_if_fail (color != NULL);
 
-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;
+	format_color = &efh->priv->colors[type];
 
-	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);
+	if (gdk_color_equal (color, format_color))
+		return;
 
-	/* 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;
+	format_color->red   = color->red;
+	format_color->green = color->green;
+	format_color->blue  = color->blue;
 
-			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);
-		}
+	switch (type) {
+		case EM_FORMAT_HTML_COLOR_BODY:
+			property_name = "body-color";
+			break;
+		case EM_FORMAT_HTML_COLOR_CITATION:
+			property_name = "citation-color";
+			break;
+		case EM_FORMAT_HTML_COLOR_CONTENT:
+			property_name = "content-color";
+			break;
+		case EM_FORMAT_HTML_COLOR_FRAME:
+			property_name = "frame-color";
+			break;
+		case EM_FORMAT_HTML_COLOR_HEADER:
+			property_name = "header-color";
+			break;
+		case EM_FORMAT_HTML_COLOR_TEXT:
+			property_name = "text-color";
+			break;
+		default:
+			g_return_if_reached ();
 	}
 
-	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);
+	g_object_notify (G_OBJECT (efh), property_name);
 }
 
-/* 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)
+EMailImageLoadingPolicy
+em_format_html_get_image_loading_policy (EMFormatHTML *efh)
 {
-	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;
+	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), 0);
 
-		/* 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);
+	return efh->priv->image_loading_policy;
+}
 
-		if (mode && *mode)
-			sprintf(ftype, ";type=%c",  *mode);
-		else
-			ftype[0] = 0;
+void
+em_format_html_set_image_loading_policy (EMFormatHTML *efh,
+                                         EMailImageLoadingPolicy policy)
+{
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
 
-		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;
+	if (policy == efh->priv->image_loading_policy)
+		return;
 
-		name = camel_content_type_param (type, "name");
-		site = camel_content_type_param (type, "site");
-		if (name == NULL)
-			goto fail;
+	efh->priv->image_loading_policy = policy;
 
-		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;
+	g_object_notify (G_OBJECT (efh), "image-loading-policy");
+}
 
-		/* RFC 2017 */
+gboolean
+em_format_html_get_mark_citations (EMFormatHTML *efh)
+{
+	guint32 flags;
 
-		urlparam = camel_content_type_param (type, "url");
-		if (urlparam == NULL)
-			goto fail;
+	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
 
-		/* 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;
+	flags = efh->text_html_flags;
 
-	content = g_strdup_printf ("<a href=\"%s\">%s</a>", url, desc);
-	camel_stream_write_string (stream, content, cancellable, NULL);
-	g_free (content);
+	return ((flags & CAMEL_MIME_FILTER_TOHTML_MARK_CITATION) != 0);
+}
 
-	g_free (url);
-	g_free (desc);
+void
+em_format_html_set_mark_citations (EMFormatHTML *efh,
+                                   gboolean mark_citations)
+{
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
 
-	return;
+	if (mark_citations)
+		efh->text_html_flags |=
+			CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+	else
+		efh->text_html_flags &=
+			~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
 
-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);
+	g_object_notify (G_OBJECT (efh), "mark-citations");
 }
 
-static void
-efh_message_deliverystatus (EMFormat *emf,
-                            CamelStream *stream,
-                            CamelMimePart *part,
-                            const EMFormatHandler *info,
-                            GCancellable *cancellable,
-                            gboolean is_fallback)
+gboolean
+em_format_html_get_only_local_photos (EMFormatHTML *efh)
 {
-	EMFormatHTML *efh = EM_FORMAT_HTML (emf);
-	CamelStream *filtered_stream;
-	CamelMimeFilter *html_filter;
-	guint32 rgb = 0x737373;
-	gchar *content;
+	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
 
-	/* 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);
+	return efh->priv->only_local_photos;
+}
 
-	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);
+void
+em_format_html_set_only_local_photos (EMFormatHTML *efh,
+                                      gboolean only_local_photos)
+{
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
 
-	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);
+	efh->priv->only_local_photos = only_local_photos;
 
-	camel_stream_write_string (stream, "</div>", cancellable, NULL);
+	g_object_notify (G_OBJECT (efh), "only-local-photos");
 }
 
-static void
-emfh_write_related (EMFormat *emf,
-                    CamelStream *stream,
-                    EMFormatPURI *puri,
-                    GCancellable *cancellable)
+gboolean
+em_format_html_get_show_sender_photo (EMFormatHTML *efh)
 {
-	em_format_format_content (emf, stream, puri->part, cancellable);
+	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
 
-	camel_stream_close (stream, cancellable, NULL);
+	return efh->priv->show_sender_photo;
 }
 
-static void
-emfh_multipart_related_check (struct _EMFormatHTMLJob *job,
-                              GCancellable *cancellable)
+void
+em_format_html_set_show_sender_photo (EMFormatHTML *efh,
+                                      gboolean show_sender_photo)
 {
-	EMFormat *format;
-	GList *link;
-	gchar *oldpartid;
-
-	if (g_cancellable_is_cancelled (cancellable))
-		return;
-
-	format = EM_FORMAT (job->format);
-
-	d(printf(" running multipart/related check task\n"));
-	oldpartid = g_strdup (format->part_id->str);
-
-	link = g_queue_peek_head_link (job->puri_level->data);
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
 
-	if (!link) {
-		g_string_printf (format->part_id, "%s", oldpartid);
-		g_free (oldpartid);
-		return;
-	}
+	efh->priv->show_sender_photo = show_sender_photo;
 
-	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);
-			}
-			/* else it was probably added by a previous format this loop */
-		}
+	g_object_notify (G_OBJECT (efh), "show-sender-photo");
+}
 
-		link = g_list_next (link);
-	}
+gboolean
+em_format_html_get_show_real_date (EMFormatHTML *efh)
+{
+	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
 
-	g_string_printf (format->part_id, "%s", oldpartid);
-	g_free (oldpartid);
+	return efh->priv->show_real_date;
 }
 
-/* RFC 2387 */
-static void
-efh_multipart_related (EMFormat *emf,
-                       CamelStream *stream,
-                       CamelMimePart *part,
-                       const EMFormatHandler *info,
-                       GCancellable *cancellable,
-                       gboolean is_fallback)
+void
+em_format_html_set_show_real_date (EMFormatHTML *efh,
+                                   gboolean show_real_date)
 {
-	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;
-	}
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
 
-	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;
+	efh->priv->show_real_date = show_real_date;
 
-		/* strip <>'s */
-		len = strlen (start) - 2;
-		start++;
+	g_object_notify (G_OBJECT (efh), "show-real-date");
+}
 
-		for (i = 0; i < nparts; i++) {
-			body_part = camel_multipart_get_part (mp, i);
-			cid = camel_mime_part_get_content_id (body_part);
+gboolean
+em_format_html_get_animate_images (EMFormatHTML *efh)
+{
+	g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
 
-			if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
-				display_part = body_part;
-				displayid = i;
-				break;
-			}
-		}
-	} else {
-		display_part = camel_multipart_get_part (mp, 0);
-	}
+	return efh->priv->animate_images;
+}
 
-	if (display_part == NULL) {
-		em_format_part_as (
-			emf, stream, part,
-			"multipart/mixed", cancellable);
-		return;
-	}
+void
+em_format_html_set_animate_images (EMFormatHTML *efh,
+                                   gboolean animate_images)
+{
+	g_return_if_fail (EM_IS_FORMAT_HTML (efh));
 
-	em_format_push_level (emf);
+	efh->priv->animate_images = animate_images;
 
-	partidlen = emf->part_id->len;
+        g_object_notify (G_OBJECT (efh), "animate-images");
+}
 
-	/* 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));
-		}
-	}
+CamelMimePart *
+em_format_html_file_part (EMFormatHTML *efh,
+                          const gchar *mime_type,
+                          const gchar *filename,
+                          GCancellable *cancellable)
+{
+	CamelMimePart *part;
+	CamelStream *stream;
+	CamelDataWrapper *dw;
+	gchar *basename;
 
-	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);
+	stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL);
+	if (stream == NULL)
+		return 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);
+	dw = camel_data_wrapper_new ();
+	camel_data_wrapper_construct_from_stream_sync (
+		dw, stream, cancellable, NULL);
+	g_object_unref (stream);
+	if (mime_type)
+		camel_data_wrapper_set_mime_type (dw, mime_type);
+	part = camel_mime_part_new ();
+	camel_medium_set_content ((CamelMedium *) part, dw);
+	g_object_unref (dw);
+	basename = g_path_get_basename (filename);
+	camel_mime_part_set_filename (part, basename);
+	g_free (basename);
 
-	em_format_pull_level (emf);
+	return part;
 }
 
-static void
-efh_write_image (EMFormat *emf,
-                 CamelStream *stream,
-                 EMFormatPURI *puri,
-                 GCancellable *cancellable)
+void
+em_format_html_format_cert_infos (GQueue *cert_infos,
+                                  GString *output_buffer)
 {
-	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
+	GQueue valid = G_QUEUE_INIT;
+	GList *head, *link;
+
+	g_return_if_fail (cert_infos != NULL);
+	g_return_if_fail (output_buffer != NULL);
 
-	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);
-}
+	head = g_queue_peek_head_link (cert_infos);
 
-static void
-efh_image (EMFormat *emf,
-           CamelStream *stream,
-           CamelMimePart *part,
-           const EMFormatHandler *info,
-           GCancellable *cancellable,
-           gboolean is_fallback)
-{
-	EMFormatPURI *puri;
-	gchar *content;
+	/* 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;
 
-	puri = em_format_add_puri (
-		emf, sizeof (EMFormatPURI), NULL, part, efh_write_image);
+		if ((cinfo->name != NULL && *cinfo->name != '\0') ||
+		    (cinfo->email != NULL && *cinfo->email != '\0')) {
+			g_queue_push_tail (&valid, cinfo);
+		}
+	}
 
-	content = g_strdup_printf (
-		"<img hspace=10 vspace=10 src=\"%s\">", puri->cid);
-	camel_stream_write_string (stream, content, cancellable, NULL);
-	g_free (content);
-}
+	if (g_queue_is_empty (&valid))
+		return;
 
-/* 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 },
+	g_string_append (output_buffer, " (");
 
-	/* 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. */
+	while (!g_queue_is_empty (&valid)) {
+		CamelCipherCertInfo *cinfo;
 
-	{ (gchar *) "image/jpg", efh_image },
-	{ (gchar *) "image/pjpeg", efh_image },
+		cinfo = g_queue_pop_head (&valid);
 
-	/* special internal types */
+		if (cinfo->name != NULL && *cinfo->name != '\0') {
+			g_string_append (output_buffer, cinfo->name);
 
-	{ (gchar *) "x-evolution/message/rfc822", efh_format_message }
-};
+			if (cinfo->email != NULL && *cinfo->email != '\0') {
+				g_string_append (output_buffer, " <");
+				g_string_append (output_buffer, cinfo->email);
+				g_string_append (output_buffer, ">");
+			}
 
-static void
-efh_builtin_init (EMFormatHTMLClass *efhc)
-{
-	EMFormatClass *efc;
-	gint ii;
+		} else if (cinfo->email != NULL && *cinfo->email != '\0') {
+			g_string_append (output_buffer, cinfo->email);
+		}
 
-	efc = (EMFormatClass *) efhc;
+		if (!g_queue_is_empty (&valid))
+			g_string_append (output_buffer, ", ");
+	}
 
-	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);
-			}
-			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);
+		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";
 			}
 
-			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, ')');
-}
-
-/* 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;
+        /* 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);
 
-	return camel_data_cache_get (
-		emfh_http_cache, EMFH_HTTP_CACHE_PATH, image_uri, NULL);
+	g_object_unref (loader);
 }
-
diff --git a/mail/em-format-html.h b/mail/em-format-html.h
index bc6a171..9749c37 100644
--- a/mail/em-format-html.h
+++ b/mail/em-format-html.h
@@ -30,7 +30,6 @@
 
 #include <em-format/em-format.h>
 #include <misc/e-web-view.h>
-#include <gtkhtml/gtkhtml-embedded.h>
 #include <libemail-engine/e-mail-enums.h>
 
 /* Standard GObject macros */
@@ -57,6 +56,7 @@ G_BEGIN_DECLS
 typedef struct _EMFormatHTML EMFormatHTML;
 typedef struct _EMFormatHTMLClass EMFormatHTMLClass;
 typedef struct _EMFormatHTMLPrivate EMFormatHTMLPrivate;
+typedef struct _EMFormatWidgetPURI EMFormatWidgetPURI;
 
 enum _em_format_html_header_flags {
 	EM_FORMAT_HTML_HEADER_TO = 1 << 0,
@@ -65,16 +65,6 @@ enum _em_format_html_header_flags {
 };
 
 typedef enum {
-	EM_FORMAT_HTML_STATE_NONE = 0,
-	EM_FORMAT_HTML_STATE_RENDERING
-} EMFormatHTMLState;
-
-typedef enum {
-	EM_FORMAT_HTML_HEADERS_STATE_EXPANDED = 0, /* Default value */
-	EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED
-} EMFormatHTMLHeadersState;
-
-typedef enum {
 	EM_FORMAT_HTML_COLOR_BODY,	/* header area background */
 	EM_FORMAT_HTML_COLOR_CITATION,	/* citation font color */
 	EM_FORMAT_HTML_COLOR_CONTENT,	/* message area background */
@@ -84,94 +74,13 @@ typedef enum {
 	EM_FORMAT_HTML_NUM_COLOR_TYPES
 } EMFormatHTMLColorType;
 
-/* A HTMLJob will be executed in another thread, in sequence.
- * It's job is to write to its stream, close it if successful,
- * then exit. */
-
-typedef struct _EMFormatHTMLJob EMFormatHTMLJob;
-
-typedef void	(*EMFormatHTMLJobCallback)	(EMFormatHTMLJob *job,
-						 GCancellable *cancellable);
-
-/**
- * struct _EMFormatHTMLJob - A formatting job.
- *
- * @format: Set by allocation function.
- * @stream: Free for use by caller.
- * @puri_level: Set by allocation function.
- * @base: Set by allocation function, used to save state.
- * @callback: This callback will always be invoked, only once, even if the user
- * cancelled the display.  So the callback should free any extra data
- * it allocated every time it is called.
- * @u: Union data, free for caller to use.
- *
- * This object is used to queue a long-running-task which cannot be
- * processed in the primary thread.  When its turn comes, the job will
- * be de-queued and the @callback invoked to perform its processing,
- * restoring various state to match the original state.  This is used
- * for image loading and other internal tasks.
- *
- * This object is struct-subclassable.  Only em_format_html_job_new()
- * may be used to allocate these.
- **/
-struct _EMFormatHTMLJob {
-	EMFormatHTML *format;
-	CamelStream *stream;
-
-	/* We need to track the state of the visibility tree at
-	 * the point this uri was generated */
-	GNode *puri_level;
-	CamelURL *base;
-
-	EMFormatHTMLJobCallback callback;
-	union {
-		gchar *uri;
-		CamelMedium *msg;
-		EMFormatPURI *puri;
-		GNode *puri_level;
-		gpointer data;
-	} u;
-};
-
-/* Pending object (classid: url) */
-typedef struct _EMFormatHTMLPObject EMFormatHTMLPObject;
-
-typedef gboolean
-		(*EMFormatHTMLPObjectFunc)	(EMFormatHTML *md,
-						 GtkHTMLEmbedded *eb,
-						 EMFormatHTMLPObject *pobject);
-
-/**
- * struct _EMFormatHTMLPObject - Pending object.
- *
- * @free: Invoked when the object is no longer needed.
- * @format: The parent formatter.
- * @classid: The assigned class id as passed to add_pobject().
- * @func: Callback function.
- * @part: The part as passed to add_pobject().
- *
- * This structure is used to track OBJECT tags which have been
- * inserted into the HTML stream.  When GtkHTML requests them the
- * @func will be invoked to create the embedded widget.
- *
- * This object is struct-subclassable.  Only
- * em_format_html_add_pobject() may be used to allocate these.
- **/
-struct _EMFormatHTMLPObject {
-	void (*free)(EMFormatHTMLPObject *);
-	EMFormatHTML *format;
-
-	gchar *classid;
-
-	EMFormatHTMLPObjectFunc func;
-	CamelMimePart *part;
-};
-
 #define EM_FORMAT_HTML_HEADER_NOCOLUMNS (EM_FORMAT_HEADER_LAST)
 
 /* header already in html format */
 #define EM_FORMAT_HTML_HEADER_HTML (EM_FORMAT_HEADER_LAST<<1)
 #define EM_FORMAT_HTML_HEADER_NODEC (EM_FORMAT_HEADER_LAST<<2)
+#define EM_FORMAT_HTML_HEADER_NOLINKS (EM_FORMAT_HEADER_LAST<<3)
+#define EM_FORMAT_HTML_HEADER_HIDDEN (EM_FORMAT_HEADER_LAST<<4)
 
 #define EM_FORMAT_HTML_HEADER_LAST (EM_FORMAT_HEADER_LAST<<8)
 
@@ -197,14 +106,13 @@ struct _EMFormatHTMLPObject {
  * @load_http:2:
  * @load_http_now:1:
  * @mark_citations:1:
- * @simple_headers:1:
  * @hide_headers:1:
  * @show_icon:1:
  *
  * Most of these fields are private or read-only.
  *
  * The base HTML formatter object.  This object drives HTML generation
- * into a GtkHTML parser.  It also handles text to HTML conversion,
+ * into a WebKit parser.  It also handles text to HTML conversion,
  * multipart/related objects and inline images.
  **/
 struct _EMFormatHTML {
@@ -216,12 +124,9 @@ struct _EMFormatHTML {
 	GSList *headers;
 
 	guint32 text_html_flags; /* default flags for text to html conversion */
-	guint simple_headers:1; /* simple header format, no box/table */
 	guint hide_headers:1; /* no headers at all */
 	guint show_icon:1; /* show an icon when the sender used Evo */
 	guint32 header_wrap_flags;
-
-	EMFormatHTMLState state; /* actual state of the object */
 };
 
 struct _EMFormatHTMLClass {
@@ -231,8 +136,6 @@ struct _EMFormatHTMLClass {
 };
 
 GType		em_format_html_get_type		(void);
-EWebView *	em_format_html_get_web_view	(EMFormatHTML *efh);
-void		em_format_html_load_images	(EMFormatHTML *efh);
 void		em_format_html_get_color	(EMFormatHTML *efh,
 						 EMFormatHTMLColorType type,
 						 GdkColor *color);
@@ -260,65 +163,61 @@ gboolean	em_format_html_get_show_sender_photo
 void		em_format_html_set_show_sender_photo
 						(EMFormatHTML *efh,
 						 gboolean show_sender_photo);
-
-/* retrieves a pseudo-part icon wrapper for a file */
-CamelMimePart *	em_format_html_file_part	(EMFormatHTML *efh,
-						 const gchar *mime_type,
-						 const gchar *filename,
-						 GCancellable *cancellable);
-
-/* for implementers */
-EMFormatHTMLPObject *
-		em_format_html_add_pobject	(EMFormatHTML *efh,
-						 gsize size,
-						 const gchar *classid,
-						 CamelMimePart *part,
-						 EMFormatHTMLPObjectFunc func);
-EMFormatHTMLPObject *
-		em_format_html_find_pobject	(EMFormatHTML *efh,
-						 const gchar *classid);
-EMFormatHTMLPObject *
-		em_format_html_find_pobject_func
-						(EMFormatHTML *efh,
-						 CamelMimePart *part,
-						 EMFormatHTMLPObjectFunc func);
-void		em_format_html_remove_pobject	(EMFormatHTML *efh,
-						 EMFormatHTMLPObject *pobject);
-void		em_format_html_clear_pobject	(EMFormatHTML *efh);
-EMFormatHTMLJob *
-		em_format_html_job_new		(EMFormatHTML *efh,
-						 EMFormatHTMLJobCallback callback,
-						 gpointer data);
-void		em_format_html_job_queue	(EMFormatHTML *efh,
-						 EMFormatHTMLJob *job);
+gboolean        em_format_html_get_animate_images
+                                                (EMFormatHTML *efh);
+void            em_format_html_set_animate_images
+                                                (EMFormatHTML *efh,
+                                                 gboolean animate_images);
 void		em_format_html_clone_sync	(CamelFolder *folder,
 						 const gchar *message_uid,
 						 CamelMimeMessage *message,
 						 EMFormatHTML *efh,
 						 EMFormat *source);
-
 gboolean	em_format_html_get_show_real_date
 						(EMFormatHTML *efh);
 void		em_format_html_set_show_real_date
 						(EMFormatHTML *efh,
 						 gboolean show_real_date);
-EMFormatHTMLHeadersState
-		em_format_html_get_headers_state
-						(EMFormatHTML *efh);
-void		em_format_html_set_headers_state
-						(EMFormatHTML *efh,
-						 EMFormatHTMLHeadersState state);
-gboolean	em_format_html_get_headers_collapsable
-						(EMFormatHTML *efh);
-void		em_format_html_set_headers_collapsable
-						(EMFormatHTML *efh,
-						 gboolean collapsable);
+
+/* retrieves a pseudo-part icon wrapper for a file */
+CamelMimePart *	em_format_html_file_part	(EMFormatHTML *efh,
+						 const gchar *mime_type,
+						 const gchar *filename,
+						 GCancellable *cancellable);
+
 void		em_format_html_format_cert_infos
 						(GQueue *cert_infos,
 						 GString *output_buffer);
 
-CamelStream *	em_format_html_get_cached_image	(EMFormatHTML *efh,
-						 const gchar *image_uri);
+void		em_format_html_format_message	(EMFormatHTML *efh,
+						 CamelStream *stream,
+						 GCancellable *cancellable);
+
+void		em_format_html_format_message_part
+						(EMFormatHTML *efh,
+						 const gchar *part_id,
+						 CamelStream *stream,
+						 GCancellable *cancellable);
+
+void		em_format_html_format_headers	(EMFormatHTML *efh,
+						 CamelStream *stream,
+						 CamelMedium *part,
+						 gboolean all_headers,
+						 GCancellable *cancellable);
+void		em_format_html_format_header	(EMFormat *emf,
+						 GString *buffer,
+						 CamelMedium *part,
+						 struct _camel_header_raw *header,
+						 guint32 flags,
+						 const gchar *charset);
+
+gboolean        em_format_html_can_load_images  (EMFormatHTML *efh);
+
+void            em_format_html_animation_extract_frame
+                                                (const GByteArray *anim,
+                                                 gchar **frame,
+                                                 gsize *len);
+
 G_END_DECLS
 
 #endif /* EM_FORMAT_HTML_H */
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 4d74b80..a9b5712 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -72,9 +72,11 @@
 
 #include "e-mail-tag-editor.h"
 #include "em-composer-utils.h"
-#include "em-format-quote.h"
+#include "em-format-html-display.h"
 #include "em-format-html-print.h"
 #include "em-utils.h"
+#include "e-mail-printer.h"
+#include "em-format/em-format-quote.h"
 
 /* XXX This is a dirty hack on a dirty hack.  We really need
  *     to rework or get rid of the functions that use this. */
@@ -380,7 +382,7 @@ em_utils_flag_for_followup (EMailReader *reader,
 	EMailBackend *backend;
 	EShellSettings *shell_settings;
 	EShellBackend *shell_backend;
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	GtkWidget *editor;
 	GtkWindow *window;
 	CamelTag *tags;
@@ -470,8 +472,8 @@ em_utils_flag_for_followup (EMailReader *reader,
 	camel_folder_thaw (folder);
 	camel_tag_list_free (&tags);
 
-	formatter = e_mail_reader_get_formatter (reader);
-	em_format_queue_redraw (EM_FORMAT (formatter));
+	display = e_mail_reader_get_mail_display (reader);
+	e_mail_display_reload (display);
 
 exit:
 	/* XXX We shouldn't be freeing this. */
@@ -616,26 +618,44 @@ em_utils_write_messages_to_stream (CamelFolder *folder,
 	return res;
 }
 
+static void
+do_print_msg_to_file (GObject *source,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+
+	EMFormatHTML *efh = EM_FORMAT_HTML (source);
+	gchar *filename = user_data;
+
+	EMailPrinter *printer;
+
+	printer = e_mail_printer_new (efh);
+	e_mail_printer_set_export_filename (printer, filename);
+	g_signal_connect_swapped (printer, "done",
+		G_CALLBACK (g_object_unref), printer);
+
+	e_mail_printer_print (printer, TRUE, NULL);
+
+	g_object_unref (efh);
+}
+
 static gboolean
 em_utils_print_messages_to_file (CamelFolder *folder,
                                  const gchar *uid,
                                  const gchar *filename)
 {
-	EMFormatHTMLPrint *efhp;
+	EMFormatHTMLDisplay *efhd;
 	CamelMimeMessage *message;
 
 	message = camel_folder_get_message_sync (folder, uid, NULL, NULL);
 	if (message == NULL)
 		return FALSE;
 
-	efhp = em_format_html_print_new (NULL, GTK_PRINT_OPERATION_ACTION_EXPORT);
-	efhp->export_filename = g_strdup (filename);
-	efhp->async = FALSE;
+	efhd = em_format_html_display_new ();
+	((EMFormat *) efhd)->message_uid = g_strdup (uid);
 
-	em_format_html_print_message (efhp, message, folder, uid);
-
-	g_object_unref (efhp);
-	g_object_unref (message);
+	em_format_parse_async ((EMFormat *) efhd, message, folder, NULL,
+		(GAsyncReadyCallback) do_print_msg_to_file, g_strdup (filename));
 
 	return TRUE;
 }
@@ -1173,7 +1193,7 @@ em_utils_message_to_html (CamelMimeMessage *message,
 	camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (mem), buf);
 
 	emfq = em_format_quote_new (credits, mem, flags);
-	((EMFormat *) emfq)->composer = TRUE;
+	em_format_set_composer ((EMFormat *) emfq, TRUE);
 
 	if (!source) {
 		GSettings *settings;
@@ -1189,10 +1209,30 @@ em_utils_message_to_html (CamelMimeMessage *message,
 	}
 
 	/* FIXME Not passing a GCancellable here. */
-	em_format_format_clone (
-		EM_FORMAT (emfq), NULL, NULL, message, source, NULL);
-	if (validity_found)
-		*validity_found = ((EMFormat *)emfq)->validity_found;
+	em_format_parse (EM_FORMAT (emfq), message, NULL, NULL);
+
+	if (validity_found) {
+		GList *iter;
+		EMFormat *emf = (EMFormat *) emfq;
+
+		if (validity_found)
+			*validity_found = 0;
+
+		/* Return all found validities */
+		for (iter = emf->mail_part_list; iter; iter = iter->next) {
+
+			EMFormatPURI *puri = iter->data;
+			if (!puri)
+				continue;
+
+			if (*validity_found && puri->validity_type)
+				*validity_found |= puri->validity_type;
+		}
+
+	}
+
+	em_format_quote_write (emfq, mem, NULL);
+
 	g_object_unref (emfq);
 
 	if (append && *append)
diff --git a/mail/mail.error.xml b/mail/mail.error.xml
index 2a516d9..b5a714f 100644
--- a/mail/mail.error.xml
+++ b/mail/mail.error.xml
@@ -520,5 +520,10 @@ An mbox account will be created to preserve the old mbox folders. You can delete
     <_secondary xml:space="preserve">The attachment named {0} is a hidden file and may contain sensitive data. Please review it before sending.</_secondary>
   </error>
 
+  <error id="printing-failed" type="error">
+    <_primary>Printing failed.</_primary>
+    <_secondary>The printer replied &quot;{0}&quot;.</_secondary>
+  </error>
+
 </error-list>
 
diff --git a/modules/Makefile.am b/modules/Makefile.am
index ee3cdfd..671215b 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -35,6 +35,7 @@ SUBDIRS = \
 	plugin-manager \
 	spamassassin \
 	startup-wizard \
+	web-inspector \
 	$(MONO_DIR) \
 	$(PYTHON_DIR) \
 	$(ONLINE_ACCOUNTS_DIR) \
diff --git a/modules/addressbook/e-book-shell-content.c b/modules/addressbook/e-book-shell-content.c
index 0183512..f3457e8 100644
--- a/modules/addressbook/e-book-shell-content.c
+++ b/modules/addressbook/e-book-shell-content.c
@@ -286,20 +286,11 @@ book_shell_content_constructed (GObject *object)
 		EAB_CONTACT_DISPLAY (widget),
 		EAB_CONTACT_DISPLAY_RENDER_NORMAL);
 
-	eab_contact_display_set_orientation (
-		EAB_CONTACT_DISPLAY (widget),
-		priv->orientation);
-
 	eab_contact_display_set_show_maps (
 		EAB_CONTACT_DISPLAY (widget),
 		priv->preview_show_maps);
 
 	g_object_bind_property (
-		object, "orientation",
-		widget, "orientation",
-		G_BINDING_SYNC_CREATE);
-
-	g_object_bind_property (
 		object, "preview-show-maps",
 		widget, "show-maps",
 		G_BINDING_SYNC_CREATE);
diff --git a/modules/mail/e-mail-config-format-html.c b/modules/mail/e-mail-config-format-html.c
index 31ca88b..dad5f9a 100644
--- a/modules/mail/e-mail-config-format-html.c
+++ b/modules/mail/e-mail-config-format-html.c
@@ -76,6 +76,11 @@ mail_config_format_html_constructed (GObject *object)
 		extensible, "show-real-date",
 		G_BINDING_SYNC_CREATE);
 
+	g_object_bind_property (
+		shell_settings, "mail-show-animated-images",
+		extensible, "animate-images",
+		G_BINDING_SYNC_CREATE);
+
 	/* Chain up to parent's constructed() method. */
 	G_OBJECT_CLASS (parent_class)->constructed (object);
 }
diff --git a/modules/mail/e-mail-config-web-view.c b/modules/mail/e-mail-config-web-view.c
index 80f3f65..813256e 100644
--- a/modules/mail/e-mail-config-web-view.c
+++ b/modules/mail/e-mail-config-web-view.c
@@ -151,12 +151,6 @@ mail_config_web_view_realize (GtkWidget *widget,
 {
 	g_object_bind_property (
 		extension->shell_settings,
-		"mail-show-animated-images",
-		widget, "animate",
-		G_BINDING_SYNC_CREATE);
-
-	g_object_bind_property (
-		extension->shell_settings,
 		"composer-inline-spelling",
 		widget, "inline-spelling",
 		G_BINDING_SYNC_CREATE);
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index 25902f3..a30c024 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -835,37 +835,70 @@ e_mail_labels_get_filter_options (void)
 	return g_slist_reverse (list);
 }
 
+static void
+message_parsed_cb (GObject *source_object,
+                   GAsyncResult *res,
+                   gpointer user_data)
+{
+	EMFormatHTML *formatter = EM_FORMAT_HTML (source_object);
+	GObject *preview = user_data;
+	EMailDisplay *display;
+
+        display = g_object_get_data (preview, "mbox-imp-display");
+	e_mail_display_set_formatter (display, formatter);
+	e_mail_display_load (display, EM_FORMAT (formatter)->uri_base);
+}
+
 /* utility functions for mbox importer */
 static void
 mbox_create_preview_cb (GObject *preview,
                         GtkWidget **preview_widget)
 {
-	EMFormatHTMLDisplay *format;
-	EWebView *web_view;
+	EMailDisplay *display;
 
 	g_return_if_fail (preview != NULL);
 	g_return_if_fail (preview_widget != NULL);
 
-	format = em_format_html_display_new ();
-	g_object_set_data_full (
-		preview, "mbox-imp-formatter", format, g_object_unref);
-	web_view = em_format_html_get_web_view (EM_FORMAT_HTML (format));
+	display = g_object_new (E_TYPE_MAIL_DISPLAY, NULL);
+        g_object_set_data_full (preview, "mbox-imp-display",
+				g_object_ref (display), g_object_unref);
 
-	*preview_widget = GTK_WIDGET (web_view);
+        *preview_widget = GTK_WIDGET (display);
 }
 
 static void
 mbox_fill_preview_cb (GObject *preview,
                       CamelMimeMessage *msg)
 {
-	EMFormatHTMLDisplay *format;
+	EMailDisplay *display;
+	EMFormat *formatter;
+	GHashTable *formatters;
+	SoupSession *session;
+	gchar *mail_uri;
 
 	g_return_if_fail (preview != NULL);
 	g_return_if_fail (msg != NULL);
 
-	format = g_object_get_data (preview, "mbox-imp-formatter");
-	g_return_if_fail (format != NULL);
+	display = g_object_get_data (preview, "mbox-imp-display");
+	g_return_if_fail (display != NULL);
+
+	session = webkit_get_default_session ();
+        formatters = g_object_get_data (G_OBJECT (session), "formatters");
+	if (!formatters) {
+		formatters = g_hash_table_new_full (g_str_hash, g_str_equal,
+			(GDestroyNotify) g_free, NULL);
+                g_object_set_data (G_OBJECT (session), "formatters", formatters);
+	}
+
+	mail_uri = em_format_build_mail_uri (NULL, msg->message_id, NULL, NULL);
+
+	formatter = EM_FORMAT (em_format_html_display_new ());
+	formatter->message_uid = g_strdup (msg->message_id);
+	formatter->uri_base = g_strdup (mail_uri);
+
+        /* Don't free the mail_uri!! */
+	g_hash_table_insert (formatters, mail_uri, formatter);
 
-	/* FIXME Not passing a GCancellable here. */
-	em_format_format (EM_FORMAT (format), NULL, NULL, msg, NULL);
+	em_format_parse_async (formatter, msg, NULL, NULL,
+			       message_parsed_cb, preview);
 }
diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c
index 5bb60e3..1bf6d4e 100644
--- a/modules/mail/e-mail-shell-content.c
+++ b/modules/mail/e-mail-shell-content.c
@@ -307,8 +307,8 @@ mail_shell_content_get_backend (EMailReader *reader)
 	return e_mail_reader_get_backend (reader);
 }
 
-static EMFormatHTML *
-mail_shell_content_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_shell_content_get_mail_display (EMailReader *reader)
 {
 	EMailShellContent *mail_shell_content;
 
@@ -317,8 +317,7 @@ mail_shell_content_get_formatter (EMailReader *reader)
 	/* Forward this to our internal EMailView, which
 	 * also implements the EMailReader interface. */
 	reader = E_MAIL_READER (mail_shell_content->priv->mail_view);
-
-	return e_mail_reader_get_formatter (reader);
+	return e_mail_reader_get_mail_display (reader);
 }
 
 static gboolean
@@ -464,7 +463,7 @@ e_mail_shell_content_reader_init (EMailReaderInterface *interface)
 {
 	interface->get_action_group = mail_shell_content_get_action_group;
 	interface->get_backend = mail_shell_content_get_backend;
-	interface->get_formatter = mail_shell_content_get_formatter;
+	interface->get_mail_display = mail_shell_content_get_mail_display;
 	interface->get_hide_deleted = mail_shell_content_get_hide_deleted;
 	interface->get_message_list = mail_shell_content_get_message_list;
 	interface->get_popup_menu = mail_shell_content_get_popup_menu;
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index b39d22d..5487920 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -891,14 +891,16 @@ action_mail_smart_backward_cb (GtkAction *action,
 	EMailShellContent *mail_shell_content;
 	EMailShellSidebar *mail_shell_sidebar;
 	EMFolderTree *folder_tree;
-	EMFormatHTML *formatter;
 	EMailReader *reader;
 	EMailView *mail_view;
 	GtkWidget *message_list;
 	GtkToggleAction *toggle_action;
-	EWebView *web_view;
+	GtkWidget *window;
+	GtkAdjustment *adj;
+	EMailDisplay *display;
 	gboolean caret_mode;
 	gboolean magic_spacebar;
+	gdouble value;
 
 	/* This implements the so-called "Magic Backspace". */
 
@@ -914,7 +916,7 @@ action_mail_smart_backward_cb (GtkAction *action,
 	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
 
 	reader = E_MAIL_READER (mail_view);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
 	magic_spacebar = e_shell_settings_get_boolean (
@@ -923,32 +925,43 @@ action_mail_smart_backward_cb (GtkAction *action,
 	toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE));
 	caret_mode = gtk_toggle_action_get_active (toggle_action);
 
-	web_view = em_format_html_get_web_view (formatter);
-
-	if (e_web_view_scroll_backward (web_view))
+	window = gtk_widget_get_parent (GTK_WIDGET (display));
+	if (!GTK_IS_SCROLLED_WINDOW (window))
 		return;
 
-	if (caret_mode || !magic_spacebar)
-		return;
+	adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (window));
+	value = gtk_adjustment_get_value (adj);
+	if (value == 0) {
 
-	/* XXX Are two separate calls really necessary? */
+		if (caret_mode || !magic_spacebar)
+			return;
 
-	if (message_list_select (
-		MESSAGE_LIST (message_list),
-		MESSAGE_LIST_SELECT_PREVIOUS,
-		0, CAMEL_MESSAGE_SEEN))
-		return;
+                /* XXX Are two separate calls really necessary? */
 
-	if (message_list_select (
-		MESSAGE_LIST (message_list),
-		MESSAGE_LIST_SELECT_PREVIOUS |
-		MESSAGE_LIST_SELECT_WRAP, 0,
-		CAMEL_MESSAGE_SEEN))
-		return;
+		if (message_list_select (
+		    MESSAGE_LIST (message_list),
+		    MESSAGE_LIST_SELECT_PREVIOUS,
+		    0, CAMEL_MESSAGE_SEEN))
+			return;
+
+		if (message_list_select (
+		    MESSAGE_LIST (message_list),
+		    MESSAGE_LIST_SELECT_PREVIOUS |
+		    MESSAGE_LIST_SELECT_WRAP,
+		    0, CAMEL_MESSAGE_SEEN))
+			return;
+
+		em_folder_tree_select_next_path (folder_tree, TRUE);
 
-	em_folder_tree_select_prev_path (folder_tree, TRUE);
+		gtk_widget_grab_focus (message_list);
 
-	gtk_widget_grab_focus (message_list);
+	} else {
+
+		gtk_adjustment_set_value (adj,
+			value - gtk_adjustment_get_page_increment (adj));
+
+		return;
+	}
 }
 
 static void
@@ -962,14 +975,17 @@ action_mail_smart_forward_cb (GtkAction *action,
 	EMailShellContent *mail_shell_content;
 	EMailShellSidebar *mail_shell_sidebar;
 	EMFolderTree *folder_tree;
-	EMFormatHTML *formatter;
 	EMailReader *reader;
 	EMailView *mail_view;
 	GtkWidget *message_list;
+	GtkWidget *window;
+	GtkAdjustment *adj;
 	GtkToggleAction *toggle_action;
-	EWebView *web_view;
+	EMailDisplay *display;
 	gboolean caret_mode;
 	gboolean magic_spacebar;
+	gdouble value;
+	gdouble upper;
 
 	/* This implements the so-called "Magic Spacebar". */
 
@@ -985,7 +1001,7 @@ action_mail_smart_forward_cb (GtkAction *action,
 	folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
 
 	reader = E_MAIL_READER (mail_view);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
 	magic_spacebar = e_shell_settings_get_boolean (
@@ -994,32 +1010,44 @@ action_mail_smart_forward_cb (GtkAction *action,
 	toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE));
 	caret_mode = gtk_toggle_action_get_active (toggle_action);
 
-	web_view = em_format_html_get_web_view (formatter);
-
-	if (e_web_view_scroll_forward (web_view))
+	window = gtk_widget_get_parent (GTK_WIDGET (display));
+	if (!GTK_IS_SCROLLED_WINDOW (window))
 		return;
 
-	if (caret_mode || !magic_spacebar)
-		return;
+	adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (window));
+	value = gtk_adjustment_get_value (adj);
+	upper = gtk_adjustment_get_upper (adj);
+	if (value + gtk_adjustment_get_page_size (adj) >= upper) {
 
-	/* XXX Are two separate calls really necessary? */
+		if (caret_mode || !magic_spacebar)
+			return;
 
-	if (message_list_select (
-		MESSAGE_LIST (message_list),
-		MESSAGE_LIST_SELECT_NEXT,
-		0, CAMEL_MESSAGE_SEEN))
-		return;
+                /* XXX Are two separate calls really necessary? */
 
-	if (message_list_select (
-		MESSAGE_LIST (message_list),
-		MESSAGE_LIST_SELECT_NEXT |
-		MESSAGE_LIST_SELECT_WRAP,
-		0, CAMEL_MESSAGE_SEEN))
-		return;
+		if (message_list_select (
+		    MESSAGE_LIST (message_list),
+		    MESSAGE_LIST_SELECT_NEXT,
+		    0, CAMEL_MESSAGE_SEEN))
+			return;
+
+		if (message_list_select (
+		    MESSAGE_LIST (message_list),
+		    MESSAGE_LIST_SELECT_NEXT |
+		    MESSAGE_LIST_SELECT_WRAP,
+		    0, CAMEL_MESSAGE_SEEN))
+			return;
+
+		em_folder_tree_select_next_path (folder_tree, TRUE);
 
-	em_folder_tree_select_next_path (folder_tree, TRUE);
+		gtk_widget_grab_focus (message_list);
 
-	gtk_widget_grab_focus (message_list);
+	} else {
+
+		gtk_adjustment_set_value (adj,
+			value + gtk_adjustment_get_page_increment (adj));
+
+		return;
+	}
 }
 
 static void
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index 8838f1a..9a58f9d 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -331,11 +331,10 @@ mail_shell_view_popup_event_cb (EMailShellView *mail_shell_view,
                                 const gchar *uri)
 {
 	EMailShellContent *mail_shell_content;
-	EMFormatHTML *formatter;
+	EMailDisplay *display;
 	EShellView *shell_view;
 	EMailReader *reader;
 	EMailView *mail_view;
-	EWebView *web_view;
 	GtkMenu *menu;
 
 	if (uri != NULL)
@@ -345,10 +344,9 @@ mail_shell_view_popup_event_cb (EMailShellView *mail_shell_view,
 	mail_view = e_mail_shell_content_get_mail_view (mail_shell_content);
 
 	reader = E_MAIL_READER (mail_view);
-	formatter = e_mail_reader_get_formatter (reader);
-	web_view = em_format_html_get_web_view (formatter);
+	display = e_mail_reader_get_mail_display (reader);
 
-	if (e_web_view_get_cursor_image (web_view) != NULL)
+	if (e_web_view_get_cursor_image (E_WEB_VIEW (display)) != NULL)
 		return FALSE;
 
 	menu = e_mail_reader_get_popup_menu (reader);
@@ -368,76 +366,19 @@ mail_shell_view_popup_event_cb (EMailShellView *mail_shell_view,
 }
 
 static void
-mail_shell_view_scroll_cb (EMailShellView *mail_shell_view,
-                           GtkOrientation orientation,
-                           GtkScrollType scroll_type,
-                           gfloat position,
-                           GtkHTML *html)
-{
-	EShell *shell;
-	EShellView *shell_view;
-	EShellWindow *shell_window;
-	EShellSettings *shell_settings;
-	EMailShellContent *mail_shell_content;
-	EMailReader *reader;
-	EMailView *mail_view;
-	EWebView *web_view;
-	GtkWidget *message_list;
-	gboolean magic_spacebar;
-
-	web_view = E_WEB_VIEW (html);
-
-	if (html->binding_handled || e_web_view_get_caret_mode (web_view))
-		return;
-
-	if (orientation != GTK_ORIENTATION_VERTICAL)
-		return;
-
-	shell_view = E_SHELL_VIEW (mail_shell_view);
-	shell_window = e_shell_view_get_shell_window (shell_view);
-	shell = e_shell_window_get_shell (shell_window);
-	shell_settings = e_shell_get_shell_settings (shell);
-
-	magic_spacebar = e_shell_settings_get_boolean (
-		shell_settings, "mail-magic-spacebar");
-
-	if (!magic_spacebar)
-		return;
-
-	mail_shell_content = mail_shell_view->priv->mail_shell_content;
-	mail_view = e_mail_shell_content_get_mail_view (mail_shell_content);
-
-	reader = E_MAIL_READER (mail_view);
-	message_list = e_mail_reader_get_message_list (reader);
-
-	if (scroll_type == GTK_SCROLL_PAGE_FORWARD)
-		message_list_select (
-			MESSAGE_LIST (message_list),
-			MESSAGE_LIST_SELECT_NEXT,
-			0, CAMEL_MESSAGE_SEEN);
-	else
-		message_list_select (
-			MESSAGE_LIST (message_list),
-			MESSAGE_LIST_SELECT_PREVIOUS,
-			0, CAMEL_MESSAGE_SEEN);
-}
-
-static void
 mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
                                    EMailReader *reader)
 {
 	GtkWidget *message_list;
-	EMFormatHTML *formatter;
-	EWebView *web_view;
+	EMailDisplay *display;
 	EShellView *shell_view;
 	EShellTaskbar *shell_taskbar;
 
 	shell_view = E_SHELL_VIEW (mail_shell_view);
 	shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
 
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
-	web_view = em_format_html_get_web_view (formatter);
 
 	e_shell_view_update_actions (E_SHELL_VIEW (mail_shell_view));
 	e_mail_shell_view_update_sidebar (mail_shell_view);
@@ -464,23 +405,17 @@ mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
 		mail_shell_view, G_CONNECT_SWAPPED);
 
 	g_signal_connect_object (
-		web_view, "key-press-event",
+		display, "key-press-event",
 		G_CALLBACK (mail_shell_view_key_press_event_cb),
 		mail_shell_view, G_CONNECT_SWAPPED);
 
 	g_signal_connect_object (
-		web_view, "popup-event",
+		display, "popup-event",
 		G_CALLBACK (mail_shell_view_popup_event_cb),
 		mail_shell_view, G_CONNECT_SWAPPED);
 
 	g_signal_connect_object (
-		web_view, "scroll",
-		G_CALLBACK (mail_shell_view_scroll_cb),
-		mail_shell_view,
-		G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-
-	g_signal_connect_object (
-		web_view, "status-message",
+		display, "status-message",
 		G_CALLBACK (e_shell_taskbar_set_message),
 		shell_taskbar, G_CONNECT_SWAPPED);
 }
@@ -634,7 +569,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	EShellTaskbar *shell_taskbar;
 	EShellWindow *shell_window;
 	EShellSearchbar *searchbar;
-	EMFormatHTML *formatter;
 	EMFolderTree *folder_tree;
 	EActionComboBox *combo_box;
 	ERuleContext *context;
@@ -647,7 +581,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	EMailSession *session;
 	EMailReader *reader;
 	EMailView *mail_view;
-	EWebView *web_view;
+	EMailDisplay *display;
 	const gchar *source;
 	guint merge_id;
 	gint ii = 0;
@@ -691,7 +625,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
 
 	reader = E_MAIL_READER (shell_content);
-	formatter = e_mail_reader_get_formatter (reader);
+	display = e_mail_reader_get_mail_display (reader);
 	message_list = e_mail_reader_get_message_list (reader);
 
 	em_folder_tree_set_selectable_widget (folder_tree, message_list);
@@ -710,8 +644,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 		G_CALLBACK (mail_shell_view_search_filter_changed_cb),
 		mail_shell_view, G_CONNECT_SWAPPED);
 
-	web_view = em_format_html_get_web_view (formatter);
-
 	g_signal_connect_object (
 		folder_tree, "folder-selected",
 		G_CALLBACK (mail_shell_view_folder_tree_selected_cb),
@@ -784,23 +716,17 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 		mail_shell_view, G_CONNECT_SWAPPED);
 
 	g_signal_connect_object (
-		web_view, "key-press-event",
+		display, "key-press-event",
 		G_CALLBACK (mail_shell_view_key_press_event_cb),
 		mail_shell_view, G_CONNECT_SWAPPED);
 
 	g_signal_connect_object (
-		web_view, "popup-event",
+		display, "popup-event",
 		G_CALLBACK (mail_shell_view_popup_event_cb),
 		mail_shell_view, G_CONNECT_SWAPPED);
 
 	g_signal_connect_object (
-		web_view, "scroll",
-		G_CALLBACK (mail_shell_view_scroll_cb),
-		mail_shell_view,
-		G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-
-	g_signal_connect_object (
-		web_view, "status-message",
+		display, "status-message",
 		G_CALLBACK (e_shell_taskbar_set_message),
 		shell_taskbar, G_CONNECT_SWAPPED);
 
diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c
index 3f5e371..d81656f 100644
--- a/modules/mail/em-mailer-prefs.c
+++ b/modules/mail/em-mailer-prefs.c
@@ -529,10 +529,10 @@ toggle_button_init (EMMailerPrefs *prefs,
                     const gchar *key,
                     GCallback toggled)
 {
-	gboolean bool;
+	gboolean v_bool;
 
-	bool = g_settings_get_boolean (prefs->settings, key);
-	gtk_toggle_button_set_active (toggle, not ? !bool : bool);
+	v_bool = g_settings_get_boolean (prefs->settings, key);
+	gtk_toggle_button_set_active (toggle, not ? !v_bool : v_bool);
 
 	if (toggled) {
 		g_object_set_data ((GObject *) toggle, "key", (gpointer) key);
diff --git a/modules/web-inspector/Makefile.am b/modules/web-inspector/Makefile.am
new file mode 100644
index 0000000..f8a1fc9
--- /dev/null
+++ b/modules/web-inspector/Makefile.am
@@ -0,0 +1,23 @@
+module_LTLIBRARIES = libevolution-module-web-inspector.la
+
+libevolution_module_web_inspector_la_CPPFLAGS =			\
+	$(AM_CPPFLAGS)						\
+	-I$(top_srcdir)						\
+	-I$(top_srcdir)/widgets					\
+	-DG_LOG_DOMAIN=\"evolution-web-inspector\"		\
+	$(EVOLUTION_DATA_SERVER_CFLAGS)				\
+	$(GNOME_PLATFORM_CFLAGS)
+
+libevolution_module_web_inspector_la_SOURCES =			\
+	evolution-web-inspector.c
+
+libevolution_module_web_inspector_la_LIBADD =			\
+	$(top_builddir)/e-util/libeutil.la			\
+	$(top_builddir)/widgets/misc/libemiscwidgets.la		\
+	$(EVOLUTION_DATA_SERVER_LIBS)				\
+	$(GNOME_PLATFORM_LIBS)
+
+libevolution_module_web_inspector_la_LDFLAGS =			\
+	-module -avoid-version $(NO_UNDEFINED)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/web-inspector/evolution-web-inspector.c b/modules/web-inspector/evolution-web-inspector.c
new file mode 100644
index 0000000..27a21e0
--- /dev/null
+++ b/modules/web-inspector/evolution-web-inspector.c
@@ -0,0 +1,185 @@
+/*
+ * evolution-web-inspector.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <misc/e-web-view.h>
+#include <libebackend/e-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEB_INSPECTOR \
+	(e_web_inspector_get_type ())
+#define E_WEB_INSPECTOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_WEB_INSPECTOR, EWebInspector))
+
+typedef struct _EWebInspector EWebInspector;
+typedef struct _EWebInspectorClass EWebInspectorClass;
+
+struct _EWebInspector {
+	EExtension parent;
+};
+
+struct _EWebInspectorClass {
+	EExtensionClass parent_class;
+};
+
+static const gchar *ui =
+"<ui>"
+"  <popup name='context'>"
+"    <placeholder name='inspect-menu' >"
+"      <menuitem action='inspect'/>"
+"    </placeholder>"
+"  </popup>"
+"</ui>";
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_web_inspector_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (EWebInspector, e_web_inspector, E_TYPE_EXTENSION)
+
+static EWebView *
+web_inspector_get_web_view (EWebInspector *extension)
+{
+	EExtensible *extensible;
+
+	extensible = e_extension_get_extensible (E_EXTENSION (extension));
+
+	return E_WEB_VIEW (extensible);
+}
+
+static void
+web_inspector_action_inspect_cb (GtkAction *action,
+                                 EWebInspector *extension)
+{
+	WebKitWebInspector *inspector;
+	EWebView *web_view;
+
+	web_view = web_inspector_get_web_view (extension);
+	inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (web_view));
+
+	webkit_web_inspector_show (inspector);
+}
+
+static GtkActionEntry inspect_entries[] = {
+
+	{ "inspect",
+	  NULL,
+	  N_("_Inspect..."),
+	  NULL,
+	  N_("Inspect the HTML content (debugging feature)"),
+	  G_CALLBACK (web_inspector_action_inspect_cb) }
+};
+
+static WebKitWebView *
+web_inspector_inspect_web_view_cb (WebKitWebInspector *inspector,
+                                   EWebInspector *extension)
+{
+	GtkWidget *web_view;
+	GtkWidget *window;
+	const gchar *title;
+
+	title = _("Evolution Web Inspector");
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title (GTK_WINDOW (window), title);
+	gtk_widget_set_size_request (window, 600, 400);
+	gtk_widget_show (window);
+
+	web_view = webkit_web_view_new ();
+	gtk_container_add (GTK_CONTAINER (window), web_view);
+	gtk_widget_show (web_view);
+
+	return WEBKIT_WEB_VIEW (web_view);
+}
+
+static void
+web_inspector_constructed (GObject *object)
+{
+	EWebInspector *extension;
+	WebKitWebSettings *settings;
+	WebKitWebInspector *inspector;
+	GtkActionGroup *action_group;
+	GtkUIManager *ui_manager;
+	EWebView *web_view;
+	GError *error = NULL;
+
+	extension = E_WEB_INSPECTOR (object);
+	web_view = web_inspector_get_web_view (extension);
+
+	ui_manager = e_web_view_get_ui_manager (web_view);
+	action_group = e_web_view_get_action_group (web_view, "standard");
+
+	settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view));
+	g_object_set (settings, "enable-developer-extras", TRUE, NULL);
+
+	inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (web_view));
+
+	g_signal_connect (
+		inspector, "inspect-web-view",
+		G_CALLBACK (web_inspector_inspect_web_view_cb), extension);
+
+	gtk_action_group_add_actions (
+		action_group, inspect_entries,
+		G_N_ELEMENTS (inspect_entries), extension);
+
+	/* Because we are loading from a hard-coded string, there is
+	 * no chance of I/O errors.  Failure here implies a malformed
+	 * UI definition.  Full stop. */
+	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+	if (error != NULL)
+		g_error ("%s", error->message);
+}
+
+static void
+e_web_inspector_class_init (EWebInspectorClass *class)
+{
+	GObjectClass *object_class;
+	EExtensionClass *extension_class;
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->constructed = web_inspector_constructed;
+
+	extension_class = E_EXTENSION_CLASS (class);
+	extension_class->extensible_type = E_TYPE_WEB_VIEW;
+}
+
+static void
+e_web_inspector_class_finalize (EWebInspectorClass *class)
+{
+}
+
+static void
+e_web_inspector_init (EWebInspector *extension)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+	e_web_inspector_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]