[evolution/webkit: 13/196] Split functionality of EMFormat to "parse" and "write"



commit a7def251c8367014eae78c962df63b25a76fd729
Author: Dan VrÃtil <dvratil redhat com>
Date:   Fri Aug 26 15:49:46 2011 +0200

    Split functionality of EMFormat to "parse" and "write"
    
    EMFormat is now able to traverse throught the CamelMimeMessage and
    assign a EMFormatWriteFunc to each mime part. The mime part is stored
    as EMFormatPURI struct, containing various metadata including the
    write function, part ID and ptr to the part itself. Parts are stored
    in mail_part_list (GList which determines the order in which parts will
    be displayed) and mail_part_table (GHashTable which allows fast lookup
    of parts by their ID).
    
    When a content of particular part is requested, user can just find
    the part by it's ID in mail_part_table and call the write_func which
    will convert the part to HTML code ready to be displayed.
    
    Alternatively, when a part represents an iCal event or anything else
    that would be displayed as a GtkWidget, the widget_func is assigned
    instead of write_func.
    
    I tried to get rid of the way of passing informations between
    parser functions through publicly accessible properties of EMFormat,
    rather introducing EMFormatParserInfo structure passing as a parameter
    to all EMFormatParserFunc. I think it's much cleaner.
    
    The API of EMFormat is not final and most will be changed and polished
    in future.
    
    EMFormatQuote is only updated to build against the EMFormat, but most
    probably does not work as it should.

 em-format/em-format-quote.c |  344 +++--
 em-format/em-format.c       | 3591 ++++++++++++++++++++-----------------------
 em-format/em-format.h       |  535 +++----
 3 files changed, 2029 insertions(+), 2441 deletions(-)
---
diff --git a/em-format/em-format-quote.c b/em-format/em-format-quote.c
index c3f75ec..46e11de 100644
--- a/em-format/em-format-quote.c
+++ b/em-format/em-format-quote.c
@@ -39,27 +39,78 @@
 
 struct _EMFormatQuotePrivate {
 	gchar *credits;
-	CamelStream *stream;
 	EMFormatQuoteFlags flags;
 	guint32 text_html_flags;
 };
 
 static void emfq_builtin_init (EMFormatQuoteClass *efhc);
 
+static CamelMimePart * decode_inline_parts (CamelMimePart *part, GCancellable *cancellable);
+
+static void emfq_write_text_plain	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable);
+static void emfq_write_text_enriched	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable);
+static void emfq_write_text_html	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable);
+static void emfq_write_message_rfc822	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable);
+static void emfq_write_message_prefix	(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable);
+/* FIXME WEBKIT */
+static void emfq_write_source		(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable);
+
 static gpointer parent_class;
 
-static void
-emfq_dispose (GObject *object)
+/* 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)
 {
-	EMFormatQuotePrivate *priv;
+	CamelMultipart *mp;
+	CamelStream *null;
+	CamelStream *filtered_stream;
+	EMInlineFilter *inline_filter;
 
-	priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
+	g_return_val_if_fail (part != NULL, NULL);
 
-	if (priv->stream != NULL) {
-		g_object_unref (priv->stream);
-		priv->stream = NULL;
+	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);
+
+	if (!em_inline_filter_found_any (inline_filter)) {
+		g_object_unref (inline_filter);
+		return NULL;
 	}
 
+	mp = em_inline_filter_get_multipart (inline_filter);
+
+	g_object_unref (inline_filter);
+
+	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 part;
+}
+
+
+
+static void
+emfq_dispose (GObject *object)
+{
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -78,66 +129,73 @@ emfq_finalize (GObject *object)
 }
 
 static void
-emfq_format_clone (EMFormat *emf,
-                   CamelFolder *folder,
-                   const gchar *uid,
-                   CamelMimeMessage *msg,
-                   EMFormat *src,
-                   GCancellable *cancellable)
+emfq_parse (EMFormat *emf,
+	    CamelMimeMessage *msg,
+	    CamelFolder *folder,
+	    GCancellable *cancellable)
+{
+	EM_FORMAT_CLASS (parent_class)->parse (
+			emf, msg, folder, cancellable);
+}
+
+
+static void
+emfq_write (EMFormat *emf,
+	    CamelStream *stream,
+            GCancellable *cancellable)
 {
-	EMFormatQuote *emfq = (EMFormatQuote *) emf;
 	const EMFormatHandler *handle;
 	GSettings *settings;
 
 	/* Chain up to parent's format_clone() method. */
-	EM_FORMAT_CLASS (parent_class)->format_clone (
-		emf, folder, uid, msg, src, cancellable);
+	/* FIXME WEBKIT
+	EM_FORMAT_CLASS (parent_class)->write (
+		emf, stream, cancellable);
+	*/
 
 	g_seekable_seek (
-		G_SEEKABLE (emfq->priv->stream),
+		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 (
-			emfq->priv->stream, "<br>\n", cancellable, NULL);
+			stream, "<br>\n", cancellable, NULL);
 	g_object_unref (settings);
+/* FIXME WEBKIT
 	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->write_func (
+			emf, emf->mail_part_tree->data, stream, cancellable);
+
 	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);
-
+		handle->write_func (
+			emf, emf->mail_part_tree->data, stream, cancellable);
+*/
 	g_signal_emit_by_name(emf, "complete");
 }
 
 static void
 emfq_format_error (EMFormat *emf,
-                   CamelStream *stream,
                    const gchar *errmsg)
 {
 	/* Nothing to do. */
 }
 
 static void
-emfq_format_source (EMFormat *emf,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    GCancellable *cancellable)
+emfq_write_source (EMFormat *emf,
+		   EMFormatPURI *puri,
+		   CamelStream *stream,
+                   GCancellable *cancellable)
 {
 	CamelStream *filtered_stream;
 	CamelMimeFilter *html_filter;
 
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
 	filtered_stream = camel_stream_filter_new (stream);
 	html_filter = camel_mime_filter_tohtml_new (
 		CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
@@ -149,23 +207,34 @@ emfq_format_source (EMFormat *emf,
 
 	em_format_format_text (
 		emf, filtered_stream,
-		CAMEL_DATA_WRAPPER (part), cancellable);
+		CAMEL_DATA_WRAPPER (puri), cancellable);
 
 	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)
+emfq_write_attachment (EMFormat *emf,
+		       EMFormatPURI *puri,
+                       CamelStream *stream,
+                       GCancellable *cancellable)
 {
 	EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
+	const EMFormatHandler *handler;
 	gchar *text, *html;
+	CamelContentType *ct;
+	const gchar *mime_type;
 
-	if (!em_format_is_inline (emf, emf->part_id->str, part, handle))
+	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";
+	}
+
+	handler = em_format_find_handler (emf, mime_type);
+
+	if (!em_format_is_inline (emf, puri->uri, puri->part, handler))
 		return;
 
 	camel_stream_write_string (
@@ -173,7 +242,7 @@ emfq_format_attachment (EMFormat *emf,
 		"<tr><td><font size=-1>\n", cancellable, NULL);
 
 	/* output some info about it */
-	text = em_format_describe_part (part, mime_type);
+	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);
@@ -184,7 +253,8 @@ emfq_format_attachment (EMFormat *emf,
 	camel_stream_write_string (
 		stream, "</font></td></tr></table>", cancellable, NULL);
 
-	handle->handler (emf, stream, part, handle, cancellable, FALSE);
+	if (handler && handler->write_func)
+		handler->write_func (emf, puri, stream, cancellable);
 }
 
 static void
@@ -207,10 +277,11 @@ emfq_class_init (EMFormatQuoteClass *class)
 	object_class->finalize = emfq_finalize;
 
 	format_class = EM_FORMAT_CLASS (class);
-	format_class->format_clone = emfq_format_clone;
+	format_class->parse = emfq_parse;
+	/* FIXME WEBKIT
+	format_class->write = emfq_write;
+	*/
 	format_class->format_error = emfq_format_error;
-	format_class->format_source = emfq_format_source;
-	format_class->format_attachment = emfq_format_attachment;
 }
 
 static void
