[anjal] Ultra new mail rendering for Anjal.



commit 97f5b34e2ca5b11ea5975bba501c901020c56230
Author: Srinivasa Ragavan <sragavan novell com>
Date:   Tue Jun 16 12:18:55 2009 +0530

    Ultra new mail rendering for Anjal.

 src/em-format-mail-display.c | 1968 +++++++++++++++++++++++++++++++++++++
 src/em-format-mail-display.h |  122 +++
 src/em-format-mail.c         | 2193 ++++++++++++++++++++++++++++++++++++++++++
 src/em-format-mail.h         |  293 ++++++
 4 files changed, 4576 insertions(+), 0 deletions(-)
---
diff --git a/src/em-format-mail-display.c b/src/em-format-mail-display.c
new file mode 100644
index 0000000..1353a98
--- /dev/null
+++ b/src/em-format-mail-display.c
@@ -0,0 +1,1968 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gstdio.h>
+#include <gdk/gdkkeysyms.h>
+
+#ifdef G_OS_WIN32
+/* Work around 'DATADIR' and 'interface' lossage in <windows.h> */
+#define DATADIR crap_DATADIR
+#include <windows.h>
+#undef DATADIR
+#undef interface
+#endif
+
+#include <glade/glade.h>
+
+#include <glib/gi18n.h>
+
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-internet-address.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-cipher-context.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-string-utils.h>
+#include <camel/camel-operation.h>
+
+#include <misc/e-cursors.h>
+#include <e-util/e-util.h>
+
+#include <libedataserver/e-flag.h>
+#include <libedataserver/e-msgport.h>
+#include <e-util/e-dialog-utils.h>
+#include <e-util/e-icon-factory.h>
+
+#ifdef HAVE_NSS
+#include "certificate-viewer.h"
+#include "e-cert-db.h"
+#endif
+
+#include "mail/mail-config.h"
+#include "misc/e-attachment-button.h"
+#include "misc/e-attachment-view.h"
+#include "mail/e-mail-attachment-bar.h"
+#include "em-format-mail-display.h"
+#include "mail/em-icon-stream.h"
+#include "mail/em-utils.h"
+#include "mail/em-popup.h"
+#include "misc/e-icon-entry.h"
+#include "mail-message-view.h"
+#include "mail-utils.h"
+#include "mail/mail-mt.h"
+
+#ifdef G_OS_WIN32
+/* Undefine the similar macro from <pthread.h>,it doesn't check if
+ * localtime() returns NULL.
+ */
+#undef localtime_r
+
+/* The localtime() in Microsoft's C library is MT-safe */
+#define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
+#endif
+
+#define d(x)
+
+struct _EMFormatMailDisplayPrivate {
+	GtkWidget *attachment_view;  /* weak reference */
+};
+
+#if 0
+static gint efhd_html_button_press_event (GtkWidget *widget, GdkEventButton *event, EMFormatMailDisplay *efh);
+static void efhd_html_link_clicked (GtkHTML *html, const gchar *url, EMFormatMailDisplay *efhd);
+static void efhd_html_on_url (GtkHTML *html, const gchar *url, EMFormatMailDisplay *efhd);
+#endif
+
+static void efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri);
+static GtkWidget * efhd_attachment_image(EMFormatMail *efh, GtkWidget *eb, EMFormatMailPObject *pobject);
+static void efhd_message_add_bar(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
+static void efwd_draw_in_main_thread (EMFormatMailDisplay *efwd, EMFormatMailPObject *pobject);
+
+struct _attach_puri {
+	EMFormatPURI puri;
+
+	const EMFormatHandler *handle;
+
+	const gchar *snoop_mime_type;
+	char *mime_type;
+	
+	/* for the > and V buttons */
+	GtkWidget *forward, *down;
+	/* currently no way to correlate this data to the frame :( */
+	//GtkHTML *frame;
+	CamelStream *output;
+	GtkWidget *child_box;	
+	guint shown:1;
+	guint rendered:1;
+
+	/* Embedded Frame */
+	GtkWidget *html;
+	EMFormatMailPObject *pobject;
+	//GtkHTMLEmbedded *html;
+
+	/* Attachment */
+	EAttachment *attachment;
+
+	/* image stuff */
+	gint fit_width;
+	gint fit_height;
+	GtkImage *image;
+	GtkWidget *event_box;
+
+	/* Optional Text Mem Stream */
+	CamelStreamMem *mstream;
+
+	/* Signed / Encrypted */
+        camel_cipher_validity_sign_t sign;
+        camel_cipher_validity_encrypt_t encrypt;
+};
+
+
+//static void efhd_iframe_created(GtkHTML *html, GtkHTML *iframe, EMFormatMailDisplay *efh);
+/*static void efhd_url_requested(GtkHTML *html, const gchar *url, GtkHTMLStream *handle, EMFormatMailDisplay *efh);
+  static gboolean efhd_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatMailDisplay *efh);*/
+
+static void efhd_message_prefix(EMFormat *emf, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info);
+
+static const EMFormatHandler *efhd_find_handler(EMFormat *emf, const gchar *mime_type);
+static void efhd_format_clone(EMFormat *, CamelFolder *folder, const gchar *, CamelMimeMessage *msg, EMFormat *);
+static void efhd_format_error(EMFormat *emf, CamelStream *stream, const gchar *txt);
+static void efhd_format_source(EMFormat *, CamelStream *, CamelMimePart *);
+static void efhd_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const gchar *, const EMFormatHandler *);
+// static void efhd_format_optional(EMFormat *, CamelStream *, CamelMimePart *, CamelStream *);
+static void efhd_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid);
+gboolean efmd_mnemonic_show_bar (GtkWidget *widget, gboolean focus, GtkWidget *efwd);
+
+static void efhd_builtin_init(EMFormatMailDisplayClass *efhc);
+
+enum {
+	EFHD_LINK_CLICKED,
+	EFHD_POPUP_EVENT,
+	EFHD_ON_URL,
+	EFHD_LAST_SIGNAL
+};
+
+static guint efhd_signals[EFHD_LAST_SIGNAL] = { 0 };
+
+static EMFormatMailClass *efhd_parent;
+static EMFormatClass *efhd_format_class;
+
+#if 0
+static void
+efhd_gtkhtml_realise(GtkHTML *html, EMFormatMailDisplay *efhd)
+{
+	GtkStyle *style;
+
+	/* FIXME: does this have to be re-done every time we draw? */
+
+	/* My favorite thing to do... muck around with colors so we respect people's stupid themes.
+	   However, we only do this if we are rendering to the screen -- we ignore the theme
+	   when we are printing. */
+	style = gtk_widget_get_style((GtkWidget *)html);
+	if (style) {
+		gint state = GTK_WIDGET_STATE(html);
+		gushort r, g, b;
+
+		r = style->fg[state].red >> 8;
+		g = style->fg[state].green >> 8;
+		b = style->fg[state].blue >> 8;
+
+		efhd->formathtml.header_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+
+		r = style->bg[state].red >> 8;
+		g = style->bg[state].green >> 8;
+		b = style->bg[state].blue >> 8;
+
+		efhd->formathtml.body_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+
+		r = style->dark[state].red >> 8;
+		g = style->dark[state].green >> 8;
+		b = style->dark[state].blue >> 8;
+
+		efhd->formathtml.frame_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+
+		r = style->base[GTK_STATE_NORMAL].red >> 8;
+		g = style->base[GTK_STATE_NORMAL].green >> 8;
+		b = style->base[GTK_STATE_NORMAL].blue >> 8;
+
+		efhd->formathtml.content_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+
+		r = style->text[state].red >> 8;
+		g = style->text[state].green >> 8;
+		b = style->text[state].blue >> 8;
+
+		efhd->formathtml.text_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+#undef DARKER
+	}
+}
+
+static void
+efhd_gtkhtml_style_set(GtkHTML *html, GtkStyle *old, EMFormatMailDisplay *efhd)
+{
+	efhd_gtkhtml_realise(html, efhd);
+	em_format_redraw((EMFormat *)efhd);
+}
+
+#endif
+
+static void
+efhd_init(GObject *o)
+{
+	EMFormatMailDisplay *efhd = (EMFormatMailDisplay *)o;
+#define efh ((EMFormatMail *)efhd)
+
+	efhd->priv = g_malloc0(sizeof(*efhd->priv));
+#if 0
+	g_signal_connect(efh->html, "realize", G_CALLBACK(efhd_gtkhtml_realise), o);
+	g_signal_connect(efh->html, "style-set", G_CALLBACK(efhd_gtkhtml_style_set), o);
+#endif	
+	/* we want to convert url's etc */
+	efh->text_html_flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+#undef efh
+
+	efhd->nobar = getenv("EVOLUTION_NO_BAR") != NULL;
+}
+
+static void
+efhd_finalise(GObject *o)
+{
+	EMFormatMailDisplay *efhd = (EMFormatMailDisplay *)o;
+
+	/* check pending stuff */
+
+	g_free(efhd->priv);
+
+	((GObjectClass *)efhd_parent)->finalize(o);
+}
+
+static gboolean
+efhd_bool_accumulator(GSignalInvocationHint *ihint, GValue *out, const GValue *in, gpointer data)
+{
+	gboolean val = g_value_get_boolean(in);
+
+	g_value_set_boolean(out, val);
+
+	return !val;
+}
+
+static void
+efhd_class_init(GObjectClass *klass)
+{
+	((EMFormatClass *)klass)->find_handler = efhd_find_handler;
+	((EMFormatClass *)klass)->format_clone = efhd_format_clone;
+	((EMFormatClass *)klass)->format_error = efhd_format_error;
+	((EMFormatClass *)klass)->format_source = efhd_format_source;
+	((EMFormatClass *)klass)->format_attachment = efhd_format_attachment;
+//	((EMFormatClass *)klass)->format_optional = efhd_format_optional;
+	((EMFormatClass *)klass)->format_secure = efhd_format_secure;
+
+	klass->finalize = efhd_finalise;
+
+	efhd_signals[EFHD_LINK_CLICKED] =
+		g_signal_new("link_clicked",
+			     G_TYPE_FROM_CLASS(klass),
+			     G_SIGNAL_RUN_LAST,
+			     G_STRUCT_OFFSET(EMFormatMailDisplayClass, link_clicked),
+			     NULL, NULL,
+			     g_cclosure_marshal_VOID__POINTER,
+			     G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	efhd_signals[EFHD_POPUP_EVENT] =
+		g_signal_new("popup_event",
+			     G_TYPE_FROM_CLASS(klass),
+			     G_SIGNAL_RUN_LAST,
+			     G_STRUCT_OFFSET(EMFormatMailDisplayClass, popup_event),
+			     efhd_bool_accumulator, NULL,
+			     e_marshal_BOOLEAN__BOXED_POINTER_POINTER,
+			     G_TYPE_BOOLEAN, 3,
+			     GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
+			     G_TYPE_POINTER, G_TYPE_POINTER);
+
+	efhd_signals[EFHD_ON_URL] =
+		g_signal_new("on_url",
+			     G_TYPE_FROM_CLASS(klass),
+			     G_SIGNAL_RUN_LAST,
+			     G_STRUCT_OFFSET(EMFormatMailDisplayClass, on_url),
+			     NULL, NULL,
+			     g_cclosure_marshal_VOID__STRING,
+			     G_TYPE_NONE, 1,
+			     G_TYPE_STRING);
+
+	efhd_builtin_init((EMFormatMailDisplayClass *)klass);
+}
+
+GType
+em_format_mail_display_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(EMFormatMailDisplayClass),
+			NULL, NULL,
+			(GClassInitFunc)efhd_class_init,
+			NULL, NULL,
+			sizeof(EMFormatMailDisplay), 0,
+			(GInstanceInitFunc)efhd_init
+		};
+		efhd_parent = g_type_class_ref(em_format_mail_get_type());
+		efhd_format_class = g_type_class_ref(em_format_get_type());
+		type = g_type_register_static(em_format_mail_get_type(), "EMFormatMailDisplay", &info, 0);
+	}
+
+	return type;
+}
+
+static gboolean
+efhd_scroll_event(GtkWidget *w, GdkEventScroll *event, EMFormatMailDisplay *efhd)
+{
+	if(event->state & GDK_CONTROL_MASK)
+	{
+		if(event->direction == GDK_SCROLL_UP)
+		{
+//			gtk_html_zoom_in (efhd->formathtml.html);
+		}
+		else if(event->direction == GDK_SCROLL_DOWN)
+		{
+//			gtk_html_zoom_out (efhd->formathtml.html);
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
+EMFormatMailDisplay *em_format_mail_display_new(void)
+{
+	EMFormatMailDisplay *efhd;
+
+	efhd = g_object_new(em_format_mail_display_get_type(), 0);
+
+#if 0
+	g_signal_connect(efhd->formathtml.html, "iframe_created", G_CALLBACK(efhd_iframe_created), efhd);
+	g_signal_connect(efhd->formathtml.html, "link_clicked", G_CALLBACK(efhd_html_link_clicked), efhd);
+	g_signal_connect(efhd->formathtml.html, "on_url", G_CALLBACK(efhd_html_on_url), efhd);
+	g_signal_connect(efhd->formathtml.html, "button_press_event", G_CALLBACK(efhd_html_button_press_event), efhd);
+	g_signal_connect(efhd->formathtml.html,"scroll_event", G_CALLBACK(efhd_scroll_event), efhd);
+#endif
+
+	return efhd;
+}
+
+void em_format_mail_display_goto_anchor(EMFormatMailDisplay *efhd, const gchar *name)
+{
+	printf("FIXME: go to anchor '%s'\n", name);
+}
+
+void em_format_mail_display_set_animate(EMFormatMailDisplay *efhd, gboolean state)
+{
+	efhd->animate = state;
+//	gtk_html_set_animate(((EMFormatMail *)efhd)->html, state);
+}
+
+void em_format_mail_display_set_caret_mode(EMFormatMailDisplay *efhd, gboolean state)
+{
+	efhd->caret_mode = state;
+//	gtk_html_set_caret_mode(((EMFormatMail *)efhd)->html, state);
+}
+
+void
+em_format_mail_display_cut (EMFormatMailDisplay *efhd)
+{
+//	gtk_html_cut (((EMFormatMail *) efhd)->html);
+}
+
+void
+em_format_mail_display_copy (EMFormatMailDisplay *efhd)
+{
+//	gtk_html_copy (((EMFormatMail *) efhd)->html);
+}
+
+void
+em_format_mail_display_paste (EMFormatMailDisplay *efhd)
+{
+//	gtk_html_paste (((EMFormatMail *) efhd)->html, FALSE);
+}
+
+void
+em_format_mail_display_zoom_in (EMFormatMailDisplay *efhd)
+{
+//	gtk_html_zoom_in (((EMFormatMail *) efhd)->html);
+}
+
+void
+em_format_mail_display_zoom_out (EMFormatMailDisplay *efhd)
+{
+//	gtk_html_zoom_out (((EMFormatMail *) efhd)->html);
+}
+
+void
+em_format_mail_display_zoom_reset (EMFormatMailDisplay *efhd)
+{
+//	gtk_html_zoom_reset (((EMFormatMail *) efhd)->html);
+}
+
+/* ********************************************************************** */
+#if 0
+static void
+efhd_iframe_created(GtkHTML *html, GtkHTML *iframe, EMFormatMailDisplay *efh)
+{
+	d(printf("Iframe created %p ... \n", iframe));
+
+	g_signal_connect(iframe, "button_press_event", G_CALLBACK (efhd_html_button_press_event), efh);
+
+	return;
+}
+#endif
+#if 0
+static void
+efhd_get_uri_puri (GtkWidget *html, GdkEventButton *event, EMFormatMailDisplay *efhd, gchar **uri, EMFormatPURI **puri)
+{
+	gchar *url, *img_url;
+
+	g_return_if_fail (html != NULL);
+	g_return_if_fail (GTK_IS_HTML (html));
+	g_return_if_fail (efhd != NULL);
+
+	if (event) {
+		url = gtk_html_get_url_at (GTK_HTML (html), event->x, event->y);
+		img_url = gtk_html_get_image_src_at (GTK_HTML (html), event->x, event->y);
+	} else {
+		url = gtk_html_get_cursor_url (GTK_HTML (html));
+		img_url = gtk_html_get_cursor_image_src (GTK_HTML (html));
+	}
+
+	if (img_url) {
+		if (!(strstr (img_url, "://") || g_ascii_strncasecmp (img_url, "cid:", 4) == 0)) {
+			gchar *u = g_filename_to_uri (img_url, NULL, NULL);
+			g_free (img_url);
+			img_url = u;
+		}
+	}
+
+	if (puri) {
+		if (url)
+			*puri = em_format_find_puri ((EMFormat *)efhd, url);
+
+		if (!*puri && img_url)
+			*puri = em_format_find_puri ((EMFormat *)efhd, img_url);
+	}
+
+	if (uri) {
+		*uri = NULL;
+		if (img_url && g_ascii_strncasecmp (img_url, "cid:", 4) != 0) {
+			if (url)
+				*uri = g_strdup_printf ("%s\n%s", url, img_url);
+			else {
+				*uri = img_url;
+				img_url = NULL;
+			}
+		} else {
+			*uri = url;
+			url = NULL;
+		}
+	}
+
+	g_free (url);
+	g_free (img_url);
+}
+#endif
+static gint
+efhd_html_button_press_event (GtkWidget *widget, GdkEventButton *event, EMFormatMailDisplay *efhd)
+{
+	gchar *uri = NULL;
+	EMFormatPURI *puri = NULL;
+	gboolean res = FALSE;
+
+	if (event->button != 3)
+		return FALSE;
+
+	d(printf("popup button pressed\n"));
+
+//	efhd_get_uri_puri (widget, event, efhd, &uri, &puri);
+
+	if (uri && !strncmp (uri, "##", 2)) {
+		g_free (uri);
+		return TRUE;
+	}
+
+	g_signal_emit((GtkObject *)efhd, efhd_signals[EFHD_POPUP_EVENT], 0, event, uri, puri?puri->part:NULL, &res);
+
+	g_free (uri);
+
+	return res;
+}
+
+gboolean
+em_format_mail_display_popup_menu (EMFormatMailDisplay *efhd)
+{
+//	GtkHTML *html;
+	gchar *uri = NULL;
+	EMFormatPURI *puri = NULL;
+	gboolean res = FALSE;
+
+//	html = efhd->formathtml.html;
+
+//	efhd_get_uri_puri (GTK_WIDGET (html), NULL, efhd, &uri, &puri);
+
+	g_signal_emit ((GtkObject *)efhd, efhd_signals[EFHD_POPUP_EVENT], 0, NULL, uri, puri?puri->part:NULL, &res);
+
+	g_free (uri);
+
+	return res;
+}
+#if 0
+static void
+efhd_html_link_clicked (GtkHTML *html, const gchar *url, EMFormatMailDisplay *efhd)
+{
+	d(printf("link clicked event '%s'\n", url));
+	if (url && !strncmp(url, "##", 2)) {
+		if (!strcmp (url, "##TO##"))
+			if (!(((EMFormatMail *) efhd)->header_wrap_flags & EM_FORMAT_HTML_HEADER_TO))
+				((EMFormatMail *) efhd)->header_wrap_flags |= EM_FORMAT_HTML_HEADER_TO;
+			else
+				((EMFormatMail *) efhd)->header_wrap_flags &= ~EM_FORMAT_HTML_HEADER_TO;
+		else if (!strcmp (url, "##CC##"))
+			if (!(((EMFormatMail *) efhd)->header_wrap_flags & EM_FORMAT_HTML_HEADER_CC))
+				((EMFormatMail *) efhd)->header_wrap_flags |= EM_FORMAT_HTML_HEADER_CC;
+			else
+				((EMFormatMail *) efhd)->header_wrap_flags &= ~EM_FORMAT_HTML_HEADER_CC;
+		else if (!strcmp (url, "##BCC##")) {
+			if (!(((EMFormatMail *) efhd)->header_wrap_flags & EM_FORMAT_HTML_HEADER_BCC))
+				((EMFormatMail *) efhd)->header_wrap_flags |= EM_FORMAT_HTML_HEADER_BCC;
+			else
+				((EMFormatMail *) efhd)->header_wrap_flags &= ~EM_FORMAT_HTML_HEADER_BCC;
+		}
+		em_format_redraw((EMFormat *)efhd);
+	} else
+	    g_signal_emit((GObject *)efhd, efhd_signals[EFHD_LINK_CLICKED], 0, url);
+}
+
+static void
+efhd_html_on_url (GtkHTML *html, const gchar *url, EMFormatMailDisplay *efhd)
+{
+	d(printf("on_url event '%s'\n", url));
+
+	g_signal_emit((GObject *)efhd, efhd_signals[EFHD_ON_URL], 0, url);
+}
+#endif
+/* ********************************************************************** */
+
+/* TODO: move the dialogue elsehwere */
+/* FIXME: also in em-format-html.c */
+static const struct {
+	const gchar *icon, *shortdesc, *description;
+} smime_sign_table[5] = {
+	{ "stock_signature-bad", N_("Unsigned"), N_("This message is not signed. There is no guarantee that this message is authentic.") },
+	{ "stock_signature-ok", N_("Valid signature"), N_("This message is signed and is valid meaning that it is very likely that this message is authentic.") },
+	{ "stock_signature-bad", N_("Invalid signature"), N_("The signature of this message cannot be verified, it may have been altered in transit.") },
+	{ "stock_signature", N_("Valid signature, but cannot verify sender"), N_("This message is signed with a valid signature, but the sender of the message cannot be verified.") },
+	{ "stock_signature-bad", N_("Signature exists, but need public key"), N_("This message is signed with a signature, but there is no corresponding public key.") },
+
+};
+
+static const struct {
+	const gchar *icon, *shortdesc, *description;
+} smime_encrypt_table[4] = {
+	{ "stock_lock-broken", N_("Unencrypted"), N_("This message is not encrypted. Its content may be viewed in transit across the Internet.") },
+	{ "stock_lock-ok", N_("Encrypted, weak"), N_("This message is encrypted, but with a weak encryption algorithm. It would be difficult, but not impossible for an outsider to view the content of this message in a practical amount of time.") },
+	{ "stock_lock-ok", N_("Encrypted"), N_("This message is encrypted.  It would be difficult for an outsider to view the content of this message.") },
+	{ "stock_lock-ok", N_("Encrypted, strong"), N_("This message is encrypted, with a strong encryption algorithm. It would be very difficult for an outsider to view the content of this message in a practical amount of time.") },
+};
+
+static const gchar *smime_sign_colour[5] = {
+	"", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\"",""
+};
+
+struct _smime_pobject {
+	EMFormatMailPObject object;
+
+	gint signature;
+	CamelCipherValidity *valid;
+	GtkWidget *widget;
+};
+
+static void
+efhd_xpkcs7mime_free(EMFormatMailPObject *o)
+{
+	struct _smime_pobject *po = (struct _smime_pobject *)o;
+
+	if (po->widget)
+		gtk_widget_destroy(po->widget);
+	camel_cipher_validity_free(po->valid);
+}
+
+static void
+efhd_xpkcs7mime_info_response(GtkWidget *w, guint button, struct _smime_pobject *po)
+{
+	gtk_widget_destroy(w);
+	po->widget = NULL;
+}
+
+#ifdef HAVE_NSS
+static void
+efhd_xpkcs7mime_viewcert_foad(GtkWidget *w, guint button, struct _smime_pobject *po)
+{
+	gtk_widget_destroy(w);
+}
+
+static void
+efhd_xpkcs7mime_viewcert_clicked(GtkWidget *button, struct _smime_pobject *po)
+{
+	CamelCipherCertInfo *info = g_object_get_data((GObject *)button, "e-cert-info");
+	ECertDB *db = e_cert_db_peek();
+	ECert *ec = NULL;
+
+	if (info->email)
+		ec = e_cert_db_find_cert_by_email_address(db, info->email, NULL);
+
+	if (ec == NULL && info->name)
+		ec = e_cert_db_find_cert_by_nickname(db, info->name, NULL);
+
+	if (ec != NULL) {
+		GtkWidget *w = certificate_viewer_show(ec);
+
+		/* oddly enough certificate_viewer_show doesn't ... */
+		gtk_widget_show(w);
+		g_signal_connect(w, "response", G_CALLBACK(efhd_xpkcs7mime_viewcert_foad), po);
+
+		if (w && po->widget)
+			gtk_window_set_transient_for((GtkWindow *)w, (GtkWindow *)po->widget);
+
+		g_object_unref(ec);
+	} else {
+		g_warning("can't find certificate for %s <%s>", info->name?info->name:"", info->email?info->email:"");
+	}
+}
+#endif
+
+static void
+efhd_xpkcs7mime_add_cert_table(GtkWidget *vbox, CamelDList *certlist, struct _smime_pobject *po)
+{
+	CamelCipherCertInfo *info = (CamelCipherCertInfo *)certlist->head;
+	GtkTable *table = (GtkTable *)gtk_table_new(camel_dlist_length(certlist), 2, FALSE);
+	gint n = 0;
+
+	while (info->next) {
+		gchar *la = NULL;
+		const gchar *l = NULL;
+
+		if (info->name) {
+			if (info->email && strcmp(info->name, info->email) != 0)
+				l = la = g_strdup_printf("%s <%s>", info->name, info->email);
+			else
+				l = info->name;
+		} else {
+			if (info->email)
+				l = info->email;
+		}
+
+		if (l) {
+			GtkWidget *w;
+#if defined(HAVE_NSS)
+			ECertDB *db = e_cert_db_peek();
+			ECert *ec = NULL;
+#endif
+			w = gtk_label_new(l);
+			gtk_misc_set_alignment((GtkMisc *)w, 0.0, 0.5);
+			g_free(la);
+			gtk_table_attach(table, w, 0, 1, n, n+1, GTK_FILL, GTK_FILL, 3, 3);
+#if defined(HAVE_NSS)
+			w = gtk_button_new_with_mnemonic(_("_View Certificate"));
+			gtk_table_attach(table, w, 1, 2, n, n+1, 0, 0, 3, 3);
+			g_object_set_data((GObject *)w, "e-cert-info", info);
+			g_signal_connect(w, "clicked", G_CALLBACK(efhd_xpkcs7mime_viewcert_clicked), po);
+
+			if (info->email)
+				ec = e_cert_db_find_cert_by_email_address(db, info->email, NULL);
+			if (ec == NULL && info->name)
+				ec = e_cert_db_find_cert_by_nickname(db, info->name, NULL);
+
+			if (ec == NULL)
+				gtk_widget_set_sensitive(w, FALSE);
+			else
+				g_object_unref(ec);
+#else
+			w = gtk_label_new (_("This certificate is not viewable"));
+			gtk_table_attach(table, w, 1, 2, n, n+1, 0, 0, 3, 3);
+#endif
+			n++;
+		}
+
+		info = info->next;
+	}
+
+	gtk_box_pack_start((GtkBox *)vbox, (GtkWidget *)table, TRUE, TRUE, 6);
+}
+
+static void
+efhd_xpkcs7mime_validity_clicked(GtkWidget *button, EMFormatMailPObject *pobject)
+{
+	struct _smime_pobject *po = (struct _smime_pobject *)pobject;
+	GladeXML *xml;
+	GtkWidget *vbox, *w;
+	gchar *gladefile;
+
+	if (po->widget)
+		/* FIXME: window raise? */
+		return;
+
+	gladefile = g_build_filename (
+#ifndef _WIN32
+#ifdef EVOLUTION_2_26
+				      EVOLUTION226_PRIVDATADIR"glade/",
+#else
+				      EVOLUTION228_PRIVDATADIR"glade/",
+#endif
+#else
+				      _e_get_gladedir (),
+#endif
+				      "mail-dialogs.glade",
+				      NULL);
+	xml = glade_xml_new(gladefile, "message_security_dialog", NULL);
+	g_free (gladefile);
+
+	po->widget = glade_xml_get_widget(xml, "message_security_dialog");
+
+	vbox = glade_xml_get_widget(xml, "signature_vbox");
+	w = gtk_label_new (_(smime_sign_table[po->valid->sign.status].description));
+	gtk_misc_set_alignment((GtkMisc *)w, 0.0, 0.5);
+	gtk_label_set_line_wrap((GtkLabel *)w, TRUE);
+	gtk_box_pack_start((GtkBox *)vbox, w, TRUE, TRUE, 6);
+	if (po->valid->sign.description) {
+		GtkTextBuffer *buffer;
+
+		buffer = gtk_text_buffer_new(NULL);
+		gtk_text_buffer_set_text(buffer, po->valid->sign.description, strlen(po->valid->sign.description));
+		w = g_object_new(gtk_scrolled_window_get_type(),
+				 "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
+				 "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
+				 "shadow_type", GTK_SHADOW_IN,
+				 "child", g_object_new(gtk_text_view_get_type(),
+						       "buffer", buffer,
+						       "cursor_visible", FALSE,
+						       "editable", FALSE,
+						       "width_request", 500,
+						       "height_request", 160,
+						       NULL),
+				 NULL);
+		g_object_unref(buffer);
+
+		gtk_box_pack_start((GtkBox *)vbox, w, TRUE, TRUE, 6);
+	}
+
+	if (!camel_dlist_empty(&po->valid->sign.signers))
+		efhd_xpkcs7mime_add_cert_table(vbox, &po->valid->sign.signers, po);
+
+	gtk_widget_show_all(vbox);
+
+	vbox = glade_xml_get_widget(xml, "encryption_vbox");
+	w = gtk_label_new(_(smime_encrypt_table[po->valid->encrypt.status].description));
+	gtk_misc_set_alignment((GtkMisc *)w, 0.0, 0.5);
+	gtk_label_set_line_wrap((GtkLabel *)w, TRUE);
+	gtk_box_pack_start((GtkBox *)vbox, w, TRUE, TRUE, 6);
+	if (po->valid->encrypt.description) {
+		GtkTextBuffer *buffer;
+
+		buffer = gtk_text_buffer_new(NULL);
+		gtk_text_buffer_set_text(buffer, po->valid->encrypt.description, strlen(po->valid->encrypt.description));
+		w = g_object_new(gtk_scrolled_window_get_type(),
+				 "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
+				 "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
+				 "shadow_type", GTK_SHADOW_IN,
+				 "child", g_object_new(gtk_text_view_get_type(),
+						       "buffer", buffer,
+						       "cursor_visible", FALSE,
+						       "editable", FALSE,
+						       "width_request", 500,
+						       "height_request", 160,
+						       NULL),
+				 NULL);
+		g_object_unref(buffer);
+
+		gtk_box_pack_start((GtkBox *)vbox, w, TRUE, TRUE, 6);
+	}
+
+	if (!camel_dlist_empty(&po->valid->encrypt.encrypters))
+		efhd_xpkcs7mime_add_cert_table(vbox, &po->valid->encrypt.encrypters, po);
+
+	gtk_widget_show_all(vbox);
+
+	g_object_unref(xml);
+
+	g_signal_connect(po->widget, "response", G_CALLBACK(efhd_xpkcs7mime_info_response), po);
+	gtk_widget_show(po->widget);
+}
+
+static GtkWidget *
+efhd_xpkcs7mime_button(EMFormatMail *efh, GtkWidget *eb, EMFormatMailPObject *pobject)
+{
+	GtkWidget *icon, *button, *box, *label;
+	struct _smime_pobject *po = (struct _smime_pobject *)pobject;
+	const gchar *icon_name;
+
+	/* FIXME: need to have it based on encryption and signing too */
+	if (po->valid->sign.status != 0)
+		icon_name = smime_sign_table[po->valid->sign.status].icon;
+	else
+		icon_name = smime_encrypt_table[po->valid->encrypt.status].icon;
+
+	icon = gtk_image_new_from_icon_name (
+		icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+	gtk_widget_show(icon);
+
+	button = gtk_button_new();
+	g_signal_connect(button, "clicked", G_CALLBACK(efhd_xpkcs7mime_validity_clicked), pobject);
+
+	gtk_container_add((GtkContainer *)button, icon);
+	gtk_widget_show(button);
+
+	box = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start ((GtkBox *)box, button, FALSE, FALSE, 6);
+	gtk_box_pack_start ((GtkBox *)eb, box, FALSE, FALSE, 0);
+	
+	if (po->valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
+		label = gtk_label_new(_(smime_sign_table[po->valid->sign.status].shortdesc));
+		gtk_box_pack_start ((GtkBox *)box, label, FALSE, FALSE, 3);
+	}
+
+	if (po->valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
+		 /* FIXME: Make way to show both these text in the next line */
+		/* if (po->valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
+			camel_stream_printf (stream, "<br>");
+		}*/
+		label = gtk_label_new(_(smime_encrypt_table[po->valid->encrypt.status].shortdesc));
+		gtk_box_pack_start ((GtkBox *)box, label, FALSE, FALSE, 3);
+	}
+
+	gtk_widget_show_all(box);
+	return NULL;
+}
+
+static void
+efhd_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid)
+{
+	/* Note: We call EMFormatClass directly, not EMFormatMail, our parent */
+	efhd_format_class->format_secure(emf, stream, part, valid);
+
+	if (emf->valid == valid
+	    && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
+		|| valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
+		gchar *classid;
+		struct _smime_pobject *pobj;
+
+		classid = g_strdup_printf("smime:///em-format-html/%s/icon/signed", emf->part_id->str);
+		pobj = (struct _smime_pobject *)em_format_mail_add_pobject((EMFormatMail *)emf, sizeof(*pobj), classid, part, efhd_xpkcs7mime_button);
+		pobj->valid = camel_cipher_validity_clone(valid);
+		pobj->object.free = efhd_xpkcs7mime_free;
+		pobj->object.body = mail_message_view_get_body(stream);
+		efwd_draw_in_main_thread ((EMFormatMailDisplay *)emf, (EMFormatMailPObject *)pobj);
+		
+		g_free(classid);
+	}
+}
+
+static void
+efhd_image(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *handle)
+{
+	gchar *classid;
+	struct _attach_puri *info;
+	EMFormatMailPObject *pobject;
+
+	classid = g_strdup_printf("image%s", ((EMFormat *)efh)->part_id->str);
+	printf("IMG %s\n", classid);
+	info = (struct _attach_puri *)em_format_add_puri((EMFormat *)efh, sizeof(*info), classid, part, efhd_attachment_frame);
+	pobject = em_format_mail_add_pobject(efh, sizeof(EMFormatMailPObject), classid, part, efhd_attachment_image);
+
+	info->handle = handle;
+	info->shown = TRUE;
+	info->rendered = TRUE;	
+	info->snoop_mime_type = ((EMFormat *) efh)->snoop_mime_type;
+#warning "fit image"	
+	if (camel_operation_cancel_check (NULL) || !info->puri.format /*|| !((EMFormatMail *)info->puri.format)->html*/) {
+		/* some fake value, we are cancelled anyway, thus doesn't matter */
+		info->fit_width = 256;
+	} else {
+
+/* 		info->fit_width = ((GtkWidget *)((EMFormatMail *)info->puri.format)->html)->allocation.width - 12;*/
+	}
+
+	info->pobject = pobject;
+	pobject->body = mail_message_view_get_body(stream);
+	efwd_draw_in_main_thread ((EMFormatMailDisplay *)efh, pobject);
+	g_free(classid);
+}
+
+/* ********************************************************************** */
+
+static EMFormatHandler type_builtin_table[] = {
+	{ (gchar *) "image/gif", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/jpeg", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/png", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-png", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/tiff", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-bmp", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/bmp", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/svg", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-cmu-raster", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-ico", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-portable-anymap", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-portable-bitmap", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-portable-graymap", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-portable-pixmap", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/x-xpixmap", (EMFormatFunc)efhd_image },
+
+	/* This is where one adds those busted, non-registered types,
+	   that some idiot mailer writers out there decide to pull out
+	   of their proverbials at random. */
+
+	{ (gchar *) "image/jpg", (EMFormatFunc)efhd_image },
+	{ (gchar *) "image/pjpeg", (EMFormatFunc)efhd_image },
+
+	{ (gchar *) "x-evolution/message/prefix", (EMFormatFunc)efhd_message_prefix },
+	{ (gchar *) "x-evolution/message/post-header", (EMFormatFunc)efhd_message_add_bar }
+};
+
+static void
+efhd_builtin_init(EMFormatMailDisplayClass *efhc)
+{
+	gint i;
+
+	for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+		em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
+
+static const EMFormatHandler *
+efhd_find_handler(EMFormat *emf, const gchar *mime_type)
+{
+	return ((EMFormatClass *) efhd_parent)->find_handler (emf, mime_type);
+}
+
+static void efhd_format_clone(EMFormat *emf, CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, EMFormat *src)
+{
+	if (emf != src)
+		((EMFormatMail *) emf)->header_wrap_flags = 0;
+
+	((EMFormatClass *)efhd_parent)->format_clone(emf, folder, uid, msg, src);
+}
+
+static void
+efhd_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+	CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)puri->part);
+
+	/* TODO: identical to efh_write_image */
+	d(printf("writing image '%s'\n", puri->cid));
+	camel_data_wrapper_decode_to_stream(dw, stream);
+	camel_stream_close(stream);
+}
+
+#warning "prefix won't be working. fix it."
+static void efhd_message_prefix(EMFormat *emf, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	const gchar *flag, *comp, *due;
+	time_t date;
+	gchar due_date[128];
+	struct tm due_tm;
+	gchar *iconpath;
+
+	if (emf->folder == NULL || emf->uid == NULL
+	    || (flag = camel_folder_get_message_user_tag(emf->folder, emf->uid, "follow-up")) == NULL
+	    || flag[0] == 0)
+		return;
+
+	/* header displayed for message-flags in mail display */
+	camel_stream_printf(stream, "<table border=1 width=\"100%%\" cellspacing=2 cellpadding=2><tr>");
+
+	comp = camel_folder_get_message_user_tag(emf->folder, emf->uid, "completed-on");
+
+	iconpath = e_icon_factory_get_icon_filename (comp && comp[0] ? "stock_flag-for-followup-done" : "stock_flag-for-followup", GTK_ICON_SIZE_MENU);
+	if (iconpath) {
+		CamelMimePart *iconpart;
+
+		iconpart = em_format_mail_file_part((EMFormatMail *)emf, "image/png", iconpath);
+		g_free (iconpath);
+		if (iconpart) {
+			gchar *classid;
+
+			classid = g_strdup_printf("icon:///em-format-html-display/%s/%s", emf->part_id->str, comp&&comp[0]?"comp":"uncomp");
+			camel_stream_printf(stream, "<td align=\"left\"><img src=\"%s\"></td>", classid);
+			(void)em_format_add_puri(emf, sizeof(EMFormatPURI), classid, iconpart, efhd_write_image);
+			g_free(classid);
+			camel_object_unref(iconpart);
+		}
+	}
+
+	camel_stream_printf(stream, "<td align=\"left\" width=\"100%%\">");
+
+	if (comp && comp[0]) {
+		date = camel_header_decode_date(comp, NULL);
+		localtime_r(&date, &due_tm);
+		e_utf8_strftime_fix_am_pm(due_date, sizeof (due_date), _("Completed on %B %d, %Y, %l:%M %p"), &due_tm);
+		camel_stream_printf(stream, "%s, %s", flag, due_date);
+	} else if ((due = camel_folder_get_message_user_tag(emf->folder, emf->uid, "due-by")) != NULL && due[0]) {
+		time_t now;
+
+		date = camel_header_decode_date(due, NULL);
+		now = time(NULL);
+		if (now > date)
+			camel_stream_printf(stream, "<b>%s</b>&nbsp;", _("Overdue:"));
+
+		localtime_r(&date, &due_tm);
+		e_utf8_strftime_fix_am_pm(due_date, sizeof (due_date), _("by %B %d, %Y, %l:%M %p"), &due_tm);
+		camel_stream_printf(stream, "%s %s", flag, due_date);
+	} else {
+		camel_stream_printf(stream, "%s", flag);
+	}
+
+	camel_stream_printf(stream, "</td></tr></table>");
+}
+
+/* TODO: if these aren't going to do anything should remove */
+static void efhd_format_error(EMFormat *emf, CamelStream *stream, const gchar *txt)
+{
+	((EMFormatClass *)efhd_parent)->format_error(emf, stream, txt);
+}
+
+static void efhd_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+	((EMFormatClass *)efhd_parent)->format_source(emf, stream, part);
+}
+
+/* ********************************************************************** */
+
+/* Checks on the widget whether it can be processed, based on the state of EMFormatMail.
+   The widget should have set "efh" data as the EMFormatMail instance. */
+static gboolean
+efhd_can_process_attachment (GtkWidget *button)
+{
+	EMFormatMail *efh;
+
+	if (!button)
+		return FALSE;
+
+	efh = g_object_get_data (G_OBJECT (button), "efh");
+
+	return efh && efh->state != EM_FORMAT_MAIL_STATE_RENDERING;
+}
+
+struct _inline_render_msg {
+	MailMsg base;
+
+	struct _attach_puri *info;
+};
+
+static void
+inline_render_exec (struct _inline_render_msg *m)
+{
+	struct _attach_puri *info = m->info;
+	EMFormatMailPObject *pobj = info->pobject;
+
+	camel_stream_printf (pobj->stream, "<HTML> <HEAD> </HEAD> <BODY>");
+
+	info->handle->handler(info->puri.format, pobj->stream, info->puri.part, info->handle);
+	camel_stream_printf (pobj->stream, "</BODY> </HTML>");
+	camel_stream_close (pobj->stream);
+	camel_object_unref(pobj->stream);
+	pobj->stream = NULL;
+}
+
+static void
+inline_render_free (struct _inline_render_msg *m)
+{
+}
+
+static MailMsgInfo inline_render_info = {
+	sizeof (struct _inline_render_msg),
+	(MailMsgDescFunc) NULL,
+	(MailMsgExecFunc) inline_render_exec,
+	(MailMsgDoneFunc) NULL,
+	(MailMsgFreeFunc) inline_render_free
+};
+
+static void
+efwd_set_inline (struct _attach_puri *info)
+{
+	struct _inline_render_msg *m;
+
+	m = mail_msg_new (&inline_render_info);
+	m->info = info;
+	mail_msg_unordered_push (m);
+	printf("Pushed %pn", info);
+}
+
+
+/* if it hasn't been processed yet, format the attachment */
+static void
+efhd_attachment_show(EPopup *ep, EPopupItem *item, gpointer data)
+{
+	struct _attach_puri *info = data;
+
+	printf("show attachment button called %p:%d %d\n", info, info->shown, info->rendered);
+
+	if (info->shown) {
+		gtk_widget_hide (info->child_box);
+		info->shown = FALSE;
+	} else {
+		gtk_widget_show (info->child_box);
+		info->shown = TRUE;
+	}
+
+	if (!info->rendered && info->child_box) {
+		efwd_set_inline(info);
+		info->rendered = TRUE;
+	}
+}
+
+static void
+efhd_attachment_button_expanded (GtkWidget *widget,
+                                 GParamSpec *pspec,
+                                 struct _attach_puri *info)
+{
+	if (!efhd_can_process_attachment (widget))
+		return;
+
+	efhd_attachment_show (NULL, NULL, info);
+}
+
+static void
+efhd_image_fit(EPopup *ep, EPopupItem *item, gpointer data)
+{
+	struct _attach_puri *info = data;
+
+#warning "fir image"
+//	info->fit_width = ((GtkWidget *)((EMFormatMail *)info->puri.format)->html)->allocation.width - 12;
+	gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height));
+}
+
+static void
+efhd_image_unfit(EPopup *ep, EPopupItem *item, gpointer data)
+{
+	struct _attach_puri *info = data;
+
+	info->fit_width = 0;
+	gtk_image_set_from_pixbuf((GtkImage *)info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height));
+}
+
+static EPopupItem efhd_menu_items[] = {
+	{ E_POPUP_BAR, (gchar *) "05.display" },
+	{ E_POPUP_ITEM, (gchar *) "05.display.00", (gchar *) N_("_View Inline"), efhd_attachment_show },
+	{ E_POPUP_ITEM, (gchar *) "05.display.00", (gchar *) N_("_Hide"), efhd_attachment_show },
+	{ E_POPUP_ITEM, (gchar *) "05.display.01", (gchar *) N_("_Fit to Width"), efhd_image_fit, NULL, NULL, EM_POPUP_PART_IMAGE },
+	{ E_POPUP_ITEM, (gchar *) "05.display.01", (gchar *) N_("Show _Original Size"), efhd_image_unfit, NULL, NULL, EM_POPUP_PART_IMAGE },
+};
+
+static void
+efhd_menu_items_free(EPopup *ep, GSList *items, gpointer data)
+{
+	g_slist_free(items);
+}
+
+static void
+efhd_popup_place_widget(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
+{
+	GtkWidget *w = user_data;
+
+	gdk_window_get_origin(gtk_widget_get_parent_window(w), x, y);
+	*x += w->allocation.x + w->allocation.width;
+	*y += w->allocation.y;
+}
+
+static gboolean
+efhd_attachment_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri *info)
+{
+	GtkMenu *menu;
+	GSList *menus = NULL;
+	EMPopup *emp;
+	EMPopupTargetPart *target;
+
+	d(printf("attachment popup, button %d\n", event->button));
+
+	if (event && event->button != 1 && event->button != 3) {
+		/* ?? gtk_propagate_event(GTK_WIDGET (user_data), (GdkEvent *)event);*/
+		return FALSE;
+	}
+
+	if (!efhd_can_process_attachment (w))
+		return FALSE;
+
+	/** @HookPoint-EMPopup: Attachment Button Context Menu
+	 * @Id: org.gnome.evolution.mail.formathtmldisplay.popup
+	 * @Class: org.gnome.evolution.mail.popup:1.0
+	 * @Target: EMPopupTargetPart
+	 *
+	 * This is the drop-down menu shown when a user clicks on the down arrow
+	 * of the attachment button in inline mail content.
+	 */
+	emp = em_popup_new("org.gnome.evolution.mail.formathtmldisplay.popup");
+	target = em_popup_target_new_part(emp, info->puri.part, info->handle?info->handle->mime_type:NULL);
+	target->target.widget = w;
+
+	/* add our local menus */
+	if (info->handle) {
+		/* show/hide menus, only if we have an inline handler */
+		menus = g_slist_prepend(menus, &efhd_menu_items[0]);
+		menus = g_slist_prepend(menus, &efhd_menu_items[info->shown?2:1]);
+		if (info->shown && info->image) {
+			if (info->fit_width != 0) {
+				if (em_icon_stream_is_resized(info->puri.cid, info->fit_width, info->fit_height))
+				    menus = g_slist_prepend(menus, &efhd_menu_items[4]);
+			} else
+				menus = g_slist_prepend(menus, &efhd_menu_items[3]);
+		}
+	}
+
+	e_popup_add_items((EPopup *)emp, menus, NULL, efhd_menu_items_free, info);
+
+	menu = e_popup_create_menu_once((EPopup *)emp, (EPopupTarget *)target, 0);
+	if (event)
+		gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
+	else
+		gtk_menu_popup(menu, NULL, NULL, (GtkMenuPositionFunc)efhd_popup_place_widget, w, 0, gtk_get_current_event_time());
+
+	return TRUE;
+}
+
+static gboolean
+efhd_image_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri *info)
+{
+	if (event && event->button != 3)
+		return FALSE;
+
+	return efhd_attachment_popup(w, event, info);
+}
+
+static gboolean
+efhd_attachment_popup_menu(GtkWidget *w, struct _attach_puri *info)
+{
+	return efhd_attachment_popup(w, NULL, info);
+}
+
+/* ********************************************************************** */
+
+static void
+efhd_drag_data_get(GtkWidget *w, GdkDragContext *drag, GtkSelectionData *data, guint info, guint time, EMFormatMailPObject *pobject)
+{
+	CamelMimePart *part = pobject->part;
+	gchar *uri, *uri_crlf, *path;
+	CamelStream *stream;
+
+	switch (info) {
+	case 0: /* mime/type request */
+		stream = camel_stream_mem_new();
+		/* TODO: shoudl format_format_text run on the content-object? */
+		/* TODO: should we just do format_content? */
+		if (camel_content_type_is (((CamelDataWrapper *)part)->mime_type, "text", "*")) {
+			/* FIXME: this should be an em_utils method, it only needs a default charset param */
+			em_format_format_text((EMFormat *)pobject->format, stream, (CamelDataWrapper *)part);
+		} else {
+			CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
+
+			camel_data_wrapper_decode_to_stream(dw, stream);
+		}
+
+		gtk_selection_data_set(data, data->target, 8,
+				       ((CamelStreamMem *)stream)->buffer->data,
+				       ((CamelStreamMem *)stream)->buffer->len);
+		camel_object_unref(stream);
+		break;
+	case 1: /* text-uri-list request */
+		/* Kludge around Nautilus requesting the same data many times */
+		uri = g_object_get_data((GObject *)w, "e-drag-uri");
+		if (uri) {
+			gtk_selection_data_set(data, data->target, 8, (guchar *)uri, strlen(uri));
+			return;
+		}
+
+		path = em_utils_temp_save_part(w, part, FALSE);
+		if (path == NULL)
+			return;
+
+		uri = g_filename_to_uri(path, NULL, NULL);
+		g_free(path);
+		uri_crlf = g_strconcat(uri, "\r\n", NULL);
+		g_free(uri);
+		gtk_selection_data_set(data, data->target, 8, (guchar *)uri_crlf, strlen(uri_crlf));
+		g_object_set_data_full((GObject *)w, "e-drag-uri", uri_crlf, g_free);
+		break;
+	default:
+		abort();
+	}
+}
+
+static void
+efhd_drag_data_delete(GtkWidget *w, GdkDragContext *drag, EMFormatMailPObject *pobject)
+{
+	gchar *uri;
+
+	uri = g_object_get_data((GObject *)w, "e-drag-uri");
+	if (uri) {
+		/* NB: this doesn't kill the dnd directory */
+		/* NB: is this ever called? */
+		/* NB even more: doesn't the e-drag-uri have \r\n
+		 * appended? (see efhd_drag_data_get())
+		 */
+		gchar *filename = g_filename_from_uri (uri, NULL, NULL);
+		g_unlink(filename);
+		g_free(filename);
+		g_object_set_data((GObject *)w, "e-drag-uri", NULL);
+	}
+}
+
+static void
+efhd_write_icon_job(struct _EMFormatMailJob *job, gint cancelled)
+{
+	EMFormatMailPObject *pobject;
+	CamelDataWrapper *dw;
+
+	if (cancelled)
+		return;
+
+	pobject = job->u.data;
+	dw = camel_medium_get_content_object((CamelMedium *)pobject->part);
+	camel_data_wrapper_decode_to_stream(dw, job->stream);
+	camel_stream_close(job->stream);
+}
+
+static void
+efhd_image_resized(GtkWidget *w, GtkAllocation *event, struct _attach_puri *info)
+{
+	GdkPixbuf *pb;
+	gint width = 500;
+
+	if (info->fit_width == 0)
+		return;
+
+#warning "fix image width"	
+	//width = ((GtkWidget *)((EMFormatMail *)info->puri.format)->html)->allocation.width - 12;
+	if (info->fit_width == width)
+		return;
+	info->fit_width = width;
+
+	pb = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height);
+	if (pb) {
+		gtk_image_set_from_pixbuf(info->image, pb);
+		g_object_unref(pb);
+	}
+}
+
+static void
+efhd_change_cursor(GtkWidget *w, GdkEventCrossing *event, struct _attach_puri *info)
+{
+	if (info->shown && info->image) {
+		if (info->fit_width != 0) {
+			if (em_icon_stream_is_resized(info->puri.cid, info->fit_width, info->fit_height))
+				e_cursor_set(w->window, E_CURSOR_ZOOM_IN);
+
+		}
+	}
+}
+
+static void
+efhd_image_fit_width(GtkWidget *widget, GdkEventButton *event, struct _attach_puri *info)
+{
+	gint width = 500;
+
+#warning "fix image width"	
+//	width = ((GtkWidget *)((EMFormatMail *)info->puri.format)->html)->allocation.width - 12;
+
+	if (info->shown && info->image) {
+		if (info->fit_width != 0) {
+			if (em_icon_stream_is_resized(info->puri.cid, info->fit_width, info->fit_height)) {
+				if(info->fit_width != width) {
+					info->fit_width = width;
+					e_cursor_set (widget->window, E_CURSOR_ZOOM_IN);
+				} else {
+					info->fit_width = 0;
+					e_cursor_set(widget->window, E_CURSOR_ZOOM_OUT);
+				}
+			}
+		} else {
+			info->fit_width = width;
+			e_cursor_set (widget->window, E_CURSOR_ZOOM_IN);
+		}
+	}
+
+	gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height));
+}
+
+/* When the puri gets freed in the formatter thread and if the image is resized, crash will happen
+   See bug #333864 So while freeing the puri, we disconnect the image attach resize attached with
+   the puri */
+
+static void
+efhd_image_unallocate (struct _EMFormatPURI * puri)
+{
+	struct _attach_puri *info = (struct _attach_puri *) puri;
+	g_signal_handlers_disconnect_by_func(info->html, efhd_image_resized, info);
+
+	g_signal_handlers_disconnect_by_func(info->event_box, efhd_image_popup, info);
+	g_signal_handlers_disconnect_by_func(info->event_box, efhd_change_cursor, info);
+	g_signal_handlers_disconnect_by_func(info->event_box, efhd_attachment_popup_menu, info);
+	g_signal_handlers_disconnect_by_func(info->event_box, efhd_image_fit_width, info);
+}
+
+static GtkWidget *
+efhd_attachment_image(EMFormatMail *efh, GtkWidget *eb, EMFormatMailPObject *pobject)
+{
+	GtkWidget *box;
+	EMFormatMailJob *job;
+	struct _attach_puri *info;
+	GdkPixbuf *pixbuf;
+	GtkTargetEntry drag_types[] = {
+		{ NULL, 0, 0 },
+		{ (gchar *) "text/uri-list", 0, 1 },
+	};
+	gchar *simple_type;
+
+	info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid);
+
+	info->image = (GtkImage *)gtk_image_new();
+	info->html = eb;
+	info->puri.free = efhd_image_unallocate;
+
+	pixbuf = em_icon_stream_get_image(pobject->classid, info->fit_width, info->fit_height);
+	printf("Search %s: %p\n", pobject->classid, pixbuf);
+	if (pixbuf && 0) {
+		gtk_image_set_from_pixbuf(info->image, pixbuf);
+		g_object_unref(pixbuf);
+	} else {
+		job = em_format_mail_job_new(efh, efhd_write_icon_job, pobject);
+		job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)info->image, pobject->classid, info->fit_width, info->fit_height, TRUE);
+#warning "happens on main thread"		
+		//em_format_mail_job_queue(efh, job);
+		CamelDataWrapper *dw;
+		dw = camel_medium_get_content_object((CamelMedium *)pobject->part);
+		camel_data_wrapper_decode_to_stream(dw, job->stream);
+		camel_stream_close(job->stream);
+
+	}
+
+	box = gtk_event_box_new();
+	info->event_box = box;
+	gtk_container_add((GtkContainer *)box, (GtkWidget *)info->image);
+	gtk_widget_show_all(box);
+	gtk_box_pack_start ((GtkBox *)eb, box, FALSE, FALSE, 0);
+
+	g_signal_connect(eb, "size_allocate", G_CALLBACK(efhd_image_resized), info);
+
+	simple_type = camel_content_type_simple(((CamelDataWrapper *)pobject->part)->mime_type);
+	camel_strdown(simple_type);
+
+	drag_types[0].target = simple_type;
+	gtk_drag_source_set(box, GDK_BUTTON1_MASK, drag_types, sizeof(drag_types)/sizeof(drag_types[0]), GDK_ACTION_COPY);
+	g_free(simple_type);
+
+	g_signal_connect(box, "drag-data-get", G_CALLBACK(efhd_drag_data_get), pobject);
+	g_signal_connect (box, "drag-data-delete", G_CALLBACK(efhd_drag_data_delete), pobject);
+
+	g_signal_connect(box, "button_press_event", G_CALLBACK(efhd_image_popup), info);
+	g_signal_connect(box, "enter-notify-event", G_CALLBACK(efhd_change_cursor), info);
+	g_signal_connect(box, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info);
+	g_signal_connect(box, "button-press-event", G_CALLBACK(efhd_image_fit_width), info);
+
+	g_object_set_data (G_OBJECT (box), "efh", efh);
+
+	return eb;
+}
+
+/* attachment button callback */
+static GtkWidget *
+efhd_attachment_button(EMFormatMail *efh, GtkWidget *eb, EMFormatMailPObject *pobject)
+{
+	EMFormatMailDisplay *efhd = (EMFormatMailDisplay *)efh;
+	struct _attach_puri *info;
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	EAttachment *attachment;
+	GtkWidget *widget, *mainbox, *topbox;
+	gpointer parent = NULL;
+	char *txt;
+
+	/* FIXME: handle default shown case */
+	d(printf("adding attachment button/content\n"));
+
+	info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid);
+	printf("%s %p %p\n", pobject->classid, info, pobject);
+	if (!info || info->forward) {
+		g_warning ("unable to expand the attachment\n");
+		return TRUE;
+	}
+
+	attachment = info->attachment;
+	e_attachment_set_shown (attachment, info->shown);
+	e_attachment_set_signed (attachment, info->sign);
+	e_attachment_set_encrypted (attachment, info->encrypt);
+	e_attachment_set_can_show (attachment, info->handle != NULL);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (eb));
+	parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL;
+
+	view = E_ATTACHMENT_VIEW (efhd->priv->attachment_view);
+	gtk_widget_show (efhd->priv->attachment_view);
+
+	store = e_attachment_view_get_store (view);
+	e_attachment_store_add_attachment (store, info->attachment);
+
+	e_attachment_load_async (
+		info->attachment, (GAsyncReadyCallback)
+		e_attachment_load_handle_error, parent);
+
+	mainbox = gtk_hbox_new(FALSE, 0);	
+	widget = gtk_label_new (NULL);
+	gtk_widget_show (widget);
+	gtk_box_pack_start((GtkBox *)mainbox, widget, FALSE, FALSE, 6);
+	
+	widget = e_attachment_button_new (view);
+	e_attachment_button_set_attachment (
+		E_ATTACHMENT_BUTTON (widget), attachment);
+	gtk_widget_show (widget);
+	gtk_box_pack_start((GtkBox *)mainbox, widget, FALSE, FALSE, 6);
+
+	g_object_set_data (G_OBJECT (widget), "efh", efh);
+
+	g_signal_connect (
+		widget, "notify::expanded",
+		G_CALLBACK (efhd_attachment_button_expanded), info);
+
+	txt = em_format_describe_part(info->puri.part, info->mime_type);
+	if (info->handle) {
+		topbox = gtk_vbox_new (FALSE, 0);
+		gtk_widget_hide(topbox);
+		info->child_box = topbox;
+	} else {
+		gtk_widget_set_sensitive(widget, FALSE);
+		GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_FOCUS);
+		info->child_box = NULL;
+	}
+
+	widget  = gtk_label_new (txt);
+	gtk_widget_show (widget);
+	gtk_box_pack_start ((GtkBox *)mainbox, widget, FALSE, FALSE, 6);
+	gtk_box_pack_start ((GtkBox *)eb, mainbox, FALSE, FALSE, 0);
+	gtk_widget_show_all(mainbox);
+	if (info->child_box)
+		gtk_box_pack_start ((GtkBox *)eb, info->child_box, FALSE, FALSE, 0);
+
+	return info->child_box;
+}
+
+/* not used currently */
+/* frame source callback */
+static void
+efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+	struct _attach_puri *info = (struct _attach_puri *)puri;
+
+	if (info->shown) {
+		d(printf("writing to frame content, handler is '%s'\n", info->handle->mime_type));
+		info->handle->handler(emf, stream, info->puri.part, info->handle);
+		camel_stream_close(stream);
+	} else {
+		/* FIXME: this is leaked if the object is closed without showing it
+		   NB: need a virtual puri_free method? */
+		info->output = stream;
+		camel_object_ref(stream);
+	}
+}
+
+static void
+efhd_bar_resize (EMFormatMail *efh,
+                 GtkAllocation *event)
+{
+	EMFormatMailDisplay *efhd = (EMFormatMailDisplay *) efh;
+	GtkWidget *widget;
+	gint width;
+#warning "fix width"
+//	widget = GTK_WIDGET (efh->html);
+//	width = widget->allocation.width - 12;
+
+	if (width > 0) {
+		widget = efhd->priv->attachment_view;
+		gtk_widget_set_size_request (widget, width, -1);
+	}
+}
+
+static GtkWidget *
+efhd_add_bar (EMFormatMail *efh,
+              GtkWidget *eb,
+              EMFormatMailPObject *pobject)
+{
+	EMFormatMailDisplay *efhd = (EMFormatMailDisplay *) efh;
+	GtkWidget *widget;
+
+	widget = e_mail_attachment_bar_new ();
+	gtk_box_pack_start (eb, widget, FALSE, FALSE, 0);
+	efhd->priv->attachment_view = widget;
+	gtk_widget_hide (widget);
+
+	g_signal_connect_swapped (
+		eb, "size-allocate",
+		G_CALLBACK (efhd_bar_resize), efh);
+
+	return widget;
+}
+
+static void
+efhd_message_add_bar (EMFormat *efh,
+                      CamelStream *stream,
+                      CamelMimePart *part,
+                      const EMFormatHandler *info)
+{
+	const gchar *classid = "attachment-bar";
+	struct _attach_puri *ainfo;
+	EMFormatMailPObject *pobject;
+
+	ainfo = (struct _attach_puri *)em_format_add_puri((EMFormat *)efh, sizeof(*ainfo), classid, part, efhd_attachment_frame);
+	pobject = em_format_mail_add_pobject (
+			(EMFormatMail *) efh,
+			sizeof (EMFormatMailPObject),
+			classid, part, efhd_add_bar);
+
+	ainfo->shown = FALSE;
+	ainfo->rendered = TRUE;	
+	ainfo->pobject = pobject;
+	pobject->body = mail_message_view_get_body(stream);
+	efwd_draw_in_main_thread ((EMFormatMailDisplay *)efh, pobject);	
+}
+
+static void
+efhd_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const gchar *mime_type, const EMFormatHandler *handle)
+{
+	gchar *classid, *text, *html;
+	struct _attach_puri *info;
+	EMFormatMailPObject *pobj;
+
+	classid = g_strdup_printf ("attachment%s", emf->part_id->str);
+	printf("%s %p\n", classid, part);
+	info = (struct _attach_puri *)em_format_add_puri (
+		emf, sizeof (*info), classid, part, efhd_attachment_frame);
+	pobj = em_format_mail_add_pobject (
+		(EMFormatMail *) emf, sizeof (EMFormatMailPObject),
+		classid, part, efhd_attachment_button);
+	info->handle = handle;
+	info->shown = em_format_is_inline (
+		emf, info->puri.part_id, info->puri.part, handle);
+	info->snoop_mime_type = emf->snoop_mime_type;
+	info->mime_type = g_strdup(mime_type);
+	info->attachment = e_attachment_new ();
+	e_attachment_set_mime_part (info->attachment, info->puri.part);
+
+	if (emf->valid) {
+		info->sign = emf->valid->sign.status;
+		info->encrypt = emf->valid->encrypt.status;
+	}
+#if 0
+	camel_stream_write_string (
+		stream, EM_FORMAT_HTML_VPAD
+		"<table cellspacing=0 cellpadding=0><tr><td>"
+		"<table width=10 cellspacing=0 cellpadding=0>"
+		"<tr><td></td></tr></table></td>");
+
+	camel_stream_printf (
+		stream, "<td><object classid=\"%s\"></object></td>", classid);
+
+	camel_stream_write_string (
+		stream, "<td><table width=3 cellspacing=0 cellpadding=0>"
+		"<tr><td></td></tr></table></td><td><font size=-1>");
+#endif
+	info->pobject = pobj;
+	pobj->body = mail_message_view_get_body (stream);
+	efwd_draw_in_main_thread ((EMFormatMailDisplay *)emf, pobj);
+#if 0
+	/* output some info about it */
+	/* FIXME: should we look up mime_type from object again? */
+	text = em_format_describe_part (part, mime_type);
+	html = camel_text_to_html (
+		text, ((EMFormatMail *)emf)->text_html_flags &
+		CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+	camel_stream_write_string (stream, html);
+	g_free (html);
+	g_free (text);
+	camel_stream_write_string (
+		stream, "</font></td></tr><tr></table>\n"
+		EM_FORMAT_MAIL_VPAD);
+#endif
+
+	if (handle && pobj->view) {
+		if (info->shown) {
+			info->rendered = 1;
+			camel_stream_printf((CamelStream *)pobj->stream,
+			    "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
+			    "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
+			    "<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n",
+			    ((EMFormatMail *)emf)->body_colour & 0xffffff,
+			    ((EMFormatMail *)emf)->header_colour & 0xffffff);
+
+	//		camel_stream_printf (pobj->stream, "<HTML> <HEAD> </HEAD> <BODY >");
+			handle->handler(emf, pobj->stream, part, handle);
+			camel_stream_printf (pobj->stream, "</BODY> </HTML>");
+			camel_stream_close (pobj->stream);
+			camel_object_unref(pobj->stream);
+			pobj->stream = NULL;			
+		} else
+			info->rendered = 0;
+	}
+
+	g_free(classid);
+}
+
+#if 0
+static void
+efhd_optional_button_show (GtkWidget *widget, GtkWidget *w)
+{
+	GtkWidget *label = g_object_get_data (G_OBJECT (widget), "text-label");
+
+	if (GTK_WIDGET_VISIBLE (w)) {
+		gtk_widget_hide (w);
+		gtk_label_set_text_with_mnemonic (GTK_LABEL (label), _("View _Unformatted"));
+	} else {
+		gtk_label_set_text_with_mnemonic (GTK_LABEL (label), _("Hide _Unformatted"));
+		gtk_widget_show (w);
+	}
+}
+
+static void
+efhd_resize (GtkWidget *w, GtkAllocation *event, EMFormatMail *efh)
+{
+	gtk_widget_set_size_request (w, ((GtkWidget *)efh->html)->allocation.width-48, 250);
+}
+
+/* optional render attachment button callback */
+static gboolean
+efhd_attachment_optional(EMFormatMail *efh, GtkHTMLEmbedded *eb, EMFormatMailPObject *pobject)
+{
+	struct _attach_puri *info;
+	GtkWidget *hbox, *vbox, *button, *mainbox, *scroll, *label, *img;
+	AtkObject *a11y;
+	GtkWidget *view;
+	GtkTextBuffer *buffer;
+
+	/* FIXME: handle default shown case */
+	d(printf("adding attachment button/content for optional rendering\n"));
+
+	info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid);
+	if (!info || info->forward) {
+		g_warning ("unable to expand the attachment\n");
+		return TRUE;
+	}
+
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	mainbox = gtk_hbox_new(FALSE, 0);
+
+	button = gtk_button_new();
+	hbox = gtk_hbox_new (FALSE, 0);
+	img = gtk_image_new_from_icon_name (
+		"stock_show-all", GTK_ICON_SIZE_BUTTON);
+	label = gtk_label_new_with_mnemonic(_("View _Unformatted"));
+	g_object_set_data (G_OBJECT (button), "text-label", (gpointer)label);
+	gtk_box_pack_start (GTK_BOX (hbox), img, TRUE, TRUE, 2);
+	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 2);
+	gtk_widget_show_all (hbox);
+	gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (hbox));
+	if (info->handle)
+		g_signal_connect(G_OBJECT (button), "clicked", G_CALLBACK(efhd_optional_button_show), scroll);
+	else {
+		gtk_widget_set_sensitive(button, FALSE);
+		GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
+	}
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start(GTK_BOX (mainbox), button, FALSE, FALSE, 6);
+
+	button = gtk_button_new();
+	hbox = gtk_hbox_new (FALSE, 0);
+	img = gtk_image_new_from_stock (
+		GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
+	label = gtk_label_new_with_mnemonic(_("O_pen With"));
+	gtk_box_pack_start (GTK_BOX (hbox), img, TRUE, TRUE, 2);
+	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 2);
+	gtk_box_pack_start (GTK_BOX (hbox), gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE), TRUE, TRUE, 2);
+	gtk_widget_show_all (hbox);
+	gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (hbox));
+
+	a11y = gtk_widget_get_accessible (button);
+	atk_object_set_name (a11y, _("Attachment"));
+
+	g_signal_connect(button, "button_press_event", G_CALLBACK(efhd_attachment_popup), info);
+	g_signal_connect(button, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info);
+	g_signal_connect(button, "clicked", G_CALLBACK(efhd_attachment_popup_menu), info);
+	gtk_box_pack_start(GTK_BOX (mainbox), button, FALSE, FALSE, 6);
+
+	gtk_widget_show_all(mainbox);
+
+	gtk_box_pack_start(GTK_BOX (vbox), mainbox, FALSE, FALSE, 6);
+
+	view = gtk_text_view_new ();
+	gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
+	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (view));
+	gtk_text_buffer_set_text (buffer, (gchar *)info->mstream->buffer->data, info->mstream->buffer->len);
+	camel_object_unref(info->mstream);
+	info->mstream = NULL;
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
+	gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (view));
+	gtk_box_pack_start(GTK_BOX (vbox), scroll, TRUE, TRUE, 6);
+	gtk_widget_show (GTK_WIDGET(view));
+
+	gtk_widget_set_size_request (scroll, (GTK_WIDGET (efh->html))->allocation.width - 48, 250);
+	g_signal_connect (scroll, "size_allocate", G_CALLBACK(efhd_resize), efh);
+	gtk_widget_show (scroll);
+
+	if (!info->shown)
+		gtk_widget_hide (scroll);
+
+	gtk_widget_show (vbox);
+	gtk_container_add(GTK_CONTAINER (eb), vbox);
+	info->handle = NULL;
+
+	return TRUE;
+}
+
+static void
+efhd_format_optional(EMFormat *emf, CamelStream *fstream, CamelMimePart *part, CamelStream *mstream)
+{
+	gchar *classid, *html;
+	struct _attach_puri *info;
+	CamelStream *stream;
+
+	if (CAMEL_IS_STREAM_FILTER (fstream) && ((CamelStreamFilter *) fstream)->source)
+		stream = ((CamelStreamFilter *) fstream)->source;
+	else
+		stream = fstream;
+
+	classid = g_strdup_printf("optional%s", emf->part_id->str);
+	info = (struct _attach_puri *)em_format_add_puri(emf, sizeof(*info), classid, part, efhd_attachment_frame);
+	em_format_mail_add_pobject((EMFormatMail *)emf, sizeof(EMFormatMailPObject), classid, part, efhd_attachment_optional);
+	info->handle = em_format_find_handler(emf, "text/plain");
+	info->shown = FALSE;
+	info->snoop_mime_type = "text/plain";
+	info->attachment = e_attachment_new ();
+	e_attachment_set_mime_part (info->attachment, info->puri.part);
+	info->mstream = (CamelStreamMem *)mstream;
+	if (emf->valid) {
+		info->sign = emf->valid->sign.status;
+		info->encrypt = emf->valid->encrypt.status;
+	}
+
+	camel_stream_write_string(stream,
+				  EM_FORMAT_HTML_VPAD
+				  "<table cellspacing=0 cellpadding=0><tr><td><h3><font size=-1 color=red>");
+
+	html = camel_text_to_html(_("Evolution cannot render this email as it is too large to process. You can view it unformatted or with an external text editor."), ((EMFormatMail *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+	camel_stream_write_string(stream, html);
+	camel_stream_write_string(stream,
+				  "</font></h3></td></tr></table>\n");
+	camel_stream_write_string(stream,
+				  "<table cellspacing=0 cellpadding=0>"
+				  "<tr>");
+	camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td></tr></table>", classid);
+
+	g_free(html);
+
+	camel_stream_write_string(stream,
+/*				  "</font></h2></td></tr></table>\n" */
+				  EM_FORMAT_HTML_VPAD);
+
+	g_free(classid);
+}
+
+#endif
+
+/* UI Threading */
+
+struct _preview_message_msg {
+	MailMsg base;
+	
+	EMFormatMailPObject *pobject;
+	EFlag *done;
+
+	unsigned int allow_cancel:1;
+	unsigned int result:1;
+	unsigned int ismain:1;
+};
+static void preview_message_exec (struct _preview_message_msg *m);
+
+static void
+preview_message_exec (struct _preview_message_msg *m)
+{
+	EMFormatMailPObject *pobj = m->pobject;
+	pobj->view = (gpointer) pobj->func(pobj->format, (gpointer)pobj->body, pobj);
+	if (pobj->view) {
+		pobj->stream = mail_message_view_create_webstream (mail_message_view_create_webview(((EMFormatMail *)pobj->format)->msg_view, pobj->view), pobj->view);
+		gtk_widget_show (pobj->view);
+	}
+	e_flag_set (m->done);
+}
+
+static void
+preview_message_free (struct _preview_message_msg *m)
+{
+	e_flag_free (m->done);
+}
+
+static MailMsgInfo preview_message_info = {
+	sizeof (struct _preview_message_msg),
+	(MailMsgDescFunc) NULL,
+	(MailMsgExecFunc) preview_message_exec,
+	(MailMsgDoneFunc) NULL,
+	(MailMsgFreeFunc) preview_message_free
+};
+
+static void
+efwd_draw_in_main_thread (EMFormatMailDisplay *efwd, EMFormatMailPObject *pobject)
+{
+	struct _preview_message_msg *m;
+
+	m = mail_msg_new (&preview_message_info);
+	m->ismain = mail_in_main_thread ();
+	m->done = e_flag_new ();
+	m->allow_cancel = TRUE;
+	m->pobject = pobject;
+	mail_msg_ref (m);
+	if (m->ismain)
+		preview_message_exec (m);
+	else
+		mail_msg_main_loop_push (m);
+
+	if (TRUE) {
+		e_flag_wait (m->done);
+		mail_msg_unref (m);
+	}
+
+	if (m->ismain)
+		mail_msg_unref (m);
+
+	return;
+}
diff --git a/src/em-format-mail-display.h b/src/em-format-mail-display.h
new file mode 100644
index 0000000..b29ce2c
--- /dev/null
+++ b/src/em-format-mail-display.h
@@ -0,0 +1,122 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/*
+  Concrete class for formatting mails to displayed html
+*/
+
+#ifndef EM_FORMAT_MAIL_DISPLAY_H
+#define EM_FORMAT_MAIL_DISPLAY_H
+
+#include "em-format-mail.h"
+
+/* Standard GObject macros */
+#define EM_TYPE_FORMAT_MAIL_DISPLAY \
+	(em_format_mail_display_get_type ())
+#define EM_FORMAT_MAIL_DISPLAY(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), EM_TYPE_FORMAT_MAIL_DISPLAY, EMFormatMailDisplay))
+#define EM_FORMAT_MAIL_DISPLAY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), EM_TYPE_FORMAT_MAIL_DISPLAY, EMFormatMailDisplayClass))
+#define EM_IS_FORMAT_MAIL_DISPLAY(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), EM_TYPE_FORMAT_MAIL_DISPLAY))
+#define EM_IS_FORMAT_MAIL_DISPLAY_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), EM_TYPE_FORMAT_MAIL_DISPLAY))
+#define EM_FORMAT_MAIL_DISPLAY_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), EM_TYPE_FORMAT_MAIL_DISPLAY, EMFormatMailDisplayClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMFormatMailDisplay EMFormatMailDisplay;
+typedef struct _EMFormatMailDisplayClass EMFormatMailDisplayClass;
+typedef struct _EMFormatMailDisplayPrivate EMFormatMailDisplayPrivate;
+
+struct _EMFormatMailDisplay {
+	EMFormatMail formathtml;
+
+	EMFormatMailDisplayPrivate *priv;
+
+	struct _ESearchingTokenizer *search_tok;
+
+	guint animate:1;
+	guint caret_mode:1;
+	guint nobar:1;
+};
+
+#define EM_FORMAT_MAIL_DISPLAY_SEARCH_PRIMARY (0)
+#define EM_FORMAT_MAIL_DISPLAY_SEARCH_SECONDARY (1)
+#define EM_FORMAT_MAIL_DISPLAY_SEARCH_ICASE (1<<8)
+
+struct _EMFormatMailDisplayClass {
+	EMFormatMailClass formathtml_class;
+
+	/* a link clicked normally */
+	void (*link_clicked)(EMFormatMailDisplay *efhd, const gchar *uri);
+	/* a part or a link button pressed event */
+	gint (*popup_event)(EMFormatMailDisplay *efhd, GdkEventButton *event, const gchar *uri, struct _CamelMimePart *part);
+	/* the mouse is over a link */
+	void (*on_url)(EMFormatMailDisplay *efhd, const gchar *uri);
+};
+
+GType		em_format_mail_display_get_type	(void);
+EMFormatMailDisplay *
+		em_format_mail_display_new	(void);
+
+void		em_format_mail_display_goto_anchor
+						(EMFormatMailDisplay *efhd,
+						 const gchar *name);
+
+void		em_format_mail_display_set_animate
+						(EMFormatMailDisplay *efhd,
+						 gboolean state);
+void		em_format_mail_display_set_caret_mode
+						(EMFormatMailDisplay *efhd,
+						 gboolean state);
+
+void		em_format_mail_display_set_search
+						(EMFormatMailDisplay *efhd,
+						 gint type,
+						 GSList *strings);
+void		em_format_mail_display_search	(EMFormatMailDisplay *efhd);
+void		em_format_mail_display_search_with
+						(EMFormatMailDisplay *efhd,
+						 gchar *word);
+void		em_format_mail_display_search_close
+						(EMFormatMailDisplay *efhd);
+
+void		em_format_mail_display_cut	(EMFormatMailDisplay *efhd);
+void		em_format_mail_display_copy	(EMFormatMailDisplay *efhd);
+void		em_format_mail_display_paste	(EMFormatMailDisplay *efhd);
+
+void		em_format_mail_display_zoom_in	(EMFormatMailDisplay *efhd);
+void		em_format_mail_display_zoom_out	(EMFormatMailDisplay *efhd);
+void		em_format_mail_display_zoom_reset
+						(EMFormatMailDisplay *efhd);
+
+gboolean	em_format_mail_display_popup_menu
+						(EMFormatMailDisplay *efhd);
+
+G_END_DECLS
+
+#endif /* EM_FORMAT_MAIL_DISPLAY_H */
diff --git a/src/em-format-mail.c b/src/em-format-mail.c
new file mode 100644
index 0000000..bef0b11
--- /dev/null
+++ b/src/em-format-mail.c
@@ -0,0 +1,2193 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Michael Zucchi <notzed ximian com>
+ *		Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#ifdef G_OS_WIN32
+/* Work around 'DATADIR' and 'interface' lossage in <windows.h> */
+#define DATADIR crap_DATADIR
+#include <windows.h>
+#undef DATADIR
+#undef interface
+#endif
+
+#include <libedataserver/e-data-server-util.h>	/* for e_utf8_strftime, what about e_time_format_time? */
+#include <libedataserver/e-time-utils.h>
+#include "e-util/e-icon-factory.h"
+#include "e-util/e-util.h"
+
+#include <glib/gi18n.h>
+
+#include <camel/camel-iconv.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-filter-enriched.h>
+#include <camel/camel-mime-filter-basic.h>
+#include <camel/camel-gpg-context.h>
+#include <camel/camel-cipher-context.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-url.h>
+#include <camel/camel-stream-fs.h>
+#include <camel/camel-string-utils.h>
+#include <camel/camel-http-stream.h>
+#include <camel/camel-data-cache.h>
+#include <camel/camel-file-utils.h>
+
+#include <libedataserver/e-msgport.h>
+
+#include "mail-component.h"
+#include "mail/mail-config.h"
+#include "mail/mail-mt.h"
+
+#include <gtkhtml/gtkhtml.h>
+#include "em-format-mail.h"
+#include "mail/em-html-stream.h"
+#include "mail/em-utils.h"
+
+#if HAVE_WEBKIT
+#include "em-webkit-stream.h"
+#endif
+
+#if HAVE_MOZILLA
+#include "em-mozembed-stream.h"
+#endif
+
+#include "mail-message-view.h"
+#include "mail-utils.h"
+
+#define d(x)
+
+#define EFM_MESSAGE_START_ANAME "evolution#message#start"
+#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>"
+
+struct _EMFormatMailCache {
+	CamelMultipart *textmp;
+
+	gchar partid[1];
+};
+
+struct _EMFormatMailPrivate {
+	struct _CamelMimeMessage *last_part;	/* not reffed, DO NOT dereference */
+	volatile gint format_id;		/* format thread id */
+	guint format_timeout_id;
+	struct _format_msg *format_timeout_msg;
+
+	/* Table that re-maps text parts into a mutlipart/mixed, EMFormatMailCache * */
+	GHashTable *text_inline_parts;
+
+	EDList pending_jobs;
+	GMutex *lock;
+};
+
+//static void efh_url_requested(GtkHTML *html, const gchar *url, GtkHTMLStream *handle, EMFormatMail *efh);
+//static gboolean efh_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatMail *efh);
+//static void efh_gtkhtml_destroy(GtkHTML *html, EMFormatMail *efh);
+
+static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
+
+static void efh_format_clone(EMFormat *emf, CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, EMFormat *emfsource);
+static void efh_format_error(EMFormat *emf, CamelStream *stream, const gchar *txt);
+static void efh_format_source(EMFormat *, CamelStream *, CamelMimePart *);
+static void efh_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const gchar *, const EMFormatHandler *);
+static void efh_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid);
+static gboolean efh_busy(EMFormat *);
+
+static void efh_builtin_init(EMFormatMailClass *efhc);
+
+static void efh_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri);
+
+static EMFormatClass *efh_parent;
+static CamelDataCache *emfh_http_cache;
+
+#define EMFH_HTTP_CACHE_PATH "http"
+
+static void
+efh_free_cache(struct _EMFormatMailCache *efhc)
+{
+	if (efhc->textmp)
+		camel_object_unref(efhc->textmp);
+	g_free(efhc);
+}
+
+static void
+efh_init(GObject *o)
+{
+	EMFormatMail *efh = (EMFormatMail *)o;
+
+	efh->priv = g_malloc0(sizeof(*efh->priv));
+
+	e_dlist_init(&efh->pending_object_list);
+	e_dlist_init(&efh->priv->pending_jobs);
+	efh->priv->lock = g_mutex_new();
+	efh->priv->format_id = -1;
+	efh->priv->text_inline_parts = g_hash_table_new_full (
+		g_str_hash, g_str_equal,
+		(GDestroyNotify) NULL,
+		(GDestroyNotify) efh_free_cache);
+
+/*	efh->html = (GtkHTML *)gtk_html_new();
+	gtk_html_set_blocking(efh->html, FALSE);
+	gtk_html_set_caret_first_focus_anchor (efh->html, EFM_MESSAGE_START_ANAME);
+	g_object_ref_sink(efh->html);
+
+	gtk_html_set_default_content_type(efh->html, "text/html; charset=utf-8");
+	gtk_html_set_editable(efh->html, FALSE);
+
+	g_signal_connect(efh->html, "destroy", G_CALLBACK(efh_gtkhtml_destroy), efh);
+	g_signal_connect(efh->html, "url_requested", G_CALLBACK(efh_url_requested), efh);
+	g_signal_connect(efh->html, "object_requested", G_CALLBACK(efh_object_requested), efh);
+*/
+	efh->body_colour = 0xeeeeee;
+	efh->header_colour = 0;
+	efh->text_colour = 0;
+	efh->frame_colour = 0x3f3f3f;
+	efh->content_colour = 0xffffff;
+	efh->text_html_flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
+		| CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+	efh->show_icon = TRUE;
+	efh->state = EM_FORMAT_MAIL_STATE_NONE;
+}
+
+#if 0
+static void
+efh_gtkhtml_destroy(GtkHTML *html, EMFormatMail *efh)
+{
+	if (efh->priv->format_timeout_id != 0) {
+		g_source_remove(efh->priv->format_timeout_id);
+		efh->priv->format_timeout_id = 0;
+		mail_msg_unref(efh->priv->format_timeout_msg);
+		efh->priv->format_timeout_msg = NULL;
+	}
+
+	/* This probably works ... */
+	if (efh->priv->format_id != -1)
+		mail_msg_cancel(efh->priv->format_id);
+
+	if (efh->html) {
+		g_object_unref(efh->html);
+		efh->html = NULL;
+	}
+}
+#endif
+
+static struct _EMFormatMailCache *
+efh_insert_cache(EMFormatMail *efh, const gchar *partid)
+{
+	struct _EMFormatMailCache *efhc;
+
+	efhc = g_malloc0(sizeof(*efh) + strlen(partid));
+	strcpy(efhc->partid, partid);
+	g_hash_table_insert(efh->priv->text_inline_parts, efhc->partid, efhc);
+
+	return efhc;
+}
+
+
+static void
+efh_finalise(GObject *o)
+{
+	EMFormatMail *efh = (EMFormatMail *)o;
+
+	/* FIXME: check for leaked stuff */
+
+	em_format_mail_clear_pobject(efh);
+
+	//efh_gtkhtml_destroy(efh->html, efh);
+
+	g_hash_table_destroy(efh->priv->text_inline_parts);
+
+	g_free(efh->priv);
+
+	((GObjectClass *)efh_parent)->finalize(o);
+}
+
+static void
+efh_base_init(EMFormatMailClass *efhklass)
+{
+	efh_builtin_init(efhklass);
+}
+
+static void
+efh_class_init(GObjectClass *klass)
+{
+	((EMFormatClass *)klass)->format_clone = efh_format_clone;
+	((EMFormatClass *)klass)->format_error = efh_format_error;
+	((EMFormatClass *)klass)->format_source = efh_format_source;
+	((EMFormatClass *)klass)->format_attachment = efh_format_attachment;
+	((EMFormatClass *)klass)->format_secure = efh_format_secure;
+	((EMFormatClass *)klass)->busy = efh_busy;
+
+	klass->finalize = efh_finalise;
+}
+
+GType
+em_format_mail_get_type(void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(EMFormatMailClass),
+			(GBaseInitFunc)efh_base_init, NULL,
+			(GClassInitFunc)efh_class_init,
+			NULL, NULL,
+			sizeof(EMFormatMail), 0,
+			(GInstanceInitFunc)efh_init
+		};
+		const gchar *base_directory = e_get_user_data_dir ();
+		gchar *path;
+
+		/* Trigger creation of mail component. */
+		mail_component_peek ();
+
+		efh_parent = g_type_class_ref(em_format_get_type());
+		type = g_type_register_static(em_format_get_type(), "EMFormatMail", &info, 0);
+#if 0
+		/* cache expiry - 2 hour access, 1 day max */
+		path = alloca(strlen(base_directory)+16);
+		sprintf(path, "%s/cache", base_directory);
+		emfh_http_cache = camel_data_cache_new(path, 0, NULL);
+		if (emfh_http_cache) {
+			camel_data_cache_set_expire_age(emfh_http_cache, 24*60*60);
+			camel_data_cache_set_expire_access(emfh_http_cache, 2*60*60);
+		}
+#endif		
+	}
+
+	return type;
+}
+
+EMFormatMail *em_format_mail_new(void)
+{
+	EMFormatMail *efh;
+
+	efh = g_object_new(em_format_mail_get_type(), NULL);
+
+	return efh;
+}
+
+/* force loading of http images */
+void em_format_mail_load_http(EMFormatMail *emfh)
+{
+	if (emfh->load_http == MAIL_CONFIG_HTTP_ALWAYS)
+		return;
+
+	/* This will remain set while we're still rendering the same message, then it wont be */
+	emfh->load_http_now = TRUE;
+	d(printf("redrawing with images forced on\n"));
+	em_format_redraw((EMFormat *)emfh);
+}
+
+void
+em_format_mail_set_load_http(EMFormatMail *emfh, gint style)
+{
+	if (emfh->load_http != style) {
+		emfh->load_http = style;
+		em_format_redraw((EMFormat *)emfh);
+	}
+}
+
+void
+em_format_mail_set_mark_citations(EMFormatMail *emfh, gint state, guint32 citation_colour)
+{
+	if (emfh->mark_citations ^ state || emfh->citation_colour != citation_colour) {
+		emfh->mark_citations = state;
+		emfh->citation_colour = citation_colour;
+
+		if (state)
+			emfh->text_html_flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+		else
+			emfh->text_html_flags &= ~CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+
+		em_format_redraw((EMFormat *)emfh);
+	}
+}
+
+CamelMimePart *
+em_format_mail_file_part(EMFormatMail *efh, const gchar *mime_type, const gchar *filename)
+{
+	CamelMimePart *part;
+	CamelStream *stream;
+	CamelDataWrapper *dw;
+	gchar *basename;
+
+	stream = camel_stream_fs_new_with_name(filename, O_RDONLY, 0);
+	if (stream == NULL)
+		return NULL;
+
+	part = camel_mime_part_new();
+	dw = camel_data_wrapper_new();
+	camel_data_wrapper_construct_from_stream(dw, stream);
+	camel_object_unref(stream);
+	if (mime_type)
+		camel_data_wrapper_set_mime_type(dw, mime_type);
+	part = camel_mime_part_new();
+	camel_medium_set_content_object((CamelMedium *)part, dw);
+	camel_object_unref(dw);
+	basename = g_path_get_basename (filename);
+	camel_mime_part_set_filename(part, basename);
+	g_free (basename);
+
+	return part;
+}
+
+/* all this api is a pain in the bum ... */
+
+EMFormatMailPObject *
+em_format_mail_add_pobject(EMFormatMail *efh, gsize size, const gchar *classid, CamelMimePart *part, EMFormatMailPObjectFunc func)
+{
+	EMFormatMailPObject *pobj;
+
+	if (size < sizeof(EMFormatMailPObject)) {
+		g_warning ("size is less than the size of EMFormatMailPObject\n");
+		size = sizeof(EMFormatMailPObject);
+	}
+
+	pobj = g_malloc0(size);
+	if (classid)
+		pobj->classid = g_strdup(classid);
+	else
+		pobj->classid = g_strdup_printf("e-object:///%s", ((EMFormat *)efh)->part_id->str);
+
+	pobj->format = efh;
+	pobj->func = func;
+	pobj->part = part;
+
+	e_dlist_addtail(&efh->pending_object_list, (EDListNode *)pobj);
+
+	return pobj;
+}
+
+EMFormatMailPObject *
+em_format_mail_find_pobject(EMFormatMail *emf, const gchar *classid)
+{
+	EMFormatMailPObject *pw;
+
+	pw = (EMFormatMailPObject *)emf->pending_object_list.head;
+	while (pw->next) {
+		if (!strcmp(pw->classid, classid))
+			return pw;
+		pw = pw->next;
+	}
+
+	return NULL;
+}
+
+EMFormatMailPObject *
+em_format_mail_find_pobject_func(EMFormatMail *emf, CamelMimePart *part, EMFormatMailPObjectFunc func)
+{
+	EMFormatMailPObject *pw;
+
+	pw = (EMFormatMailPObject *)emf->pending_object_list.head;
+	while (pw->next) {
+		if (pw->func == func && pw->part == part)
+			return pw;
+		pw = pw->next;
+	}
+
+	return NULL;
+}
+
+void
+em_format_mail_remove_pobject(EMFormatMail *emf, EMFormatMailPObject *pobject)
+{
+	e_dlist_remove((EDListNode *)pobject);
+	if (pobject->free)
+		pobject->free(pobject);
+	g_free(pobject->classid);
+	g_free(pobject);
+}
+
+void
+em_format_mail_clear_pobject(EMFormatMail *emf)
+{
+	d(printf("clearing pending objects\n"));
+	while (!e_dlist_empty(&emf->pending_object_list))
+		em_format_mail_remove_pobject(emf, (EMFormatMailPObject *)emf->pending_object_list.head);
+}
+
+struct _EMFormatMailJob *
+em_format_mail_job_new(EMFormatMail *emfh, void (*callback)(struct _EMFormatMailJob *job, gint cancelled), gpointer data)
+{
+	struct _EMFormatMailJob *job = g_malloc0(sizeof(*job));
+
+	job->format = emfh;
+	job->puri_level = ((EMFormat *)emfh)->pending_uri_level;
+	job->callback = callback;
+	job->u.data = data;
+	if (((EMFormat *)emfh)->base)
+		job->base = camel_url_copy(((EMFormat *)emfh)->base);
+
+	return job;
+}
+
+void
+em_format_mail_job_queue(EMFormatMail *emfh, struct _EMFormatMailJob *job)
+{
+	g_mutex_lock(emfh->priv->lock);
+	e_dlist_addtail(&emfh->priv->pending_jobs, (EDListNode *)job);
+	g_mutex_unlock(emfh->priv->lock);
+}
+
+/* ********************************************************************** */
+#if 0
+static void emfh_getpuri(struct _EMFormatMailJob *job, gint cancelled)
+{
+	d(printf(" running getpuri task\n"));
+	if (!cancelled)
+		job->u.puri->func((EMFormat *)job->format, job->stream, job->u.puri);
+}
+
+static void emfh_gethttp(struct _EMFormatMailJob *job, gint cancelled)
+{
+	CamelStream *cistream = NULL, *costream = NULL, *instream = NULL;
+	CamelURL *url;
+	CamelContentType *content_type;
+	CamelHttpStream *tmp_stream;
+	gssize n, total = 0, pc_complete = 0, nread = 0;
+	gchar buffer[1500];
+	const gchar *length;
+
+	if (cancelled
+	    || (url = camel_url_new(job->u.uri, NULL)) == NULL)
+		goto badurl;
+
+	d(printf(" running load uri task: %s\n", job->u.uri));
+
+	if (emfh_http_cache)
+		instream = cistream = camel_data_cache_get(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+
+	if (instream == NULL) {
+		gchar *proxy;
+
+
+		if (!(job->format->load_http_now
+		      || job->format->load_http == MAIL_CONFIG_HTTP_ALWAYS
+		      || (job->format->load_http == MAIL_CONFIG_HTTP_SOMETIMES
+			  && em_utils_in_addressbook((CamelInternetAddress *)camel_mime_message_get_from(job->format->format.message), FALSE)))) {
+			/* TODO: Ideally we would put the http requests into another queue and only send them out
+			   if the user selects 'load images', when they do.  The problem is how to maintain this
+			   state with multiple renderings, and how to adjust the thread dispatch/setup routine to handle it */
+			camel_url_free(url);
+			goto done;
+		}
+
+		instream = camel_http_stream_new(CAMEL_HTTP_METHOD_GET, ((EMFormat *)job->format)->session, url);
+		camel_http_stream_set_user_agent((CamelHttpStream *)instream, "CamelHttpStream/1.0 Evolution/" VERSION);
+		proxy = em_utils_get_proxy_uri (job->u.uri);
+		if (proxy) {
+			camel_http_stream_set_proxy ((CamelHttpStream *)instream, proxy);
+			g_free (proxy);
+		}
+		camel_operation_start(NULL, _("Retrieving `%s'"), job->u.uri);
+		tmp_stream = (CamelHttpStream *)instream;
+		content_type = camel_http_stream_get_content_type(tmp_stream);
+		length = camel_header_raw_find(&tmp_stream->headers, "Content-Length", NULL);
+		d(printf("  Content-Length: %s\n", length));
+		if (length != NULL)
+			total = atoi(length);
+		camel_content_type_unref(content_type);
+	} else
+		camel_operation_start_transient(NULL, _("Retrieving `%s'"), job->u.uri);
+
+	camel_url_free(url);
+
+	if (instream == NULL)
+		goto done;
+
+	if (emfh_http_cache != NULL && cistream == NULL)
+		costream = camel_data_cache_add(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+
+	do {
+		if (camel_operation_cancel_check (NULL)) {
+			n = -1;
+			break;
+		}
+		/* FIXME: progress reporting in percentage, can we get the length always?  do we care? */
+		n = camel_stream_read(instream, buffer, sizeof (buffer));
+		if (n > 0) {
+			nread += n;
+			/* If we didn't get a valid Content-Length header, do not try to calculate percentage */
+			if (total != 0) {
+				pc_complete = ((nread * 100) / total);
+				camel_operation_progress(NULL, pc_complete);
+			}
+			d(printf("  read %d bytes\n", n));
+			if (costream && camel_stream_write (costream, buffer, n) == -1) {
+				n = -1;
+				break;
+			}
+
+			camel_stream_write(job->stream, buffer, n);
+		}
+	} while (n>0);
+
+	/* indicates success */
+	if (n == 0)
+		camel_stream_close(job->stream);
+
+	if (costream) {
+		/* do not store broken files in a cache */
+		if (n != 0)
+			camel_data_cache_remove(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+		camel_object_unref(costream);
+	}
+
+	camel_object_unref(instream);
+done:
+	camel_operation_end(NULL);
+badurl:
+	g_free(job->u.uri);
+}
+
+/* ********************************************************************** */
+
+static void
+efh_url_requested(GtkHTML *html, const gchar *url, GtkHTMLStream *handle, EMFormatMail *efh)
+{
+	EMFormatPURI *puri;
+	struct _EMFormatMailJob *job = NULL;
+
+	d(printf("url requested, html = %p, url '%s'\n", html, url));
+
+	puri = em_format_find_visible_puri((EMFormat *)efh, url);
+	if (puri) {
+		CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)puri->part);
+		CamelContentType *ct = dw?dw->mime_type:NULL;
+
+		/* GtkHTML only handles text and images.
+		   application/octet-stream parts are the only ones
+		   which are snooped for other content.  So only try
+		   to pass these to it - any other types are badly
+		   formed or intentionally malicious emails.  They
+		   will still show as attachments anyway */
+
+		if (ct && (camel_content_type_is(ct, "text", "*")
+			   || camel_content_type_is(ct, "image", "*")
+			   || camel_content_type_is(ct, "application", "octet-stream"))) {
+			puri->use_count++;
+
+			d(printf(" adding puri job\n"));
+			job = em_format_mail_job_new(efh, emfh_getpuri, puri);
+		} else {
+			d(printf(" part is unknown type '%s', not using\n", ct?camel_content_type_format(ct):"<unset>"));
+			gtk_html_stream_close(handle, GTK_HTML_STREAM_ERROR);
+		}
+	} else if (g_ascii_strncasecmp(url, "http:", 5) == 0 || g_ascii_strncasecmp(url, "https:", 6) == 0) {
+		d(printf(" adding job, get %s\n", url));
+		job = em_format_mail_job_new(efh, emfh_gethttp, g_strdup(url));
+	} else if  (g_ascii_strncasecmp(url, "/", 1) == 0) {
+		gchar *data = NULL;
+		gsize length = 0;
+		gboolean status;
+
+		status = g_file_get_contents (url, &data, &length, NULL);
+		if (status)
+			gtk_html_stream_write (handle, data, length);
+
+		gtk_html_stream_close(handle, status? GTK_HTML_STREAM_OK : GTK_HTML_STREAM_ERROR);
+		g_free (data);
+	} else {
+		d(printf("HTML Includes reference to unknown uri '%s'\n", url));
+		gtk_html_stream_close(handle, GTK_HTML_STREAM_ERROR);
+	}
+
+	if (job) {
+		job->stream = em_html_stream_new(html, handle);
+		em_format_mail_job_queue(efh, job);
+	}
+}
+
+static gboolean
+efh_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatMail *efh)
+{
+	EMFormatMailPObject *pobject;
+	gint res = FALSE;
+
+	if (eb->classid == NULL)
+		return FALSE;
+
+	pobject = em_format_mail_find_pobject(efh, eb->classid);
+	if (pobject) {
+		/* This stops recursion of the part */
+		e_dlist_remove((EDListNode *)pobject);
+		res = pobject->func(efh, eb, pobject);
+		e_dlist_addhead(&efh->pending_object_list, (EDListNode *)pobject);
+	} else {
+		d(printf("HTML Includes reference to unknown object '%s'\n", eb->classid));
+	}
+
+	return res;
+}
+#endif
+
+/* ********************************************************************** */
+#include "mail/em-inline-filter.h"
+#include <camel/camel-stream-null.h>
+
+/* FIXME: This is duplicated in em-format-html-display, should be exported or in security module */
+static const struct {
+	const gchar *icon, *shortdesc;
+} smime_sign_table[5] = {
+	{ "stock_signature-bad", N_("Unsigned") },
+	{ "stock_signature-ok", N_("Valid signature") },
+	{ "stock_signature-bad", N_("Invalid signature") },
+	{ "stock_signature", N_("Valid signature, but cannot verify sender") },
+	{ "stock_signature-bad", N_("Signature exists, but need public key") },
+};
+
+static const struct {
+	const gchar *icon, *shortdesc;
+} smime_encrypt_table[4] = {
+	{ "stock_lock-broken", N_("Unencrypted") },
+	{ "stock_lock", N_("Encrypted, weak"),},
+	{ "stock_lock-ok", N_("Encrypted") },
+	{ "stock_lock-ok", N_("Encrypted, strong") },
+};
+
+static const gchar *smime_sign_colour[4] = {
+	"", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\""
+};
+
+/* TODO: this could probably be virtual on em-format-html
+   then we only need one version of each type handler */
+static void
+efh_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid)
+{
+	efh_parent->format_secure(emf, stream, part, valid);
+
+	/* To explain, if the validity is the same, then we are the
+	   base validity and now have a combined sign/encrypt validity
+	   we can display.  Primarily a new verification context is
+	   created when we have an embeded message. */
+	if (emf->valid == valid
+	    && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
+		|| valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
+		gchar *classid, *iconpath;
+		const gchar *icon;
+		CamelMimePart *iconpart;
+
+		camel_stream_printf (stream, "<table border=0 width=\"100%%\" cellpadding=3 cellspacing=0%s><tr>",
+				     smime_sign_colour[valid->sign.status]);
+
+		classid = g_strdup_printf("smime:///em-format-html/%s/icon/signed", emf->part_id->str);
+		camel_stream_printf(stream, "<td valign=\"top\"><img src=\"%s\"></td><td valign=\"top\" width=\"100%%\">", classid);
+
+		if (valid->sign.status != 0)
+			icon = smime_sign_table[valid->sign.status].icon;
+		else
+			icon = smime_encrypt_table[valid->encrypt.status].icon;
+		iconpath = e_icon_factory_get_icon_filename(icon, GTK_ICON_SIZE_DIALOG);
+		iconpart = em_format_mail_file_part((EMFormatMail *)emf, "image/png", iconpath);
+		if (iconpart) {
+			(void)em_format_add_puri(emf, sizeof(EMFormatPURI), classid, iconpart, efh_write_image);
+			camel_object_unref(iconpart);
+		}
+		g_free (iconpath);
+		g_free(classid);
+
+		if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
+			camel_stream_printf(stream, "%s<br>", _(smime_sign_table[valid->sign.status].shortdesc));
+		}
+
+		if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
+			camel_stream_printf(stream, "%s<br>", _(smime_encrypt_table[valid->encrypt.status].shortdesc));
+		}
+
+		camel_stream_printf(stream, "</td></tr></table>");
+	}
+}
+
+static void
+efh_text_plain(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	CamelStreamFilter *filtered_stream;
+	CamelMimeFilter *html_filter;
+	CamelMultipart *mp;
+	CamelDataWrapper *dw;
+	CamelContentType *type;
+	const gchar *format;
+	guint32 flags;
+	gint i, count, len;
+	struct _EMFormatMailCache *efhc;
+
+	flags = efh->text_html_flags;
+
+	dw = camel_medium_get_content_object((CamelMedium *)part);
+
+	/* Check for RFC 2646 flowed text. */
+	if (camel_content_type_is(dw->mime_type, "text", "plain")
+	    && (format = camel_content_type_param(dw->mime_type, "format"))
+	    && !g_ascii_strcasecmp(format, "flowed"))
+		flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
+
+	/* This scans the text part for inline-encoded data, creates
+	   a multipart of all the parts inside it. */
+
+	/* FIXME: We should discard this multipart if it only contains
+	   the original text, but it makes this hash lookup more complex */
+
+	/* TODO: We could probably put this in the superclass, since
+	   no knowledge of html is required - but this messes with
+	   filters a bit.  Perhaps the superclass should just deal with
+	   html anyway and be done with it ... */
+
+	efhc = g_hash_table_lookup(efh->priv->text_inline_parts, ((EMFormat *)efh)->part_id->str);
+	if (efhc == NULL || (mp = efhc->textmp) == NULL) {
+		EMInlineFilter *inline_filter;
+		CamelStream *null;
+		CamelContentType *ct;
+
+		/* if we had to snoop the part type to get here, then
+		 * use that as the base type, yuck */
+		if (((EMFormat *)efh)->snoop_mime_type == NULL
+		    || (ct = camel_content_type_decode(((EMFormat *)efh)->snoop_mime_type)) == NULL) {
+			ct = dw->mime_type;
+			camel_content_type_ref(ct);
+		}
+
+		null = camel_stream_null_new();
+		filtered_stream = camel_stream_filter_new_with_stream(null);
+		camel_object_unref(null);
+		inline_filter = em_inline_filter_new(camel_mime_part_get_encoding(part), ct);
+		camel_stream_filter_add(filtered_stream, (CamelMimeFilter *)inline_filter);
+		camel_data_wrapper_write_to_stream(dw, (CamelStream *)filtered_stream);
+		camel_stream_close((CamelStream *)filtered_stream);
+		camel_object_unref(filtered_stream);
+
+		mp = em_inline_filter_get_multipart(inline_filter);
+		if (efhc == NULL)
+			efhc = efh_insert_cache(efh, ((EMFormat *)efh)->part_id->str);
+		efhc->textmp = mp;
+
+		camel_object_unref(inline_filter);
+		camel_content_type_unref(ct);
+	}
+
+	filtered_stream = camel_stream_filter_new_with_stream(stream);
+	html_filter = camel_mime_filter_tohtml_new(flags, efh->citation_colour);
+	camel_stream_filter_add(filtered_stream, html_filter);
+	camel_object_unref(html_filter);
+
+	/* We handle our made-up multipart here, so we don't recursively call ourselves */
+
+	len = ((EMFormat *)efh)->part_id->len;
+	count = camel_multipart_get_number(mp);
+	for (i=0;i<count;i++) {
+		CamelMimePart *newpart = camel_multipart_get_part(mp, i);
+
+		if (!newpart)
+			continue;
+
+		type = camel_mime_part_get_content_type(newpart);
+		if (camel_content_type_is (type, "text", "*") && !camel_content_type_is(type, "text", "calendar")) {
+			camel_stream_printf (stream,
+					"<div style=\"border: solid #%06x 1px; background-color: #%06x; padding: 10px; color: #%06x;\">\n",
+					     efh->frame_colour & 0xffffff, efh->content_colour & 0xffffff, efh->text_colour & 0xffffff);
+			camel_stream_write_string(stream, "<tt>\n" EFH_MESSAGE_START);
+			em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, (CamelDataWrapper *)newpart);
+			camel_stream_flush((CamelStream *)filtered_stream);
+			camel_stream_write_string(stream, "</tt>\n");
+			camel_stream_write_string(stream, "</div>\n");
+		} else {
+			g_string_append_printf(((EMFormat *)efh)->part_id, ".inline.%d", i);
+			em_format_part((EMFormat *)efh, stream, newpart);
+			g_string_truncate(((EMFormat *)efh)->part_id, len);
+		}
+	}
+
+	camel_object_unref(filtered_stream);
+}
+
+static void
+efh_text_enriched(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	CamelStreamFilter *filtered_stream;
+	CamelMimeFilter *enriched;
+	CamelDataWrapper *dw;
+	guint32 flags = 0;
+
+	dw = camel_medium_get_content_object((CamelMedium *)part);
+
+	if (!strcmp(info->mime_type, "text/richtext")) {
+		flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
+		camel_stream_write_string( stream, "\n<!-- text/richtext -->\n");
+	} else {
+		camel_stream_write_string( stream, "\n<!-- text/enriched -->\n");
+	}
+
+	enriched = camel_mime_filter_enriched_new(flags);
+	filtered_stream = camel_stream_filter_new_with_stream (stream);
+	camel_stream_filter_add(filtered_stream, enriched);
+	camel_object_unref(enriched);
+
+	camel_stream_printf (stream,
+			     "<div style=\"border: solid #%06x 2px; background-color: #%06x; padding: 10px; color: #%06x;\">\n" EFH_MESSAGE_START,
+			     efh->frame_colour & 0xffffff, efh->content_colour & 0xffffff, efh->text_colour & 0xffffff);
+
+	em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, (CamelDataWrapper *)part);
+
+	camel_object_unref(filtered_stream);
+	camel_stream_write_string(stream, "</div>");
+}
+
+static void
+efh_write_text_html(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+#if d(!)0
+	CamelStream *out;
+	gint fd;
+	CamelDataWrapper *dw;
+
+	fd = dup(STDOUT_FILENO);
+	out = camel_stream_fs_new_with_fd(fd);
+	printf("writing text content to frame '%s'\n", puri->cid);
+	dw = camel_medium_get_content_object(puri->part);
+	if (dw)
+		camel_data_wrapper_write_to_stream(dw, out);
+	camel_object_unref(out);
+#endif
+	em_format_format_text(emf, stream, (CamelDataWrapper *)puri->part);
+}
+
+static void
+efh_text_html(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	const gchar *location;
+	/* This is set but never used for anything */
+	EMFormatPURI *puri;
+	gchar *cid = NULL;
+
+	camel_stream_printf (stream,
+			     "<div style=\"border: solid #%06x 1px; background-color: #%06x; color: #%06x;\">\n"
+			     "<!-- text/html -->\n" EFH_MESSAGE_START,
+			     efh->frame_colour & 0xffffff, efh->content_colour & 0xffffff, efh->text_colour & 0xffffff);
+
+	/* TODO: perhaps we don't need to calculate this anymore now base is handled better */
+	/* calculate our own location string so add_puri doesn't do it
+	   for us. our iframes are special cases, we need to use the
+	   proper base url to access them, but other children parts
+	   shouldn't blindly inherit the container's location. */
+	location = camel_mime_part_get_content_location(part);
+	if (location == NULL) {
+		if (((EMFormat *)efh)->base)
+			cid = camel_url_to_string(((EMFormat *)efh)->base, 0);
+		else
+			cid = g_strdup(((EMFormat *)efh)->part_id->str);
+	} else {
+		if (strchr(location, ':') == NULL && ((EMFormat *)efh)->base != NULL) {
+			CamelURL *uri;
+
+			uri = camel_url_new_with_base(((EMFormat *)efh)->base, location);
+			cid = camel_url_to_string(uri, 0);
+			camel_url_free(uri);
+		} else {
+			cid = g_strdup(location);
+		}
+	}
+
+	em_format_format_text((EMFormat *)efh, stream, (CamelDataWrapper *)part);
+#if 0	
+	puri = em_format_add_puri((EMFormat *)efh, sizeof(EMFormatPURI), cid, part, efh_write_text_html);
+	d(printf("adding iframe, location %s\n", cid));
+	camel_stream_printf(stream,
+			    "<iframe src=\"%s\" frameborder=0 scrolling=no>could not get %s</iframe>\n"
+			    "</div>\n",
+			    cid, cid);
+#endif	
+	g_free(cid);
+
+}
+
+/* This is a lot of code for something useless ... */
+static void
+efh_message_external(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	CamelContentType *type;
+	const gchar *access_type;
+	gchar *url = NULL, *desc = NULL;
+
+	if (!part) {
+		camel_stream_printf(stream, _("Unknown external-body part."));
+		return;
+	}
+
+	/* needs to be cleaner */
+	type = camel_mime_part_get_content_type(part);
+	access_type = camel_content_type_param (type, "access-type");
+	if (!access_type) {
+		camel_stream_printf(stream, _("Malformed external-body part."));
+		return;
+	}
+
+	if (!g_ascii_strcasecmp(access_type, "ftp") ||
+	    !g_ascii_strcasecmp(access_type, "anon-ftp")) {
+		const gchar *name, *site, *dir, *mode;
+		gchar *path;
+		gchar ftype[16];
+
+		name = camel_content_type_param (type, "name");
+		site = camel_content_type_param (type, "site");
+		dir = camel_content_type_param (type, "directory");
+		mode = camel_content_type_param (type, "mode");
+		if (name == NULL || site == NULL)
+			goto fail;
+
+		/* Generate the path. */
+		if (dir)
+			path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name);
+		else
+			path = g_strdup_printf("/%s", *name=='/'?name+1:name);
+
+		if (mode && *mode)
+			sprintf(ftype, ";type=%c",  *mode);
+		else
+			ftype[0] = 0;
+
+		url = g_strdup_printf ("ftp://%s%s%s";, site, path, ftype);
+		g_free (path);
+		desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url);
+	} else if (!g_ascii_strcasecmp (access_type, "local-file")) {
+		const gchar *name, *site;
+
+		name = camel_content_type_param (type, "name");
+		site = camel_content_type_param (type, "site");
+		if (name == NULL)
+			goto fail;
+
+		url = g_filename_to_uri (name, NULL, NULL);
+		if (site)
+			desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site);
+		else
+			desc = g_strdup_printf(_("Pointer to local file (%s)"), name);
+	} else if (!g_ascii_strcasecmp (access_type, "URL")) {
+		const gchar *urlparam;
+		gchar *s, *d;
+
+		/* RFC 2017 */
+
+		urlparam = camel_content_type_param (type, "url");
+		if (urlparam == NULL)
+			goto fail;
+
+		/* For obscure MIMEy reasons, the URL may be split into words */
+		url = g_strdup (urlparam);
+		s = d = url;
+		while (*s) {
+			/* FIXME: use camel_isspace */
+			if (!isspace ((guchar)*s))
+				*d++ = *s;
+			s++;
+		}
+		*d = 0;
+		desc = g_strdup_printf (_("Pointer to remote data (%s)"), url);
+	} else
+		goto fail;
+
+	camel_stream_printf(stream, "<a href=\"%s\">%s</a>", url, desc);
+	g_free(url);
+	g_free(desc);
+
+	return;
+
+fail:
+	camel_stream_printf(stream, _("Pointer to unknown external data (\"%s\" type)"), access_type);
+}
+
+static void
+efh_message_deliverystatus(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	CamelStreamFilter *filtered_stream;
+	CamelMimeFilter *html_filter;
+	guint32 rgb = 0x737373;
+
+	/* Yuck, this is copied from efh_text_plain */
+	camel_stream_printf (stream,
+			     "<div style=\"border: solid #%06x 1px; background-color: #%06x; padding: 10px; color: #%06x;\">\n",
+			     efh->frame_colour & 0xffffff, efh->content_colour & 0xffffff, efh->text_colour & 0xffffff);
+
+	filtered_stream = camel_stream_filter_new_with_stream(stream);
+	html_filter = camel_mime_filter_tohtml_new(efh->text_html_flags, rgb);
+	camel_stream_filter_add(filtered_stream, html_filter);
+	camel_object_unref(html_filter);
+
+	camel_stream_write_string(stream, "<tt>\n" EFH_MESSAGE_START);
+	em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, (CamelDataWrapper *)part);
+	camel_stream_flush((CamelStream *)filtered_stream);
+	camel_stream_write_string(stream, "</tt>\n");
+
+	camel_stream_write_string(stream, "</div>");
+}
+
+static void
+emfh_write_related(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+	em_format_format_content(emf, stream, puri->part);
+	camel_stream_close(stream);
+}
+
+static void
+emfh_multipart_related_check(struct _EMFormatMailJob *job, gint cancelled)
+{
+	struct _EMFormatPURITree *ptree;
+	EMFormatPURI *puri, *purin;
+	gchar *oldpartid;
+
+	if (cancelled)
+		return;
+
+	d(printf(" running multipart/related check task\n"));
+	oldpartid = g_strdup(((EMFormat *)job->format)->part_id->str);
+
+	ptree = job->puri_level;
+	puri = (EMFormatPURI *)ptree->uri_list.head;
+	purin = puri->next;
+	while (purin) {
+		if (puri->use_count == 0) {
+			d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count));
+			if (puri->func == emfh_write_related) {
+				g_string_printf(((EMFormat *)job->format)->part_id, "%s", puri->part_id);
+				em_format_part((EMFormat *)job->format, (CamelStream *)job->stream, puri->part);
+			}
+			/* else it was probably added by a previous format this loop */
+		}
+		puri = purin;
+		purin = purin->next;
+	}
+
+	g_string_printf(((EMFormat *)job->format)->part_id, "%s", oldpartid);
+	g_free(oldpartid);
+}
+
+/* RFC 2387 */
+static void
+efh_multipart_related(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+	CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+	CamelMimePart *body_part, *display_part = NULL;
+	CamelContentType *content_type;
+	const gchar *start;
+	gint i, nparts, partidlen, displayid = 0;
+	/* puri is set but never used */
+	EMFormatPURI *puri;
+	struct _EMFormatMailJob *job;
+
+	if (!CAMEL_IS_MULTIPART(mp)) {
+		em_format_format_source(emf, stream, part);
+		return;
+	}
+
+	nparts = camel_multipart_get_number(mp);
+	content_type = camel_mime_part_get_content_type(part);
+	start = camel_content_type_param (content_type, "start");
+	if (start && strlen(start)>2) {
+		gint len;
+		const gchar *cid;
+
+		/* strip <>'s */
+		len = strlen (start) - 2;
+		start++;
+
+		for (i=0; i<nparts; i++) {
+			body_part = camel_multipart_get_part(mp, i);
+			cid = camel_mime_part_get_content_id(body_part);
+
+			if (cid && !strncmp(cid, start, len) && strlen(cid) == len) {
+				display_part = body_part;
+				displayid = i;
+				break;
+			}
+		}
+	} else {
+		display_part = camel_multipart_get_part(mp, 0);
+	}
+
+	if (display_part == NULL) {
+		em_format_part_as(emf, stream, part, "multipart/mixed");
+		return;
+	}
+
+	em_format_push_level(emf);
+
+	partidlen = emf->part_id->len;
+
+	/* queue up the parts for possible inclusion */
+	for (i = 0; i < nparts; i++) {
+		body_part = camel_multipart_get_part(mp, i);
+		if (body_part != display_part) {
+			g_string_append_printf(emf->part_id, "related.%d", i);
+			puri = em_format_add_puri(emf, sizeof(EMFormatPURI), NULL, body_part, emfh_write_related);
+			g_string_truncate(emf->part_id, partidlen);
+			d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid));
+		}
+	}
+
+	g_string_append_printf(emf->part_id, "related.%d", displayid);
+	em_format_part(emf, stream, display_part);
+	g_string_truncate(emf->part_id, partidlen);
+	camel_stream_flush(stream);
+
+	/* queue a job to check for un-referenced parts to add as attachments */
+	job = em_format_mail_job_new((EMFormatMail *)emf, emfh_multipart_related_check, NULL);
+	job->stream = stream;
+	camel_object_ref(stream);
+	em_format_mail_job_queue((EMFormatMail *)emf, job);
+
+	em_format_pull_level(emf);
+}
+
+static void
+efh_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+	CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)puri->part);
+
+	d(printf("writing image '%s'\n", puri->cid));
+	camel_data_wrapper_decode_to_stream(dw, stream);
+	camel_stream_close(stream);
+}
+
+static void
+efh_image(EMFormatMail *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+	EMFormatPURI *puri;
+
+	puri = em_format_add_puri((EMFormat *)efh, sizeof(EMFormatPURI), NULL, part, efh_write_image);
+	d(printf("adding image '%s'\n", puri->cid));
+	camel_stream_printf(stream, "<img hspace=10 vspace=10 src=\"%s\">", puri->cid);
+}
+
+static EMFormatHandler type_builtin_table[] = {
+	{ (gchar *) "image/gif", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/jpeg", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/png", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-png", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/tiff", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-bmp", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/bmp", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/svg", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-cmu-raster", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-ico", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-portable-anymap", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-portable-bitmap", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-portable-graymap", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-portable-pixmap", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/x-xpixmap", (EMFormatFunc)efh_image },
+	{ (gchar *) "text/enriched", (EMFormatFunc)efh_text_enriched },
+	{ (gchar *) "text/plain", (EMFormatFunc)efh_text_plain },
+	{ (gchar *) "text/html", (EMFormatFunc)efh_text_html },
+	{ (gchar *) "text/richtext", (EMFormatFunc)efh_text_enriched },
+	{ (gchar *) "text/*", (EMFormatFunc)efh_text_plain },
+	{ (gchar *) "message/external-body", (EMFormatFunc)efh_message_external },
+	{ (gchar *) "message/delivery-status", (EMFormatFunc)efh_message_deliverystatus },
+	{ (gchar *) "multipart/related", (EMFormatFunc)efh_multipart_related },
+
+	/* This is where one adds those busted, non-registered types,
+	   that some idiot mailer writers out there decide to pull out
+	   of their proverbials at random. */
+
+	{ (gchar *) "image/jpg", (EMFormatFunc)efh_image },
+	{ (gchar *) "image/pjpeg", (EMFormatFunc)efh_image },
+
+	/* special internal types */
+
+	{ (gchar *) "x-evolution/message/rfc822", (EMFormatFunc)efh_format_message }
+};
+
+static void
+efh_builtin_init(EMFormatMailClass *efhc)
+{
+	gint i;
+
+	for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+		em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
+
+/* ********************************************************************** */
+
+/* Sigh, this is so we have a cancellable, async rendering thread */
+struct _format_msg {
+	MailMsg base;
+
+	EMFormatMail *format;
+	EMFormat *format_source;
+	EMHTMLStream *estream;
+	CamelFolder *folder;
+	gchar *uid;
+	CamelMimeMessage *message;
+};
+
+static gchar *
+efh_format_desc (struct _format_msg *m)
+{
+	return g_strdup(_("Formatting message"));
+}
+
+static void
+efh_format_exec (struct _format_msg *m)
+{
+	struct _EMFormatMailJob *job;
+	struct _EMFormatPURITree *puri_level;
+	gint cancelled = FALSE;
+	CamelURL *base;
+
+//	if (m->format->html == NULL)
+//		return;
+
+	camel_stream_printf((CamelStream *)m->estream,
+			    "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
+			    "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
+			    "<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n",
+			    m->format->body_colour & 0xffffff,
+			    m->format->header_colour & 0xffffff);
+
+	/* <insert top-header stuff here> */
+
+	if (((EMFormat *)m->format)->mode == EM_FORMAT_SOURCE) {
+		em_format_format_source((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message);
+	} else {
+		const EMFormatHandler *handle;
+#if 0
+		handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/prefix");
+		if (handle)
+			handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
+#endif		
+		handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/rfc822");
+		if (handle)
+			handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
+#if 1		
+		handle = em_format_find_handler((EMFormat *)m->format, "x-evolution/message/post-header-closure");
+		if (handle && !((EMFormat *)m->format)->print)
+			handle->handler((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message, handle);
+#endif		
+	}
+
+	camel_stream_flush((CamelStream *)m->estream);
+
+	puri_level = ((EMFormat *)m->format)->pending_uri_level;
+	base = ((EMFormat *)m->format)->base;
+
+	do {
+		/* now dispatch any added tasks ... */
+		g_mutex_lock(m->format->priv->lock);
+		while ((job = (struct _EMFormatMailJob *)e_dlist_remhead(&m->format->priv->pending_jobs))) {
+			g_mutex_unlock(m->format->priv->lock);
+#if 0
+			/* This is an implicit check to see if the gtkhtml has been destroyed */
+			if (!cancelled)
+				cancelled = m->format->html == NULL;
+#endif
+			/* Now do an explicit check for user cancellation */
+			if (!cancelled)
+				cancelled = camel_operation_cancel_check(NULL);
+
+			/* call jobs even if cancelled, so they can clean up resources */
+			((EMFormat *)m->format)->pending_uri_level = job->puri_level;
+			if (job->base)
+				((EMFormat *)m->format)->base = job->base;
+			job->callback(job, cancelled);
+			((EMFormat *)m->format)->base = base;
+
+			/* clean up the job */
+			camel_object_unref(job->stream);
+			if (job->base)
+				camel_url_free(job->base);
+			g_free(job);
+
+			g_mutex_lock(m->format->priv->lock);
+		}
+		g_mutex_unlock(m->format->priv->lock);
+
+		if (m->estream) {
+			/* Closing this base stream can queue more jobs, so we need
+			   to check the list again after we've finished */
+			d(printf("out of jobs, closing root stream\n"));
+			camel_stream_write_string((CamelStream *)m->estream, "</body>\n</html>\n");
+			camel_stream_close((CamelStream *)m->estream);
+			camel_object_unref(m->estream);
+			m->estream = NULL;
+		}
+
+		/* e_dlist_empty is atomic and doesn't need locking */
+	} while (!e_dlist_empty(&m->format->priv->pending_jobs));
+
+	d(printf("out of jobs, done\n"));
+
+	((EMFormat *)m->format)->pending_uri_level = puri_level;
+}
+
+static void
+efh_format_done (struct _format_msg *m)
+{
+	d(printf("formatting finished\n"));
+
+	m->format->load_http_now = FALSE;
+	m->format->priv->format_id = -1;
+	m->format->state = EM_FORMAT_MAIL_STATE_NONE;
+	g_signal_emit_by_name(m->format, "complete");
+}
+
+static void
+efh_format_free (struct _format_msg *m)
+{
+	d(printf("formatter freed\n"));
+	g_object_unref(m->format);
+	if (m->estream) {
+		camel_stream_close((CamelStream *)m->estream);
+		camel_object_unref(m->estream);
+	}
+	if (m->folder)
+		camel_object_unref(m->folder);
+	g_free(m->uid);
+	if (m->message)
+		camel_object_unref(m->message);
+	if (m->format_source)
+		g_object_unref(m->format_source);
+}
+
+static MailMsgInfo efh_format_info = {
+	sizeof (struct _format_msg),
+	(MailMsgDescFunc) efh_format_desc,
+	(MailMsgExecFunc) efh_format_exec,
+	(MailMsgDoneFunc) efh_format_done,
+	(MailMsgFreeFunc) efh_format_free
+};
+
+static gboolean
+efh_format_timeout(struct _format_msg *m)
+{
+#if 0	
+	GtkHTMLStream *hstream;
+#endif	
+	EMFormatMail *efh = m->format;
+	struct _EMFormatMailPrivate *p = efh->priv;
+
+//	if (m->format->html == NULL) {
+//		mail_msg_unref(m);
+//		return FALSE;
+//	}
+
+	d(printf("timeout called ...\n"));
+	if (p->format_id != -1) {
+		d(printf(" still waiting for cancellation to take effect, waiting ...\n"));
+		return TRUE;
+	}
+
+	g_return_val_if_fail (e_dlist_empty(&p->pending_jobs), FALSE);
+
+	d(printf(" ready to go, firing off format thread\n"));
+
+	/* call super-class to kick it off */
+	efh_parent->format_clone((EMFormat *)efh, m->folder, m->uid, m->message, m->format_source);
+	em_format_mail_clear_pobject(m->format);
+
+	/* FIXME: method off EMFormat? */
+	if (((EMFormat *)efh)->valid) {
+		camel_cipher_validity_free(((EMFormat *)efh)->valid);
+		((EMFormat *)efh)->valid = NULL;
+		((EMFormat *)efh)->valid_parent = NULL;
+	}
+
+	if (m->message == NULL) {
+#if 0		
+		hstream = gtk_html_begin(efh->html);
+		gtk_html_stream_close(hstream, GTK_MAIL_STREAM_OK);
+#endif		
+		mail_msg_unref(m);
+		p->last_part = NULL;
+	} else {
+		efh->state = EM_FORMAT_MAIL_STATE_RENDERING;
+		m->estream = mail_message_view_create_webstream(mail_message_view_create_webview (efh->msg_view, efh->body), efh->body);
+#if 0
+		if (p->last_part != m->message) {
+			hstream = gtk_html_begin (efh->html);
+			gtk_html_stream_printf (hstream, "<h5>%s</h5>", _("Formatting Message..."));
+			gtk_html_stream_close (hstream, GTK_MAIL_STREAM_OK);
+		}
+
+		hstream = NULL;
+		m->estream = (EMHTMLStream *)em_html_stream_new(efh->html, hstream);
+#endif
+		if (p->last_part == m->message) {
+			mail_message_view_set_web_flags (m->estream,
+						  WEB_BEGIN_KEEP_SCROLL | WEB_BEGIN_KEEP_IMAGES
+						  | WEB_BEGIN_BLOCK_UPDATES | WEB_BEGIN_BLOCK_IMAGES);
+#if 0			
+			em_html_stream_set_flags (m->estream,
+						  GTK_HTML_BEGIN_KEEP_SCROLL | GTK_HTML_BEGIN_KEEP_IMAGES
+						  | GTK_HTML_BEGIN_BLOCK_UPDATES | GTK_HTML_BEGIN_BLOCK_IMAGES);
+#endif			
+		} else {
+			/* clear cache of inline-scanned text parts */
+			g_hash_table_remove_all(p->text_inline_parts);
+
+			p->last_part = m->message;
+		}
+
+		efh->priv->format_id = m->base.seq;
+		mail_msg_unordered_push (m);
+	}
+
+	efh->priv->format_timeout_id = 0;
+	efh->priv->format_timeout_msg = NULL;
+
+	return FALSE;
+}
+
+static void efh_format_clone(EMFormat *emf, CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, EMFormat *emfsource)
+{
+	EMFormatMail *efh = (EMFormatMail *)emf;
+	struct _format_msg *m;
+
+	/* How to sub-class ?  Might need to adjust api ... */
+#if 0
+	if (efh->html == NULL)
+		return;
+#endif
+	d(printf("efh_format called\n"));
+	if (efh->priv->format_timeout_id != 0) {
+		d(printf(" timeout for last still active, removing ...\n"));
+		g_source_remove(efh->priv->format_timeout_id);
+		efh->priv->format_timeout_id = 0;
+		mail_msg_unref(efh->priv->format_timeout_msg);
+		efh->priv->format_timeout_msg = NULL;
+	}
+
+	m = mail_msg_new(&efh_format_info);
+	m->format = (EMFormatMail *)emf;
+	g_object_ref(emf);
+	m->format_source = emfsource;
+	if (emfsource)
+		g_object_ref(emfsource);
+	m->folder = folder;
+	if (folder)
+		camel_object_ref(folder);
+	m->uid = g_strdup(uid);
+	m->message = msg;
+	if (msg)
+		camel_object_ref(msg);
+
+	if (efh->priv->format_id == -1) {
+		d(printf(" idle, forcing format\n"));
+		efh_format_timeout(m);
+	} else {
+		d(printf(" still busy, cancelling and queuing wait\n"));
+		/* cancel and poll for completion */
+		mail_msg_cancel(efh->priv->format_id);
+		efh->priv->format_timeout_msg = m;
+		efh->priv->format_timeout_id = g_timeout_add(100, (GSourceFunc)efh_format_timeout, m);
+	}
+}
+
+static void efh_format_error(EMFormat *emf, CamelStream *stream, const gchar *txt)
+{
+	gchar *html;
+
+	html = camel_text_to_html (txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL|CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+	camel_stream_printf(stream, "<em><font color=\"red\">%s</font></em><br>", html);
+	g_free(html);
+}
+
+static void
+efh_format_text_header (EMFormatMail *emfh, CamelStream *stream, const gchar *label, const gchar *value, guint32 flags)
+{
+	const gchar *fmt, *html;
+	gchar *mhtml = NULL;
+	gboolean is_rtl;
+
+	if (value == NULL)
+		return;
+
+	while (*value == ' ')
+		value++;
+
+	if (!(flags & EM_FORMAT_MAIL_HEADER_MAIL))
+		html = mhtml = camel_text_to_html (value, emfh->text_html_flags, 0);
+	else
+		html = value;
+
+	is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
+	if (emfh->simple_headers) {
+		fmt = "<b>%s</b>: %s<br>";
+	} else {
+		if (flags & EM_FORMAT_MAIL_HEADER_NOCOLUMNS) {
+			if (flags & EM_FORMAT_HEADER_BOLD) {
+				fmt = "<tr><td><b>%s:</b> %s</td></tr>";
+			} else {
+				fmt = "<tr><td>%s: %s</td></tr>";
+			}
+		} else if (flags & EM_FORMAT_MAIL_HEADER_NODEC) {
+			if (is_rtl)
+				fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+			else
+				fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
+		} else {
+
+			if (flags & EM_FORMAT_HEADER_BOLD) {
+				if (is_rtl)
+					fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
+				else
+					fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
+			} else {
+				if (is_rtl)
+					fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
+				else
+					fmt = "<tr><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
+			}
+		}
+	}
+
+	camel_stream_printf(stream, fmt, label, html);
+	g_free(mhtml);
+}
+
+static const gchar *addrspec_hdrs[] = {
+	"Sender", "From", "Reply-To", "To", "Cc", "Bcc",
+	"Resent-Sender", "Resent-From", "Resent-Reply-To",
+	"Resent-To", "Resent-Cc", "Resent-Bcc", NULL
+};
+
+static gchar *
+efh_format_address (EMFormatMail *efh, GString *out, struct _camel_header_address *a, gchar *field)
+{
+	guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
+	gchar *name, *mailto, *addr;
+	gint i=0;
+	gboolean wrap = FALSE;
+	gchar *str = NULL;
+	gint limit = mail_config_get_address_count ();
+
+	if (field ) {
+		if ((!strcmp (field, _("To")) && !(efh->header_wrap_flags & EM_FORMAT_MAIL_HEADER_TO))
+		    || (!strcmp (field, _("Cc")) && !(efh->header_wrap_flags & EM_FORMAT_MAIL_HEADER_CC))
+		    || (!strcmp (field, _("Bcc")) && !(efh->header_wrap_flags & EM_FORMAT_MAIL_HEADER_BCC)))
+		    wrap = TRUE;
+	}
+
+	while (a) {
+		if (a->name)
+			name = camel_text_to_html (a->name, flags, 0);
+		else
+			name = NULL;
+
+		switch (a->type) {
+		case CAMEL_HEADER_ADDRESS_NAME:
+			if (name && *name) {
+				gchar *real, *mailaddr;
+
+				g_string_append_printf (out, "%s &lt;", name);
+				/* rfc2368 for mailto syntax and url encoding extras */
+				if ((real = camel_header_encode_phrase ((guchar *)a->name))) {
+					mailaddr = g_strdup_printf("%s <%s>", real, a->v.addr);
+					g_free (real);
+					mailto = camel_url_encode (mailaddr, "?=&()");
+					g_free (mailaddr);
+				} else {
+					mailto = camel_url_encode (a->v.addr, "?=&()");
+				}
+			} else {
+				mailto = camel_url_encode (a->v.addr, "?=&()");
+			}
+			addr = camel_text_to_html (a->v.addr, flags, 0);
+			g_string_append_printf (out, "<a href=\"mailto:%s\";>%s</a>", mailto, addr);
+			g_free (mailto);
+			g_free (addr);
+
+			if (name && *name)
+				g_string_append (out, "&gt;");
+			break;
+		case CAMEL_HEADER_ADDRESS_GROUP:
+			g_string_append_printf (out, "%s: ", name);
+			efh_format_address (efh, out, a->v.members, field);
+			g_string_append_printf (out, ";");
+			break;
+		default:
+			g_warning ("Invalid address type");
+			break;
+		}
+
+		g_free (name);
+
+		i++;
+		a = a->next;
+		if (a)
+			g_string_append (out, ", ");
+
+		/* Let us add a '...' if we have more addresses */
+		if (limit > 0 && wrap && a && (i>(limit-1))) {
+
+			if (!strcmp (field, _("To"))) {
+
+				g_string_append (out, "<a href=\"##TO##\">...</a>");
+//				str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/plus.png\" /></a>  ", EVOLUTION_ICONSDIR);
+
+				return str;
+			}
+			else if (!strcmp (field, _("Cc"))) {
+				g_string_append (out, "<a href=\"##CC##\">...</a>");
+//				str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/plus.png\" /></a>  ", EVOLUTION_ICONSDIR);
+
+				return str;
+			}
+			else if (!strcmp (field, _("Bcc"))) {
+				g_string_append (out, "<a href=\"##BCC##\">...</a>");
+//				str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/plus.png\" /></a>  ", EVOLUTION_ICONSDIR);
+
+				return str;
+			}
+		}
+
+	}
+
+	if (limit > 0 && i>(limit)) {
+
+
+		if (!strcmp (field, _("To"))) {
+	//		str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/minus.png\" /></a>  ", EVOLUTION_ICONSDIR);
+		}
+		else if (!strcmp (field, _("Cc"))) {
+	//		str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/minus.png\" /></a>  ", EVOLUTION_ICONSDIR);
+		}
+		else if (!strcmp (field, _("Bcc"))) {
+	//		str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/minus.png\" /></a>  ", EVOLUTION_ICONSDIR);
+		}
+	}
+
+	return str;
+
+}
+
+static void
+canon_header_name (gchar *name)
+{
+	gchar *inptr = name;
+
+	/* canonicalise the header name... first letter is
+	 * capitalised and any letter following a '-' also gets
+	 * capitalised */
+
+	if (*inptr >= 'a' && *inptr <= 'z')
+		*inptr -= 0x20;
+
+	inptr++;
+
+	while (*inptr) {
+		if (inptr[-1] == '-' && *inptr >= 'a' && *inptr <= 'z')
+			*inptr -= 0x20;
+		else if (*inptr >= 'A' && *inptr <= 'Z')
+			*inptr += 0x20;
+
+		inptr++;
+	}
+}
+
+static void
+efh_format_header(EMFormat *emf, CamelStream *stream, CamelMedium *part, struct _camel_header_raw *header, guint32 flags, const gchar *charset)
+{
+	EMFormatMail *efh = (EMFormatMail *)emf;
+	gchar *name, *buf, *value = NULL;
+	const gchar *label, *txt;
+	gboolean addrspec = FALSE;
+	gchar *str_field = NULL;
+	gint i;
+
+	name = alloca(strlen(header->name)+1);
+	strcpy(name, header->name);
+	canon_header_name (name);
+
+	for (i = 0; addrspec_hdrs[i]; i++) {
+		if (!strcmp(name, addrspec_hdrs[i])) {
+			addrspec = TRUE;
+			break;
+		}
+	}
+
+	label = _(name);
+
+	if (addrspec) {
+		struct _camel_header_address *addrs;
+		GString *html;
+		gchar *img;
+
+		buf = camel_header_unfold (header->value);
+		if (!(addrs = camel_header_address_decode (buf, emf->charset ? emf->charset : emf->default_charset))) {
+			g_free (buf);
+			return;
+		}
+
+		g_free (buf);
+
+		html = g_string_new("");
+		img = efh_format_address(efh, html, addrs, (gchar *)label);
+
+		if (img) {
+			str_field = g_strdup_printf ("%s%s:", img, label);
+			label = str_field;
+			flags |= EM_FORMAT_MAIL_HEADER_NODEC;
+			g_free (img);
+		}
+
+		camel_header_address_unref(addrs);
+		txt = value = html->str;
+		g_string_free(html, FALSE);
+
+		flags |= EM_FORMAT_HEADER_BOLD | EM_FORMAT_MAIL_HEADER_MAIL;
+	} else if (!strcmp (name, "Subject")) {
+		buf = camel_header_unfold (header->value);
+		txt = value = camel_header_decode_string (buf, charset);
+		g_free (buf);
+
+		flags |= EM_FORMAT_HEADER_BOLD;
+	} else if (!strcmp(name, "X-evolution-mailer")) {
+		/* pseudo-header */
+		label = _("Mailer");
+		txt = value = camel_header_format_ctext (header->value, charset);
+		flags |= EM_FORMAT_HEADER_BOLD;
+	} else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) {
+		gint msg_offset, local_tz;
+		time_t msg_date;
+		struct tm local;
+
+		txt = header->value;
+		while (*txt == ' ' || *txt == '\t')
+			txt++;
+
+		/* Show the local timezone equivalent in brackets if the sender is remote */
+		msg_date = camel_header_decode_date(txt, &msg_offset);
+		e_localtime_with_offset(msg_date, &local, &local_tz);
+
+		/* Convert message offset to minutes (e.g. -0400 --> -240) */
+		msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100);
+		/* Turn into offset from localtime, not UTC */
+		msg_offset -= local_tz / 60;
+
+		if (msg_offset) {
+			gchar buf[256], *html;
+
+			msg_offset += (local.tm_hour * 60) + local.tm_min;
+			if (msg_offset >= (24 * 60) || msg_offset < 0) {
+				/* translators: strftime format for local time equivalent in Date header display, with day */
+				gchar *msg = g_strdup_printf("<I>%s</I>", _(" (%a, %R %Z)"));
+				e_utf8_strftime(buf, sizeof(buf), msg, &local);
+				g_free(msg);
+			} else {
+				/* translators: strftime format for local time equivalent in Date header display, without day */
+				gchar *msg = g_strdup_printf("<I>%s</I>", _(" (%R %Z)"));
+				e_utf8_strftime(buf, sizeof(buf), msg, &local);
+				g_free(msg);
+			}
+
+			html = camel_text_to_html(txt, efh->text_html_flags, 0);
+			txt = value = g_strdup_printf("%s %s", html, buf);
+			g_free(html);
+			flags |= EM_FORMAT_MAIL_HEADER_MAIL;
+		}
+
+		flags |= EM_FORMAT_HEADER_BOLD;
+	} else if (!strcmp(name, "Newsgroups")) {
+		struct _camel_header_newsgroup *ng, *scan;
+		GString *html;
+
+		buf = camel_header_unfold (header->value);
+
+		if (!(ng = camel_header_newsgroups_decode (buf))) {
+			g_free (buf);
+			return;
+		}
+
+		g_free (buf);
+
+		html = g_string_new("");
+		scan = ng;
+		while (scan) {
+			g_string_append_printf(html, "<a href=\"news:%s\";>%s</a>", scan->newsgroup, scan->newsgroup);
+			scan = scan->next;
+			if (scan)
+				g_string_append_printf(html, ", ");
+		}
+
+		camel_header_newsgroups_free(ng);
+
+		txt = html->str;
+		g_string_free(html, FALSE);
+		flags |= EM_FORMAT_HEADER_BOLD|EM_FORMAT_MAIL_HEADER_MAIL;
+	} else if (!strcmp (name, "Received") || !strncmp (name, "X-", 2)) {
+		/* don't unfold Received nor extension headers */
+		txt = value = camel_header_decode_string(header->value, charset);
+	} else {
+		/* don't unfold Received nor extension headers */
+		buf = camel_header_unfold (header->value);
+		txt = value = camel_header_decode_string (buf, charset);
+		g_free (buf);
+	}
+
+	efh_format_text_header(efh, stream, label, txt, flags);
+
+	g_free (value);
+	g_free (str_field);
+}
+
+static void
+efh_format_headers(EMFormatMail *efh, CamelStream *stream, CamelMedium *part)
+{
+	EMFormat *emf = (EMFormat *) efh;
+	EMFormatHeader *h;
+	const gchar *charset;
+	CamelContentType *ct;
+	struct _camel_header_raw *header;
+	gboolean have_icon = FALSE;
+	const gchar *photo_name = NULL;
+	CamelInternetAddress *cia = NULL;
+	gboolean face_decoded  = FALSE, contact_has_photo = FALSE;
+	guchar *face_header_value = NULL;
+	gsize face_header_len = 0;
+	gchar *header_sender = NULL, *header_from = NULL, *name;
+	gboolean mail_from_delegate = FALSE;
+	const gchar *hdr_charset;
+
+	if (!part)
+		return;
+
+	ct = camel_mime_part_get_content_type((CamelMimePart *)part);
+	charset = camel_content_type_param (ct, "charset");
+	charset = camel_iconv_charset_name(charset);
+
+//	if (!efh->simple_headers)
+//		camel_stream_printf(stream,
+//				    "<font color=\"#%06x\">\n"
+//				    "<table cellpadding=\"0\" width=\"100%%\">",
+//				    efh->header_colour & 0xffffff);
+	if (!efh->simple_headers)
+		camel_stream_printf(stream,
+				    "<table cellpadding=\"0\" width=\"100%%\">");
+
+
+	hdr_charset = emf->charset ? emf->charset : emf->default_charset;
+
+	header = ((CamelMimePart *)part)->headers;
+	while (header) {
+		if (!g_ascii_strcasecmp (header->name, "Sender")) {
+			struct _camel_header_address *addrs;
+			GString *html;
+
+			if (!(addrs = camel_header_address_decode (header->value, hdr_charset)))
+				break;
+
+			html = g_string_new("");
+			name = efh_format_address(efh, html, addrs, header->name);
+
+			header_sender = html->str;
+			camel_header_address_unref(addrs);
+
+			g_string_free(html, FALSE);
+			g_free (name);
+		} else if (!g_ascii_strcasecmp (header->name, "From")) {
+			struct _camel_header_address *addrs;
+			GString *html;
+
+			if (!(addrs = camel_header_address_decode (header->value, hdr_charset)))
+				break;
+
+			html = g_string_new("");
+			name = efh_format_address(efh, html, addrs, header->name);
+
+			header_from = html->str;
+			camel_header_address_unref(addrs);
+
+			g_string_free(html, FALSE);
+			g_free(name);
+		} else if (!g_ascii_strcasecmp (header->name, "X-Evolution-Mail-From-Delegate")) {
+			mail_from_delegate = TRUE;
+		}
+
+		header = header->next;
+	}
+
+	if (header_sender && header_from && mail_from_delegate) {
+		camel_stream_printf(stream, "<tr><td><table border=1 width=\"100%%\" cellspacing=2 cellpadding=2><tr>");
+		if(gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
+			camel_stream_printf (stream, "<td align=\"right\" width=\"100%%\">");
+		else
+			camel_stream_printf (stream, "<td align=\"left\" width=\"100%%\">");
+		/* To translators: This message suggests to the receipients that the sender of the mail is
+		   different from the one listed in From field.
+		*/
+		camel_stream_printf(stream, _("This message was sent by <b>%s</b> on behalf of <b>%s</b>"), header_sender, header_from);
+		camel_stream_printf(stream, "</td></tr></table></td></tr>");
+	}
+
+	g_free (header_sender);
+	g_free (header_from);
+
+	if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
+		camel_stream_printf (stream, "<tr><td><table width=\"100%%\" border=0 cellpadding=\"0\">\n");
+	else
+		camel_stream_printf (stream, "<tr><td><table border=0 cellpadding=\"0\">\n");
+
+	/* dump selected headers */
+	h = (EMFormatHeader *)emf->header_list.head;
+	if (emf->mode == EM_FORMAT_ALLHEADERS) {
+		header = ((CamelMimePart *)part)->headers;
+		while (header) {
+			efh_format_header(emf, stream, part, header, EM_FORMAT_MAIL_HEADER_NOCOLUMNS, charset);
+			header = header->next;
+		}
+	} else {
+		gint mailer_shown = FALSE;
+		while (h->next) {
+			gint mailer, face;
+
+			header = ((CamelMimePart *)part)->headers;
+			mailer = !g_ascii_strcasecmp (h->name, "X-Evolution-Mailer");
+			face = !g_ascii_strcasecmp (h->name, "Face");
+
+			while (header) {
+				if (emf->show_photo && !photo_name && !g_ascii_strcasecmp (header->name, "From"))
+					photo_name = header->value;
+
+				if (!mailer_shown && mailer && (!g_ascii_strcasecmp (header->name, "X-Mailer") ||
+								!g_ascii_strcasecmp (header->name, "User-Agent") ||
+								!g_ascii_strcasecmp (header->name, "X-Newsreader") ||
+								!g_ascii_strcasecmp (header->name, "X-MimeOLE"))) {
+					struct _camel_header_raw xmailer, *use_header = NULL;
+
+					if (!g_ascii_strcasecmp (header->name, "X-MimeOLE")) {
+						for (use_header = header->next; use_header; use_header = use_header->next) {
+							if (!g_ascii_strcasecmp (use_header->name, "X-Mailer") ||
+							    !g_ascii_strcasecmp (use_header->name, "User-Agent") ||
+							    !g_ascii_strcasecmp (use_header->name, "X-Newsreader")) {
+								/* even we have X-MimeOLE, then use rather the standard one, when available */
+								break;
+							}
+						}
+					}
+
+					if (!use_header)
+						use_header = header;
+
+					xmailer.name = (gchar *) "X-Evolution-Mailer";
+					xmailer.value = use_header->value;
+					mailer_shown = TRUE;
+
+					efh_format_header (emf, stream, part, &xmailer, h->flags, charset);
+					if (strstr(use_header->value, "Evolution"))
+						have_icon = TRUE;
+				} else if (!face_decoded && face && !g_ascii_strcasecmp (header->name, "Face")) {
+					gchar *cp = header->value;
+
+					/* Skip over spaces */
+					while (*cp == ' ')
+						cp++;
+
+					face_header_value = g_base64_decode (cp, &face_header_len);
+					face_header_value = g_realloc (face_header_value, face_header_len + 1);
+					face_header_value[face_header_len] = 0;
+					face_decoded = TRUE;
+				/* Showing an encoded "Face" header makes little sense */
+				} else if (!g_ascii_strcasecmp (header->name, h->name) && !face) {
+					efh_format_header(emf, stream, part, header, h->flags, charset);
+				}
+
+				header = header->next;
+			}
+			h = h->next;
+		}
+	}
+
+	if (!efh->simple_headers) {
+		camel_stream_printf(stream, "</table></td>");
+
+		if (photo_name) {
+			gchar *classid;
+			CamelMimePart *photopart;
+
+			cia = camel_internet_address_new();
+			camel_address_decode((CamelAddress *) cia, (const gchar *) photo_name);
+			photopart = em_utils_contact_photo (cia, emf->photo_local);
+
+			if (photopart) {
+				contact_has_photo = TRUE;
+				classid = g_strdup_printf("icon:///em-format-html/%s/photo/header",
+				emf->part_id->str);
+				camel_stream_printf(stream,
+					"<td align=\"right\" valign=\"top\"><img width=64 src=\"%s\"></td>",
+					classid);
+				em_format_add_puri(emf, sizeof(EMFormatPURI), classid,
+					photopart, efh_write_image);
+				camel_object_unref(photopart);
+
+				g_free(classid);
+			}
+			camel_object_unref(cia);
+		}
+
+		if (!contact_has_photo && face_decoded) {
+			gchar *classid;
+			CamelMimePart *part;
+
+			part = camel_mime_part_new ();
+			camel_mime_part_set_content ((CamelMimePart *) part, (const gchar *) face_header_value, face_header_len, "image/png");
+			classid = g_strdup_printf("icon:///em-format-html/face/photo/header");
+			camel_stream_printf(stream, "<td align=\"right\" valign=\"top\"><img width=48 src=\"%s\"></td>", classid);
+			em_format_add_puri(emf, sizeof(EMFormatPURI), classid, part, efh_write_image);
+			camel_object_unref(part);
+		}
+
+		if (have_icon && efh->show_icon) {
+			GtkIconInfo *icon_info;
+			gchar *classid;
+			CamelMimePart *iconpart = NULL;
+
+			classid = g_strdup_printf("icon:///em-format-html/%s/icon/header", emf->part_id->str);
+			camel_stream_printf(stream, "<td align=\"right\" valign=\"top\"><img width=16 height=16 src=\"%s\"></td>", classid);
+
+			icon_info = gtk_icon_theme_lookup_icon (
+				gtk_icon_theme_get_default (),
+				"evolution", 16, GTK_ICON_LOOKUP_NO_SVG);
+			if (icon_info != NULL) {
+				iconpart = em_format_mail_file_part (
+					(EMFormatMail *) emf, "image/png",
+					gtk_icon_info_get_filename (icon_info));
+				gtk_icon_info_free (icon_info);
+			}
+
+			if (iconpart) {
+				em_format_add_puri(emf, sizeof(EMFormatPURI), classid, iconpart, efh_write_image);
+				camel_object_unref(iconpart);
+			}
+			g_free(classid);
+		}
+		camel_stream_printf (stream, "</tr></table>\n</font>\n");
+	}
+}
+
+static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+	const EMFormatHandler *handle;
+
+	/* TODO: make this validity stuff a method */
+	EMFormatMail *efh = (EMFormatMail *) emf;
+	CamelCipherValidity *save = emf->valid, *save_parent = emf->valid_parent;
+
+	emf->valid = NULL;
+	emf->valid_parent = NULL;
+
+	if (emf->message != (CamelMimeMessage *)part)
+		camel_stream_printf(stream, "<blockquote>\n");
+
+	/* Hide headers of the first message. We render them differently. How about for Evolution ?*/
+	if (!efh->hide_headers && emf->message != (CamelMimeMessage *)part)
+		efh_format_headers(efh, stream, (CamelMedium *)part);
+
+#if 1
+	handle = em_format_find_handler(emf, "x-evolution/message/post-header");
+	if (handle)
+		handle->handler(emf, stream, part, handle);
+#endif
+
+	camel_stream_printf(stream, EM_FORMAT_MAIL_VPAD);
+	em_format_part(emf, stream, part);
+
+	if (emf->message != (CamelMimeMessage *)part)
+		camel_stream_printf(stream, "</blockquote>\n");
+
+	camel_cipher_validity_free(emf->valid);
+
+	emf->valid = save;
+	emf->valid_parent = save_parent;
+}
+
+static void efh_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+	CamelStreamFilter *filtered_stream;
+	CamelMimeFilter *html_filter;
+	CamelDataWrapper *dw = (CamelDataWrapper *)part;
+
+	filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
+	html_filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL
+						    | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
+						    | CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
+	camel_stream_filter_add(filtered_stream, html_filter);
+	camel_object_unref(html_filter);
+
+	camel_stream_write_string((CamelStream *)stream, "<table><tr><td><tt>");
+	em_format_format_text(emf, (CamelStream *)filtered_stream, dw);
+	camel_object_unref(filtered_stream);
+
+	camel_stream_write_string(stream, "</tt></td></tr></table>");
+}
+
+static void
+efh_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const gchar *mime_type, const EMFormatHandler *handle)
+{
+	gchar *text, *html;
+#if 0
+	/* we display all inlined attachments only */
+
+	/* this could probably be cleaned up ... */
+	camel_stream_write_string(stream,
+				  "<table border=1 cellspacing=0 cellpadding=0><tr><td>"
+				  "<table width=10 cellspacing=0 cellpadding=0>"
+				  "<tr><td></td></tr></table></td>"
+				  "<td><table width=3 cellspacing=0 cellpadding=0>"
+				  "<tr><td></td></tr></table></td><td><font size=-1>\n");
+
+	/* output some info about it */
+	text = em_format_describe_part(part, mime_type);
+	html = camel_text_to_html(text, ((EMFormatMail *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+	camel_stream_write_string(stream, html);
+	g_free(html);
+	g_free(text);
+
+	camel_stream_write_string(stream, "</font></td></tr><tr></table>");
+#endif
+	if (handle && em_format_is_inline(emf, emf->part_id->str, part, handle))
+		handle->handler(emf, stream, part, handle);
+}
+
+static gboolean
+efh_busy(EMFormat *emf)
+{
+	return (((EMFormatMail *)emf)->priv->format_id != -1);
+}
+
+void
+em_format_mail_set_message_view (EMFormatMail *efw, struct _MailMessageView *mmv, GtkWidget *body)
+{
+	efw->msg_view = mmv;
+	efw->body = body;
+}
diff --git a/src/em-format-mail.h b/src/em-format-mail.h
new file mode 100644
index 0000000..e6141ca
--- /dev/null
+++ b/src/em-format-mail.h
@@ -0,0 +1,293 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ *		Michael Zucchi <notzed ximian com>
+ *		Srinivasa Ragavan <sragavan novell com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/*
+  Concrete class for formatting mails to html
+*/
+
+#ifndef EM_FORMAT_MAIL_H
+#define EM_FORMAT_MAIL_H
+
+#include <mail/em-format.h>
+#include <mail/mail-config.h>
+#include <camel/camel-medium.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-url.h>
+
+#include "mail-message-view.h"
+
+/* Standard GObject macros */
+#define EM_TYPE_FORMAT_MAIL \
+	(em_format_mail_get_type ())
+#define EM_FORMAT_MAIL(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), EM_TYPE_FORMAT_MAIL, EMFormatMail))
+#define EM_FORMAT_MAIL_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), EM_TYPE_FORMAT_MAIL, EMFormatMailClass))
+#define EM_IS_FORMAT_MAIL(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), EM_TYPE_FORMAT_MAIL))
+#define EM_IS_FORMAT_MAIL_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), EM_TYPE_FORMAT_MAIL))
+#define EM_FORMAT_MAIL_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), EM_TYPE_FORMAT_MAIL, EMFormatMailClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMFormatMail EMFormatMail;
+typedef struct _EMFormatMailClass EMFormatMailClass;
+typedef struct _EMFormatMailPrivate EMFormatMailPrivate;
+
+typedef enum {
+	MAIL_BEGIN_KEEP_SCROLL = 1 << 0,
+	MAIL_BEGIN_KEEP_IMAGES = 1 << 1,
+	MAIL_BEGIN_BLOCK_UPDATES = 1 << 2,
+	MAIL_BEGIN_BLOCK_IMAGES = 1 << 3,
+	/*enable autochange content_type*/
+	MAIL_BEGIN_CHANGECONTENTTYPE = 1 << 4
+} MailBeginFlags;
+
+enum _em_format_mail_header_flags {
+	EM_FORMAT_MAIL_HEADER_TO = 1<<0,
+	EM_FORMAT_MAIL_HEADER_CC = 1<<1,
+	EM_FORMAT_MAIL_HEADER_BCC = 1<<2
+};
+
+typedef enum {
+	EM_FORMAT_MAIL_STATE_NONE = 0,
+	EM_FORMAT_MAIL_STATE_RENDERING
+} EMFormatMailState;
+
+/* A MailJob will be executed in another thread, in sequence.
+   It's job is to write to its stream, close it if successful,
+   then exit. */
+
+typedef struct _EMFormatMailJob EMFormatMailJob;
+
+/**
+ * struct _EMFormatMailJob - A formatting job.
+ *
+ * @next: Double linked list header.
+ * @prev: Double linked list header.
+ * @format: Set by allocation function.
+ * @stream: Free for use by caller.
+ * @puri_level: Set by allocation function.
+ * @base: Set by allocation function, used to save state.
+ * @callback: This callback will always be invoked, only once, even if the user
+ * cancelled the display.  So the callback should free any extra data
+ * it allocated every time it is called.
+ * @u: Union data, free for caller to use.
+ *
+ * This object is used to queue a long-running-task which cannot be
+ * processed in the primary thread.  When its turn comes, the job will
+ * be de-queued and the @callback invoked to perform its processing,
+ * restoring various state to match the original state.  This is used
+ * for image loading and other internal tasks.
+ *
+ * This object is struct-subclassable.  Only em_format_mail_job_new()
+ * may be used to allocate these.
+ **/
+struct _EMFormatMailJob {
+	EMFormatMailJob *next;
+	EMFormatMailJob *prev;
+
+	EMFormatMail *format;
+	CamelStream *stream;
+
+	/* We need to track the state of the visibility tree at
+	   the point this uri was generated */
+	struct _EMFormatPURITree *puri_level;
+	CamelURL *base;
+
+	void (*callback)(EMFormatMailJob *job, gint cancelled);
+	union {
+		gchar *uri;
+		CamelMedium *msg;
+		EMFormatPURI *puri;
+		struct _EMFormatPURITree *puri_level;
+		gpointer data;
+	} u;
+};
+
+/* Pending object (classid: url) */
+typedef struct _EMFormatMailPObject EMFormatMailPObject;
+
+typedef GtkWidget * (*EMFormatMailPObjectFunc)(EMFormatMail *md, GtkWidget *eb, EMFormatMailPObject *pobject);
+
+/**
+ * struct _EMFormatMailPObject - Pending object.
+ *
+ * @next: Double linked list header.
+ * @prev: Double linked list header.
+ * @free: Invoked when the object is no longer needed.
+ * @format: The parent formatter.
+ * @classid: The assigned class id as passed to add_pobject().
+ * @func: Callback function.
+ * @part: The part as passed to add_pobject().
+ *
+ * This structure is used to track OBJECT tags which have been
+ * inserted into the Mail stream.  When GtkHTML requests them the
+ * @func will be invoked to create the embedded widget.
+ *
+ * This object is struct-subclassable.  Only
+ * em_format_mail_add_pobject() may be used to allocate these.
+ **/
+struct _EMFormatMailPObject {
+	EMFormatMailPObject *next;
+	EMFormatMailPObject *prev;
+
+	void (*free)(EMFormatMailPObject *);
+	EMFormatMail *format;
+
+	gchar *classid;
+
+	EMFormatMailPObjectFunc func;
+	CamelMimePart *part;
+	/* Private */
+	GtkWidget *body;
+	GtkWidget *view;
+	struct _CamelStream *stream;	
+};
+
+#define EM_FORMAT_MAIL_HEADER_NOCOLUMNS (EM_FORMAT_HEADER_LAST)
+#define EM_FORMAT_MAIL_HEADER_MAIL (EM_FORMAT_HEADER_LAST<<1) /* header already in html format */
+#define EM_FORMAT_MAIL_HEADER_NODEC (EM_FORMAT_HEADER_LAST<<2) /* header already in html format */
+#define EM_FORMAT_MAIL_HEADER_LAST (EM_FORMAT_HEADER_LAST<<8)
+
+#define EM_FORMAT_MAIL_VPAD "<table cellspacing=0 cellpadding=3><tr><td><a name=\"padding\"></a></td></tr></table>\n"
+
+/**
+ * struct _EMFormatMail - Mail formatter object.
+ *
+ * @format:
+ * @priv:
+ * @msg_view:
+ * @body:
+ * @pending_object_list:
+ * @headers:
+ * @text_mail_flags:
+ * @body_colour:
+ * @header_colour:
+ * @text_colour:
+ * @frame_colour:
+ * @content_colour:
+ * @citation_colour:
+ * @load_http:2:
+ * @load_http_now:1:
+ * @mark_citations:1:
+ * @simple_headers:1:
+ * @hide_headers:1:
+ * @show_icon:1:
+ *
+ * Most of these fields are private or read-only.
+ *
+ * The base HTML formatter object.  This object drives HTML generation
+ * into a GtkHTML parser.  It also handles text to HTML conversion,
+ * multipart/related objects and inline images.
+ **/
+struct _EMFormatMail {
+	EMFormat format;
+
+	EMFormatMailPrivate *priv;
+
+	GtkWidget *msg_view;
+	GtkWidget *body;
+
+	EDList pending_object_list;
+
+	GSList *headers;
+
+	guint32 text_html_flags; /* default flags for text to html conversion */
+	guint32 body_colour;	/* header box colour */
+	guint32 header_colour;
+	guint32 text_colour;
+	guint32 frame_colour;
+	guint32 content_colour;
+	guint32 citation_colour;
+	guint load_http:2;
+	guint load_http_now:1;
+	guint mark_citations:1;
+	guint simple_headers:1; /* simple header format, no box/table */
+	guint hide_headers:1; /* no headers at all */
+	guint show_icon:1; /* show an icon when the sender used Evo */
+	guint32 header_wrap_flags;
+
+	EMFormatMailState state; /* actual state of the object */
+};
+
+struct _EMFormatMailClass {
+	EMFormatClass format_class;
+};
+
+GType		em_format_mail_get_type		(void);
+EMFormatMail *	em_format_mail_new		(void);
+void		em_format_mail_load_http	(EMFormatMail *efh);
+
+void		em_format_mail_set_load_http	(EMFormatMail *efh,
+						 gint style);
+void		em_format_mail_set_mark_citations
+						(EMFormatMail *efh,
+						 gint state,
+						 guint32 citation_colour);
+
+/* retrieves a pseudo-part icon wrapper for a file */
+CamelMimePart *	em_format_mail_file_part	(EMFormatMail *efh,
+						 const gchar *mime_type,
+						 const gchar *filename);
+
+/* for implementers */
+EMFormatMailPObject *
+		em_format_mail_add_pobject	(EMFormatMail *efh,
+						 gsize size,
+						 const gchar *classid,
+						 CamelMimePart *part,
+						 EMFormatMailPObjectFunc func);
+EMFormatMailPObject *
+		em_format_mail_find_pobject	(EMFormatMail *efh,
+						 const gchar *classid);
+EMFormatMailPObject *
+		em_format_mail_find_pobject_func(EMFormatMail *efh,
+						 CamelMimePart *part,
+						 EMFormatMailPObjectFunc func);
+void		em_format_mail_remove_pobject	(EMFormatMail *efh,
+						 EMFormatMailPObject *pobject);
+void		em_format_mail_clear_pobject	(EMFormatMail *efh);
+
+EMFormatMailJob *
+		em_format_mail_job_new		(EMFormatMail *efh,
+						 void (*callback)(EMFormatMailJob *job, gint cancelled),
+						 gpointer data);
+void		em_format_mail_job_queue	(EMFormatMail *efh,
+						 EMFormatMailJob *job);
+void 		em_format_mail_set_message_view (EMFormatMail *efw, 
+						 struct _MailMessageView *mmv, 
+						 GtkWidget *body);
+
+G_END_DECLS
+
+#endif /* EM_FORMAT_MAIL_H */



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