[anjal] Ultra new mail rendering for Anjal.
- From: Srinivasa Ragavan <sragavan src gnome org>
- To: svn-commits-list gnome org
- Subject: [anjal] Ultra new mail rendering for Anjal.
- Date: Tue, 16 Jun 2009 13:41:48 -0400 (EDT)
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> ", _("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> </b></th></tr>";
+ else
+ fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s<b> </b></th><td valign=top>%s</td></tr>";
+ } else {
+
+ if (flags & EM_FORMAT_HEADER_BOLD) {
+ if (is_rtl)
+ fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b> </b></th></tr>";
+ else
+ fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s:<b> </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> </b></td></tr>";
+ else
+ fmt = "<tr><td align=\"right\" valign=\"top\" nowrap>%s:<b> </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 <", 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, ">");
+ 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]