@@ -266,7 +337,6 @@ em_format_quote_new (const gchar *credits,
 	emfq = g_object_new (EM_TYPE_FORMAT_QUOTE, NULL);
 
 	emfq->priv->credits = g_strdup (credits);
-	emfq->priv->stream = g_object_ref (stream);
 	emfq->priv->flags = flags;
 
 	return emfq;
@@ -444,8 +514,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;
@@ -522,12 +592,10 @@ emfq_format_headers (EMFormatQuote *emfq,
 }
 
 static void
-emfq_format_message_prefix (EMFormat *emf,
-                            CamelStream *stream,
-                            CamelMimePart *part,
-                            const EMFormatHandler *info,
-                            GCancellable *cancellable,
-                            gboolean is_fallback)
+emfq_write_message_prefix (EMFormat *emf,
+			   EMFormatPURI *puri,
+			   CamelStream *stream,
+			   GCancellable *cancellable)
 {
 	EMFormatQuote *emfq = (EMFormatQuote *) emf;
 
@@ -540,12 +608,10 @@ emfq_format_message_prefix (EMFormat *emf,
 }
 
 static void
-emfq_format_message (EMFormat *emf,
-                     CamelStream *stream,
-                     CamelMimePart *part,
-                     const EMFormatHandler *info,
-                     GCancellable *cancellable,
-                     gboolean is_fallback)
+emfq_write_message_rfc822 (EMFormat *emf,
+                     	   EMFormatPURI *puri,
+                     	   CamelStream *stream,
+                     	   GCancellable *cancellable)
 {
 	EMFormatQuote *emfq = (EMFormatQuote *) emf;
 	GString *buffer;
@@ -559,84 +625,35 @@ emfq_format_message (EMFormat *emf,
 			"key=\"orig\" value=\"1\">-->\n"
 			"<blockquote type=cite>\n");
 
-	if (((CamelMimePart *) emf->message) != part) {
+	if (((CamelMimePart *) emf->message) != puri->part) {
 		g_string_append_printf (
 			buffer,
 			"%s</br>\n",
 			_("-------- Forwarded Message --------"));
-		emfq_format_headers (emfq, buffer, (CamelMedium *) part);
+		emfq_format_headers (emfq, buffer, (CamelMedium *) puri->part);
 	} else if (emfq->priv->flags & EM_FORMAT_QUOTE_HEADERS)
-		emfq_format_headers (emfq, buffer, (CamelMedium *) part);
+		emfq_format_headers (emfq, buffer, (CamelMedium *) puri->part);
 
 	camel_stream_write (
 		stream, buffer->str, buffer->len, cancellable, NULL);
 
-	em_format_part (emf, stream, part, cancellable);
+	puri->write_func (emf, puri, stream, cancellable);
 
 	if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
 		camel_stream_write_string (
 			stream, "</blockquote><!--+GtkHTML:"
 			"<DATA class=\"ClueFlow\" clear=\"orig\">-->",
 			cancellable, NULL);
-}
 
-/* 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;
-	EMInlineFilter *inline_filter;
-
-	g_return_val_if_fail (part != NULL, NULL);
-
-	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);
-
-	if (!em_inline_filter_found_any (inline_filter)) {
-		g_object_unref (inline_filter);
-		return NULL;
-	}
-
-	mp = em_inline_filter_get_multipart (inline_filter);
-
-	g_object_unref (inline_filter);
-
-	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 part;
+	g_string_free (buffer, TRUE);
 }
 
+
 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,
+					   GCancellable *cancellable)
 {
 	EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
 	CamelStream *filtered_stream;
@@ -647,10 +664,11 @@ emfq_text_plain (EMFormat *emf,
 	const gchar *format;
 	guint32 rgb = 0x737373, flags;
 
-	if (!part)
+	if (!puri->part)
 		return;
 
-	mp = decode_inline_parts (part, cancellable);
+	/* WEBKIT FIXME: This should be already pre-parsed
+	mp = decode_inline_parts (emp->part, cancellable);
 	if (mp) {
 		if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) {
 			em_format_part (emf, stream, mp, cancellable);
@@ -661,11 +679,12 @@ emfq_text_plain (EMFormat *emf,
 
 		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 +706,31 @@ 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,
+                    	  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 +749,16 @@ 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,
+	              GCancellable *cancellable)
 {
 	EMFormatQuotePrivate *priv;
 
@@ -757,40 +780,31 @@ 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", 0, emfq_write_text_plain, },
+	{ (gchar *) "text/enriched", 0, emfq_write_text_enriched, },
+	{ (gchar *) "text/richtext", 0, emfq_write_text_enriched, },
+	{ (gchar *) "text/html", 0, emfq_write_text_html, },
+	{ (gchar *) "text/*", 0, 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/rfc822", 0, emfq_write_message_rfc822, },
+	{ (gchar *) "x-evolution/message/prefix", 0, emfq_write_message_prefix, },
+	{ (gchar *) "x-evolution/message/attachment", 0, emfq_write_attachment, },
 };
 
 static void
@@ -798,7 +812,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.c b/em-format/em-format.c
index 8482435..45b5f4f 100644
--- a/em-format/em-format.c
+++ b/em-format/em-format.c
@@ -25,9 +25,7 @@
 #include <config.h>
 #endif
 
-#include <stdio.h>
 #include <string.h>
-
 #include <gio/gio.h>
 #include <glib/gi18n-lib.h>
 
@@ -42,978 +40,1062 @@
 
 #define d(x)
 
-typedef struct _EMFormatCache EMFormatCache;
+struct _EMFormatPrivate {
+	GNode *current_node;
 
-/* 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 maintian 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 */
+	CamelSession *session;
 
-	guint state:2;		/* inline state */
+	CamelURL *base_url;
 
-	gchar partid[1];
+	gchar *charset;
+	gchar *default_charset;
+	gboolean composer;
 };
 
-#define INLINE_UNSET (0)
-#define INLINE_ON (1)
-#define INLINE_OFF (2)
-
-static void emf_builtin_init (EMFormatClass *);
-
 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];
+
+/* 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_message_rfc822		(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_source			(EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+
+/* WRITERS */
+static void emf_write_message_rfc822		(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable) {};
+static void emf_write_text			(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable) {};
+static void emf_write_source			(EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, GCancellable *cancellable) {}
+
+static void emf_error				(EMFormat *emf, const gchar *message) {};
+static void emf_source				(EMFormat *emf, CamelStream *stream, GCancellable *cancellable);
+
+/**************************************************************************/
 
 static void
-emf_free_cache (EMFormatCache *efc)
+preserve_charset_in_content_type (CamelMimePart *ipart,
+                                  CamelMimePart *opart)
 {
-	if (efc->valid)
-		camel_cipher_validity_free (efc->valid);
-	if (efc->secured)
-		g_object_unref (efc->secured);
-	g_free (efc);
-}
+	CamelDataWrapper *data_wrapper;
+	CamelContentType *content_type;
+	const gchar *charset;
 
-static EMFormatCache *
-emf_insert_cache (EMFormat *emf,
-                  const gchar *partid)
-{
-	EMFormatCache *new;
+	g_return_if_fail (ipart != NULL);
+	g_return_if_fail (opart != NULL);
 
-	new = g_malloc0 (sizeof (*new) + strlen (partid));
-	strcpy (new->partid, partid);
-	g_hash_table_insert (emf->inline_table, new->partid, new);
+	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
+	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
 
-	return new;
-}
+	if (content_type == NULL)
+		return;
 
-static void
-emf_clone_inlines (gpointer key,
-                   gpointer val,
-                   gpointer data)
-{
-	EMFormatCache *emfc = val, *new;
-
-	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));
-}
+	charset = camel_content_type_param (content_type, "charset");
 
-static gboolean
-emf_clear_puri_node (GNode *node)
-{
-	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);
-	}
+	if (charset == NULL || *charset == '\0')
+		return;
 
-	g_queue_free (queue);
+	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
+	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
 
-	return FALSE;
+	camel_content_type_set_param (content_type, "charset", charset);
 }
 
-static void
-emf_finalize (GObject *object)
+static CamelMimePart *
+get_related_display_part (CamelMimePart *part,
+			  			  gint *out_displayid)
 {
-	EMFormat *emf = EM_FORMAT (object);
+	CamelMultipart *mp;
+	CamelMimePart *body_part, *display_part = NULL;
+	CamelContentType *content_type;
+	const gchar *start;
+	gint i, nparts, displayid = 0;
 
-	if (emf->session)
-		g_object_unref (emf->session);
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	if (emf->message)
-		g_object_unref (emf->message);
+	if (!CAMEL_IS_MULTIPART (mp))
+		return NULL;
 
-	if (emf->folder)
-		g_object_unref (emf->folder);
+	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;
 
-	g_hash_table_destroy (emf->inline_table);
+		/* strip <>'s from CID */
+		len = strlen (start) - 2;
+		start++;
 
-	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 (emf->pending_uri_table != NULL)
-		g_hash_table_destroy (emf->pending_uri_table);
-
-	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);
+		for (i = 0; i < nparts; i++) {
+			body_part = camel_multipart_get_part (mp, i);
+			cid = camel_mime_part_get_content_id (body_part);
+
+			if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
+				display_part = body_part;
+				displayid = i;
+				break;
+			}
+		}
+	} else {
+		display_part = camel_multipart_get_part (mp, 0);
 	}
 
-	/* FIXME: check pending jobs */
+	if (out_displayid)
+		*out_displayid = displayid;
 
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (parent_class)->finalize (object);
+	return display_part;
 }
 
-static const EMFormatHandler *
-emf_find_handler (EMFormat *emf,
-                  const gchar *mime_type)
+static gboolean
+related_display_part_is_attachment (EMFormat *emf,
+				    				CamelMimePart *part)
 {
-	EMFormatClass *emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf);
+	CamelMimePart *display_part;
 
-	return g_hash_table_lookup (emfc->type_handlers, mime_type);
+	display_part = get_related_display_part (part, NULL);
+	return display_part && em_format_is_attachment (emf, display_part);
+}
+
+/**************************************************************************/
+void
+em_format_empty_parser (EMFormat *emf,
+						CamelMimePart *part,
+						GString *part_id,
+						EMFormatParserInfo *info,
+						GCancellable *cancellable)
+{
+	/* DO NOTHING */
 }
 
+#ifdef ENABLE_SMIME
 static void
-emf_format_clone (EMFormat *emf,
-                  CamelFolder *folder,
-                  const gchar *uid,
-                  CamelMimeMessage *msg,
-                  EMFormat *emfsource,
-                  GCancellable *cancellable)
+emf_parse_application_xpkcs7mime (EMFormat *emf,
+				  				  CamelMimePart *part,
+								  GString *part_id,
+								  EMFormatParserInfo *info,
+								  GCancellable *cancellable)
 {
-	em_format_clear_puri_tree (emf);
-
-	if (emf != emfsource) {
-		g_hash_table_remove_all (emf->inline_table);
-		if (emfsource) {
-			GList *link;
-
-			/* 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);
-
-			em_format_clear_headers (emf);
-
-			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);
-			}
-		}
-	}
+	CamelCipherContext *context;
+	CamelMimePart *opart;
+	CamelCipherValidity *valid;
+	GError *local_error = NULL;
 
-	/* 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 (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	if (uid != emf->uid) {
-		g_free (emf->uid);
-		emf->uid = g_strdup (uid);
-	}
+	context = camel_smime_context_new (emf->priv->session);
 
-	if (msg != emf->message) {
-		if (emf->message)
-			g_object_unref (emf->message);
-		if (msg)
-			g_object_ref (msg);
-		emf->message = msg;
+	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);
+
+		em_format_parse_part_as (emf, part, part_id, info, NULL, cancellable);
+	} else {
+		EMFormatParserInfo encinfo = {
+				info->handler,
+				EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_SMIME,
+				camel_cipher_validity_clone (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);
 	}
 
-	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)
-		g_string_append_printf(emf->part_id, ".%p", (gpointer) folder);
-	if (uid != NULL)
-		g_string_append_printf(emf->part_id, ".%s", uid);
+	g_object_unref (opart);
+	g_object_unref (context);
 }
+#endif
 
+/* RFC 4155 */
 static void
-emf_format_secure (EMFormat *emf,
-                   CamelStream *stream,
-                   CamelMimePart *part,
-                   CamelCipherValidity *valid,
-                   GCancellable *cancellable)
+emf_parse_application_mbox (EMFormat *emf,
+                      	    CamelMimePart *mime_part,
+                      	    GString *part_id,
+                      	    EMFormatParserInfo *info,
+                      	    GCancellable *cancellable)
 {
-	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. */
+	CamelMimeParser *parser;
+	CamelStream *mem_stream;
+	camel_mime_parser_state_t state;
+	gint old_len;
+	gint messages;
 
-	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);
-	}
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	emf->valid_parent = valid;
+	/* Extract messages from the application/mbox part and
+	 * render them as a flat list of messages. */
 
-	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);
+	/* 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. */
 
-	emf->valid_parent = save;
-}
+	/* 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? */
 
-static gboolean
-emf_busy (EMFormat *emf)
-{
-	return FALSE;
-}
+	parser = camel_mime_parser_new ();
+	camel_mime_parser_scan_from (parser, TRUE);
 
-static gboolean
-emf_is_inline (EMFormat *emf,
-               const gchar *part_id,
-               CamelMimePart *mime_part,
-               const EMFormatHandler *handle)
-{
-	EMFormatCache *emfc;
-	const gchar *disposition;
+	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);
 
-	if (handle == NULL)
-		return FALSE;
+	old_len = part_id->len;
 
-	emfc = g_hash_table_lookup (emf->inline_table, part_id);
-	if (emfc && emfc->state != INLINE_UNSET)
-		return emfc->state & 1;
+	/* Extract messages from the mbox. */
+	messages = 0;
+	state = camel_mime_parser_step (parser, NULL, NULL);
 
-	/* Some types need to override the disposition.
-	 * e.g. application/x-pkcs7-mime */
-	if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION)
-		return TRUE;
+	em_format_push_level (emf);
+	while (state == CAMEL_MIME_PARSER_STATE_FROM) {
+		CamelMimeMessage *message;
 
-	disposition = camel_mime_part_get_disposition (mime_part);
-	if (disposition != NULL)
-		return g_ascii_strcasecmp (disposition, "inline") == 0;
+		message = camel_mime_message_new ();
+		mime_part = CAMEL_MIME_PART (message);
 
-	/* Otherwise, use the default for this handler type. */
-	return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0;
-}
+		if (!camel_mime_part_construct_from_parser_sync (
+			mime_part, parser, NULL, NULL)) {
+			g_object_unref (message);
+			break;
+		}
 
-static void
-emf_base_init (EMFormatClass *class)
-{
-	class->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
-	emf_builtin_init (class);
-}
+		g_string_append_printf (part_id, ".mbox.%d", messages);
+		em_format_parse_part (emf, CAMEL_MIME_PART (message),
+				part_id, info, cancellable);
+		g_string_truncate (part_id, old_len);
 
-static void
-emf_class_init (EMFormatClass *class)
-{
-	GObjectClass *object_class;
+		g_object_unref (message);
 
-	parent_class = g_type_class_peek_parent (class);
+		/* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
+		camel_mime_parser_step (parser, NULL, NULL);
 
-	object_class = G_OBJECT_CLASS (class);
-	object_class->finalize = emf_finalize;
+		state = camel_mime_parser_step (parser, NULL, NULL);
 
-	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;
+		messages++;
+	}
+	em_format_pull_level (emf);
 
-	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);
+	g_object_unref (parser);
 }
 
+/* RFC 1740 */
 static void
-emf_init (EMFormat *emf)
+emf_parse_multipart_alternative (EMFormat *emf,
+                           	 	 CamelMimePart *part,
+	                           	 GString *part_id,
+    	                       	 EMFormatParserInfo *info,
+        	                   	 GCancellable *cancellable)
 {
-	EShell *shell;
-	EShellSettings *shell_settings;
+	CamelMultipart *mp;
+	gint i, nparts, bestid = 0;
+	CamelMimePart *best = NULL;
 
-	emf->priv = EM_FORMAT_GET_PRIVATE (emf);
-	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;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	emf->message = NULL;
-	emf->folder = NULL;
-	emf->uid = NULL;
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	shell = e_shell_get_default ();
-	shell_settings = e_shell_get_shell_settings (shell);
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
-	emf->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
-	g_return_if_fail (emf->session != NULL);
+	/* 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;
+		/* GByteArray *ba; */
 
-	g_object_ref (emf->session);
-}
+		if (g_cancellable_is_cancelled (cancellable))
+			return;
 
-GType
-em_format_get_type (void)
-{
-	static GType type = 0;
+		/* is it correct to use the passed in *part here? */
+		mpart = camel_multipart_get_part (mp, i);
 
-	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 */
-		};
+		if (mpart == NULL)
+			continue;
 
-		type = g_type_register_static (
-			G_TYPE_OBJECT, "EMFormat", &type_info, 0);
-	}
+		/* 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);
 
-	return type;
-}
+		if (content_size == 0)
+			continue;
 
-/**
- * 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 hanlder 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)
-{
-	info->old = g_hash_table_lookup (emfc->type_handlers, info->mime_type);
-	g_hash_table_insert (emfc->type_handlers, (gpointer) info->mime_type, info);
-}
+		/* FIXME WEBKIT: This SHOULD do the same as the previous block of
+		 * code, thought it seems to somehow corrupt/disable/remove the
+		 * other mime part, so that has no content
+		data_wrapper = camel_medium_get_content ((CamelMedium *) mpart);
+		ba = camel_data_wrapper_get_byte_array (data_wrapper);
+		if (ba->len == 0)
+			continue;
+		*/
 
-struct _class_handlers {
-	EMFormatClass *old;
-	EMFormatClass *new;
-};
+		type = camel_mime_part_get_content_type (mpart);
+		mime_type = camel_content_type_simple (type);
 
-static void
-merge_missing (gpointer key,
-               gpointer value,
-               gpointer userdata)
-{
-	struct _class_handlers *classes = (struct _class_handlers *) userdata;
-	EMFormatHandler *info;
+		camel_strdown (mime_type);
 
-	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 (!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_free (mime_type);
+	}
+
+	if (best) {
+		gint len = part_id->len;
 
+		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);
 }
 
-void
-em_format_merge_handler (EMFormat *new,
-                         EMFormat *old)
+/* RFC 1740 */
+static void
+emf_parse_multipart_appledouble (EMFormat *emf,
+                           	 	 CamelMimePart *part,
+	                           	 GString *part_id,
+    	                      	 EMFormatParserInfo *info,
+        	                   	 GCancellable *cancellable)
 {
-	EMFormatClass *oldc = (EMFormatClass *) G_OBJECT_GET_CLASS (old);
-	EMFormatClass *newc = (EMFormatClass *) G_OBJECT_GET_CLASS (new);
-	struct _class_handlers fclasses;
+	CamelMultipart *mp;
+	CamelMimePart *mime_part;
+
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	fclasses.old = oldc;
-	fclasses.new = newc;
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	g_hash_table_foreach (oldc->type_handlers, merge_missing, &fclasses);
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
+	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_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)
+static void
+emf_parse_multipart_encrypted (EMFormat *emf,
+			       CamelMimePart *part,
+                               GString *part_id,
+			       EMFormatParserInfo *info,
+			       GCancellable *cancellable)
 {
-	EMFormatHandler *current;
+	CamelCipherContext *context;
+	const gchar *protocol;
+	CamelMimePart *opart;
+	CamelCipherValidity *valid;
+	CamelMultipartEncrypted *mpe;
+	GError *local_error = NULL;
 
-	/* TODO: thread issues? */
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	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);
+	/* should this perhaps run off a key of ".secured" ? */
+	/* FIXME WEBKIT what do we do about this?
+	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;
+	}
+	*/
+
+	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;
+	}
+
+	/* 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;
+	}
+
+	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 {
-		while (current && current->old != info)
-			current = current->old;
-		g_return_if_fail (current != NULL);
-		current->old = info->old;
+		EMFormatParserInfo encinfo = {
+				info->handler,
+				EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP,
+				camel_cipher_validity_clone (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);
 	}
+
+	/* TODO: Make sure when we finalize this part, it is zero'd out */
+	g_object_unref (opart);
+	g_object_unref (context);
 }
 
-/**
- * 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)
+/* RFC 2046 */
+static void
+emf_parse_multipart_mixed (EMFormat *emf,
+                     	   CamelMimePart *part,
+                     	   GString *part_id,
+                     	   EMFormatParserInfo *info,
+                     	   GCancellable *cancellable)
 {
-	EMFormatClass *class;
+	CamelMultipart *mp;
+	gint i, nparts, len;
 
-	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
-	g_return_val_if_fail (mime_type != NULL, NULL);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_val_if_fail (class->find_handler != NULL, NULL);
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-	return class->find_handler (emf, mime_type);
-}
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
-/**
- * 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;
+	em_format_push_level (emf);
 
-	s = strchr (mime_type, '/');
-	if (s == NULL)
-		mime = (gchar *) mime_type;
-	else {
-		gsize len = (s - mime_type) + 1;
+	len = part_id->len;
+	nparts = camel_multipart_get_number (mp);
+	for (i = 0; i < nparts; i++) {
+		CamelMimePart *subpart;
 
-		mime = g_alloca (len + 2);
-		strncpy (mime, mime_type, len);
-		strcpy(mime+len, "*");
+		subpart = camel_multipart_get_part (mp, i);
+
+		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);
 	}
 
-	return em_format_find_handler (emf, mime);
+	em_format_pull_level (emf);
 }
 
-/**
- * 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_signed (EMFormat *emf,
+                      	    CamelMimePart *part,
+                      	    GString *part_id,
+                      	    EMFormatParserInfo *info,
+                      	    GCancellable *cancellable)
 {
-	EMFormatPURI *puri;
-	const gchar *tmp;
+	CamelMimePart *cpart;
+	CamelMultipartSigned *mps;
+	CamelCipherContext *cipher = NULL;
+	guint32 validity_type;
 
-	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);
+	/* should this perhaps run off a key of ".secured" ? */
+	/* FIXME WEBKIT: What do we do with this?
+	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;
 	}
+	*/
 
-	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;
+	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;
 	}
 
-	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);
+	/* 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;
+			}
+	}
 
-		d(printf("built cid '%s'\n", puri->cid));
+	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;
 
-		/* 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. */
+		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 {
-			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);
+			gint i, nparts, len = part_id->len;
+			nparts = camel_multipart_get_number (CAMEL_MULTIPART (mps));
+			em_format_push_level (emf);
+			for (i = 0; i < nparts; i++) {
+				CamelMimePart *subpart;
+				EMFormatParserInfo signinfo = {
+						info->handler,
+						validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED,
+						camel_cipher_validity_clone (valid)
+				};
+
+				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);
 			}
+			em_format_pull_level (emf);
 		}
-	}
 
-	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);
+		g_object_unref (cipher);
+	}
+}
 
-	g_queue_push_tail (emf->pending_uri_level->data, puri);
+/* 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 (puri->uri)
-		g_hash_table_insert (emf->pending_uri_table, puri->uri, puri);
-	g_hash_table_insert (emf->pending_uri_table, puri->cid, puri);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-	return puri;
-}
+	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
 
-/**
- * em_format_push_level:
- * @emf:
- *
- * This is used to build a heirarchy 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 automaticall update
- * the base location.
- **/
-void
-em_format_push_level (EMFormat *emf)
-{
-	GNode *node;
+	if (!CAMEL_IS_MULTIPART (mp)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
-	g_return_if_fail (EM_IS_FORMAT (emf));
+	display_part = get_related_display_part (part, &displayid);
 
-	node = g_node_new (g_queue_new ());
+	if (display_part == NULL) {
+		emf_parse_multipart_mixed (
+			emf, part, part_id, info, cancellable);
+		return;
+	}
 
-	if (emf->pending_uri_tree == NULL)
-		emf->pending_uri_tree = node;
-	else
-		g_node_append (emf->pending_uri_tree, node);
+	em_format_push_level (emf);
 
-	emf->pending_uri_level = node;
-}
+	/* 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);
 
-/**
- * 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);
+	/* 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);
+		}
+	}
 
-	emf->pending_uri_level = emf->pending_uri_level->parent;
+	em_format_pull_level (emf);
 }
 
-/**
- * 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_message_rfc822 (EMFormat *emf,
+                    	  CamelMimePart *part,
+                    	  GString *part_id,
+                    	  EMFormatParserInfo *info,
+                    	  GCancellable *cancellable)
 {
-	GNode *node;
+	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+	gint len;
+	EMFormatPURI *puri;
 
-	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
-	g_return_val_if_fail (uri != NULL, NULL);
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
+
+	if (!CAMEL_IS_MIME_MESSAGE (dw)) {
+		emf_parse_source (emf, part, part_id, info, cancellable);
+		return;
+	}
 
-	node = emf->pending_uri_level;
+	len = part_id->len;
+	g_string_append_printf(part_id, ".rfc822");
 
-	while (node != NULL) {
-		GQueue *queue = node->data;
-		GList *link;
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emf_write_message_rfc822;
+	g_string_truncate (part_id, len);
 
-		link = g_queue_peek_head_link (queue);
+	em_format_add_puri (emf, puri);
+}
 
-		while (link != NULL) {
-			EMFormatPURI *pw = link->data;
+static void
+emf_parse_message_deliverystatus (EMFormat *emf,
+                            	  CamelMimePart *part,
+                            	  GString *part_id,
+                            	  EMFormatParserInfo *info,
+                            	  GCancellable *cancellable)
+{
+	EMFormatPURI *puri;
+	gint len;
 
-			if (g_strcmp0 (pw->uri, uri) == 0)
-				return pw;
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-			if (g_strcmp0 (pw->cid, uri) == 0)
-				return pw;
+	len = part_id->len;
+	g_string_append (part_id, ".deliverystatus");
 
-			link = g_list_next (link);
-		}
+	puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+	puri->write_func = emf_write_text;
 
-		node = node->parent;
-	}
+	g_string_truncate (part_id, len);
 
-	return NULL;
+	em_format_add_puri (emf, puri);
 }
 
-/**
- * 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)
+static void
+emf_parse_inlinepgp_signed (EMFormat *emf,
+                      	    CamelMimePart *ipart,
+                      	    GString *part_id,
+			    EMFormatParserInfo *info,
+			    GCancellable *cancellable)
 {
-	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);
+	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;
 
-	return g_hash_table_lookup (emf->pending_uri_table, uri);
-}
+	if (g_cancellable_is_cancelled (cancellable))
+		return;
 
-/**
- * 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)
-{
-	if (emf->pending_uri_table == NULL)
-		emf->pending_uri_table =
-			g_hash_table_new (g_str_hash, g_str_equal);
+	if (!ipart) {
+		em_format_format_error(emf, _("Unknown error verifying signature"));
+		return;
+	}
 
-	else {
-		g_hash_table_remove_all (emf->pending_uri_table);
+	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;
+	}
 
-		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);
+	/* Setup output stream */
+	ostream = camel_stream_mem_new ();
+	filtered_stream = camel_stream_filter_new (ostream);
 
-		emf->pending_uri_tree = NULL;
-		emf->pending_uri_level = NULL;
-	}
+	/* 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);
 
-	em_format_push_level (emf);
-}
+	/* 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);
 
-/* 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;
-
-	d(printf("format_part_as()\n"));
-
-	emf->snoop_mime_type = NULL;
-
-	/* 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";
-		}
+	/* 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);
 
-		handle = em_format_find_handler (emf, mime_type);
-		if (handle == NULL) {
-			handle = em_format_fallback_handler (emf, mime_type);
-			is_fallback = TRUE;
-		}
+	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);
 
-		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";
-	}
+	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);
 
-	EM_FORMAT_GET_CLASS (emf)->format_attachment (
-		emf, stream, part, mime_type, handle, cancellable);
+	opart = camel_mime_part_new ();
+	camel_medium_set_content ((CamelMedium *) opart, dw);
+	camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) opart, dw->mime_type);
 
-finish:
-	emf->base = base_save;
-	emf->snoop_mime_type = snoop_save;
+	/* 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 = EM_FORMAT_VALIDITY_FOUND_SIGNED | EM_FORMAT_VALIDITY_FOUND_PGP;
+	signinfo.validity = camel_cipher_validity_clone (valid);
+	em_format_parse_part (emf, opart, part_id, &signinfo, cancellable);
+	g_string_truncate (part_id, len);
 
-	if (base)
-		camel_url_free (base);
+	/* Clean Up */
+	g_object_unref (dw);
+	g_object_unref (opart);
+	g_object_unref (ostream);
+	g_object_unref (cipher);
 }
 
-void
-em_format_part (EMFormat *emf,
-                CamelStream *stream,
-                CamelMimePart *mime_part,
-                GCancellable *cancellable)
+static void
+emf_parse_inlinepgp_encrypted (EMFormat *emf,
+			       CamelMimePart *ipart,
+			       GString *part_id,
+			       EMFormatParserInfo *info,
+			       GCancellable *cancellable)
 {
-	gchar *mime_type;
+	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 (CAMEL_MEDIUM (mime_part));
+	dw = camel_medium_get_content ((CamelMedium *) opart);
 	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);
+
+	/* 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);
+
+	/* Pass it off to the real formatter */
+	len = part_id->len;
+	g_string_append (part_id, ".inlinegpg_encrypted");
+	encinfo.handler = info->handler;
+	encinfo.validity_type = EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP;
+	encinfo.validity = camel_cipher_validity_clone (valid);
+	em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+	g_string_truncate (part_id, len);
+
+	/* Clean Up */
+	g_object_unref (opart);
+	g_object_unref (cipher);
 }
 
-/**
- * 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_source (EMFormat *emf,
+				  CamelMimePart *part,
+				  GString *part_id,
+				  EMFormatParserInfo *info,
+				  GCancellable *cancellable)
 {
-	EMFormatClass *class;
+	EMFormatPURI *puri;
+	gint len;
 
-	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);
+	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 = emf_write_source;
+	g_string_truncate (part_id, len);
 
-	class->format_clone (emf, folder, uid, message, source, cancellable);
+	em_format_add_puri (emf, puri);
 }
 
+/**************************************************************************/
+
 void
-em_format_format (EMFormat *emf,
-                  CamelFolder *folder,
-                  const gchar *uid,
-                  CamelMimeMessage *message,
-                  GCancellable *cancellable)
+em_format_empty_writer (EMFormat *emf,
+			EMFormatPURI *puri,
+			CamelStream *stream,
+			GCancellable *cancellable)
 {
-	/* em_format_format_clone() will check the arguments. */
-	em_format_format_clone (emf, folder, uid, message, NULL, cancellable);
+	/* DO NOTHING */
 }
 
-/**
- * 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_source (EMFormat *emf,
+	    CamelStream *stream,
+	    GCancellable *cancellable)
 {
+	GByteArray *ba;
+	gchar *data;
+
 	g_return_if_fail (EM_IS_FORMAT (emf));
 
-	if (emf->mode == mode)
-		return;
+	ba = camel_data_wrapper_get_byte_array ((CamelDataWrapper *) emf->message);
 
-	emf->mode = mode;
+	data = g_strndup ((gchar *) ba->data, ba->len);
+	camel_stream_write_string (stream, data, cancellable, NULL);
+	g_free (data);
 }
 
-/**
- * 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)
+/**************************************************************************/
+
+static void
+emf_parse (EMFormat *emf,
+	   CamelMimeMessage *message,
+	   CamelFolder *folder,
+	   GCancellable *cancellable)
 {
-	if ((emf->charset && charset && g_ascii_strcasecmp (emf->charset, charset) == 0)
-	    || (emf->charset == NULL && charset == NULL)
-	    || (emf->charset == charset))
-		return;
+	GString *part_id;
+	EMFormatParserInfo info = { 0 };
 
-	g_free (emf->charset);
-	emf->charset = g_strdup (charset);
-}
+	g_return_if_fail (EM_IS_FORMAT (emf));
 
-/**
- * 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)
-{
-	if ((emf->default_charset && charset &&
-	    g_ascii_strcasecmp (emf->default_charset, charset) == 0)
-	    || (emf->default_charset == NULL && charset == NULL)
-	    || (emf->default_charset == charset))
+	if (g_cancellable_is_cancelled (cancellable))
 		return;
 
-	g_free (emf->default_charset);
-	emf->default_charset = g_strdup (charset);
+	if (message) {
+		g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+		if (emf->message)
+			g_object_unref (emf->message);
+		emf->message = g_object_ref (message);
+	}
+
+	if (folder) {
+		g_return_if_fail (CAMEL_IS_FOLDER  (folder));
+
+		if (emf->folder)
+			g_object_unref (emf->folder);
+		emf->folder = g_object_ref (folder);
+	}
+
+	g_return_if_fail (emf->message);
+	g_return_if_fail (emf->folder);
+
+
+	part_id = g_string_new ("");
+	/* Begun parsing the message */
+	em_format_parse_part (emf, CAMEL_MIME_PART (message), part_id, &info, cancellable);
+
+	/* Now call some internal handlers */
+	em_format_parse_part_as (emf, CAMEL_MIME_PART (message), part_id, &info,
+			"x-evolution/message/post-header", cancellable);
+
+
+	g_string_free (part_id, TRUE);
 }
 
-/**
- * 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;
 
-	while ((eh = g_queue_pop_head (&emf->header_list)) != NULL)
-		g_free (eh);
+	if (handle == NULL)
+		return FALSE;
+
+	/* WEBKIT FIXME
+	emfc = g_hash_table_lookup (emf->inline_table, part_id);
+	if (emfc && emfc->state != INLINE_UNSET)
+		return emfc->state & 1;
+	*/
+
+	/* 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;
+
+	/* 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 },
+		{ (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/*", emf_parse_multipart_mixed, },
+		{ (gchar *) "message/rfc822", emf_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE },
+		{ (gchar *) "message/news", emf_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE },
+		{ (gchar *) "message/delivery-status", emf_parse_message_deliverystatus, },
+		{ (gchar *) "message/*", emf_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE },
+
+		/* 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/source", emf_parse_source, },
+};
+
 /* note: also copied in em-mailer-prefs.c */
 static const struct {
 	const gchar *name;
@@ -1030,1340 +1112,854 @@ 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) {
+		g_object_unref (emf->message);
+		emf->message = 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->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;
+	}
+
+	/* 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 *class)
 {
-	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);
+	class->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 (class->type_handlers,
+				type_handlers[i].mime_type,
+				&type_handlers[i]);
+	}
+}
+
+static void
+em_format_class_init (EMFormatClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
 
-	return class->is_inline (emf, part_id, mime_part, handle);
+	g_type_class_add_private (class, sizeof (EMFormatPrivate));
+
+	class->format_error = emf_error;
+	class->format_source = emf_source;
+	class->parse = emf_parse;
+	class->is_inline = emf_is_inline;
+
+	object_class = G_OBJECT_CLASS (class);
+	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));
 }
 
-/**
- * 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;
+	gint ii;
 
-	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,
+			(GDestroyNotify) g_free, (GDestroyNotify) em_format_puri_free);
+
+	shell = e_shell_get_default ();
+	shell_settings = e_shell_get_shell_settings (shell);
+
+	emf->priv->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
+	g_return_if_fail (emf->priv->session);
+
+	g_object_ref (emf->priv->session);
+
+	/* 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,
+			default_headers[ii].flags);
+}
+
+EMFormat*
+em_format_new (void)
+{
+	EMFormat *emf = g_object_new (EM_TYPE_FORMAT, NULL);
 
-	emfc->state = state ? INLINE_ON : INLINE_OFF;
+	return emf;
 }
 
-void
-em_format_format_attachment (EMFormat *emf,
-                             CamelStream *stream,
-                             CamelMimePart *mime_part,
-                             const gchar *mime_type,
-                             const EMFormatHandler *info,
-                             GCancellable *cancellable)
+GType
+em_format_get_type (void)
 {
-	EMFormatClass *class;
+	static GType type = 0;
 
-	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);
+	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_attachment != NULL);
+		type = g_type_register_static (
+				G_TYPE_OBJECT, "EMFormat", &type_info, 0);
+	}
 
-	class->format_attachment (
-		emf, stream, mime_part, mime_type, info, cancellable);
+	return type;
 }
 
 void
-em_format_format_error (EMFormat *emf,
-                        CamelStream *stream,
-                        const gchar *format,
-                        ...)
+em_format_set_charset (EMFormat *emf,
+		       const gchar *charset)
 {
-	EMFormatClass *class;
-	gchar *errmsg;
-	va_list ap;
-
 	g_return_if_fail (EM_IS_FORMAT (emf));
-	g_return_if_fail (CAMEL_IS_STREAM (stream));
-	g_return_if_fail (format != NULL);
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_if_fail (class->format_error != NULL);
+	if (emf->priv->charset)
+		g_free (emf->priv->charset);
 
-	va_start (ap, format);
-	errmsg = g_strdup_vprintf (format, ap);
-	class->format_error (emf, stream, errmsg);
-	g_free (errmsg);
-	va_end (ap);
+	emf->priv->charset = g_strdup (charset);
+
+	g_object_notify (G_OBJECT (emf), "charset");
 }
 
-void
-em_format_format_secure (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *mime_part,
-                         CamelCipherValidity *valid,
-                         GCancellable *cancellable)
+const gchar*
+em_format_get_charset (EMFormat *emf)
 {
-	EMFormatClass *class;
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+
+	return emf->priv->charset;
+}
 
+void
+em_format_set_default_charset (EMFormat *emf,
+			       const gchar *charset)
+{
 	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->default_charset)
+		g_free (emf->priv->default_charset);
 
-	class->format_secure (emf, stream, mime_part, valid, cancellable);
+	emf->priv->default_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), "default-charset");
 }
 
-void
-em_format_format_source (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *mime_part,
-                         GCancellable *cancellable)
+const gchar*
+em_format_get_default_charset (EMFormat *emf)
 {
-	EMFormatClass *class;
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+
+	return emf->priv->default_charset;
+}
 
+void
+em_format_set_composer (EMFormat *emf,
+			gboolean composer)
+{
 	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);
+	if (emf->priv->composer && composer)
+		return;
 
-	class->format_source (emf, stream, mime_part, cancellable);
+	emf->priv->composer = composer;
+
+	g_object_notify (G_OBJECT (emf), "composer");
 }
 
 gboolean
-em_format_busy (EMFormat *emf)
+em_format_get_composer (EMFormat *emf)
 {
-	EMFormatClass *class;
-
 	g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
 
-	class = EM_FORMAT_GET_CLASS (emf);
-	g_return_val_if_fail (class->busy != NULL, FALSE);
+	return emf->priv->composer;
+}
+
+void
+em_format_set_base_url (EMFormat *emf,
+			CamelURL *url)
+{
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (url);
+
+	if (emf->priv->base_url)
+		camel_url_free (emf->priv->base_url);
 
-	return class->busy (emf);
+	emf->priv->base_url = camel_url_copy (url);
+
+	g_object_notify (G_OBJECT (emf), "base-url");
 }
 
-/* should this be virtual? */
 void
-em_format_format_content (EMFormat *emf,
-                          CamelStream *stream,
-                          CamelMimePart *part,
-                          GCancellable *cancellable)
+em_format_set_base_url_string (EMFormat *emf,
+			       const gchar *url_string)
 {
-	CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (url_string && *url_string);
 
-	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);
+	if (emf->priv->base_url)
+		camel_url_free (emf->priv->base_url);
+
+	emf->priv->base_url = camel_url_new (url_string, NULL);
+
+	g_object_notify (G_OBJECT (emf), "base-url");
+}
+
+CamelURL*
+em_format_get_base_url (EMFormat *emf)
+{
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+
+	return emf->priv->base_url;
 }
 
 /**
- * em_format_format_content:
+ * em_format_clear_headers:
  * @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.
+ * Clear the list of headers to be displayed.  This will force all headers to
+ * be shown.
  **/
 void
-em_format_format_text (EMFormat *emf,
-                       CamelStream *stream,
-                       CamelDataWrapper *dw,
-                       GCancellable *cancellable)
+em_format_clear_headers (EMFormat *emf)
 {
-	CamelStream *filter_stream;
-	CamelMimeFilter *filter;
-	const gchar *charset = NULL;
-	CamelMimeFilterWindows *windows = NULL;
-	CamelStream *mem_stream = NULL;
-	gsize size;
-	gsize max;
-	GSettings *settings;
-
-	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;
+	EMFormatHeader *eh;
 
-		/* 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_return_if_fail (EM_IS_FORMAT (emf));
 
-		null = camel_stream_null_new ();
-		filter_stream = camel_stream_filter_new (null);
-		g_object_unref (null);
+	while ((eh = g_queue_pop_head (&emf->header_list)) != NULL)
+		g_free (eh);
+}
 
-		windows = (CamelMimeFilterWindows *) camel_mime_filter_windows_new (charset);
-		camel_stream_filter_add (
-			CAMEL_STREAM_FILTER (filter_stream),
-			CAMEL_MIME_FILTER (windows));
+/**
+ * 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)
+{
+	EMFormatHeader *h;
 
-		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);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (name && *name);
 
-		charset = camel_mime_filter_windows_real_charset (windows);
-	} else if (charset == NULL) {
-		charset = emf->default_charset;
-	}
+	h = g_malloc (sizeof (*h) + strlen (name));
+	h->flags = flags;
+	strcpy (h->name, name);
+	g_queue_push_tail (&emf->header_list, h);
+}
 
-	mem_stream = (CamelStream *) camel_stream_mem_new ();
-	filter_stream = camel_stream_filter_new (mem_stream);
+void
+em_format_push_level (EMFormat *emf)
+{
+	/*
+	GNode *node;
 
-	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);
+	node = g_node_new (NULL);
+	if (emf->mail_part_tree == NULL) {
+		emf->mail_part_tree = node;
+	} else {
+		g_node_append (emf->priv->current_node, node);
 	}
 
-	max = -1;
+	emf->priv->current_node = node;
 
-	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);
+	puri_level++;
+	printf("###### push_level to %d\n", puri_level);
+	*/
+}
 
-	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);
+void
+em_format_pull_level (EMFormat *emf)
+{
+	/*
+	g_return_if_fail (emf->priv->current_node);
 
-	g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
+	emf->priv->current_node = emf->priv->current_node->parent;
 
-	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);
-	}
+	puri_level--;
+	printf("###### pull_level to %d\n", puri_level);
+	*/
+}
 
-	if (windows)
-		g_object_unref (windows);
+void
+em_format_add_puri (EMFormat *emf,
+		    		EMFormatPURI *puri)
+{
+	emf->mail_part_list = g_list_append (emf->mail_part_list, puri);
 
-	g_object_unref (mem_stream);
+	g_hash_table_insert (emf->mail_part_table,
+			puri->uri, puri);
+
+
+	printf("  added PURI '%s', type %s\n", puri->uri,
+			camel_content_type_simple (
+					camel_mime_part_get_content_type (puri->part)));
 }
 
-/**
- * em_format_describe_part:
- * @part:
- * @mimetype:
- *
- * Generate a simple textual description of a part, @mime_type represents the
- * the content.
- *
- * Return value:
- **/
-gchar *
-em_format_describe_part (CamelMimePart *part,
-                         const gchar *mime_type)
+void
+em_format_class_add_handler (EMFormatClass *emfc,
+		  	    			 EMFormatHandler *handler)
 {
-	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);
+	EMFormatHandler *old_handler;
 
-	filename = camel_mime_part_get_filename (part);
-	description = camel_mime_part_get_description (part);
+	g_return_if_fail (EM_IS_FORMAT_CLASS (emfc));
+	g_return_if_fail (handler);
 
-	if (!filename || !*filename) {
-		CamelDataWrapper *content;
+	old_handler = g_hash_table_lookup (
+			emfc->type_handlers, handler->mime_type);
 
-		content = camel_medium_get_content (CAMEL_MEDIUM (part));
+	handler->old = old_handler;
 
-		if (CAMEL_IS_MIME_MESSAGE (content))
-			filename = camel_mime_message_get_subject (
-				CAMEL_MIME_MESSAGE (content));
+	/* 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;
 	}
 
-	if (filename != NULL && *filename != '\0') {
-		gchar *basename = g_path_get_basename (filename);
-		g_string_append_printf (stext, " (%s)", basename);
-		g_free (basename);
+	if (old_handler && handler->write_func == NULL) {
+		handler->write_func = old_handler->write_func;
 	}
 
-	if (description != NULL && *description != '\0' &&
-		g_strcmp0 (filename, description) != 0)
-		g_string_append_printf (stext, ", \"%s\"", description);
-
-	return g_string_free (stext, FALSE);
+	g_hash_table_insert (emfc->type_handlers,
+			handler->mime_type, handler);
 }
 
-static void
-add_validity_found (EMFormat *emf,
-                    CamelCipherValidity *valid)
+void
+em_format_class_remove_handler (EMFormatClass *emfc,
+								EMFormatHandler *handler)
 {
-	g_return_if_fail (emf != NULL);
-
-	if (!valid)
-		return;
-
-	if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
-		emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SIGNED;
+	g_return_if_fail (EM_IS_FORMAT_CLASS (emfc));
+	g_return_if_fail (handler);
 
-	if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE)
-		emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_ENCRYPTED;
+	g_hash_table_remove (emfc->type_handlers, handler->mime_type);
 }
 
-/* ********************************************************************** */
-
-static void
-preserve_charset_in_content_type (CamelMimePart *ipart,
-                                  CamelMimePart *opart)
+const EMFormatHandler *
+em_format_find_handler (EMFormat *emf,
+						const gchar *mime_type)
 {
-	CamelDataWrapper *data_wrapper;
-	CamelContentType *content_type;
-	const gchar *charset;
+	EMFormatClass *emfc;
 
-	g_return_if_fail (ipart != NULL);
-	g_return_if_fail (opart != NULL);
+	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+	g_return_val_if_fail (mime_type && *mime_type, NULL);
 
-	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
-	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
+	emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf);
 
-	if (content_type == NULL)
-		return;
+	return g_hash_table_lookup (
+			emfc->type_handlers, mime_type);
+}
 
-	charset = camel_content_type_param (content_type, "charset");
+/**
+ * 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;
 
-	if (charset == NULL || *charset == '\0')
-		return;
+	s = strchr (mime_type, '/');
+	if (s == NULL)
+		mime = (gchar *) mime_type;
+	else {
+		gsize len = (s-mime_type)+1;
 
-	data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
-	content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
+		mime = g_alloca (len+2);
+		strncpy (mime, mime_type, len);
+		strcpy(mime+len, "*");
+	}
 
-	camel_content_type_set_param (content_type, "charset", charset);
+	return em_format_find_handler (emf, mime);
 }
 
-#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_parse (EMFormat *emf,
+				 CamelMimeMessage *message,
+				 CamelFolder *folder,
+				 GCancellable *cancellable)
 {
-	CamelCipherContext *context;
-	CamelMimePart *opart;
-	CamelCipherValidity *valid;
-	EMFormatCache *emfc;
-	GError *local_error = NULL;
-
-	/* 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;
-	}
+	EMFormatClass *class;
 
-	context = camel_smime_context_new (emf->session);
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
 
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
-		EM_FORMAT_VALIDITY_FOUND_SMIME;
+	class = EM_FORMAT_GET_CLASS (emf);
+	g_return_if_fail (class->parse != NULL);
 
-	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);
+	class->parse (emf, message, folder, cancellable);
+}
 
-		em_format_part_as (emf, stream, part, NULL, cancellable);
+void
+em_format_parse_part_as (EMFormat *emf,
+						 CamelMimePart *part,
+						 GString *part_id,
+						 EMFormatParserInfo *info,
+						 const gchar *mime_type,
+						 GCancellable *cancellable)
+{
+	const EMFormatHandler *handler;
+
+	handler = em_format_find_handler (emf, mime_type);
+	if (handler) {
+		EMFormatParserInfo ninfo = {
+			handler,
+			info ? info->validity_type : 0,
+			info ? info->validity : 0
+		};
+		handler->parse_func (emf, part, part_id, &ninfo, cancellable);
 	} else {
-		if (emfc == NULL)
-			emfc = emf_insert_cache (emf, emf->part_id->str);
+		EMFormatPURI *puri;
 
-		emfc->valid = camel_cipher_validity_clone (valid);
-		g_object_ref ((emfc->secured = opart));
-
-		add_validity_found (emf, valid);
-		em_format_format_secure (
-			emf, stream, opart, valid, cancellable);
+		puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+		em_format_add_puri (emf, puri);
 	}
-
-	g_object_unref (opart);
-	g_object_unref (context);
 }
-#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_parse_part (EMFormat *emf,
+		      		  CamelMimePart *part,
+				      GString *part_id,
+				      EMFormatParserInfo *info,
+				      GCancellable *cancellable)
 {
-	CamelMultipart *mp;
-	CamelMimePart *mime_part;
-	gint len;
-
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+	CamelContentType *ct;
+	gchar *mime_type;
 
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
+	ct = camel_mime_part_get_content_type (part);
+	if (ct) {
+		mime_type = camel_content_type_simple (ct);
+	} else {
+		mime_type = (gchar *) "text/plain";
 	}
 
-	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);
+	em_format_parse_part_as (emf, part, part_id, info, mime_type, cancellable);
 
+	if (ct)
+		g_free (mime_type);
 }
 
-/* RFC 2046 */
-static void
-emf_multipart_mixed (EMFormat *emf,
-                     CamelStream *stream,
-                     CamelMimePart *part,
-                     const EMFormatHandler *info,
-                     GCancellable *cancellable,
-                     gboolean is_fallback)
-{
-	CamelMultipart *mp;
-	gint i, nparts, len;
 
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+gboolean
+em_format_is_inline (EMFormat *emf,
+		     const gchar *part_id,
+		     CamelMimePart *part,
+		     const EMFormatHandler *handler)
+{
+	EMFormatClass *class;
 
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
-	}
+	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);
 
-	em_format_push_level (emf);
+	class = EM_FORMAT_GET_CLASS (emf);
+	g_return_val_if_fail (class->is_inline != NULL, FALSE);
 
+	return class->is_inline (emf, part_id, part, handler);
 
-	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);
-	}
 }
 
-static gboolean	related_display_part_is_attachment
-						(EMFormat *emf,
-						 CamelMimePart *part);
 
-/* RFC 1740 */
-static void
-emf_multipart_alternative (EMFormat *emf,
-                           CamelStream *stream,
-                           CamelMimePart *part,
-                           const EMFormatHandler *info,
-                           GCancellable *cancellable,
-                           gboolean is_fallback)
-{
-	CamelMultipart *mp;
-	gint i, nparts, bestid = 0;
-	CamelMimePart *best = NULL;
-
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, cancellable);
-		return;
-	}
-
-	em_format_push_level (emf);
-
-	/* 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;
-
-		/* is it correct to use the passed in *part here? */
-		part = camel_multipart_get_part (mp, i);
-
-		if (part == 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 (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);
-
-		if (content_size == 0)
-			continue;
-
-		type = camel_mime_part_get_content_type (part);
-		mime_type = camel_content_type_simple (type);
-
-		camel_strdown (mime_type);
-
-		/*if (want_plain && !strcmp (mime_type, "text/plain"))
-		  return part;*/
-
-		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;
-		}
-
-		g_free (mime_type);
-	}
-
-	if (best) {
-		gint len = emf->part_id->len;
-
-		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);
-}
-
-static void
-emf_multipart_encrypted (EMFormat *emf,
-                         CamelStream *stream,
-                         CamelMimePart *part,
-                         const EMFormatHandler *info,
-                         GCancellable *cancellable,
-                         gboolean is_fallback)
+void
+em_format_format_error (EMFormat *emf,
+                        const gchar *format,
+                        ...)
 {
-	CamelCipherContext *context;
-	const gchar *protocol;
-	CamelMimePart *opart;
-	CamelCipherValidity *valid;
-	CamelMultipartEncrypted *mpe;
-	EMFormatCache *emfc;
-	GError *local_error = NULL;
-
-	/* 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;
-	}
-
-	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;
-	}
-
-	/* 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;
-	}
-
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
-		EM_FORMAT_VALIDITY_FOUND_PGP;
-
-	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);
-
-		em_format_part_as (
-			emf, stream, part,
-			"multipart/mixed", cancellable);
-	} else {
-		if (emfc == NULL)
-			emfc = emf_insert_cache (emf, emf->part_id->str);
+	EMFormatClass *class;
+	gchar *errmsg;
+	va_list ap;
 
-		emfc->valid = camel_cipher_validity_clone (valid);
-		g_object_ref ((emfc->secured = opart));
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (format != NULL);
 
-		add_validity_found (emf, valid);
-		em_format_format_secure (
-			emf, stream, opart, valid, cancellable);
-	}
+	class = EM_FORMAT_GET_CLASS (emf);
+	g_return_if_fail (class->format_error != NULL);
 
-	/* TODO: Make sure when we finalize this part, it is zero'd out */
-	g_object_unref (opart);
-	g_object_unref (context);
+	va_start (ap, format);
+	errmsg = g_strdup_vprintf (format, ap);
+	class->format_error (emf, errmsg);
+	g_free (errmsg);
+	va_end (ap);
 }
 
-static CamelMimePart *
-get_related_display_part (CamelMimePart *part,
-                          gint *out_displayid)
+void
+em_format_format_source (EMFormat *emf,
+			 CamelStream *stream,
+			 GCancellable *cancellable)
 {
-	CamelMultipart *mp;
-	CamelMimePart *body_part, *display_part = NULL;
-	CamelContentType *content_type;
-	const gchar *start;
-	gint i, nparts, displayid = 0;
-
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
-	if (!CAMEL_IS_MULTIPART (mp))
-		return NULL;
-
-	nparts = camel_multipart_get_number (mp);
-	content_type = camel_mime_part_get_content_type (part);
-	start = camel_content_type_param (content_type, "start");
-	if (start && strlen (start) > 2) {
-		gint len;
-		const gchar *cid;
-
-		/* strip <>'s */
-		len = strlen (start) - 2;
-		start++;
-
-		for (i = 0; i < nparts; i++) {
-			body_part = camel_multipart_get_part (mp, i);
-			cid = camel_mime_part_get_content_id (body_part);
-
-			if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
-				display_part = body_part;
-				displayid = i;
-				break;
-			}
-		}
-	} else {
-		display_part = camel_multipart_get_part (mp, 0);
-	}
-
-	if (out_displayid)
-		*out_displayid = displayid;
-
-	return display_part;
-}
+	EMFormatClass *class;
 
-static gboolean
-related_display_part_is_attachment (EMFormat *emf,
-                                    CamelMimePart *part)
-{
-	CamelMimePart *display_part;
+	g_return_if_fail (EM_IS_FORMAT (emf));
+	g_return_if_fail (CAMEL_IS_STREAM (stream));
 
-	display_part = get_related_display_part (part, NULL);
-	return display_part && em_format_is_attachment (emf, display_part);
-}
+	class = EM_FORMAT_GET_CLASS (emf);
+	g_return_if_fail (class->format_source != NULL);
 
-static void
-emf_write_related (EMFormat *emf,
-                   CamelStream *stream,
-                   EMFormatPURI *puri,
-                   GCancellable *cancellable)
-{
-	em_format_format_content (emf, stream, puri->part, cancellable);
-	camel_stream_close (stream, cancellable, NULL);
+	class->format_source (emf, stream, cancellable);
 }
 
-/* RFC 2387 */
-static void
-emf_multipart_related (EMFormat *emf,
+/**
+ * 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,
-                       CamelMimePart *part,
-                       const EMFormatHandler *info,
-                       GCancellable *cancellable,
-                       gboolean is_fallback)
-{
-	CamelMultipart *mp;
-	CamelMimePart *body_part, *display_part = NULL;
-	gint i, nparts, partidlen, displayid = 0;
-	gchar *oldpartid;
-	GList *link;
-
-	mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
-	if (!CAMEL_IS_MULTIPART (mp)) {
-		em_format_format_source (emf, stream, part, 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;
-	}
-
-	em_format_push_level (emf);
-
-	oldpartid = g_strdup (emf->part_id->str);
-	partidlen = emf->part_id->len;
-
-	/* 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);
-
-	link = g_queue_peek_head_link (emf->pending_uri_level->data);
-
-	while (link && link->next != NULL) {
-		EMFormatPURI *puri = link->data;
-
-		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);
-			}
-		}
-
-		link = g_list_next (link);
-	}
-
-	g_string_printf(emf->part_id, "%s", oldpartid);
-	g_free (oldpartid);
-
-	em_format_pull_level (emf);
-}
-
-static void
-emf_multipart_signed (EMFormat *emf,
-                      CamelStream *stream,
-                      CamelMimePart *part,
-                      const EMFormatHandler *info,
-                      GCancellable *cancellable,
-                      gboolean is_fallback)
-{
-	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;
-	}
-
-	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);
-	} else {
-		CamelCipherValidity *valid;
-		GError *local_error = NULL;
-
-		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);
-
-			em_format_part_as (
-				emf, stream, part,
-				"multipart/mixed", cancellable);
-		} else {
-			if (emfc == NULL)
-				emfc = emf_insert_cache (emf, emf->part_id->str);
-
-			emfc->valid = camel_cipher_validity_clone (valid);
-			g_object_ref ((emfc->secured = cpart));
-
-			add_validity_found (emf, valid);
-			em_format_format_secure (
-				emf, stream, cpart, valid, cancellable);
-		}
-
-		g_object_unref (cipher);
-	}
-}
-
-/* RFC 4155 */
-static void
-emf_application_mbox (EMFormat *emf,
-                      CamelStream *stream,
-                      CamelMimePart *mime_part,
-                      const EMFormatHandler *info,
-                      GCancellable *cancellable,
-                      gboolean is_fallback)
-{
-	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. */
-
-	/* 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? */
-
-	handle = em_format_find_handler (emf, "x-evolution/message/rfc822");
-	g_return_if_fail (handle != NULL);
-
-	parser = camel_mime_parser_new ();
-	camel_mime_parser_scan_from (parser, TRUE);
-
-	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;
-
-		message = camel_mime_message_new ();
-		mime_part = CAMEL_MIME_PART (message);
-
-		if (!camel_mime_part_construct_from_parser_sync (
-			mime_part, parser, NULL, NULL)) {
-			g_object_unref (message);
-			break;
-		}
-
-		/* Render the message. */
-		handle->handler (
-			emf, stream, mime_part,
-			handle, cancellable, FALSE);
-
-		g_object_unref (message);
-
-		/* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
-		camel_mime_parser_step (parser, NULL, NULL);
-
-		state = camel_mime_parser_step (parser, NULL, NULL);
-	}
-
-	g_object_unref (parser);
-}
-
-static void
-emf_message_rfc822 (EMFormat *emf,
-                    CamelStream *stream,
-                    CamelMimePart *part,
-                    const EMFormatHandler *info,
-                    GCancellable *cancellable,
-                    gboolean is_fallback)
+                       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);
-
-	g_string_truncate (emf->part_id, len);
-
-	g_free (emf->current_message_part_id);
-	emf->current_message_part_id = parent_message_part_id;
-}
-
-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);
-}
+	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;
 
-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;
+		/* 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... */
 
-	if (!ipart) {
-		em_format_format_error(emf, stream, _("Unknown error verifying signature"));
-		return;
-	}
+		null = camel_stream_null_new ();
+		filter_stream = camel_stream_filter_new (null);
+		g_object_unref (null);
 
-	emf->validity_found |=
-		EM_FORMAT_VALIDITY_FOUND_SIGNED |
-		EM_FORMAT_VALIDITY_FOUND_PGP;
+		windows = (CamelMimeFilterWindows *) camel_mime_filter_windows_new (charset);
+		camel_stream_filter_add (
+			CAMEL_STREAM_FILTER (filter_stream),
+			CAMEL_MIME_FILTER (windows));
 
-	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;
+		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);
+
+		charset = camel_mime_filter_windows_real_charset (windows);
+	} else if (charset == NULL) {
+		charset = emf->priv->default_charset;
 	}
 
-	/* Setup output stream */
-	ostream = camel_stream_mem_new ();
-	filtered_stream = camel_stream_filter_new (ostream);
+	mem_stream = (CamelStream *) camel_stream_mem_new ();
+	filter_stream = camel_stream_filter_new (mem_stream);
 
-	/* 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);
+	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);
+	}
 
-	/* 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 {
+		/* FIXME WEBKIT
+		EM_FORMAT_GET_CLASS (emf)->format_optional (
+			emf, stream, (CamelMimePart *) dw,
+			mem_stream, cancellable);
+		*/
+	}
 
-	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));
 }
 
 /**
@@ -2454,35 +2050,144 @@ em_format_snoop_type (CamelMimePart *part)
 	return res;
 
 	/* We used to load parts to check their type, we dont anymore,
-	 * see bug #11778 for some discussion */
+	   see bug #211778 for some discussion */
 }
 
 gchar*
 em_format_build_mail_uri (CamelFolder *folder,
 			  const gchar *message_uid,
-			  const gchar *part_uid,
-			  EMFormat *emf)
+			  const gchar *part_uid)
 {
 	CamelStore *store;
+	gchar *uri, *tmp;
 
 	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
 	g_return_val_if_fail (message_uid && *message_uid, NULL);
-	g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
 
 	store = camel_folder_get_parent_store (folder);
 
-	if (part_uid) {
-		return g_strdup_printf ("mail://%s/%s/%s?part_id=%s&formatter=%d",
-			camel_service_get_uid (CAMEL_SERVICE (store)),
-			camel_folder_get_full_name (folder),
-			message_uid,
-			part_uid,
-			GPOINTER_TO_INT (emf));
+	if (part_uid && *part_uid) {
+		uri = g_strdup_printf ("mail://%s/%s/%s?part_id=%s",
+				camel_service_get_uid (CAMEL_SERVICE (store)),
+				camel_folder_get_full_name (folder),
+				message_uid,
+				part_uid);
+	} else {
+		uri = g_strdup_printf ("mail://%s/%s/%s",
+				camel_service_get_uid (CAMEL_SERVICE (store)),
+				camel_folder_get_full_name (folder),
+				message_uid);
+	}
+
+	/* 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 (uri, '@');
+	if (tmp) {
+		tmp[0] = '/';
+	}
+
+	return uri;
+}
+
+
+/**************************************************************************/
+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);
+	else
+		puri->part = NULL;
+
+	if (uri)
+		puri->uri = g_strdup (uri);
+	else
+		puri->uri = NULL;
+
+	puri->cid = NULL;
+
+	puri->validity_type = 0;
+	puri->validity = NULL;
+	puri->validity_parent = NULL;
+
+	puri->write_func = NULL;
+	puri->widget_func = NULL;
+
+	puri->free = NULL;
+
+	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->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,
+		      GCancellable *cancellable)
+{
+	const EMFormatHandler *handler;
+	CamelContentType *ct;
+	gchar *mime_type;
+
+	g_return_if_fail (puri);
+	g_return_if_fail (CAMEL_IS_STREAM (stream));
+
+	ct = camel_mime_part_get_content_type (puri->part);
+	if (ct) {
+		mime_type = camel_content_type_simple (ct);
+	} else {
+		mime_type = (gchar *) "plain/text";
+	}
+
+	if (puri->write_func) {
+		puri->write_func (puri->emf, puri, stream, cancellable);
+	} else {
+		handler = em_format_find_handler (puri->emf, mime_type);
+		if (handler && handler->write_func) {
+			handler->write_func (puri->emf,
+					puri, stream, cancellable);
+		}
 	}
 
-	return g_strdup_printf ("mail://%s/%s/%s?formatter=%d",
-		camel_service_get_uid (CAMEL_SERVICE (store)),
-		camel_folder_get_full_name (folder),
-		message_uid,
-		GPOINTER_TO_INT (emf));
+	if (ct)
+		g_free (mime_type);
 }
diff --git a/em-format/em-format.h b/em-format/em-format.h
index 8ebd0b6..02767de 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,96 +48,66 @@
 
 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 void	(*EMFormatFunc)			(EMFormat *emf,
-						 CamelStream *stream,
-						 CamelMimePart *mime_part,
-						 const EMFormatHandler *info,
-						 GCancellable *cancellable,
-						 gboolean is_fallback);
+typedef void	(*EMFormatParseFunc)	(EMFormat *emf,
+					 CamelMimePart *part,
+					 GString *part_id,
+					 EMFormatParserInfo *info,
+					 GCancellable *cancellable);
+typedef void	(*EMFormatWriteFunc)	(EMFormat *emf,
+					 EMFormatPURI *puri,
+					 CamelStream *stream,
+					 GCancellable *cancellable);
+typedef GtkWidget*	(*EMFormatWidgetFunc)
+					(EMFormat *emf,
+					 EMFormatPURI *puri,
+					 GCancellable *cancellable);
 
-typedef enum {
-	EM_FORMAT_MODE_NORMAL,
-	EM_FORMAT_MODE_ALLHEADERS,
-	EM_FORMAT_MODE_SOURCE
-} EMFormatMode;
 
-/**
- * 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 struct _EMFormatHandler EMFormatHandler;
+
 typedef enum {
 	EM_FORMAT_HANDLER_INLINE = 1 << 0,
 	EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1 << 1
 } 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.
- *
- **/
 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;
-
-	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 */
-
-	EMFormatPURIFunc func;
-	CamelMimePart *part;
+ * Use this struct to pass additional information between
+ * EMFormatParseFunc's.
+ * Much cleaner then setting public property of EMFormat.
+ */
+struct _EMFormatParserInfo {
+	const EMFormatHandler *handler;
 
-	guint use_count;	/* used by multipart/related to see if it was accessed */
+	/* EM_FORMAT_VALIDITY_* flags */
+	guint32 validity_type;
+	CamelCipherValidity *validity;
 };
 
 struct _EMFormatHeader {
@@ -151,92 +118,42 @@ struct _EMFormatHeader {
 #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 _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;
-
-	/* The current message */
-	CamelMimeMessage *message;
-
-	CamelFolder *folder;
-	gchar *uid;
-
-	/* 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;
-
-	/* If empty, then all. */
-	GQueue header_list;
 
-	/* Used for authentication when required. */
-	CamelSession *session;
+struct _EMFormatPURI {
+	CamelMimePart *part;
 
-	/* Content-Base header or absolute Content-Location, for any part. */
-	CamelURL *base;
+	EMFormat *emf;
+	EMFormatWriteFunc write_func;
+	EMFormatWidgetFunc widget_func;
 
-	/* If we snooped an application/octet-stream, what we snooped. */
-	const gchar *snoop_mime_type;
+	gchar *uri;
+	gchar *cid;
 
-	/* For validity enveloping. */
-	CamelCipherValidity *valid;
-	CamelCipherValidity *valid_parent;
+	/* EM_FORMAT_VALIDITY_* flags */
+	guint32 validity_type;
+	CamelCipherValidity *validity;
+	CamelCipherValidity *validity_parent;
 
-	/* For checking whether we found any signed or encrypted parts. */
-	guint32 validity_found;
+	gboolean is_attachment;
 
-	/* For forcing inlining. */
-	GHashTable *inline_table;
+	void (*free)(EMFormatPURI *puri); /* optional callback for freeing user-fields */
+};
 
-	/* Global URI lookup table for message. */
-	GHashTable *pending_uri_table;
+struct _EMFormat {
+	GObject parent;
+	EMFormatPrivate *priv;
 
-	/* 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;
+	CamelMimeMessage *message;
+	CamelFolder *folder;
+	gchar *message_uid;
 
-	/* The current level to search from. */
-	GNode *pending_uri_level;
+	/* 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;
 
-	EMFormatMode mode;		/* source/headers/etc */
-	gchar *charset;			/* charset override */
-	gchar *default_charset;		/* charset fallback */
-	gboolean composer;		/* formatting from composer? */
-	gboolean print;			/* formatting for printing? */
+	/* If empty, then all. */
+	GQueue header_list;
 };
 
 struct _EMFormatClass {
@@ -244,192 +161,142 @@ 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);
-
-	/* 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);
-
-	/* 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);
-
-	/* signals */
-	/* complete, alternative to polling busy, for asynchronous work */
-	void		(*complete)		(EMFormat *emf);
+	void 		(*format_error) 			(EMFormat *emf,
+											 const gchar *message);
+
+	void		(*parse)					(EMFormat *emf,
+											 CamelMimeMessage *message,
+											 CamelFolder *folder,
+											 GCancellable *cancellable);
+	gboolean 	(*is_inline)				(EMFormat *emf,
+											 const gchar *part_id,
+											 CamelMimePart *part,
+											 const EMFormatHandler *handler);
+	void		(*format_attachment)		(EMFormat *emf,
+											 EMFormatPURI *puri,
+											 GCancellable *cancellable);
+	void		(*format_optional)			(EMFormat *emf,
+											 EMFormatPURI *puri,
+											 CamelStream *mstream,
+											 GCancellable *cancellable);
+	void		(*format_secure)			(EMFormat *emf,
+										 	 EMFormatPURI *puri,
+											 GCancellable *cancellable);
+	void		(*format_source)			(EMFormat *emf,
+							 				 CamelStream *stream,
+											 GCancellable *cancellable);
 };
 
-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_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);
-
-gchar *		em_format_build_mail_uri	(CamelFolder *folder,
-						 const gchar *message_uid,
-						 const gchar *part_uid,
-						 EMFormat *emf);
-
-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_add_header 		(EMFormat *emf,
+											 const gchar *name,
+											 guint32 flags);
+void 			em_format_push_level		(EMFormat *emf);
+void 			em_format_pull_level		(EMFormat *emf);
+void			em_format_add_puri			(EMFormat *emf,
+											 EMFormatPURI *puri);
+
+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_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);
+
+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);
+void			em_format_format_source		(EMFormat *emf,
+											 CamelStream *stream,
+											 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);
+
+/* 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,
+											 GCancellable *cancellable);
+
+
+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,
+											 GCancellable *cancellable);
 
 #endif /* EM_FORMAT_H */



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