aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--data/Makefile.am2
-rw-r--r--data/webview-print.css62
-rw-r--r--data/webview.css147
-rw-r--r--em-format/Makefile.am6
-rw-r--r--em-format/em-format-quote.c792
-rw-r--r--em-format/em-format-quote.h3
-rw-r--r--em-format/em-format.c3840
-rw-r--r--em-format/em-format.h566
-rw-r--r--mail/Makefile.am20
-rw-r--r--mail/e-mail-attachment-bar.c94
-rw-r--r--mail/e-mail-attachment-bar.h4
-rw-r--r--mail/e-mail-browser.c113
-rw-r--r--mail/e-mail-browser.h6
-rw-r--r--mail/e-mail-display.c1430
-rw-r--r--mail/e-mail-display.h50
-rw-r--r--mail/e-mail-notebook-view.c18
-rw-r--r--mail/e-mail-paned-view.c82
-rw-r--r--mail/e-mail-paned-view.h1
-rw-r--r--mail/e-mail-printer.c859
-rw-r--r--mail/e-mail-printer.h85
-rw-r--r--mail/e-mail-reader-utils.c151
-rw-r--r--mail/e-mail-reader-utils.h3
-rw-r--r--mail/e-mail-reader.c445
-rw-r--r--mail/e-mail-reader.h6
-rw-r--r--mail/e-mail-request.c771
-rw-r--r--mail/e-mail-request.h36
-rw-r--r--mail/em-account-editor.c6
-rw-r--r--mail/em-composer-utils.c31
-rw-r--r--mail/em-format-hook.c23
-rw-r--r--mail/em-format-hook.h4
-rw-r--r--mail/em-format-html-display.c1350
-rw-r--r--mail/em-format-html-display.h49
-rw-r--r--mail/em-format-html-print.c681
-rw-r--r--mail/em-format-html-print.h19
-rw-r--r--mail/em-format-html.c3755
-rw-r--r--mail/em-format-html.h191
-rw-r--r--mail/em-html-stream.c182
-rw-r--r--mail/em-html-stream.h77
-rw-r--r--mail/em-utils.c74
-rw-r--r--mail/mail.error.xml5
-rw-r--r--modules/Makefile.am1
-rw-r--r--modules/addressbook/e-book-shell-content.c9
-rw-r--r--modules/mail/e-mail-config-format-html.c5
-rw-r--r--modules/mail/e-mail-config-web-view.c6
-rw-r--r--modules/mail/e-mail-shell-backend.c57
-rw-r--r--modules/mail/e-mail-shell-content.c9
-rw-r--r--modules/mail/e-mail-shell-view-actions.c116
-rw-r--r--modules/mail/e-mail-shell-view-private.c100
-rw-r--r--modules/mail/em-mailer-prefs.c6
-rw-r--r--modules/web-inspector/Makefile.am23
-rw-r--r--modules/web-inspector/evolution-web-inspector.c185
51 files changed, 9805 insertions, 6751 deletions
diff --git a/data/Makefile.am b/data/Makefile.am
index bb6b2c18a3..9d9a8869c4 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -39,6 +39,8 @@ convert_DATA = evolution.convert
themedir = $(privdatadir)/theme
dist_theme_DATA = \
default.css \
+ webview.css \
+ webview-print.css \
tab-bar-background.png \
tab-switcher.png \
tab-switcher-hover.png \
diff --git a/data/webview-print.css b/data/webview-print.css
new file mode 100644
index 0000000000..a2c5292d56
--- /dev/null
+++ b/data/webview-print.css
@@ -0,0 +1,62 @@
+html, body {
+ padding: 0;
+ margin: 0;
+}
+
+body {
+ /* Use margin so that children can safely use width=100% */
+ margin: 10px;
+}
+
+h1,h2,h3 {
+ color: #7f7f7f;
+}
+
+th {
+ color: #7f7f7f;
+ text-align: left;
+ font-weight: normal;
+ vertical-align: top;
+}
+
+.header {
+ color: #7f7f7f;
+}
+
+.pre {
+ font-family: monospace;
+}
+
+.part-container {
+ width: 100%;
+ background: #FFF;
+ margin-top: 2px;
+ margin-bottom: 3px;
+ border-width: 0px;
+ border-style: none;
+}
+
+.part-container-inner-margin {
+ margin: 8px;
+}
+
+/***** PRINTING *******/
+
+.printing-header {
+ margin-bottom: 20px;
+}
+
+.printing-header h1,
+.attachments-list h1 {
+ font-size: 20px;
+}
+
+.printing-header th {
+ text-align: right;
+ font-weight: bold;
+}
+
+.attachments-list th {
+ font-weight: bold;
+}
+
diff --git a/data/webview.css b/data/webview.css
new file mode 100644
index 0000000000..9ff822013e
--- /dev/null
+++ b/data/webview.css
@@ -0,0 +1,147 @@
+html, body {
+ padding: 0;
+ margin: 0;
+}
+
+body {
+ /* Use margin so that children can safely use width=100% */
+ margin: 10px;
+}
+
+h1, h2, h3 {
+ color: #7f7f7f;
+}
+
+th {
+ color: #7f7f7f;
+ text-align: left;
+ font-weight: normal;
+ vertical-align: top;
+}
+
+.header {
+ color: #7f7f7f;
+}
+
+.pre {
+ font-family: monospace;
+}
+
+span.navigable, div.navigable, p.navigable {
+ cursor: pointer;
+ text-decoration: underline;
+ color: #003399;
+}
+
+img.navigable {
+ cursor: pointer;
+ margin-right: 4px;
+}
+
+.attachments {
+ background: #FFF;
+ border: 1px solid silver;
+ margin: 10px 10px 10px 10px;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 0;
+}
+
+.attachment {
+ margin-left: 8px;
+ margin-right: 8px;
+}
+
+.attachment-wrapper
+{
+ margin-right: 8px;
+}
+
+.part-container {
+ width: 100%;
+ height: 100%;
+ background: #FFF;
+ margin-top: 2px;
+ margin-bottom: 3px;
+ border-width: 1px;
+ border-style: solid;
+}
+
+.part-container-inner-margin {
+ margin: 8px;
+}
+
+object { /* GtkWidgets */
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+.__evo-highlight {
+ color: purple;
+ font-weight: bold;
+}
+
+/***** PRINTING *******/
+
+.printing-header {
+ margin-bottom: 20px;
+}
+
+.printing-header h1,
+.attachments-list h1 {
+ font-size: 20px;
+}
+
+.printing-header th {
+ text-align: right;
+ font-weight: bold;
+}
+
+.attachments-list th {
+ font-weight: bold;
+}
+
+/******* ITIP *********/
+.itip.icon {
+ float: left;
+ margin-right: 5px;
+}
+
+.itip.content {
+ float: left;
+ max-width: 90%;
+}
+
+.itip.description {
+ margin: 5px;
+}
+
+.itip tr {
+ vertical-align: middle;
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
+.itip th {
+ color: #000;
+ vertical-align: middle;
+}
+
+#table_row_summary td {
+ font-weight: bold;
+}
+
+#table_row_buttons button {
+ line-height: 28px;
+ min-width: 150px;
+ white-space: nowrap;
+}
+
+#table_row_buttons img {
+ margin-right: 5px;
+ vertical-align: middle;
+}
+
+#text_row_buttons td {
+ text-align: center;
+}
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index 278bcb2527..392a195044 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -13,7 +13,8 @@ libemformat_la_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/widgets \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
- $(GNOME_PLATFORM_CFLAGS)
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(LIBSOUP_CFLAGS)
libemformat_la_SOURCES = \
$(emformatinclude_HEADERS) \
@@ -28,6 +29,7 @@ libemformat_la_LIBADD = \
$(top_builddir)/e-util/libeutil.la \
$(top_builddir)/shell/libeshell.la \
$(EVOLUTION_DATA_SERVER_LIBS) \
- $(GNOME_PLATFORM_LIBS)
+ $(GNOME_PLATFORM_LIBS) \
+ $(LIBSOUP_LIBS)
-include $(top_srcdir)/git.mk
diff --git a/em-format/em-format-quote.c b/em-format/em-format-quote.c
index c3f75ec14d..4822f115d7 100644
--- a/em-format/em-format-quote.c
+++ b/em-format/em-format-quote.c
@@ -39,237 +39,73 @@
struct _EMFormatQuotePrivate {
gchar *credits;
- CamelStream *stream;
EMFormatQuoteFlags flags;
guint32 text_html_flags;
};
static void emfq_builtin_init (EMFormatQuoteClass *efhc);
-static gpointer parent_class;
-
-static void
-emfq_dispose (GObject *object)
-{
- EMFormatQuotePrivate *priv;
-
- priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
-
- if (priv->stream != NULL) {
- g_object_unref (priv->stream);
- priv->stream = NULL;
- }
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-emfq_finalize (GObject *object)
-{
- EMFormatQuotePrivate *priv;
-
- priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
-
- g_free (priv->credits);
-
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
+static CamelMimePart * decode_inline_parts (CamelMimePart *part, GCancellable *cancellable);
-static void
-emfq_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormat *src,
- GCancellable *cancellable)
-{
- EMFormatQuote *emfq = (EMFormatQuote *) emf;
- const EMFormatHandler *handle;
- GSettings *settings;
+static void emfq_parse_text_plain (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emfq_parse_text_enriched (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emfq_parse_text_html (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emfq_parse_attachment (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
- /* Chain up to parent's format_clone() method. */
- EM_FORMAT_CLASS (parent_class)->format_clone (
- emf, folder, uid, msg, src, cancellable);
+static void emfq_write_text_plain (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emfq_write_text_enriched (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emfq_write_text_html (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
- g_seekable_seek (
- G_SEEKABLE (emfq->priv->stream),
- 0, G_SEEK_SET, NULL, NULL);
-
- settings = g_settings_new ("org.gnome.evolution.mail");
- if (g_settings_get_boolean (
- settings, "composer-top-signature"))
- camel_stream_write_string (
- emfq->priv->stream, "<br>\n", cancellable, NULL);
- g_object_unref (settings);
- handle = em_format_find_handler(emf, "x-evolution/message/prefix");
- if (handle)
- handle->handler (
- emf, emfq->priv->stream,
- CAMEL_MIME_PART (msg),
- handle, cancellable, FALSE);
- handle = em_format_find_handler(emf, "x-evolution/message/rfc822");
- if (handle)
- handle->handler (
- emf, emfq->priv->stream,
- CAMEL_MIME_PART (msg),
- handle, cancellable, FALSE);
-
- camel_stream_flush (emfq->priv->stream, cancellable, NULL);
-
- g_signal_emit_by_name(emf, "complete");
-}
-
-static void
-emfq_format_error (EMFormat *emf,
- CamelStream *stream,
- const gchar *errmsg)
-{
- /* Nothing to do. */
-}
+static gpointer parent_class;
-static void
-emfq_format_source (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- GCancellable *cancellable)
+/* Decodes inline encoded parts of 'part'. The returned pointer,
+ * if not NULL, should be unreffed with g_object_unref(). */
+static CamelMimePart *
+decode_inline_parts (CamelMimePart *part,
+ GCancellable *cancellable)
{
+ CamelMultipart *mp;
+ CamelStream *null;
CamelStream *filtered_stream;
- CamelMimeFilter *html_filter;
+ EMInlineFilter *inline_filter;
- filtered_stream = camel_stream_filter_new (stream);
- html_filter = camel_mime_filter_tohtml_new (
- CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
- CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT, 0);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream), html_filter);
- g_object_unref (html_filter);
+ g_return_val_if_fail (part != NULL, NULL);
- em_format_format_text (
- emf, filtered_stream,
- CAMEL_DATA_WRAPPER (part), cancellable);
+ null = camel_stream_null_new ();
+ filtered_stream = camel_stream_filter_new (null);
+ g_object_unref (null);
+ inline_filter = em_inline_filter_new (
+ camel_mime_part_get_encoding (part),
+ camel_mime_part_get_content_type (part));
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream),
+ CAMEL_MIME_FILTER (inline_filter));
+ camel_data_wrapper_decode_to_stream_sync (
+ camel_medium_get_content (CAMEL_MEDIUM (part)),
+ filtered_stream, cancellable, NULL);
+ camel_stream_close (filtered_stream, cancellable, NULL);
g_object_unref (filtered_stream);
-}
-
-static void
-emfq_format_attachment (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const gchar *mime_type,
- const EMFormatHandler *handle,
- GCancellable *cancellable)
-{
- EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
- gchar *text, *html;
-
- if (!em_format_is_inline (emf, emf->part_id->str, part, handle))
- return;
-
- camel_stream_write_string (
- stream, "<table border=1 cellspacing=0 cellpadding=0>"
- "<tr><td><font size=-1>\n", cancellable, NULL);
-
- /* output some info about it */
- text = em_format_describe_part (part, mime_type);
- html = camel_text_to_html (
- text, emfq->priv->text_html_flags &
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- camel_stream_write_string (stream, html, cancellable, NULL);
- g_free (html);
- g_free (text);
-
- camel_stream_write_string (
- stream, "</font></td></tr></table>", cancellable, NULL);
-
- handle->handler (emf, stream, part, handle, cancellable, FALSE);
-}
-
-static void
-emfq_base_init (EMFormatQuoteClass *class)
-{
- emfq_builtin_init (class);
-}
-
-static void
-emfq_class_init (EMFormatQuoteClass *class)
-{
- GObjectClass *object_class;
- EMFormatClass *format_class;
-
- parent_class = g_type_class_peek_parent (class);
- g_type_class_add_private (class, sizeof (EMFormatQuotePrivate));
-
- object_class = G_OBJECT_CLASS (class);
- object_class->dispose = emfq_dispose;
- object_class->finalize = emfq_finalize;
-
- format_class = EM_FORMAT_CLASS (class);
- format_class->format_clone = emfq_format_clone;
- format_class->format_error = emfq_format_error;
- format_class->format_source = emfq_format_source;
- format_class->format_attachment = emfq_format_attachment;
-}
-
-static void
-emfq_init (EMFormatQuote *emfq)
-{
- emfq->priv = EM_FORMAT_QUOTE_GET_PRIVATE (emfq);
-
- /* we want to convert url's etc */
- emfq->priv->text_html_flags =
- CAMEL_MIME_FILTER_TOHTML_PRE |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
-}
-GType
-em_format_quote_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- static const GTypeInfo type_info = {
- sizeof (EMFormatQuoteClass),
- (GBaseInitFunc) emfq_base_init,
- (GBaseFinalizeFunc) NULL,
- (GClassInitFunc) emfq_class_init,
- (GClassFinalizeFunc) NULL,
- NULL, /* class_data */
- sizeof (EMFormatQuote),
- 0, /* n_preallocs */
- (GInstanceInitFunc) emfq_init,
- NULL /* value_table */
- };
-
- type = g_type_register_static (
- EM_TYPE_FORMAT, "EMFormatQuote", &type_info, 0);
+ if (!em_inline_filter_found_any (inline_filter)) {
+ g_object_unref (inline_filter);
+ return NULL;
}
- return type;
-}
-
-EMFormatQuote *
-em_format_quote_new (const gchar *credits,
- CamelStream *stream,
- EMFormatQuoteFlags flags)
-{
- EMFormatQuote *emfq;
-
- g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
-
- /* Steam must also be seekable so we can reset its position. */
- g_return_val_if_fail (G_IS_SEEKABLE (stream), NULL);
+ mp = em_inline_filter_get_multipart (inline_filter);
- emfq = g_object_new (EM_TYPE_FORMAT_QUOTE, NULL);
+ g_object_unref (inline_filter);
- emfq->priv->credits = g_strdup (credits);
- emfq->priv->stream = g_object_ref (stream);
- emfq->priv->flags = flags;
+ if (mp) {
+ part = camel_mime_part_new ();
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp));
+ g_object_unref (mp);
+ } else {
+ g_object_ref (part);
+ }
- return emfq;
+ return part;
}
static void
@@ -296,18 +132,18 @@ emfq_format_text_header (EMFormatQuote *emfq,
if (flags & EM_FORMAT_HEADER_BOLD)
g_string_append_printf (
- buffer, "<b>%s</b>: %s<br>", label, html);
+ buffer, "<b>%s</b>: %s<br>", label, html);
else
g_string_append_printf (
- buffer, "%s: %s<br>", label, html);
+ buffer, "%s: %s<br>", label, html);
g_free (mhtml);
}
static const gchar *addrspec_hdrs[] = {
- "Sender", "From", "Reply-To", "To", "Cc", "Bcc",
- "Resent-Sender", "Resent-from", "Resent-Reply-To",
- "Resent-To", "Resent-cc", "Resent-Bcc", NULL
+ "Sender", "From", "Reply-To", "To", "Cc", "Bcc",
+ "Resent-Sender", "Resent-from", "Resent-Reply-To",
+ "Resent-To", "Resent-cc", "Resent-Bcc", NULL
};
#if 0
@@ -315,7 +151,7 @@ static const gchar *addrspec_hdrs[] = {
/* For Translators only: The following strings are
* used in the header table in the preview pane. */
static gchar *i18n_hdrs[] = {
- N_("From"), N_("Reply-To"), N_("To"), N_("Cc"), N_("Bcc")
+ N_("From"), N_("Reply-To"), N_("To"), N_("Cc"), N_("Bcc")
};
#endif
@@ -337,18 +173,18 @@ emfq_format_address (GString *out,
if (name && *name) {
gchar *real, *mailaddr;
- g_string_append_printf (out, "%s &lt;", name);
- /* rfc2368 for mailto syntax and url encoding extras */
+ g_string_append_printf (out, "%s &lt;", name);
+ /* rfc2368 for mailto syntax and url encoding extras */
if ((real = camel_header_encode_phrase ((guchar *) a->name))) {
- mailaddr = g_strdup_printf ("%s <%s>", real, a->v.addr);
+ mailaddr = g_strdup_printf ("%s <%s>", real, a->v.addr);
g_free (real);
- mailto = camel_url_encode (mailaddr, "?=&()");
+ mailto = camel_url_encode (mailaddr, "?=&()");
g_free (mailaddr);
} else {
- mailto = camel_url_encode (a->v.addr, "?=&()");
+ mailto = camel_url_encode (a->v.addr, "?=&()");
}
} else {
- mailto = camel_url_encode (a->v.addr, "?=&()");
+ mailto = camel_url_encode (a->v.addr, "?=&()");
}
addr = camel_text_to_html (a->v.addr, flags, 0);
g_string_append_printf (
@@ -358,15 +194,15 @@ emfq_format_address (GString *out,
g_free (addr);
if (name && *name)
- g_string_append (out, "&gt;");
+ g_string_append (out, "&gt;");
break;
case CAMEL_HEADER_ADDRESS_GROUP:
- g_string_append_printf (out, "%s: ", name);
+ g_string_append_printf (out, "%s: ", name);
emfq_format_address (out, a->v.members);
- g_string_append_printf (out, ";");
+ g_string_append_printf (out, ";");
break;
default:
- g_warning ("Invalid address type");
+ g_warning ("Invalid address type");
break;
}
@@ -374,7 +210,7 @@ emfq_format_address (GString *out,
a = a->next;
if (a)
- g_string_append (out, ", ");
+ g_string_append (out, ", ");
}
}
@@ -383,20 +219,20 @@ canon_header_name (gchar *name)
{
gchar *inptr = name;
- /* canonicalise the header name... first letter is
- * capitalised and any letter following a '-' also gets
- * capitalised */
+ /* canonicalise the header name... first letter is
+ * capitalised and any letter following a '-' also gets
+ * capitalised */
if (g_ascii_islower (*inptr))
- *inptr = g_ascii_toupper (*inptr);
+ *inptr = g_ascii_toupper (*inptr);
inptr++;
while (*inptr) {
if (inptr[-1] == '-' && g_ascii_islower (*inptr))
- *inptr = g_ascii_toupper (*inptr);
+ *inptr = g_ascii_toupper (*inptr);
else if (g_ascii_isupper (*inptr))
- *inptr = g_ascii_tolower (*inptr);
+ *inptr = g_ascii_tolower (*inptr);
inptr++;
}
@@ -422,8 +258,8 @@ emfq_format_header (EMFormat *emf,
strcpy (name, namein);
canon_header_name (name);
- /* Never quote Bcc headers */
- if (g_str_equal (name, "Bcc") || g_str_equal (name, "Resent-Bcc"))
+ /* Never quote Bcc headers */
+ if (g_str_equal (name, "Bcc") || g_str_equal (name, "Resent-Bcc"))
return;
for (i = 0; addrspec_hdrs[i]; i++) {
@@ -444,8 +280,8 @@ emfq_format_header (EMFormat *emf,
buf = camel_header_unfold (txt);
addrs = camel_header_address_decode (
- txt, emf->charset ?
- emf->charset : emf->default_charset);
+ txt, em_format_get_charset (emf) ?
+ em_format_get_charset (emf) : em_format_get_default_charset (emf));
if (addrs == NULL) {
g_free (buf);
return;
@@ -453,29 +289,29 @@ emfq_format_header (EMFormat *emf,
g_free (buf);
- html = g_string_new ("");
+ html = g_string_new ("");
emfq_format_address (html, addrs);
camel_header_address_unref (addrs);
txt = value = html->str;
g_string_free (html, FALSE);
flags |= EM_FORMAT_HEADER_BOLD;
is_html = TRUE;
- } else if (!strcmp (name, "Subject")) {
+ } else if (!strcmp (name, "Subject")) {
txt = camel_mime_message_get_subject (msg);
- label = _("Subject");
+ label = _("Subject");
flags |= EM_FORMAT_HEADER_BOLD;
- } else if (!strcmp (name, "X-Evolution-Mailer")) { /* pseudo-header */
- if (!(txt = camel_medium_get_header (part, "x-mailer")))
- if (!(txt = camel_medium_get_header (part, "user-agent")))
- if (!(txt = camel_medium_get_header (part, "x-newsreader")))
- if (!(txt = camel_medium_get_header (part, "x-mimeole")))
+ } else if (!strcmp (name, "X-Evolution-Mailer")) { /* pseudo-header */
+ if (!(txt = camel_medium_get_header (part, "x-mailer")))
+ if (!(txt = camel_medium_get_header (part, "user-agent")))
+ if (!(txt = camel_medium_get_header (part, "x-newsreader")))
+ if (!(txt = camel_medium_get_header (part, "x-mimeole")))
return;
txt = value = camel_header_format_ctext (txt, charset);
- label = _("Mailer");
+ label = _("Mailer");
flags |= EM_FORMAT_HEADER_BOLD;
- } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) {
+ } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) {
if (!(txt = camel_medium_get_header (part, name)))
return;
@@ -506,10 +342,10 @@ emfq_format_headers (EMFormatQuote *emfq,
return;
ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
- charset = camel_content_type_param (ct, "charset");
+ charset = camel_content_type_param (ct, "charset");
charset = camel_iconv_charset_name (charset);
- /* dump selected headers */
+ /* dump selected headers */
link = g_queue_peek_head_link (&emf->header_list);
while (link != NULL) {
EMFormatHeader *h = link->data;
@@ -518,154 +354,335 @@ emfq_format_headers (EMFormatQuote *emfq,
link = g_list_next (link);
}
- g_string_append (buffer, "<br>\n");
+ g_string_append (buffer, "<br>\n");
}
static void
-emfq_format_message_prefix (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+emfq_dispose (GObject *object)
{
- EMFormatQuote *emfq = (EMFormatQuote *) emf;
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
- if (emfq->priv->credits != NULL) {
- camel_stream_write_string (
- stream, emfq->priv->credits, NULL, NULL);
- camel_stream_write_string (
- stream, "<br>\n", NULL, NULL);
- }
+static void
+emfq_finalize (GObject *object)
+{
+ EMFormatQuotePrivate *priv;
+
+ priv = EM_FORMAT_QUOTE_GET_PRIVATE (object);
+
+ g_free (priv->credits);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
+/******************************************************************************/
static void
-emfq_format_message (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+emfq_parse_text_plain (EMFormat * emf,
+ CamelMimePart * part,
+ GString * part_id,
+ EMFormatParserInfo * info,
+ GCancellable * cancellable)
{
- EMFormatQuote *emfq = (EMFormatQuote *) emf;
- GString *buffer;
+ EMFormatPURI *puri;
+ CamelMimePart *mp;
+ gint len;
- buffer = g_string_sized_new (1024);
+ len = part_id->len;
+ g_string_append (part_id, ".text_plain");
- if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
- g_string_append (
- buffer,
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
- "key=\"orig\" value=\"1\">-->\n"
- "<blockquote type=cite>\n");
+ mp = decode_inline_parts (part, cancellable);
+ if (mp) {
- if (((CamelMimePart *) emf->message) != part) {
- g_string_append_printf (
- buffer,
- "%s</br>\n",
- _("-------- Forwarded Message --------"));
- emfq_format_headers (emfq, buffer, (CamelMedium *) part);
- } else if (emfq->priv->flags & EM_FORMAT_QUOTE_HEADERS)
- emfq_format_headers (emfq, buffer, (CamelMedium *) part);
+ if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) {
+ em_format_parse_part (emf, mp, part_id, info, cancellable);
+ }
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
+ g_object_unref (mp);
+ }
- em_format_part (emf, stream, part, cancellable);
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = emfq_write_text_plain;
+ puri->mime_type = g_strdup ("text/html");
+ em_format_add_puri (emf, puri);
- if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
- camel_stream_write_string (
- stream, "</blockquote><!--+GtkHTML:"
- "<DATA class=\"ClueFlow\" clear=\"orig\">-->",
- cancellable, NULL);
+ g_string_truncate (part_id, len);
}
-/* Decodes inline encoded parts of 'part'. The returned pointer,
- * if not NULL, should be unreffed with g_object_unref(). */
-static CamelMimePart *
-decode_inline_parts (CamelMimePart *part,
- GCancellable *cancellable)
+static void
+emfq_parse_text_html (EMFormat * emf,
+ CamelMimePart * part,
+ GString * part_id,
+ EMFormatParserInfo * info,
+ GCancellable * cancellable)
{
- CamelMultipart *mp;
- CamelStream *null;
- CamelStream *filtered_stream;
- EMInlineFilter *inline_filter;
+ EMFormatPURI *puri;
+ gint len;
- g_return_val_if_fail (part != NULL, NULL);
+ len = part_id->len;
+ g_string_append (part_id, ".text_html");
- null = camel_stream_null_new ();
- filtered_stream = camel_stream_filter_new (null);
- g_object_unref (null);
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = emfq_write_text_html;
+ puri->mime_type = g_strdup ("text/html");
+ em_format_add_puri (emf, puri);
- inline_filter = em_inline_filter_new (
- camel_mime_part_get_encoding (part),
- camel_mime_part_get_content_type (part));
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream),
- CAMEL_MIME_FILTER (inline_filter));
- camel_data_wrapper_decode_to_stream_sync (
- camel_medium_get_content (CAMEL_MEDIUM (part)),
- filtered_stream, cancellable, NULL);
- camel_stream_close (filtered_stream, cancellable, NULL);
- g_object_unref (filtered_stream);
+ g_string_truncate (part_id, len);
+}
- if (!em_inline_filter_found_any (inline_filter)) {
- g_object_unref (inline_filter);
- return NULL;
+static void
+emfq_parse_text_enriched (EMFormat * emf,
+ CamelMimePart * part,
+ GString * part_id,
+ EMFormatParserInfo * info,
+ GCancellable * cancellable)
+{
+ EMFormatPURI *puri;
+ gint len;
+
+ len = part_id->len;
+ g_string_append (part_id, ".text_enriched");
+
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = emfq_write_text_enriched;
+ puri->mime_type = g_strdup ("text/html");
+ em_format_add_puri (emf, puri);
+
+ g_string_truncate (part_id, len);
+}
+
+static void
+emfq_parse_attachment (EMFormat * emf,
+ CamelMimePart * part,
+ GString * part_id,
+ EMFormatParserInfo * info,
+ GCancellable * cancellable)
+{
+ EMFormatPURI *puri;
+ gint len;
+
+ len = part_id->len;
+ g_string_append (part_id, ".attachment");
+
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = emfq_write_text_html;
+ puri->mime_type = g_strdup ("text/html");
+ puri->is_attachment = TRUE;
+ em_format_add_puri (emf, puri);
+
+ g_string_truncate (part_id, len);
+}
+
+/******************************************************************************/
+
+static void
+emfq_write_attachment (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
+ const EMFormatHandler *handler;
+ gchar *text, *html;
+ CamelContentType *ct;
+ const gchar *mime_type;
+
+ ct = camel_mime_part_get_content_type (puri->part);
+ if (ct) {
+ mime_type = camel_content_type_simple (ct);
+ camel_content_type_unref (ct);
+ } else {
+ mime_type = "application/octet-stream";
}
- mp = em_inline_filter_get_multipart (inline_filter);
+ handler = em_format_find_handler (emf, mime_type);
- g_object_unref (inline_filter);
+ if (!em_format_is_inline (emf, puri->uri, puri->part, handler))
+ return;
- if (mp) {
- part = camel_mime_part_new ();
- camel_medium_set_content (
- CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp));
- g_object_unref (mp);
+ camel_stream_write_string (
+ stream, "<table border=1 cellspacing=0 cellpadding=0>"
+ "<tr><td><font size=-1>\n", cancellable, NULL);
+
+ /* output some info about it */
+ text = em_format_describe_part (puri->part, mime_type);
+ html = camel_text_to_html (
+ text, emfq->priv->text_html_flags &
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+ camel_stream_write_string (stream, html, cancellable, NULL);
+ g_free (html);
+ g_free (text);
+
+ camel_stream_write_string (
+ stream, "</font></td></tr></table>", cancellable, NULL);
+
+ if (handler && handler->write_func)
+ handler->write_func (emf, puri, stream, info, cancellable);
+}
+
+static void
+emfq_base_init (EMFormatQuoteClass *klass)
+{
+ emfq_builtin_init (klass);
+}
+
+static void
+emfq_class_init (EMFormatQuoteClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (EMFormatQuotePrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = emfq_dispose;
+ object_class->finalize = emfq_finalize;
+}
+
+static void
+emfq_init (EMFormatQuote *emfq)
+{
+ emfq->priv = EM_FORMAT_QUOTE_GET_PRIVATE (emfq);
+
+ /* we want to convert url's etc */
+ emfq->priv->text_html_flags =
+ CAMEL_MIME_FILTER_TOHTML_PRE |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+}
+
+GType
+em_format_quote_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EMFormatQuoteClass),
+ (GBaseInitFunc) emfq_base_init,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) emfq_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMFormatQuote),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) emfq_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ EM_TYPE_FORMAT, "EMFormatQuote", &type_info, 0);
+ }
+
+ return type;
+}
+
+EMFormatQuote *
+em_format_quote_new (const gchar *credits,
+ CamelStream *stream,
+ EMFormatQuoteFlags flags)
+{
+ EMFormatQuote *emfq;
+
+ g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
+
+ /* Steam must also be seekable so we can reset its position. */
+ g_return_val_if_fail (G_IS_SEEKABLE (stream), NULL);
+
+ emfq = g_object_new (EM_TYPE_FORMAT_QUOTE, NULL);
+
+ emfq->priv->credits = g_strdup (credits);
+ emfq->priv->flags = flags;
+
+ return emfq;
+}
+
+void
+em_format_quote_write (EMFormatQuote * emfq,
+ CamelStream * stream,
+ GCancellable * cancellable)
+{
+ EMFormat *emf;
+ GSettings *settings;
+ GList *iter;
+ EMFormatWriterInfo info = { 0 };
+
+ emf = (EMFormat *) emfq;
+
+ g_seekable_seek (
+ G_SEEKABLE (stream),
+ 0, G_SEEK_SET, NULL, NULL);
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ if (g_settings_get_boolean (
+ settings, "composer-top-signature"))
+ camel_stream_write_string (
+ stream, "<br>\n", cancellable, NULL);
+ g_object_unref (settings);
+
+ if (emfq->priv->credits && *emfq->priv->credits) {
+ gchar *credits = g_strdup_printf ("%s<br/>", emfq->priv->credits);
+ camel_stream_write_string (stream, credits, cancellable, NULL);
+ g_free (credits);
} else {
- g_object_ref (part);
+ camel_stream_write_string (stream, "<br/>", cancellable, NULL);
}
- return part;
+ if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
+ camel_stream_write_string (stream,
+ "<!--+GtkHTML:<DATA class=\"ClueFlow\" "
+ "key=\"orig\" value=\"1\">-->\n"
+ "<blockquote type=cite>\n", cancellable, NULL);
+
+ for (iter = emf->mail_part_list; iter; iter = iter->next) {
+ EMFormatPURI *puri = iter->data;
+
+ if (puri->is_attachment || !puri->write_func)
+ continue;
+
+ puri = iter->data;
+
+ if (emfq->priv->flags & EM_FORMAT_QUOTE_HEADERS) {
+ GString *buffer = g_string_new ("");
+ emfq_format_headers (emfq, buffer, (CamelMedium *) puri->part);
+ camel_stream_write_string (stream, buffer->str, cancellable, NULL);
+ g_string_free (buffer, TRUE);
+ }
+
+ puri->write_func (emf, puri, stream, &info, cancellable);
+ }
+
+ if (emfq->priv->flags & EM_FORMAT_QUOTE_CITE)
+ camel_stream_write_string (
+ stream, "</blockquote><!--+GtkHTML:"
+ "<DATA class=\"ClueFlow\" clear=\"orig\">-->",
+ cancellable, NULL);
}
static void
-emfq_text_plain (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+emfq_write_text_plain (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
EMFormatQuote *emfq = EM_FORMAT_QUOTE (emf);
CamelStream *filtered_stream;
CamelMimeFilter *html_filter;
CamelMimeFilter *sig_strip;
- CamelMimePart *mp;
CamelContentType *type;
const gchar *format;
guint32 rgb = 0x737373, flags;
- if (!part)
+ if (!puri->part)
return;
- mp = decode_inline_parts (part, cancellable);
- if (mp) {
- if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) {
- em_format_part (emf, stream, mp, cancellable);
- g_object_unref (mp);
-
- return;
- }
-
- g_object_unref (mp);
- }
-
flags = emfq->priv->text_html_flags;
/* Check for RFC 2646 flowed text. */
- type = camel_mime_part_get_content_type (part);
+ type = camel_mime_part_get_content_type (puri->part);
if (camel_content_type_is(type, "text", "plain")
&& (format = camel_content_type_param(type, "format"))
&& !g_ascii_strcasecmp(format, "flowed"))
@@ -687,25 +704,32 @@ emfq_text_plain (EMFormat *emf,
em_format_format_text (
EM_FORMAT (emfq), filtered_stream,
- CAMEL_DATA_WRAPPER (part), cancellable);
+ CAMEL_DATA_WRAPPER (puri->part), cancellable);
camel_stream_flush (filtered_stream, cancellable, NULL);
g_object_unref (filtered_stream);
}
static void
-emfq_text_enriched (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+emfq_write_text_enriched (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
CamelStream *filtered_stream;
CamelMimeFilter *enriched;
guint32 flags = 0;
+ CamelContentType *ct;
+ const gchar *mime_type = NULL;
+
+ ct = camel_mime_part_get_content_type (puri->part);
+ if (ct) {
+ mime_type = camel_content_type_simple (ct);
+ camel_content_type_unref (ct);
+ }
- if (g_strcmp0 (info->mime_type, "text/richtext") == 0) {
+ if (g_strcmp0 (mime_type, "text/richtext") == 0) {
flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
camel_stream_write_string (
stream, "\n<!-- text/richtext -->\n",
@@ -724,18 +748,17 @@ emfq_text_enriched (EMFormat *emf,
camel_stream_write_string (stream, "<br><hr><br>", cancellable, NULL);
em_format_format_text (
- emf, filtered_stream, CAMEL_DATA_WRAPPER (part), cancellable);
+ emf, filtered_stream, CAMEL_DATA_WRAPPER (puri->part), cancellable);
camel_stream_flush (filtered_stream, cancellable, NULL);
g_object_unref (filtered_stream);
}
static void
-emfq_text_html (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+emfq_write_text_html (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
EMFormatQuotePrivate *priv;
@@ -757,40 +780,29 @@ emfq_text_html (EMFormat *emf,
em_format_format_text (
emf, filtered_stream,
- (CamelDataWrapper *) part, cancellable);
+ (CamelDataWrapper *) puri->part, cancellable);
camel_stream_flush (filtered_stream, cancellable, NULL);
g_object_unref (filtered_stream);
} else {
em_format_format_text (
emf, stream,
- (CamelDataWrapper *) part, cancellable);
+ (CamelDataWrapper *) puri->part, cancellable);
}
}
-static void
-emfq_ignore (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- /* NOOP */
-}
-
+/****************************************************************************/
static EMFormatHandler type_builtin_table[] = {
- { (gchar *) "text/plain", emfq_text_plain },
- { (gchar *) "text/enriched", emfq_text_enriched },
- { (gchar *) "text/richtext", emfq_text_enriched },
- { (gchar *) "text/html", emfq_text_html },
- { (gchar *) "text/*", emfq_text_plain },
- { (gchar *) "message/external-body", emfq_ignore },
- { (gchar *) "multipart/appledouble", emfq_ignore },
+ { (gchar *) "text/plain", emfq_parse_text_plain, emfq_write_text_plain, },
+ { (gchar *) "text/enriched", emfq_parse_text_enriched, emfq_write_text_enriched, },
+ { (gchar *) "text/richtext", emfq_parse_text_enriched, emfq_write_text_enriched, },
+ { (gchar *) "text/html", emfq_parse_text_html, emfq_write_text_html, },
+ { (gchar *) "text/*", emfq_parse_text_plain, emfq_write_text_plain, },
+ { (gchar *) "message/external-body", em_format_empty_parser, em_format_empty_writer, },
+ { (gchar *) "multipart/appledouble", em_format_empty_parser, em_format_empty_writer, },
/* internal evolution types */
- { (gchar *) "x-evolution/evolution-rss-feed", emfq_text_html },
- { (gchar *) "x-evolution/message/rfc822", emfq_format_message },
- { (gchar *) "x-evolution/message/prefix", emfq_format_message_prefix },
+ { (gchar *) "x-evolution/evolution-rss-feed", 0, emfq_write_text_html, },
+ { (gchar *) "x-evolution/message/attachment", emfq_parse_attachment, emfq_write_attachment, },
};
static void
@@ -798,7 +810,9 @@ emfq_builtin_init (EMFormatQuoteClass *efhc)
{
gint ii;
+ EMFormatClass *emfc = (EMFormatClass *) efhc;
+
for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
em_format_class_add_handler (
- EM_FORMAT_CLASS (efhc), &type_builtin_table[ii]);
+ emfc, &type_builtin_table[ii]);
}
diff --git a/em-format/em-format-quote.h b/em-format/em-format-quote.h
index 5c1882eb32..be3640735a 100644
--- a/em-format/em-format-quote.h
+++ b/em-format/em-format-quote.h
@@ -69,6 +69,9 @@ GType em_format_quote_get_type (void);
EMFormatQuote * em_format_quote_new (const gchar *credits,
CamelStream *stream,
EMFormatQuoteFlags flags);
+void em_format_quote_write (EMFormatQuote *emfq,
+ CamelStream *stream,
+ GCancellable *cancellable);
G_END_DECLS
diff --git a/em-format/em-format.c b/em-format/em-format.c
index d476036f77..4abe35482c 100644
--- a/em-format/em-format.c
+++ b/em-format/em-format.c
@@ -25,1018 +25,1577 @@
#include <config.h>
#endif
-#include <stdio.h>
#include <string.h>
-
#include <gio/gio.h>
#include <glib/gi18n-lib.h>
+#include <libsoup/soup-uri.h>
#include "em-format.h"
#include "e-util/e-util.h"
#include "shell/e-shell.h"
#include "shell/e-shell-settings.h"
+#define d(x)
+
#define EM_FORMAT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), EM_TYPE_FORMAT, EMFormatPrivate))
-#define d(x)
-
-typedef struct _EMFormatCache EMFormatCache;
-
struct _EMFormatPrivate {
- guint redraw_idle_id;
-};
+ GNode *current_node;
-/* Used to cache various data/info for redraws
- * The validity stuff could be cached at a higher level but this is easier
- * This absolutely relies on the partid being _globally unique_
- * This is still kind of yucky, we should maintain a full tree of all this data,
- * along with/as part of the puri tree */
-struct _EMFormatCache {
- CamelCipherValidity *valid; /* validity copy */
- CamelMimePart *secured; /* encrypted subpart */
+ CamelSession *session;
- guint state:2; /* inline state */
+ CamelURL *base_url;
- gchar partid[1];
-};
+ gchar *charset;
+ gchar *default_charset;
+ gboolean composer;
-#define INLINE_UNSET (0)
-#define INLINE_ON (1)
-#define INLINE_OFF (2)
+ gint last_error;
+};
-static void emf_builtin_init (EMFormatClass *);
+enum {
+ PROP_0,
+ PROP_CHARSET,
+ PROP_DEFAULT_CHARSET,
+ PROP_COMPOSER,
+ PROP_BASE_URL
+};
enum {
- EMF_COMPLETE,
- EMF_LAST_SIGNAL
+ REDRAW_REQUESTED,
+ LAST_SIGNAL
};
+gint signals[LAST_SIGNAL];
+
static gpointer parent_class;
-static guint signals[EMF_LAST_SIGNAL];
-static void
-emf_free_cache (EMFormatCache *efc)
-{
- if (efc->valid)
- camel_cipher_validity_free (efc->valid);
- if (efc->secured)
- g_object_unref (efc->secured);
- g_free (efc);
-}
+/* PARSERS */
+static void emf_parse_application_xpkcs7mime (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_application_mbox (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_alternative (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_appledouble (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_encrypted (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_mixed (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_signed (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_related (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_multipart_digest (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_message_deliverystatus (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_inlinepgp_signed (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_inlinepgp_encrypted (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_message (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_headers (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_post_headers (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void emf_parse_source (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+
+/* WRITERS */
+static void emf_write_text (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emf_write_source (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void emf_write_error (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
+/**************************************************************************/
-static EMFormatCache *
-emf_insert_cache (EMFormat *emf,
- const gchar *partid)
+static gboolean
+is_secured (CamelMimePart *part)
{
- EMFormatCache *new;
-
- new = g_malloc0 (sizeof (*new) + strlen (partid));
- strcpy (new->partid, partid);
- g_hash_table_insert (emf->inline_table, new->partid, new);
+ CamelContentType *ct = camel_mime_part_get_content_type (part);
- return new;
+ return (camel_content_type_is (ct, "multipart", "signed") ||
+ camel_content_type_is (ct, "multipart", "encrypted") ||
+ camel_content_type_is (ct, "application", "x-inlinepgp-signed") ||
+ camel_content_type_is (ct, "application", "x-inlinepgp-encrypted") ||
+ camel_content_type_is (ct, "application", "x-pkcs7-mime") ||
+ camel_content_type_is (ct, "application", "pkcs7-mime"));
}
static void
-emf_clone_inlines (gpointer key,
- gpointer val,
- gpointer data)
+preserve_charset_in_content_type (CamelMimePart *ipart,
+ CamelMimePart *opart)
{
- EMFormatCache *emfc = val, *new;
+ CamelDataWrapper *data_wrapper;
+ CamelContentType *content_type;
+ const gchar *charset;
- new = emf_insert_cache ((EMFormat *) data, emfc->partid);
- new->state = emfc->state;
- if (emfc->valid)
- new->valid = camel_cipher_validity_clone (emfc->valid);
- if (emfc->secured)
- g_object_ref ((new->secured = emfc->secured));
-}
+ g_return_if_fail (ipart != NULL);
+ g_return_if_fail (opart != NULL);
-static gboolean
-emf_clear_puri_node (GNode *node)
-{
- GQueue *queue = node->data;
- EMFormatPURI *pn;
+ data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
+ content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
- while ((pn = g_queue_pop_head (queue)) != NULL) {
- if (pn->free != NULL)
- pn->free (pn);
- g_free (pn->uri);
- g_free (pn->cid);
- g_free (pn->part_id);
- if (pn->part != NULL)
- g_object_unref (pn->part);
- g_free (pn);
- }
+ if (content_type == NULL)
+ return;
+
+ charset = camel_content_type_param (content_type, "charset");
- g_queue_free (queue);
+ if (charset == NULL || *charset == '\0')
+ return;
- return FALSE;
+ data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
+ content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
+
+ camel_content_type_set_param (content_type, "charset", charset);
}
-static void
-emf_finalize (GObject *object)
+static CamelMimePart *
+get_related_display_part (CamelMimePart *part,
+ gint *out_displayid)
{
- EMFormat *emf = EM_FORMAT (object);
-
- if (emf->priv->redraw_idle_id > 0)
- g_source_remove (emf->priv->redraw_idle_id);
+ CamelMultipart *mp;
+ CamelMimePart *body_part, *display_part = NULL;
+ CamelContentType *content_type;
+ const gchar *start;
+ gint i, nparts, displayid = 0;
- if (emf->session)
- g_object_unref (emf->session);
+ mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
- if (emf->message)
- g_object_unref (emf->message);
+ if (!CAMEL_IS_MULTIPART (mp))
+ return NULL;
- g_hash_table_destroy (emf->inline_table);
+ 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;
- em_format_clear_headers (emf);
- camel_cipher_validity_free (emf->valid);
- g_free (emf->charset);
- g_free (emf->default_charset);
- g_string_free (emf->part_id, TRUE);
- g_free (emf->current_message_part_id);
- g_free (emf->uid);
+ /* strip <>'s from CID */
+ len = strlen (start) - 2;
+ start++;
- if (emf->pending_uri_table != NULL)
- g_hash_table_destroy (emf->pending_uri_table);
+ for (i = 0; i < nparts; i++) {
+ body_part = camel_multipart_get_part (mp, i);
+ cid = camel_mime_part_get_content_id (body_part);
- if (emf->pending_uri_tree != NULL) {
- g_node_traverse (
- emf->pending_uri_tree,
- G_IN_ORDER, G_TRAVERSE_ALL, -1,
- (GNodeTraverseFunc) emf_clear_puri_node, NULL);
- g_node_destroy (emf->pending_uri_tree);
+ if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
+ display_part = body_part;
+ displayid = i;
+ break;
+ }
+ }
+ } else {
+ display_part = camel_multipart_get_part (mp, 0);
}
- /* FIXME: check pending jobs */
+ if (out_displayid)
+ *out_displayid = displayid;
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ return display_part;
}
-static const EMFormatHandler *
-emf_find_handler (EMFormat *emf,
- const gchar *mime_type)
+static gboolean
+related_display_part_is_attachment (EMFormat *emf,
+ CamelMimePart *part)
{
- EMFormatClass *emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf);
+ CamelMimePart *display_part;
- return g_hash_table_lookup (emfc->type_handlers, mime_type);
+ display_part = get_related_display_part (part, NULL);
+ return display_part && em_format_is_attachment (emf, display_part);
+}
+
+/**************************************************************************/
+void
+em_format_empty_parser (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ /* DO NOTHING */
}
+#ifdef ENABLE_SMIME
static void
-emf_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormat *emfsource,
- GCancellable *cancellable)
+emf_parse_application_xpkcs7mime (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- /* Cancel any pending redraws. */
- if (emf->priv->redraw_idle_id > 0) {
- g_source_remove (emf->priv->redraw_idle_id);
- emf->priv->redraw_idle_id = 0;
+ CamelCipherContext *context;
+ CamelMimePart *opart;
+ CamelCipherValidity *valid;
+ GError *local_error = NULL;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ context = camel_smime_context_new (emf->priv->session);
+
+ opart = camel_mime_part_new ();
+ valid = camel_cipher_context_decrypt_sync (
+ context, part, opart, cancellable, &local_error);
+ preserve_charset_in_content_type (part, opart);
+ if (valid == NULL) {
+ em_format_format_error (
+ emf, "%s",
+ local_error->message ? local_error->message :
+ _("Could not parse S/MIME message: Unknown error"));
+ g_clear_error (&local_error);
+ } else {
+ EMFormatParserInfo encinfo = {
+ info->handler,
+ info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_SMIME,
+ valid
+ };
+ gint len = part_id->len;
+
+ g_string_append (part_id, ".encrypted");
+ em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+ g_string_truncate (part_id, len);
+
+ /* Add a widget with details about the encryption, but only when
+ * the encrypted isn't itself secured, in that case it has created
+ * the button itself */
+ if (!is_secured (opart)) {
+ g_string_append (part_id, ".encrypted.button");
+ em_format_parse_part_as (emf, part, part_id, &encinfo,
+ "x-evolution/message/x-secure-button", cancellable);
+ g_string_truncate (part_id, len);
+ }
+
+ camel_cipher_validity_free (valid);
}
- em_format_clear_puri_tree (emf);
+ g_object_unref (opart);
+ g_object_unref (context);
+}
+#endif
+
+/* RFC 4155 */
+static void
+emf_parse_application_mbox (EMFormat *emf,
+ CamelMimePart *mime_part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ CamelMimeParser *parser;
+ CamelStream *mem_stream;
+ camel_mime_parser_state_t state;
+ gint old_len;
+ gint messages;
- if (emf != emfsource) {
- g_hash_table_remove_all (emf->inline_table);
- if (emfsource) {
- GList *link;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- /* We clone the current state here */
- g_hash_table_foreach (emfsource->inline_table, emf_clone_inlines, emf);
- emf->mode = emfsource->mode;
- g_free (emf->charset);
- emf->charset = g_strdup (emfsource->charset);
- g_free (emf->default_charset);
- emf->default_charset = g_strdup (emfsource->default_charset);
+ /* Extract messages from the application/mbox part and
+ * render them as a flat list of messages. */
- em_format_clear_headers (emf);
+ /* XXX If the mbox has multiple messages, maybe render them
+ * as a multipart/digest so each message can be expanded
+ * or collapsed individually.
+ *
+ * See attachment_handler_mail_x_uid_list() for example. */
- link = g_queue_peek_head_link (&emfsource->header_list);
- while (link != NULL) {
- struct _EMFormatHeader *h = link->data;
- em_format_add_header (emf, h->name, h->flags);
- link = g_list_next (link);
- }
+ /* XXX This is based on em_utils_read_messages_from_stream().
+ * Perhaps refactor that function to return an array of
+ * messages instead of assuming we want to append them
+ * to a folder? */
+
+ parser = camel_mime_parser_new ();
+ camel_mime_parser_scan_from (parser, TRUE);
+
+ mem_stream = camel_stream_mem_new ();
+ camel_data_wrapper_decode_to_stream_sync (
+ camel_medium_get_content (CAMEL_MEDIUM (mime_part)),
+ mem_stream, NULL, NULL);
+ g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
+ camel_mime_parser_init_with_stream (parser, mem_stream, NULL);
+ g_object_unref (mem_stream);
+
+ old_len = part_id->len;
+
+ /* Extract messages from the mbox. */
+ messages = 0;
+ state = camel_mime_parser_step (parser, NULL, NULL);
+
+ while (state == CAMEL_MIME_PARSER_STATE_FROM) {
+ CamelMimeMessage *message;
+
+ message = camel_mime_message_new ();
+ mime_part = CAMEL_MIME_PART (message);
+
+ if (!camel_mime_part_construct_from_parser_sync (
+ mime_part, parser, NULL, NULL)) {
+ g_object_unref (message);
+ break;
}
- }
- /* what a mess */
- if (folder != emf->folder) {
- if (emf->folder)
- g_object_unref (emf->folder);
- if (folder)
- g_object_ref (folder);
- emf->folder = folder;
- }
+ g_string_append_printf (part_id, ".mbox.%d", messages);
+ em_format_parse_part_as (emf, CAMEL_MIME_PART (message),
+ part_id, info, "message/rfc822", cancellable);
+ g_string_truncate (part_id, old_len);
- if (uid != emf->uid) {
- g_free (emf->uid);
- emf->uid = g_strdup (uid);
- }
+ g_object_unref (message);
- if (msg != emf->message) {
- if (emf->message)
- g_object_unref (emf->message);
- if (msg)
- g_object_ref (msg);
- emf->message = msg;
+ /* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
+ camel_mime_parser_step (parser, NULL, NULL);
+
+ state = camel_mime_parser_step (parser, NULL, NULL);
+
+ messages++;
}
- g_free (emf->current_message_part_id);
- emf->current_message_part_id = g_strdup ("root-message");
- g_string_truncate (emf->part_id, 0);
- if (folder != NULL)
- /* TODO build some string based on the folder name/location? */
- g_string_append_printf(emf->part_id, ".%p", (gpointer) folder);
- if (uid != NULL)
- g_string_append_printf(emf->part_id, ".%s", uid);
+ g_object_unref (parser);
}
+/* RFC 1740 */
static void
-emf_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- CamelCipherValidity *valid,
- GCancellable *cancellable)
+emf_parse_multipart_alternative (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- CamelCipherValidity *save = emf->valid_parent;
- gint len;
+ CamelMultipart *mp;
+ gint i, nparts, bestid = 0;
+ CamelMimePart *best = NULL;
- /* Note that this also requires support from higher up in the class chain
- * - validity needs to be cleared when you start output
- * - also needs to be cleared (but saved) whenever you start a new message. */
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- if (emf->valid == NULL) {
- emf->valid = valid;
- } else {
- g_queue_push_tail (&emf->valid_parent->children, valid);
- camel_cipher_validity_envelope (emf->valid_parent, valid);
+ mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+
+ if (!CAMEL_IS_MULTIPART (mp)) {
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
}
- emf->valid_parent = valid;
+ /* as per rfc, find the last part we know how to display */
+ nparts = camel_multipart_get_number (mp);
+ for (i = 0; i < nparts; i++) {
+ CamelMimePart *mpart;
+ CamelDataWrapper *data_wrapper;
+ CamelContentType *type;
+ CamelStream *null_stream;
+ gchar *mime_type;
+ gsize content_size;
- len = emf->part_id->len;
- g_string_append_printf(emf->part_id, ".secured");
- em_format_part (emf, stream, part, cancellable);
- g_string_truncate (emf->part_id, len);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- emf->valid_parent = save;
-}
+ /* is it correct to use the passed in *part here? */
+ mpart = camel_multipart_get_part (mp, i);
-static gboolean
-emf_busy (EMFormat *emf)
-{
- return FALSE;
-}
+ if (mpart == NULL)
+ continue;
-static gboolean
-emf_is_inline (EMFormat *emf,
- const gchar *part_id,
- CamelMimePart *mime_part,
- const EMFormatHandler *handle)
-{
- EMFormatCache *emfc;
- const gchar *disposition;
+ /* This may block even though the stream does not.
+ * XXX Pretty inefficient way to test if the MIME part
+ * is empty. Surely there's a quicker way? */
+ null_stream = camel_stream_null_new ();
+ data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (mpart));
+ camel_data_wrapper_decode_to_stream_sync (
+ data_wrapper, null_stream, cancellable, NULL);
+ content_size = CAMEL_STREAM_NULL (null_stream)->written;
+ g_object_unref (null_stream);
- if (handle == NULL)
- return FALSE;
+ if (content_size == 0)
+ continue;
- emfc = g_hash_table_lookup (emf->inline_table, part_id);
- if (emfc && emfc->state != INLINE_UNSET)
- return emfc->state & 1;
+ type = camel_mime_part_get_content_type (mpart);
+ mime_type = camel_content_type_simple (type);
- /* Some types need to override the disposition.
- * e.g. application/x-pkcs7-mime */
- if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION)
- return TRUE;
+ camel_strdown (mime_type);
- disposition = camel_mime_part_get_disposition (mime_part);
- if (disposition != NULL)
- return g_ascii_strcasecmp (disposition, "inline") == 0;
+ if (!em_format_is_attachment (emf, mpart) &&
+ ((camel_content_type_is (type, "multipart", "related") == 0) ||
+ !related_display_part_is_attachment (emf, mpart)) &&
+ (em_format_find_handler (emf, mime_type)
+ || (best == NULL && em_format_fallback_handler (emf, mime_type)))) {
+ best = mpart;
+ bestid = i;
+ }
- /* Otherwise, use the default for this handler type. */
- return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0;
+ g_free (mime_type);
+ }
+
+ if (best) {
+ gint len = part_id->len;
+
+ g_string_append_printf(part_id, ".alternative.%d", bestid);
+ em_format_parse_part (emf, best, part_id, info, cancellable);
+ g_string_truncate (part_id, len);
+ } else
+ emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
}
+/* RFC 1740 */
static void
-emf_base_init (EMFormatClass *class)
+emf_parse_multipart_appledouble (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- class->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
- emf_builtin_init (class);
+ CamelMultipart *mp;
+ CamelMimePart *mime_part;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+
+ if (!CAMEL_IS_MULTIPART (mp)) {
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
+ }
+
+ mime_part = camel_multipart_get_part (mp, 1);
+ if (mime_part) {
+ gint len;
+ /* try the data fork for something useful, doubtful but who knows */
+ len = part_id->len;
+ g_string_append_printf(part_id, ".appledouble.1");
+ em_format_parse_part (emf, mime_part, part_id, info, cancellable);
+ g_string_truncate (part_id, len);
+ } else {
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ }
}
static void
-emf_class_init (EMFormatClass *class)
+emf_parse_multipart_encrypted (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- GObjectClass *object_class;
+ CamelCipherContext *context;
+ const gchar *protocol;
+ CamelMimePart *opart;
+ CamelCipherValidity *valid;
+ CamelMultipartEncrypted *mpe;
+ GError *local_error = NULL;
- parent_class = g_type_class_peek_parent (class);
- g_type_class_add_private (class, sizeof (EMFormatPrivate));
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- object_class = G_OBJECT_CLASS (class);
- object_class->finalize = emf_finalize;
+ mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part);
+ if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) {
+ em_format_format_error (
+ emf, _("Could not parse MIME message. "
+ "Displaying as source."));
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
+ }
- class->find_handler = emf_find_handler;
- class->format_clone = emf_format_clone;
- class->format_secure = emf_format_secure;
- class->busy = emf_busy;
- class->is_inline = emf_is_inline;
+ /* Currently we only handle RFC2015-style PGP encryption. */
+ protocol = camel_content_type_param (
+ ((CamelDataWrapper *)mpe)->mime_type, "protocol");
+ if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) {
+ em_format_format_error (emf, _("Unsupported encryption type for multipart/encrypted"));
+ emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
+ return;
+ }
- signals[EMF_COMPLETE] = g_signal_new (
- "complete",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EMFormatClass, complete),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ context = camel_gpg_context_new (emf->priv->session);
+ opart = camel_mime_part_new ();
+ valid = camel_cipher_context_decrypt_sync (
+ context, part, opart, cancellable, &local_error);
+ preserve_charset_in_content_type (part, opart);
+ if (valid == NULL) {
+ em_format_format_error (
+ emf, local_error->message ?
+ _("Could not parse PGP/MIME message") :
+ _("Could not parse PGP/MIME message: Unknown error"));
+ if (local_error->message != NULL)
+ em_format_format_error (
+ emf, "%s", local_error->message);
+ g_clear_error (&local_error);
+ emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
+ } else {
+ gint len = part_id->len;
+
+ EMFormatParserInfo encinfo = {
+ info->handler,
+ info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP,
+ };
+
+ if (info->validity)
+ camel_cipher_validity_envelope (valid, info->validity);
+
+ encinfo.validity = valid;
+
+ g_string_append (part_id, ".encrypted");
+ em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+ g_string_truncate (part_id, len);
+
+ /* Add a widget with details about the encryption, but only when
+ * the encrypted isn't itself secured, in that case it has created
+ * the button itself */
+ if (!is_secured (opart)) {
+ g_string_append (part_id, ".encrypted.button");
+ em_format_parse_part_as (emf, part, part_id, &encinfo,
+ "x-evolution/message/x-secure-button", cancellable);
+ g_string_truncate (part_id, len);
+ }
+
+ camel_cipher_validity_free (valid);
+ }
+
+ /* TODO: Make sure when we finalize this part, it is zero'd out */
+ g_object_unref (opart);
+ g_object_unref (context);
}
+/* RFC 2046 */
static void
-emf_init (EMFormat *emf)
+emf_parse_multipart_mixed (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EShell *shell;
- EShellSettings *shell_settings;
+ CamelMultipart *mp;
+ gint i, nparts, len;
- emf->priv = EM_FORMAT_GET_PRIVATE (emf);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- emf->inline_table = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) NULL,
- (GDestroyNotify) emf_free_cache);
- emf->composer = FALSE;
- emf->print = FALSE;
- g_queue_init (&emf->header_list);
- em_format_default_headers (emf);
- emf->part_id = g_string_new("");
- emf->current_message_part_id = NULL;
- emf->validity_found = 0;
+ mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
- shell = e_shell_get_default ();
- shell_settings = e_shell_get_shell_settings (shell);
+ if (!CAMEL_IS_MULTIPART (mp)) {
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
+ }
+
+ len = part_id->len;
+ nparts = camel_multipart_get_number (mp);
+ for (i = 0; i < nparts; i++) {
+ CamelMimePart *subpart;
- emf->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
- g_return_if_fail (emf->session != NULL);
+ subpart = camel_multipart_get_part (mp, i);
- g_object_ref (emf->session);
+ g_string_append_printf(part_id, ".mixed.%d", i);
+ em_format_parse_part (emf, subpart, part_id, info, cancellable);
+ g_string_truncate (part_id, len);
+ }
}
-GType
-em_format_get_type (void)
+static void
+emf_parse_multipart_signed (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- static GType type = 0;
+ CamelMimePart *cpart;
+ CamelMultipartSigned *mps;
+ CamelCipherContext *cipher = NULL;
+ guint32 validity_type;
- if (G_UNLIKELY (type == 0)) {
- static const GTypeInfo type_info = {
- sizeof (EMFormatClass),
- (GBaseInitFunc) emf_base_init,
- (GBaseFinalizeFunc) NULL,
- (GClassInitFunc) emf_class_init,
- (GClassFinalizeFunc) NULL,
- NULL, /* class_data */
- sizeof (EMFormat),
- 0, /* n_preallocs */
- (GInstanceInitFunc) emf_init,
- NULL /* value_table */
- };
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- type = g_type_register_static (
- G_TYPE_OBJECT, "EMFormat", &type_info, 0);
+ mps = (CamelMultipartSigned *) camel_medium_get_content ((CamelMedium *) part);
+ if (!CAMEL_IS_MULTIPART_SIGNED (mps)
+ || (cpart = camel_multipart_get_part ((CamelMultipart *) mps,
+ CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
+ em_format_format_error (
+ emf, _("Could not parse MIME message. "
+ "Displaying as source."));
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
}
- return type;
-}
+ /* FIXME: Should be done via a plugin interface */
+ /* FIXME: duplicated in em-format-html-display.c */
+ if (mps->protocol) {
+#ifdef ENABLE_SMIME
+ if (g_ascii_strcasecmp("application/x-pkcs7-signature", mps->protocol) == 0
+ || g_ascii_strcasecmp("application/pkcs7-signature", mps->protocol) == 0) {
+ cipher = camel_smime_context_new (emf->priv->session);
+ validity_type = EM_FORMAT_VALIDITY_FOUND_SMIME;
+ } else
+#endif
+ if (g_ascii_strcasecmp("application/pgp-signature", mps->protocol) == 0) {
+ cipher = camel_gpg_context_new (emf->priv->session);
+ validity_type = EM_FORMAT_VALIDITY_FOUND_PGP;
+ }
+ }
-/**
- * em_format_class_add_handler:
- * @emfc: EMFormatClass
- * @info: Callback information.
- *
- * Add a mime type handler to this class. This is only used by
- * implementing classes. The @info.old pointer will automatically be
- * setup to point to the old handler if one was already set. This can
- * be used for overrides a fallback.
- *
- * When a mime type described by @info is encountered, the callback will
- * be invoked. Note that @info may be extended by sub-classes if
- * they require additional context information.
- *
- * Use a mime type of "foo/ *" to insert a fallback handler for type "foo".
- **/
-void
-em_format_class_add_handler (EMFormatClass *emfc,
- EMFormatHandler *info)
-{
- info->old = g_hash_table_lookup (emfc->type_handlers, info->mime_type);
- g_hash_table_insert (emfc->type_handlers, (gpointer) info->mime_type, info);
-}
+ if (cipher == NULL) {
+ em_format_format_error(emf, _("Unsupported signature format"));
+ emf_parse_multipart_mixed (emf, part, part_id, info, cancellable);
+ } else {
+ CamelCipherValidity *valid;
+ GError *local_error = NULL;
-struct _class_handlers {
- EMFormatClass *old;
- EMFormatClass *new;
-};
+ valid = camel_cipher_context_verify_sync (
+ cipher, part, cancellable, &local_error);
+ if (valid == NULL) {
+ em_format_format_error (
+ emf, local_error->message ?
+ _("Error verifying signature") :
+ _("Unknown error verifying signature"));
+ if (local_error->message != NULL)
+ em_format_format_error (
+ emf, "%s",
+ local_error->message);
+ g_clear_error (&local_error);
+ emf_parse_multipart_mixed (emf, part, part_id,info, cancellable);
+ } else {
+ gint i, nparts, len = part_id->len;
+ gboolean secured;
+
+ EMFormatParserInfo signinfo = {
+ info->handler,
+ info->validity_type | validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED,
+ };
+
+ if (info->validity)
+ camel_cipher_validity_envelope (valid, info->validity);
+ signinfo.validity = valid;
+
+ nparts = camel_multipart_get_number (CAMEL_MULTIPART (mps));
+ secured = FALSE;
+ for (i = 0; i < nparts; i++) {
+ CamelMimePart *subpart;
+ subpart = camel_multipart_get_part (CAMEL_MULTIPART (mps), i);
+
+ g_string_append_printf(part_id, ".signed.%d", i);
+ em_format_parse_part (emf, subpart, part_id, &signinfo, cancellable);
+ g_string_truncate (part_id, len);
+
+ if (!secured)
+ secured = is_secured (subpart);
+ }
-static void
-merge_missing (gpointer key,
- gpointer value,
- gpointer userdata)
-{
- struct _class_handlers *classes = (struct _class_handlers *) userdata;
- EMFormatHandler *info;
+ /* Add a widget with details about the encryption, but only when
+ * the encrypted isn't itself secured, in that case it has created
+ * the button itself */
+ if (!secured) {
+ g_string_append (part_id, ".signed.button");
+ em_format_parse_part_as (emf, part, part_id, &signinfo,
+ "x-evolution/message/x-secure-button", cancellable);
+ g_string_truncate (part_id, len);
+ }
- info = g_hash_table_lookup (classes->new->type_handlers, key);
- if (!info) {
- /* Might be from a plugin */
- g_hash_table_insert (classes->new->type_handlers, key, value);
+ camel_cipher_validity_free (valid);
+ }
}
+ g_object_unref (cipher);
}
-void
-em_format_merge_handler (EMFormat *new,
- EMFormat *old)
+/* RFC 2046 */
+static void
+emf_parse_multipart_digest (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatClass *oldc = (EMFormatClass *) G_OBJECT_GET_CLASS (old);
- EMFormatClass *newc = (EMFormatClass *) G_OBJECT_GET_CLASS (new);
- struct _class_handlers fclasses;
+ CamelMultipart *mp;
+ gint i, nparts, len;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- fclasses.old = oldc;
- fclasses.new = newc;
+ mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
- g_hash_table_foreach (oldc->type_handlers, merge_missing, &fclasses);
+ if (!CAMEL_IS_MULTIPART (mp)) {
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
+ }
+ len = part_id->len;
+ nparts = camel_multipart_get_number (mp);
+ for (i = 0; i < nparts; i++) {
+ CamelMimePart *subpart;
+ CamelContentType *ct;
+ gchar *cts;
+ const EMFormatHandler *handler;
+
+ subpart = camel_multipart_get_part (mp, i);
+
+ if (!subpart)
+ continue;
+
+ g_string_append_printf(part_id, ".digest.%d", i);
+
+ ct = camel_mime_part_get_content_type (subpart);
+ /* According to RFC this shouldn't happen, but who knows... */
+ if (ct && !camel_content_type_is (ct, "message", "rfc822")) {
+ cts = camel_content_type_simple (ct);
+ em_format_parse_part_as (emf, part, part_id, info, cts, cancellable);
+ g_free (cts);
+ g_string_truncate (part_id, len);
+ continue;
+ }
+
+ handler = em_format_find_handler (emf, "message/rfc822");
+ if (handler && handler->parse_func)
+ handler->parse_func (emf, subpart, part_id, info, cancellable);
+
+ g_string_truncate (part_id, len);
+ }
}
-/**
- * em_format_class_remove_handler:
- * @emfc:
- * @info:
- *
- * Remove a handler. @info must be a value which was previously
- * added.
- **/
-void
-em_format_class_remove_handler (EMFormatClass *emfc,
- EMFormatHandler *info)
+/* RFC 2387 */
+static void
+emf_parse_multipart_related (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatHandler *current;
+ CamelMultipart *mp;
+ CamelMimePart *body_part, *display_part = NULL;
+ gint i, nparts, partidlen, displayid = 0;
- /* TODO: thread issues? */
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- current = g_hash_table_lookup (emfc->type_handlers, info->mime_type);
- if (current == info) {
- current = info->old;
- if (current)
- g_hash_table_insert (
- emfc->type_handlers,
- (gpointer) current->mime_type, current);
- else
- g_hash_table_remove (
- emfc->type_handlers, info->mime_type);
- } else {
- while (current && current->old != info)
- current = current->old;
- g_return_if_fail (current != NULL);
- current->old = info->old;
+ mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
+
+ if (!CAMEL_IS_MULTIPART (mp)) {
+ emf_parse_source (emf, part, part_id, info, cancellable);
+ return;
+ }
+
+ display_part = get_related_display_part (part, &displayid);
+
+ if (display_part == NULL) {
+ emf_parse_multipart_mixed (
+ emf, part, part_id, info, cancellable);
+ return;
+ }
+
+ /* The to-be-displayed part goes first */
+ partidlen = part_id->len;
+ g_string_append_printf(part_id, ".related.%d", displayid);
+ em_format_parse_part (emf, display_part, part_id, info, cancellable);
+ g_string_truncate (part_id, partidlen);
+
+ /* Process the related parts */
+ nparts = camel_multipart_get_number (mp);
+ for (i = 0; i < nparts; i++) {
+ body_part = camel_multipart_get_part (mp, i);
+ if (body_part != display_part) {
+ g_string_append_printf(part_id, ".related.%d", i);
+ em_format_parse_part (emf, body_part, part_id, info, cancellable);
+ g_string_truncate (part_id, partidlen);
+ }
}
}
-/**
- * em_format_find_handler:
- * @emf:
- * @mime_type:
- *
- * Find a format handler by @mime_type.
- *
- * Return value: NULL if no handler is available.
- **/
-const EMFormatHandler *
-em_format_find_handler (EMFormat *emf,
- const gchar *mime_type)
+static void
+emf_parse_message_deliverystatus (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatClass *class;
+ EMFormatPURI *puri;
+ gint len;
- g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
- g_return_val_if_fail (mime_type != NULL, NULL);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ len = part_id->len;
+ g_string_append (part_id, ".deliverystatus");
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_val_if_fail (class->find_handler != NULL, NULL);
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = emf_write_text;
+ puri->mime_type = g_strdup ("text/html");
+ puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+ puri->validity_type = info->validity_type;
- return class->find_handler (emf, mime_type);
+ g_string_truncate (part_id, len);
+
+ em_format_add_puri (emf, puri);
}
-/**
- * em_format_fallback_handler:
- * @emf:
- * @mime_type:
- *
- * Try to find a format handler based on the major type of the @mime_type.
- *
- * The subtype is replaced with "*" and a lookup performed.
- *
- * Return value:
- **/
-const EMFormatHandler *
-em_format_fallback_handler (EMFormat *emf,
- const gchar *mime_type)
+static void
+emf_parse_inlinepgp_signed (EMFormat *emf,
+ CamelMimePart *ipart,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- gchar *mime, *s;
+ CamelStream *filtered_stream;
+ CamelMimeFilterPgp *pgp_filter;
+ CamelContentType *content_type;
+ CamelCipherContext *cipher;
+ CamelCipherValidity *valid;
+ CamelDataWrapper *dw;
+ CamelMimePart *opart;
+ CamelStream *ostream;
+ gchar *type;
+ gint len;
+ GError *local_error = NULL;
+ EMFormatParserInfo signinfo;
+ GByteArray *ba;
- s = strchr (mime_type, '/');
- if (s == NULL)
- mime = (gchar *) mime_type;
- else {
- gsize len = (s - mime_type) + 1;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- mime = g_alloca (len + 2);
- strncpy (mime, mime_type, len);
- strcpy(mime+len, "*");
+ if (!ipart) {
+ em_format_format_error(emf, _("Unknown error verifying signature"));
+ return;
}
- return em_format_find_handler (emf, mime);
+ cipher = camel_gpg_context_new (emf->priv->session);
+ /* Verify the signature of the message */
+ valid = camel_cipher_context_verify_sync (
+ cipher, ipart, cancellable, &local_error);
+ if (!valid) {
+ em_format_format_error (
+ emf, local_error->message ?
+ _("Error verifying signature") :
+ _("Unknown error verifying signature"));
+ if (local_error->message)
+ em_format_format_error (
+ emf, "%s", local_error->message);
+ emf_parse_source (emf, ipart, part_id, info, cancellable);
+ /* XXX I think this will loop:
+ * em_format_part_as(emf, stream, part, "text/plain"); */
+ g_clear_error (&local_error);
+ g_object_unref (cipher);
+ return;
+ }
+
+ /* Setup output stream */
+ ostream = camel_stream_mem_new ();
+ filtered_stream = camel_stream_filter_new (ostream);
+
+ /* Add PGP header / footer filter */
+ pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new ();
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream),
+ CAMEL_MIME_FILTER (pgp_filter));
+ g_object_unref (pgp_filter);
+
+ /* Pass through the filters that have been setup */
+ dw = camel_medium_get_content ((CamelMedium *) ipart);
+ camel_data_wrapper_decode_to_stream_sync (
+ dw, (CamelStream *) filtered_stream, cancellable, NULL);
+ camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL);
+ g_object_unref (filtered_stream);
+
+ /* Create a new text/plain MIME part containing the signed
+ * content preserving the original part's Content-Type params. */
+ content_type = camel_mime_part_get_content_type (ipart);
+ type = camel_content_type_format (content_type);
+ content_type = camel_content_type_decode (type);
+ g_free (type);
+
+ g_free (content_type->type);
+ content_type->type = g_strdup ("text");
+ g_free (content_type->subtype);
+ content_type->subtype = g_strdup ("plain");
+ type = camel_content_type_format (content_type);
+ camel_content_type_unref (content_type);
+
+ ba = camel_stream_mem_get_byte_array ((CamelStreamMem *) ostream);
+ opart = camel_mime_part_new ();
+ camel_mime_part_set_content (opart, (gchar *) ba->data, ba->len, type);
+ g_free (type);
+
+ if (info->validity)
+ camel_cipher_validity_envelope (valid, info->validity);
+
+ /* Pass it off to the real formatter */
+ len = part_id->len;
+ g_string_append (part_id, ".inlinepgp_signed");
+ signinfo.handler = info->handler;
+ signinfo.validity_type = info->validity_type | EM_FORMAT_VALIDITY_FOUND_SIGNED | EM_FORMAT_VALIDITY_FOUND_PGP;
+ signinfo.validity = valid;
+ em_format_parse_part (emf, opart, part_id, &signinfo, cancellable);
+ g_string_truncate (part_id, len);
+
+ /* Add a widget with details about the encryption, but only when
+ * the encrypted isn't itself secured, in that case it has created
+ * the button itself */
+ if (!is_secured (opart)) {
+ g_string_append (part_id, ".inlinepgp_signed.button");
+ em_format_parse_part_as (emf, opart, part_id, &signinfo,
+ "x-evolution/message/x-secure-button", cancellable);
+ g_string_truncate (part_id, len);
+ }
+
+ /* Clean Up */
+ camel_cipher_validity_free (valid);
+ g_object_unref (dw);
+ g_object_unref (opart);
+ g_object_unref (ostream);
+ g_object_unref (cipher);
}
-/**
- * em_format_add_puri:
- * @emf:
- * @size:
- * @cid: Override the autogenerated content id.
- * @part:
- * @func:
- *
- * Add a pending-uri handler. When formatting parts that reference
- * other parts, a pending-uri (PURI) can be used to track the reference.
- *
- * @size is used to allocate the structure, so that it can be directly
- * subclassed by implementors.
- *
- * @cid can be used to override the key used to retreive the PURI, if NULL,
- * then the content-location and the content-id of the @part are stored
- * as lookup keys for the part.
- *
- * FIXME: This may need a free callback.
- *
- * Return value: A new PURI, with a referenced copy of @part, and the cid
- * always set. The uri will be set if one is available. Clashes
- * are resolved by forgetting the old PURI in the global index.
- **/
-EMFormatPURI *
-em_format_add_puri (EMFormat *emf,
- gsize size,
- const gchar *cid,
- CamelMimePart *part,
- EMFormatPURIFunc func)
+static void
+emf_parse_inlinepgp_encrypted (EMFormat *emf,
+ CamelMimePart *ipart,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatPURI *puri;
- const gchar *tmp;
+ CamelCipherContext *cipher;
+ CamelCipherValidity *valid;
+ CamelMimePart *opart;
+ CamelDataWrapper *dw;
+ gchar *mime_type;
+ gint len;
+ GError *local_error = NULL;
+ EMFormatParserInfo encinfo;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ cipher = camel_gpg_context_new (emf->priv->session);
+ opart = camel_mime_part_new ();
- d(printf("adding puri for part: %s\n", emf->part_id->str));
+ /* Decrypt the message */
+ valid = camel_cipher_context_decrypt_sync (
+ cipher, ipart, opart, cancellable, &local_error);
+
+ if (!valid) {
+ em_format_format_error (
+ emf, _("Could not parse PGP message: "));
+ if (local_error->message != NULL)
+ em_format_format_error (
+ emf, "%s", local_error->message);
+ else
+ em_format_format_error (
+ emf, _("Unknown error"));
+ emf_parse_source (emf, ipart, part_id, info, cancellable);
+ /* XXX I think this will loop:
+ * em_format_part_as(emf, stream, part, "text/plain"); */
- if (size < sizeof (*puri)) {
- g_warning (
- "size (%" G_GSIZE_FORMAT
- ") less than size of puri\n", size);
- size = sizeof (*puri);
+ g_clear_error (&local_error);
+ g_object_unref (cipher);
+ g_object_unref (opart);
+ return;
}
- puri = g_malloc0 (size);
+ dw = camel_medium_get_content ((CamelMedium *) opart);
+ mime_type = camel_data_wrapper_get_mime_type (dw);
- puri->format = emf;
- puri->func = func;
- puri->use_count = 0;
- puri->cid = g_strdup (cid);
- puri->part_id = g_strdup (emf->part_id->str);
+ /* this ensures to show the 'opart' as inlined, if possible */
+ if (mime_type && g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) {
+ const gchar *snoop = em_format_snoop_type (opart);
- if (part) {
- g_object_ref (part);
- puri->part = part;
+ if (snoop)
+ camel_data_wrapper_set_mime_type (dw, snoop);
}
- if (part != NULL && cid == NULL) {
- tmp = camel_mime_part_get_content_id (part);
- if (tmp)
- puri->cid = g_strdup_printf("cid:%s", tmp);
- else
- puri->cid = g_strdup_printf("em-no-cid:%s", emf->part_id->str);
+ preserve_charset_in_content_type (ipart, opart);
+ g_free (mime_type);
- d(printf("built cid '%s'\n", puri->cid));
+ if (info->validity)
+ camel_cipher_validity_envelope (valid, info->validity);
- /* Not quite same as old behaviour, it also put in the
- * relative uri and a fallback for no parent uri. */
- tmp = camel_mime_part_get_content_location (part);
- puri->uri = NULL;
- if (tmp == NULL) {
- /* No location, don't set a uri at all,
- * html parts do this themselves. */
- } else {
- if (strchr (tmp, ':') == NULL && emf->base != NULL) {
- CamelURL *uri;
-
- uri = camel_url_new_with_base (emf->base, tmp);
- puri->uri = camel_url_to_string (uri, 0);
- camel_url_free (uri);
- } else {
- puri->uri = g_strdup (tmp);
- }
- }
+ /* Pass it off to the real formatter */
+ len = part_id->len;
+ g_string_append (part_id, ".inlinepgp_encrypted");
+ encinfo.handler = info->handler;
+ encinfo.validity_type = info->validity_type | EM_FORMAT_VALIDITY_FOUND_ENCRYPTED | EM_FORMAT_VALIDITY_FOUND_PGP;
+ encinfo.validity = valid;
+ em_format_parse_part (emf, opart, part_id, &encinfo, cancellable);
+ g_string_truncate (part_id, len);
+
+ /* Add a widget with details about the encryption, but only when
+ * the encrypted isn't itself secured, in that case it has created
+ * the button itself */
+ if (!is_secured (opart)) {
+ g_string_append (part_id, ".inlinepgp_encrypted.button");
+ em_format_parse_part_as (emf, opart, part_id, &encinfo,
+ "x-evolution/message/x-secure-button", cancellable);
+ g_string_truncate (part_id, len);
}
- g_return_val_if_fail (puri->cid != NULL, NULL);
- g_return_val_if_fail (emf->pending_uri_level != NULL, NULL);
- g_return_val_if_fail (emf->pending_uri_table != NULL, NULL);
+ /* Clean Up */
+ camel_cipher_validity_free (valid);
+ g_object_unref (opart);
+ g_object_unref (cipher);
+}
- g_queue_push_tail (emf->pending_uri_level->data, puri);
+static void
+emf_parse_message (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ /* Headers */
+ info->force_handler = TRUE;
+ em_format_parse_part_as (emf, part, part_id, info,
+ "x-evolution/message/headers", cancellable);
- if (puri->uri)
- g_hash_table_insert (emf->pending_uri_table, puri->uri, puri);
- g_hash_table_insert (emf->pending_uri_table, puri->cid, puri);
+ /* Anything that comes between headers and message body */
+ info->force_handler = TRUE;
+ em_format_parse_part_as (emf, part, part_id, info,
+ "x-evolution/message/post-headers", cancellable);
- return puri;
+ /* Begin parsing the message */
+ info->force_handler = FALSE;
+ em_format_parse_part (emf, part, part_id, info, cancellable);
}
-/**
- * em_format_push_level:
- * @emf:
- *
- * This is used to build a hierarchy of visible PURI objects based on
- * the structure of the message. Used by multipart/alternative formatter.
- *
- * FIXME: This could probably also take a uri so it can automatically update
- * the base location.
- **/
-void
-em_format_push_level (EMFormat *emf)
+static void
+emf_parse_headers (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- GNode *node;
+ EMFormatPURI *puri;
+ gint len;
- g_return_if_fail (EM_IS_FORMAT (emf));
+ len = part_id->len;
+ g_string_append (part_id, ".headers");
- node = g_node_new (g_queue_new ());
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = info->handler->write_func;
+ puri->mime_type = g_strdup ("text/html");
+ em_format_add_puri (emf, puri);
- if (emf->pending_uri_tree == NULL)
- emf->pending_uri_tree = node;
- else
- g_node_append (emf->pending_uri_tree, node);
+ g_string_truncate (part_id, len);
+}
- emf->pending_uri_level = node;
+static void
+emf_parse_post_headers (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ /* Add attachment bar */
+ info->force_handler = TRUE;
+ em_format_parse_part_as (emf, part, part_id, info,
+ "x-evolution/message/attachment-bar", cancellable);
}
-/**
- * em_format_pull_level:
- * @emf:
- *
- * Drop a level of visibility back to the parent. Note that
- * no PURI values are actually freed.
- **/
+static void
+emf_parse_source (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatPURI *puri;
+ gint len;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ len = part_id->len;
+ g_string_append (part_id, ".source");
+
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = info->handler->write_func;
+ puri->mime_type = g_strdup ("text/html");
+ g_string_truncate (part_id, len);
+
+ em_format_add_puri (emf, puri);
+}
+
+/**************************************************************************/
+
void
-em_format_pull_level (EMFormat *emf)
+em_format_empty_writer (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ /* DO NOTHING */
+}
+
+static void
+emf_write_error (EMFormat * emf,
+ EMFormatPURI * puri,
+ CamelStream * stream,
+ EMFormatWriterInfo * info,
+ GCancellable * cancellable)
+{
+ camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) puri->part,
+ stream, cancellable, NULL);
+}
+
+static void
+emf_write_text (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ CamelContentType *ct;
+
+ ct = camel_mime_part_get_content_type (puri->part);
+ if (!camel_content_type_is (ct, "text", "plain")) {
+ camel_stream_write_string (stream, _("Cannot proccess non-text mime/part"),
+ cancellable, NULL);
+ return;
+ }
+
+ camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) puri->part,
+ stream, cancellable, NULL);
+}
+
+static void
+emf_write_source (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
+ GByteArray *ba;
+ gchar *data;
+
g_return_if_fail (EM_IS_FORMAT (emf));
- g_return_if_fail (emf->pending_uri_level != NULL);
- emf->pending_uri_level = emf->pending_uri_level->parent;
+ ba = camel_data_wrapper_get_byte_array ((CamelDataWrapper *) puri->part);
+
+ data = g_strndup ((gchar *) ba->data, ba->len);
+ camel_stream_write_string (stream, data, cancellable, NULL);
+ g_free (data);
}
-/**
- * em_format_find_visible_puri:
- * @emf:
- * @uri:
- *
- * Search for a PURI based on the visibility defined by :push_level()
- * and :pull_level().
- *
- * Return value:
- **/
-EMFormatPURI *
-em_format_find_visible_puri (EMFormat *emf,
- const gchar *uri)
+/**************************************************************************/
+
+static gboolean
+emf_is_inline (EMFormat *emf,
+ const gchar *part_id,
+ CamelMimePart *mime_part,
+ const EMFormatHandler *handle)
{
- GNode *node;
+ //EMFormatCache *emfc;
+ const gchar *disposition;
- g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
- g_return_val_if_fail (uri != NULL, NULL);
+ if (handle == NULL)
+ return FALSE;
- node = emf->pending_uri_level;
+ /* Some types need to override the disposition.
+ * e.g. application/x-pkcs7-mime */
+ if (handle->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION)
+ return TRUE;
- while (node != NULL) {
- GQueue *queue = node->data;
- GList *link;
+ disposition = camel_mime_part_get_disposition (mime_part);
+ if (disposition != NULL)
+ return g_ascii_strcasecmp (disposition, "inline") == 0;
- link = g_queue_peek_head_link (queue);
+ /* Otherwise, use the default for this handler type. */
+ return (handle->flags & EM_FORMAT_HANDLER_INLINE) != 0;
+}
- while (link != NULL) {
- EMFormatPURI *pw = link->data;
+/**************************************************************************/
- if (g_strcmp0 (pw->uri, uri) == 0)
- return pw;
+static EMFormatHandler type_handlers[] = {
+#ifdef ENABLE_SMIME
+ { (gchar *) "application/x-pkcs7-mime", emf_parse_application_xpkcs7mime, 0, EM_FORMAT_HANDLER_INLINE_DISPOSITION },
+#endif
+ { (gchar *) "application/mbox", emf_parse_application_mbox, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE },
+ { (gchar *) "multipart/alternative", emf_parse_multipart_alternative, },
+ { (gchar *) "multipart/appledouble", emf_parse_multipart_appledouble, },
+ { (gchar *) "multipart/encrypted", emf_parse_multipart_encrypted, },
+ { (gchar *) "multipart/mixed", emf_parse_multipart_mixed, },
+ { (gchar *) "multipart/signed", emf_parse_multipart_signed, },
+ { (gchar *) "multipart/related", emf_parse_multipart_related, },
+ { (gchar *) "multipart/digest", emf_parse_multipart_digest, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE },
+ { (gchar *) "multipart/*", emf_parse_multipart_mixed, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE },
+ { (gchar *) "message/deliverystatus", emf_parse_message_deliverystatus, 0, },
+
+ /* Ignore PGP signature part */
+ { (gchar *) "application/pgp-signature", em_format_empty_parser, },
+
+ /* Insert brokenly-named parts here */
+#ifdef ENABLE_SMIME
+ { (gchar *) "application/pkcs7-mime", emf_parse_application_xpkcs7mime, 0, EM_FORMAT_HANDLER_INLINE_DISPOSITION },
+#endif
- if (g_strcmp0 (pw->cid, uri) == 0)
- return pw;
+ /* internal types */
+ { (gchar *) "application/x-inlinepgp-signed", emf_parse_inlinepgp_signed, },
+ { (gchar *) "application/x-inlinepgp-encrypted", emf_parse_inlinepgp_encrypted, },
+ { (gchar *) "x-evolution/message", emf_parse_message, 0, EM_FORMAT_HANDLER_COMPOUND_TYPE },
+ { (gchar *) "x-evolution/message/headers", emf_parse_headers, },
+ { (gchar *) "x-evolution/message/post-headers", emf_parse_post_headers, },
+ { (gchar *) "x-evolution/message/source", emf_parse_source, emf_write_source },
+};
- link = g_list_next (link);
- }
+/* note: also copied in em-mailer-prefs.c */
+static const struct {
+ const gchar *name;
+ guint32 flags;
+} default_headers[] = {
+ { N_("From"), EM_FORMAT_HEADER_BOLD },
+ { N_("Reply-To"), EM_FORMAT_HEADER_BOLD },
+ { N_("To"), EM_FORMAT_HEADER_BOLD },
+ { N_("Cc"), EM_FORMAT_HEADER_BOLD },
+ { N_("Bcc"), EM_FORMAT_HEADER_BOLD },
+ { N_("Subject"), EM_FORMAT_HEADER_BOLD },
+ { N_("Date"), EM_FORMAT_HEADER_BOLD },
+ { N_("Newsgroups"), EM_FORMAT_HEADER_BOLD },
+ { N_("Face"), 0 },
+};
+
+static void
+em_format_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EMFormat *emf = EM_FORMAT (object);
- node = node->parent;
+ switch (property_id) {
+ case PROP_CHARSET:
+ g_value_set_string (
+ value, em_format_get_charset (emf));
+ return;
+ case PROP_DEFAULT_CHARSET:
+ g_value_set_string (
+ value, em_format_get_default_charset (emf));
+ return;
+ case PROP_COMPOSER:
+ g_value_set_boolean (
+ value, em_format_get_composer (emf));
+ return;
+ case PROP_BASE_URL:
+ g_value_set_object (
+ value, em_format_get_base_url (emf));
+ return;
}
- return NULL;
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
-/**
- * em_format_find_puri:
- * @emf:
- * @uri:
- *
- * Search for a PURI based on a uri. Both the content-id
- * and content-location are checked.
- *
- * Return value:
- **/
-EMFormatPURI *
-em_format_find_puri (EMFormat *emf,
- const gchar *uri)
+static void
+em_format_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
- g_return_val_if_fail (uri != NULL, NULL);
+ EMFormat *emf = EM_FORMAT (object);
- g_return_val_if_fail (emf->pending_uri_table != NULL, NULL);
+ switch (property_id) {
+ case PROP_CHARSET:
+ em_format_set_charset (emf,
+ g_value_get_string (value));
+ return;
+ case PROP_DEFAULT_CHARSET:
+ em_format_set_default_charset (emf,
+ g_value_get_string (value));
+ return;
+ case PROP_COMPOSER:
+ em_format_set_composer (emf,
+ g_value_get_boolean (value));
+ return;
+ case PROP_BASE_URL:
+ em_format_set_base_url (emf,
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- return g_hash_table_lookup (emf->pending_uri_table, uri);
}
-/**
- * em_format_clear_puri_tree:
- * @emf:
- *
- * For use by implementors to clear out the message structure
- * data.
- **/
-void
-em_format_clear_puri_tree (EMFormat *emf)
+static void
+em_format_finalize (GObject *object)
{
- if (emf->pending_uri_table == NULL)
- emf->pending_uri_table =
- g_hash_table_new (g_str_hash, g_str_equal);
+ EMFormat *emf = EM_FORMAT (object);
- else {
- g_hash_table_remove_all (emf->pending_uri_table);
+ if (emf->message_uid) {
+ g_free (emf->message_uid);
+ emf->message_uid = NULL;
+ }
+
+ if (emf->uri_base) {
+ g_free (emf->uri_base);
+ emf->uri_base = NULL;
+ }
+
+ if (emf->message) {
+ g_object_unref (emf->message);
+ emf->message = NULL;
+ }
+
+ if (emf->folder) {
+ g_object_unref (emf->folder);
+ emf->folder = NULL;
+ }
+
+ if (emf->mail_part_table) {
+ /* This will destroy all the EMFormatPURI objects stored
+ * inside!!!! */
+ g_hash_table_destroy (emf->mail_part_table);
+ emf->mail_part_table = NULL;
+ }
+
+ if (emf->mail_part_list) {
+ g_list_free (emf->mail_part_list);
+ emf->mail_part_list = NULL;
+ }
+
+ if (emf->priv->base_url) {
+ camel_url_free (emf->priv->base_url);
+ emf->priv->base_url = NULL;
+ }
- g_node_traverse (
- emf->pending_uri_tree,
- G_IN_ORDER, G_TRAVERSE_ALL, -1,
- (GNodeTraverseFunc) emf_clear_puri_node, NULL);
- g_node_destroy (emf->pending_uri_tree);
+ if (emf->priv->session) {
+ g_object_unref (emf->priv->session);
+ emf->priv->session = NULL;
+ }
- emf->pending_uri_tree = NULL;
- emf->pending_uri_level = NULL;
+ if (emf->priv->charset) {
+ g_free (emf->priv->charset);
+ emf->priv->charset = NULL;
}
- em_format_push_level (emf);
+ em_format_clear_headers (emf);
+
+ /* Chain up to parent's finalize() method */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
-/* use mime_type == NULL to force showing as application/octet-stream */
-void
-em_format_part_as (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const gchar *mime_type,
- GCancellable *cancellable)
+static void
+em_format_base_init (EMFormatClass *klass)
{
- const EMFormatHandler *handle = NULL;
- const gchar *snoop_save = emf->snoop_mime_type, *tmp;
- CamelURL *base_save = emf->base, *base = NULL;
- gchar *basestr = NULL;
+ gint i;
- d(printf("format_part_as()\n"));
+ klass->type_handlers = g_hash_table_new (g_str_hash, g_str_equal);
- emf->snoop_mime_type = NULL;
+ for (i = 0; i < G_N_ELEMENTS (type_handlers); i++) {
+ g_hash_table_insert (klass->type_handlers,
+ type_handlers[i].mime_type,
+ &type_handlers[i]);
+ }
+}
- /* RFC 2110, we keep track of content-base, and absolute content-location headers
- * This is actually only required for html, but, *shrug * */
- tmp = camel_medium_get_header((CamelMedium *)part, "Content-Base");
- if (tmp == NULL) {
- tmp = camel_mime_part_get_content_location (part);
- if (tmp && strchr (tmp, ':') == NULL)
- tmp = NULL;
- } else {
- tmp = basestr = camel_header_location_decode (tmp);
- }
- d(printf("content-base is '%s'\n", tmp?tmp:"<unset>"));
- if (tmp
- && (base = camel_url_new (tmp, NULL))) {
- emf->base = base;
- d(printf("Setting content base '%s'\n", tmp));
- }
- g_free (basestr);
-
- if (mime_type != NULL) {
- gboolean is_fallback = FALSE;
- if (g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) {
- emf->snoop_mime_type = mime_type = em_format_snoop_type (part);
- if (mime_type == NULL)
- mime_type = "application/octet-stream";
- }
+static void
+em_format_class_init (EMFormatClass *klass)
+{
+ GObjectClass *object_class;
- handle = em_format_find_handler (emf, mime_type);
- if (handle == NULL) {
- handle = em_format_fallback_handler (emf, mime_type);
- is_fallback = TRUE;
- }
+ parent_class = g_type_class_peek_parent (klass);
+
+ g_type_class_add_private (klass, sizeof (EMFormatPrivate));
+
+ klass->is_inline = emf_is_inline;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = em_format_finalize;
+ object_class->get_property = em_format_get_property;
+ object_class->set_property = em_format_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_CHARSET,
+ g_param_spec_string ("charset",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_DEFAULT_CHARSET,
+ g_param_spec_string ("default-charset",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_COMPOSER,
+ g_param_spec_boolean ("composer",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_BASE_URL,
+ g_param_spec_pointer ("base-url",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ signals[REDRAW_REQUESTED] = g_signal_new (
+ "redraw-requested",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMFormatClass, redraw_requested),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,0);
+}
- if (handle != NULL
- && !em_format_is_attachment (emf, part)) {
- d(printf("running handler for type '%s'\n", mime_type));
- handle->handler (
- emf, stream, part, handle,
- cancellable, is_fallback);
- goto finish;
- }
- d(printf("this type is an attachment? '%s'\n", mime_type));
- } else {
- mime_type = "application/octet-stream";
- }
+static void
+mail_part_table_item_free (gpointer data)
+{
+ GList *iter = data;
+ EMFormatPURI *puri = iter->data;
+
+ em_format_puri_free (puri);
+}
+
+static void
+em_format_init (EMFormat *emf)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
- EM_FORMAT_GET_CLASS (emf)->format_attachment (
- emf, stream, part, mime_type, handle, cancellable);
+ emf->priv = G_TYPE_INSTANCE_GET_PRIVATE (emf,
+ EM_TYPE_FORMAT, EMFormatPrivate);
-finish:
- emf->base = base_save;
- emf->snoop_mime_type = snoop_save;
+ emf->message = NULL;
+ emf->folder = NULL;
+ emf->mail_part_list = NULL;
+ emf->mail_part_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) mail_part_table_item_free);
+ /* No need to free the key, because it's owned and free'd by the PURI */
- if (base)
- camel_url_free (base);
+ shell = e_shell_get_default ();
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ emf->priv->last_error = 0;
+
+ emf->priv->session = e_shell_settings_get_pointer (shell_settings, "mail-session");
+ g_return_if_fail (emf->priv->session);
+
+ g_object_ref (emf->priv->session);
+
+ em_format_default_headers (emf);
}
-void
-em_format_part (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- GCancellable *cancellable)
+EMFormat *
+em_format_new (void)
{
- gchar *mime_type;
- CamelDataWrapper *dw;
+ EMFormat *emf = g_object_new (EM_TYPE_FORMAT, NULL);
- dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
- mime_type = camel_data_wrapper_get_mime_type (dw);
- if (mime_type != NULL) {
- camel_strdown (mime_type);
- em_format_part_as (
- emf, stream, mime_part, mime_type, cancellable);
- g_free (mime_type);
- } else
- em_format_part_as (
- emf, stream, mime_part, "text/plain", cancellable);
+ return emf;
}
-/**
- * em_format_format_clone:
- * @emf: an #EMFormat
- * @folder: a #CamelFolder or %NULL
- * @uid: Message UID or %NULL
- * @msg: a #CamelMimeMessage or %NULL
- * @emfsource: Used as a basis for user-altered layout, e.g. inline viewed
- * attachments.
- * @cancellable: a #GCancellable, or %NULL
- *
- * Format a message @msg. If @emfsource is non NULL, then the status of
- * inlined expansion and so forth is copied direction from @emfsource.
- *
- * By passing the same value for @emf and @emfsource, you can perform
- * a display refresh, or it can be used to generate an identical layout,
- * e.g. to print what the user has shown inline.
- **/
-void
-em_format_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *message,
- EMFormat *source,
- GCancellable *cancellable)
+GType
+em_format_get_type (void)
{
- EMFormatClass *class;
+ static GType type = 0;
- g_return_if_fail (EM_IS_FORMAT (emf));
- g_return_if_fail (folder == NULL || CAMEL_IS_FOLDER (folder));
- g_return_if_fail (message == NULL || CAMEL_IS_MIME_MESSAGE (message));
- g_return_if_fail (source == NULL || EM_IS_FORMAT (source));
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EMFormatClass),
+ (GBaseInitFunc) em_format_base_init,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) em_format_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMFormat),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_format_init,
+ NULL /* value_table */
+ };
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_if_fail (class->format_clone != NULL);
+ type = g_type_register_static (
+ G_TYPE_OBJECT, "EMFormat", &type_info, 0);
+ }
- class->format_clone (emf, folder, uid, message, source, cancellable);
+ return type;
}
void
-em_format_format (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *message,
- GCancellable *cancellable)
+em_format_set_charset (EMFormat *emf,
+ const gchar *charset)
{
- /* em_format_format_clone() will check the arguments. */
- em_format_format_clone (emf, folder, uid, message, NULL, cancellable);
+ g_return_if_fail (EM_IS_FORMAT (emf));
+
+ if (emf->priv->charset)
+ g_free (emf->priv->charset);
+
+ emf->priv->charset = g_strdup (charset);
+
+ g_object_notify (G_OBJECT (emf), "charset");
}
-static gboolean
-format_redraw_idle_cb (EMFormat *emf)
+const gchar *
+em_format_get_charset (EMFormat *emf)
{
- emf->priv->redraw_idle_id = 0;
-
- /* FIXME Not passing a GCancellable here. */
- em_format_format_clone (
- emf, emf->folder, emf->uid, emf->message, emf, NULL);
+ g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
- return FALSE;
+ return emf->priv->charset;
}
void
-em_format_queue_redraw (EMFormat *emf)
+em_format_set_default_charset (EMFormat *emf,
+ const gchar *charset)
{
g_return_if_fail (EM_IS_FORMAT (emf));
- if (emf->priv->redraw_idle_id == 0)
- emf->priv->redraw_idle_id = g_idle_add (
- (GSourceFunc) format_redraw_idle_cb, emf);
+ if (emf->priv->default_charset)
+ g_free (emf->priv->default_charset);
+
+ emf->priv->default_charset = g_strdup (charset);
+
+ g_object_notify (G_OBJECT (emf), "default-charset");
+}
+
+const gchar *
+em_format_get_default_charset (EMFormat *emf)
+{
+ g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+
+ return emf->priv->default_charset;
}
-/**
- * em_format_set_mode:
- * @emf:
- * @type:
- *
- * Set display mode, EM_FORMAT_MODE_SOURCE, EM_FORMAT_MODE_ALLHEADERS,
- * or EM_FORMAT_MODE_NORMAL.
- **/
void
-em_format_set_mode (EMFormat *emf,
- EMFormatMode mode)
+em_format_set_composer (EMFormat *emf,
+ gboolean composer)
{
g_return_if_fail (EM_IS_FORMAT (emf));
- if (emf->mode == mode)
+ if (emf->priv->composer && composer)
return;
- emf->mode = mode;
+ emf->priv->composer = composer;
- /* force redraw if type changed afterwards */
- if (emf->message != NULL)
- em_format_queue_redraw (emf);
+ g_object_notify (G_OBJECT (emf), "composer");
+}
+
+gboolean
+em_format_get_composer (EMFormat *emf)
+{
+ g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
+
+ return emf->priv->composer;
}
-/**
- * em_format_set_charset:
- * @emf:
- * @charset:
- *
- * set override charset on formatter. message will be redisplayed if
- * required.
- **/
void
-em_format_set_charset (EMFormat *emf,
- const gchar *charset)
+em_format_set_base_url (EMFormat *emf,
+ CamelURL *url)
{
- if ((emf->charset && charset && g_ascii_strcasecmp (emf->charset, charset) == 0)
- || (emf->charset == NULL && charset == NULL)
- || (emf->charset == charset))
- return;
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (url);
- g_free (emf->charset);
- emf->charset = g_strdup (charset);
+ if (emf->priv->base_url)
+ camel_url_free (emf->priv->base_url);
- if (emf->message)
- em_format_queue_redraw (emf);
+ emf->priv->base_url = camel_url_copy (url);
+
+ g_object_notify (G_OBJECT (emf), "base-url");
}
-/**
- * em_format_set_default_charset:
- * @emf:
- * @charset:
- *
- * Set the fallback, default system charset to use when no other charsets
- * are present. Message will be redisplayed if required (and sometimes
- * redisplayed when it isn't).
- **/
void
-em_format_set_default_charset (EMFormat *emf,
- const gchar *charset)
+em_format_set_base_url_string (EMFormat *emf,
+ const gchar *url_string)
{
- if ((emf->default_charset && charset &&
- g_ascii_strcasecmp (emf->default_charset, charset) == 0)
- || (emf->default_charset == NULL && charset == NULL)
- || (emf->default_charset == charset))
- return;
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (url_string && *url_string);
+
+ if (emf->priv->base_url)
+ camel_url_free (emf->priv->base_url);
- g_free (emf->default_charset);
- emf->default_charset = g_strdup (charset);
+ emf->priv->base_url = camel_url_new (url_string, NULL);
- if (emf->message && emf->charset == NULL)
- em_format_queue_redraw (emf);
+ g_object_notify (G_OBJECT (emf), "base-url");
+}
+
+CamelURL *
+em_format_get_base_url (EMFormat *emf)
+{
+ g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+
+ return emf->priv->base_url;
}
/**
@@ -1051,44 +1610,26 @@ em_format_clear_headers (EMFormat *emf)
{
EMFormatHeader *eh;
- while ((eh = g_queue_pop_head (&emf->header_list)) != NULL)
- g_free (eh);
-}
+ g_return_if_fail (EM_IS_FORMAT (emf));
-/* note: also copied in em-mailer-prefs.c */
-static const struct {
- const gchar *name;
- guint32 flags;
-} default_headers[] = {
- { N_("From"), EM_FORMAT_HEADER_BOLD },
- { N_("Reply-To"), EM_FORMAT_HEADER_BOLD },
- { N_("To"), EM_FORMAT_HEADER_BOLD },
- { N_("Cc"), EM_FORMAT_HEADER_BOLD },
- { N_("Bcc"), EM_FORMAT_HEADER_BOLD },
- { N_("Subject"), EM_FORMAT_HEADER_BOLD },
- { N_("Date"), EM_FORMAT_HEADER_BOLD },
- { N_("Newsgroups"), EM_FORMAT_HEADER_BOLD },
- { N_("Face"), 0 },
-};
+ while ((eh = g_queue_pop_head (&emf->header_list)) != NULL) {
+ em_format_header_free (eh);
+ }
+
+}
-/**
- * em_format_default_headers:
- * @emf:
- *
- * Set the headers to show to the default list.
- *
- * From, Reply-To, To, Cc, Bcc, Subject and Date.
- **/
void
em_format_default_headers (EMFormat *emf)
{
gint ii;
- em_format_clear_headers (emf);
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ /* Set the default headers */
+ em_format_clear_headers (emf);
for (ii = 0; ii < G_N_ELEMENTS (default_headers); ii++)
em_format_add_header (
- emf, default_headers[ii].name,
+ emf, default_headers[ii].name, NULL,
default_headers[ii].flags);
}
@@ -1096,6 +1637,7 @@ em_format_default_headers (EMFormat *emf)
* em_format_add_header:
* @emf:
* @name: The name of the header, as it will appear during output.
+ * @value: Value of the header. Can be NULL.
* @flags: EM_FORMAT_HEAD_* defines to control display attributes.
*
* Add a specific header to show. If any headers are set, they will
@@ -1106,248 +1648,465 @@ em_format_default_headers (EMFormat *emf)
void
em_format_add_header (EMFormat *emf,
const gchar *name,
+ const gchar *value,
guint32 flags)
{
EMFormatHeader *h;
- h = g_malloc (sizeof (*h) + strlen (name));
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (name && *name);
+
+ h = em_format_header_new (name, value);
h->flags = flags;
- strcpy (h->name, name);
g_queue_push_tail (&emf->header_list, h);
}
-/**
- * em_format_is_attachment:
- * @emf:
- * @part: Part to check.
- *
- * Returns true if the part is an attachment.
- *
- * A part is not considered an attachment if it is a
- * multipart, or a text part with no filename. It is used
- * to determine if an attachment header should be displayed for
- * the part.
- *
- * Content-Disposition is not checked.
- *
- * Return value: TRUE/FALSE
- **/
-gint
-em_format_is_attachment (EMFormat *emf,
- CamelMimePart *part)
+void
+em_format_add_header_struct (EMFormat *emf,
+ EMFormatHeader *header)
{
- /*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (header && header->name);
- if (!dw)
- return 0;
+ em_format_add_header (emf, header->name, header->value, header->flags);
+}
- /*printf("checking is attachment %s/%s\n", ct->type, ct->subtype);*/
- return !(camel_content_type_is (dw->mime_type, "multipart", "*")
- || camel_content_type_is (
- dw->mime_type, "application", "x-pkcs7-mime")
- || camel_content_type_is (
- dw->mime_type, "application", "pkcs7-mime")
- || camel_content_type_is (
- dw->mime_type, "application", "x-inlinepgp-signed")
- || camel_content_type_is (
- dw->mime_type, "application", "x-inlinepgp-encrypted")
- || camel_content_type_is (
- dw->mime_type, "x-evolution", "evolution-rss-feed")
- || camel_content_type_is (dw->mime_type, "text", "calendar")
- || camel_content_type_is (dw->mime_type, "text", "x-calendar")
- || (camel_content_type_is (dw->mime_type, "text", "*")
- && camel_mime_part_get_filename (part) == NULL));
+void
+em_format_remove_header (EMFormat * emf,
+ const gchar *name,
+ const gchar *value)
+{
+ GList *iter = NULL;
+
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (name && *name);
+
+ iter = g_queue_peek_head_link (&emf->header_list);
+ while (iter) {
+ EMFormatHeader *header = iter->data;
+
+ if (!header->value || !*header->value) {
+ GList *next = iter->next;
+ if (g_strcmp0 (name, header->name) == 0)
+ g_queue_delete_link (&emf->header_list, iter);
+
+ iter = next;
+ continue;
+ }
+
+ if (value && *value) {
+ if ((g_strcmp0 (name, header->name) == 0) &&
+ (g_strcmp0 (value, header->value) == 0))
+ break;
+ } else {
+ if (g_strcmp0 (name, header->name) == 0)
+ break;
+ }
+
+ iter = iter->next;
+ }
+
+ if (iter) {
+ em_format_header_free (iter->data);
+ g_queue_delete_link (&emf->header_list, iter);
+ }
+}
+
+void
+em_format_remove_header_struct (EMFormat * emf,
+ const EMFormatHeader * header)
+{
+ g_return_if_fail (header);
+
+ em_format_remove_header (emf, header->name, header->value);
+}
+
+void
+em_format_add_puri (EMFormat *emf,
+ EMFormatPURI *puri)
+{
+ GList *item;
+
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (puri != NULL);
+
+ emf->mail_part_list = g_list_append (emf->mail_part_list, puri);
+ item = g_list_last (emf->mail_part_list);
+
+ g_hash_table_insert (emf->mail_part_table,
+ puri->uri, item);
+
+ d(printf("Added PURI %s\n", puri->uri));
+}
+
+EMFormatPURI *
+em_format_find_puri (EMFormat *emf,
+ const gchar *id)
+{
+ GList *list_iter;
+
+ /* First handle CIDs... */
+ if (g_str_has_prefix (id, "CID:") || g_str_has_prefix (id, "cid:")) {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, emf->mail_part_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ EMFormatPURI *puri = ((GList *) value)->data;
+ if (g_strcmp0 (puri->cid, id) == 0)
+ return puri;
+ }
+
+ return NULL;
+ }
+
+ list_iter = g_hash_table_lookup (emf->mail_part_table, id);
+ if (list_iter)
+ return list_iter->data;
+
+ return NULL;
+}
+
+void
+em_format_class_add_handler (EMFormatClass *emfc,
+ EMFormatHandler *handler)
+{
+ EMFormatHandler *old_handler;
+
+ g_return_if_fail (EM_IS_FORMAT_CLASS (emfc));
+ g_return_if_fail (handler);
+
+ old_handler = g_hash_table_lookup (
+ emfc->type_handlers, handler->mime_type);
+
+ handler->old = old_handler;
+
+ /* If parse_func or write_func of the new handler is not set,
+ * use function from the old handler (if it exists).
+ * This way we can assign a new write_func for to an existing
+ * parse_func */
+ if (old_handler && handler->parse_func == NULL) {
+ handler->parse_func = old_handler->parse_func;
+ }
+
+ if (old_handler && handler->write_func == NULL) {
+ handler->write_func = old_handler->write_func;
+ }
+
+ g_hash_table_insert (emfc->type_handlers,
+ handler->mime_type, handler);
+}
+
+void
+em_format_class_remove_handler (EMFormatClass *emfc,
+ EMFormatHandler *handler)
+{
+ g_return_if_fail (EM_IS_FORMAT_CLASS (emfc));
+ g_return_if_fail (handler);
+
+ g_hash_table_remove (emfc->type_handlers, handler->mime_type);
+}
+
+const EMFormatHandler *
+em_format_find_handler (EMFormat *emf,
+ const gchar *mime_type)
+{
+ EMFormatClass *emfc;
+ gchar *s;
+ const EMFormatHandler *handler;
+
+ g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+ g_return_val_if_fail (mime_type && *mime_type, NULL);
+
+ emfc = (EMFormatClass *) G_OBJECT_GET_CLASS (emf);
+
+ s = g_ascii_strdown (mime_type, -1);
+
+ handler = g_hash_table_lookup (
+ emfc->type_handlers, s);
+
+ g_free (s);
+
+ return handler;
}
/**
- * em_format_is_inline:
+ * em_format_fallback_handler:
* @emf:
- * @part:
- * @part_id: format->part_id part id of this part.
- * @handle: handler for this part
+ * @mime_type:
*
- * Returns true if the part should be displayed inline. Any part with
- * a Content-Disposition of inline, or if the @handle has a default
- * inline set, will be shown inline.
+ * Try to find a format handler based on the major type of the @mime_type.
*
- * :set_inline() called on the same part will override any calculated
- * value.
+ * The subtype is replaced with "*" and a lookup performed.
*
* Return value:
**/
-gboolean
-em_format_is_inline (EMFormat *emf,
- const gchar *part_id,
- CamelMimePart *mime_part,
- const EMFormatHandler *handle)
+const EMFormatHandler *
+em_format_fallback_handler (EMFormat *emf,
+ const gchar *mime_type)
{
- EMFormatClass *class;
+ gchar *mime, *s;
- g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
- g_return_val_if_fail (part_id != NULL, FALSE);
- g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), FALSE);
+ s = strchr (mime_type, '/');
+ if (s == NULL)
+ mime = (gchar *) mime_type;
+ else {
+ gsize len = (s - mime_type) + 1;
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_val_if_fail (class->is_inline != NULL, FALSE);
+ mime = g_alloca (len + 2);
+ strncpy (mime, mime_type, len);
+ strcpy(mime+len, "*");
+ }
- return class->is_inline (emf, part_id, mime_part, handle);
+ return em_format_find_handler (emf, mime);
}
-/**
- * em_format_set_inline:
- * @emf:
- * @part_id: id of part
- * @state:
- *
- * Force the attachment @part to be expanded or hidden explictly to match
- * @state. This is used only to record the change for a redraw or
- * cloned layout render and does not force a redraw.
- **/
void
-em_format_set_inline (EMFormat *emf,
- const gchar *part_id,
- gint state)
+em_format_parse (EMFormat *emf,
+ CamelMimeMessage *message,
+ CamelFolder *folder,
+ GCancellable *cancellable)
{
- EMFormatCache *emfc;
+ GString *part_id;
+ EMFormatPURI *puri;
+ EMFormatParserInfo info = { 0 };
g_return_if_fail (EM_IS_FORMAT (emf));
- g_return_if_fail (part_id != NULL);
- emfc = g_hash_table_lookup (emf->inline_table, part_id);
- if (emfc == NULL) {
- emfc = emf_insert_cache (emf, part_id);
- } else if (emfc->state != INLINE_UNSET && (emfc->state & 1) == state)
+ if (g_cancellable_is_cancelled (cancellable))
return;
- emfc->state = state ? INLINE_ON : INLINE_OFF;
+ if (message) {
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ if (emf->message)
+ g_object_unref (emf->message);
+ emf->message = g_object_ref (message);
+ }
+
+ if (folder) {
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
- if (emf->message)
- em_format_queue_redraw (emf);
+ if (emf->folder)
+ g_object_unref (emf->folder);
+ emf->folder = g_object_ref (folder);
+ }
+
+ /* Before the actual parsing starts, let child classes prepare themselves. */
+ if (EM_FORMAT_GET_CLASS (emf)->preparse)
+ EM_FORMAT_GET_CLASS (emf)->preparse (emf);
+
+ part_id = g_string_new (".message");
+
+ /* Create a special PURI with entire message */
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI),
+ (CamelMimePart *) emf->message, part_id->str);
+ puri->mime_type = g_strdup ("text/html");
+ em_format_add_puri (emf, puri);
+
+ info.force_handler = TRUE;
+ em_format_parse_part_as (emf, CAMEL_MIME_PART (emf->message), part_id, &info,
+ "x-evolution/message", cancellable);
+
+ g_string_free (part_id, TRUE);
}
void
-em_format_format_attachment (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- const gchar *mime_type,
- const EMFormatHandler *info,
- GCancellable *cancellable)
+em_format_write (EMFormat *emf,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- EMFormatClass *class;
+ EMFormatClass *emf_class;
g_return_if_fail (EM_IS_FORMAT (emf));
g_return_if_fail (CAMEL_IS_STREAM (stream));
- g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
- g_return_if_fail (mime_type != NULL);
- g_return_if_fail (info != NULL);
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_if_fail (class->format_attachment != NULL);
+ emf_class = EM_FORMAT_GET_CLASS (emf);
+ if (emf_class->write)
+ emf_class->write (emf, stream, info, cancellable);
+}
- class->format_attachment (
- emf, stream, mime_part, mime_type, info, cancellable);
+static void
+emf_start_async_parser (GSimpleAsyncResult *result,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ em_format_parse (EM_FORMAT (object), NULL, NULL, cancellable);
}
void
-em_format_format_error (EMFormat *emf,
- CamelStream *stream,
- const gchar *format,
- ...)
+em_format_parse_async (EMFormat *emf,
+ CamelMimeMessage *message,
+ CamelFolder *folder,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- EMFormatClass *class;
- gchar *errmsg;
- va_list ap;
+ GSimpleAsyncResult *result;
g_return_if_fail (EM_IS_FORMAT (emf));
- g_return_if_fail (CAMEL_IS_STREAM (stream));
- g_return_if_fail (format != NULL);
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_if_fail (class->format_error != NULL);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- va_start (ap, format);
- errmsg = g_strdup_vprintf (format, ap);
- class->format_error (emf, stream, errmsg);
- g_free (errmsg);
- va_end (ap);
+ if (message) {
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ if (emf->message)
+ g_object_unref (emf->message);
+
+ emf->message = g_object_ref (message);
+
+ }
+
+ if (folder) {
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+ if (emf->folder)
+ g_object_unref (emf->folder);
+
+ emf->folder = g_object_ref (folder);
+
+ }
+
+ result = g_simple_async_result_new (G_OBJECT (emf), callback,
+ user_data, em_format_parse_async);
+ g_simple_async_result_run_in_thread (result, emf_start_async_parser,
+ G_PRIORITY_DEFAULT, cancellable);
}
void
-em_format_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- CamelCipherValidity *valid,
+em_format_parse_part_as (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ const gchar *mime_type,
GCancellable *cancellable)
{
- EMFormatClass *class;
-
- g_return_if_fail (EM_IS_FORMAT (emf));
- g_return_if_fail (CAMEL_IS_STREAM (stream));
- g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
- g_return_if_fail (valid != NULL);
+ const EMFormatHandler *handler;
+ const CamelContentDisposition *disposition;
+ EMFormatParserInfo ninfo = {
+ .handler = 0,
+ .validity_type = info ? info->validity_type : 0,
+ .validity = info ? info->validity : 0,
+ .force_handler = 0
+ };
+
+ /* Let everything that claims to be an attachment or inlined part to be parsed
+ * as an attachment. The parser will decide how to display it. */
+ disposition = camel_mime_part_get_content_disposition (part);
+ if (!info->force_handler && disposition &&
+ (g_strcmp0 (disposition->disposition, "attachment") == 0)) {
+ ninfo.is_attachment = TRUE;
+ handler = em_format_find_handler (emf, "x-evolution/message/attachment");
+ ninfo.handler = handler;
+
+ if (handler && handler->parse_func)
+ handler->parse_func (emf, part, part_id, &ninfo, cancellable);
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_if_fail (class->format_secure != NULL);
+ return;
+ }
- class->format_secure (emf, stream, mime_part, valid, cancellable);
+ handler = em_format_find_handler (emf, mime_type);
+ if (handler && handler->parse_func) {
+ ninfo.handler = handler;
+ handler->parse_func (emf, part, part_id, &ninfo, cancellable);
+ } else {
+ handler = em_format_find_handler (emf, "x-evolution/message/attachment");
+ ninfo.handler = handler;
- if (emf->valid_parent == NULL && emf->valid != NULL) {
- camel_cipher_validity_free (emf->valid);
- emf->valid = NULL;
+ /* When this fails, something is probably very wrong...*/
+ if (handler && handler->parse_func)
+ handler->parse_func (emf, part, part_id, &ninfo, cancellable);
}
}
void
-em_format_format_source (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- GCancellable *cancellable)
+em_format_parse_part (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatClass *class;
+ CamelContentType *ct;
+ gchar *mime_type;
- g_return_if_fail (EM_IS_FORMAT (emf));
- g_return_if_fail (CAMEL_IS_STREAM (stream));
- g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
+ ct = camel_mime_part_get_content_type (part);
+ if (ct) {
+ mime_type = camel_content_type_simple (ct);
+ } else {
+ mime_type = (gchar *) "text/plain";
+ }
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_if_fail (class->format_source != NULL);
+ em_format_parse_part_as (emf, part, part_id, info, mime_type, cancellable);
- class->format_source (emf, stream, mime_part, cancellable);
+ if (ct)
+ g_free (mime_type);
}
gboolean
-em_format_busy (EMFormat *emf)
+em_format_is_inline (EMFormat *emf,
+ const gchar *part_id,
+ CamelMimePart *part,
+ const EMFormatHandler *handler)
{
- EMFormatClass *class;
+ EMFormatClass *klass;
g_return_val_if_fail (EM_IS_FORMAT (emf), FALSE);
+ g_return_val_if_fail (part_id && *part_id, FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_PART (part), FALSE);
+ g_return_val_if_fail (handler, FALSE);
- class = EM_FORMAT_GET_CLASS (emf);
- g_return_val_if_fail (class->busy != NULL, FALSE);
+ klass = EM_FORMAT_GET_CLASS (emf);
+ g_return_val_if_fail (klass->is_inline != NULL, FALSE);
+
+ return klass->is_inline (emf, part_id, part, handler);
- return class->busy (emf);
}
-/* should this be virtual? */
void
-em_format_format_content (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- GCancellable *cancellable)
+em_format_format_error (EMFormat *emf,
+ const gchar *format,
+ ...)
{
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
+ EMFormatPURI *puri;
+ CamelMimePart *part;
+ const EMFormatHandler *handler;
+ gchar *errmsg;
+ gchar *uri;
+ va_list ap;
+
+ g_return_if_fail (EM_IS_FORMAT (emf));
+ g_return_if_fail (format != NULL);
+
+ va_start (ap, format);
+ errmsg = g_strdup_vprintf (format, ap);
- if (camel_content_type_is (dw->mime_type, "text", "*"))
- em_format_format_text (
- emf, stream, (CamelDataWrapper *) part, cancellable);
+ part = camel_mime_part_new ();
+ camel_mime_part_set_content (part, errmsg, strlen (errmsg), "text/plain");
+ g_free (errmsg);
+ va_end (ap);
+
+ handler = em_format_find_handler (emf, "x-evolution/error");
+
+ emf->priv->last_error++;
+ uri = g_strdup_printf (".error.%d", emf->priv->last_error);
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, uri);
+ puri->mime_type = g_strdup ("text/html");
+ if (handler && handler->write_func)
+ puri->write_func = handler->write_func;
else
- camel_data_wrapper_decode_to_stream_sync (
- dw, stream, cancellable, NULL);
+ puri->write_func = emf_write_error;
+
+ em_format_add_puri (emf, puri);
+
+ g_free (uri);
+ g_object_unref (part);
}
/**
- * em_format_format_content:
+ * em_format_format_text:
* @emf:
* @stream: Where to write the converted text
* @part: Part whose container is to be formatted
@@ -1370,8 +2129,11 @@ em_format_format_text (EMFormat *emf,
gsize max;
GSettings *settings;
- if (emf->charset) {
- charset = emf->charset;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ if (emf->priv->charset) {
+ charset = emf->priv->charset;
} else if (dw->mime_type
&& (charset = camel_content_type_param (dw->mime_type, "charset"))
&& g_ascii_strncasecmp(charset, "iso-8859-", 9) == 0) {
@@ -1398,7 +2160,7 @@ em_format_format_text (EMFormat *emf,
charset = camel_mime_filter_windows_real_charset (windows);
} else if (charset == NULL) {
- charset = emf->default_charset;
+ charset = emf->priv->default_charset;
}
mem_stream = (CamelStream *) camel_stream_mem_new ();
@@ -1422,8 +2184,6 @@ em_format_format_text (EMFormat *emf,
g_object_unref (settings);
size = camel_data_wrapper_decode_to_stream_sync (
- emf->mode == EM_FORMAT_MODE_SOURCE ?
- (CamelDataWrapper *) dw :
camel_medium_get_content ((CamelMedium *) dw),
(CamelStream *) filter_stream, cancellable, NULL);
camel_stream_flush ((CamelStream *) filter_stream, cancellable, NULL);
@@ -1431,14 +2191,23 @@ em_format_format_text (EMFormat *emf,
g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
- if (max == -1 || size == -1 || size < (max * 1024) || emf->composer) {
+ if (max == -1 || size == -1 || size < (max * 1024) || emf->priv->composer) {
camel_stream_write_to_stream (
mem_stream, (CamelStream *) stream, cancellable, NULL);
- camel_stream_flush ((CamelStream *) stream, cancellable, NULL);
+ camel_stream_flush ((CamelStream *) mem_stream, cancellable, NULL);
} else {
- EM_FORMAT_GET_CLASS (emf)->format_optional (
- emf, stream, (CamelMimePart *) dw,
- mem_stream, cancellable);
+ /* Parse it as an attachment */
+ CamelMimePart *part = camel_mime_part_new ();
+ EMFormatParserInfo info = { 0 };
+ GString *part_id = g_string_new (".attachment");
+ camel_medium_set_content ((CamelMedium *) part, dw);
+
+ info.is_attachment = TRUE;
+ em_format_parse_part_as (emf, part, part_id, &info,
+ "x-evolution/message/attachment", cancellable);
+
+ g_string_free (part_id, TRUE);
+ g_object_unref (part);
}
if (windows)
@@ -1452,7 +2221,7 @@ em_format_format_text (EMFormat *emf,
* @part:
* @mimetype:
*
- * Generate a simple textual description of a part, @mime_type represents the
+ * Generate a simple textual description of a part, @mime_type represents
* the content.
*
* Return value:
@@ -1500,909 +2269,48 @@ em_format_describe_part (CamelMimePart *part,
return g_string_free (stext, FALSE);
}
-static void
-add_validity_found (EMFormat *emf,
- CamelCipherValidity *valid)
-{
- g_return_if_fail (emf != NULL);
-
- if (!valid)
- return;
-
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
- emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SIGNED;
-
- if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE)
- emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_ENCRYPTED;
-}
-
-/* ********************************************************************** */
-
-static void
-preserve_charset_in_content_type (CamelMimePart *ipart,
- CamelMimePart *opart)
-{
- CamelDataWrapper *data_wrapper;
- CamelContentType *content_type;
- const gchar *charset;
-
- g_return_if_fail (ipart != NULL);
- g_return_if_fail (opart != NULL);
-
- data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (ipart));
- content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
-
- if (content_type == NULL)
- return;
-
- charset = camel_content_type_param (content_type, "charset");
-
- if (charset == NULL || *charset == '\0')
- return;
-
- data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (opart));
- content_type = camel_data_wrapper_get_mime_type_field (data_wrapper);
-
- camel_content_type_set_param (content_type, "charset", charset);
-}
-
-#ifdef ENABLE_SMIME
-static void
-emf_application_xpkcs7mime (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelCipherContext *context;
- CamelMimePart *opart;
- CamelCipherValidity *valid;
- EMFormatCache *emfc;
- GError *local_error = NULL;
-
- /* should this perhaps run off a key of ".secured" ? */
- emfc = g_hash_table_lookup (emf->inline_table, emf->part_id->str);
- if (emfc && emfc->valid) {
- em_format_format_secure (
- emf, stream, emfc->secured,
- camel_cipher_validity_clone (emfc->valid),
- cancellable);
- return;
- }
-
- context = camel_smime_context_new (emf->session);
-
- emf->validity_found |=
- EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
- EM_FORMAT_VALIDITY_FOUND_SMIME;
-
- opart = camel_mime_part_new ();
- valid = camel_cipher_context_decrypt_sync (
- context, part, opart, cancellable, &local_error);
- preserve_charset_in_content_type (part, opart);
- if (valid == NULL) {
- em_format_format_error (
- emf, stream, "%s",
- local_error->message ? local_error->message :
- _("Could not parse S/MIME message: Unknown error"));
- g_clear_error (&local_error);
-
- em_format_part_as (emf, stream, part, NULL, cancellable);
- } else {
- if (emfc == NULL)
- emfc = emf_insert_cache (emf, emf->part_id->str);
-
- emfc->valid = camel_cipher_validity_clone (valid);
- g_object_ref ((emfc->secured = opart));
-
- add_validity_found (emf, valid);
- em_format_format_secure (
- emf, stream, opart, valid, cancellable);
- }
-
- g_object_unref (opart);
- g_object_unref (context);
-}
-#endif
-
-/* RFC 1740 */
-static void
-emf_multipart_appledouble (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMultipart *mp;
- CamelMimePart *mime_part;
- gint len;
-
- mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
- if (!CAMEL_IS_MULTIPART (mp)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- mime_part = camel_multipart_get_part (mp, 1);
- if (mime_part) {
- /* try the data fork for something useful, doubtful but who knows */
- len = emf->part_id->len;
- g_string_append_printf(emf->part_id, ".appledouble.1");
- em_format_part (emf, stream, mime_part, cancellable);
- g_string_truncate (emf->part_id, len);
- } else
- em_format_format_source (emf, stream, part, cancellable);
-
-}
-
-/* RFC ??? */
-static void
-emf_multipart_mixed (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMultipart *mp;
- gint i, nparts, len;
-
- mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
- if (!CAMEL_IS_MULTIPART (mp)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- len = emf->part_id->len;
- nparts = camel_multipart_get_number (mp);
- for (i = 0; i < nparts; i++) {
- part = camel_multipart_get_part (mp, i);
- g_string_append_printf(emf->part_id, ".mixed.%d", i);
- em_format_part (emf, stream, part, cancellable);
- g_string_truncate (emf->part_id, len);
- }
-}
-
-static gboolean related_display_part_is_attachment
- (EMFormat *emf,
- CamelMimePart *part);
-
-/* RFC 1740 */
-static void
-emf_multipart_alternative (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMultipart *mp;
- gint i, nparts, bestid = 0;
- CamelMimePart *best = NULL;
-
- mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
- if (!CAMEL_IS_MULTIPART (mp)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- /* as per rfc, find the last part we know how to display */
- nparts = camel_multipart_get_number (mp);
- for (i = 0; i < nparts; i++) {
- CamelDataWrapper *data_wrapper;
- CamelContentType *type;
- CamelStream *null_stream;
- gchar *mime_type;
- gsize content_size;
-
- /* is it correct to use the passed in *part here? */
- part = camel_multipart_get_part (mp, i);
-
- if (part == NULL)
- continue;
-
- /* This may block even though the stream does not.
- * XXX Pretty inefficient way to test if the MIME part
- * is empty. Surely there's a quicker way? */
- null_stream = camel_stream_null_new ();
- data_wrapper = camel_medium_get_content (CAMEL_MEDIUM (part));
- camel_data_wrapper_decode_to_stream_sync (
- data_wrapper, null_stream, cancellable, NULL);
- content_size = CAMEL_STREAM_NULL (null_stream)->written;
- g_object_unref (null_stream);
-
- if (content_size == 0)
- continue;
-
- type = camel_mime_part_get_content_type (part);
- mime_type = camel_content_type_simple (type);
-
- camel_strdown (mime_type);
-
- /*if (want_plain && !strcmp (mime_type, "text/plain"))
- return part;*/
-
- if (!em_format_is_attachment (emf, part) &&
- (!camel_content_type_is (type, "multipart", "related") ||
- !related_display_part_is_attachment (emf, part)) &&
- (em_format_find_handler (emf, mime_type)
- || (best == NULL && em_format_fallback_handler (emf, mime_type)))) {
- best = part;
- bestid = i;
- }
-
- g_free (mime_type);
- }
-
- if (best) {
- gint len = emf->part_id->len;
-
- g_string_append_printf(emf->part_id, ".alternative.%d", bestid);
- em_format_part (emf, stream, best, cancellable);
- g_string_truncate (emf->part_id, len);
- } else
- emf_multipart_mixed (
- emf, stream, part, info, cancellable, is_fallback);
-}
-
-static void
-emf_multipart_encrypted (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelCipherContext *context;
- const gchar *protocol;
- CamelMimePart *opart;
- CamelCipherValidity *valid;
- CamelMultipartEncrypted *mpe;
- EMFormatCache *emfc;
- GError *local_error = NULL;
-
- /* should this perhaps run off a key of ".secured" ? */
- emfc = g_hash_table_lookup (emf->inline_table, emf->part_id->str);
- if (emfc && emfc->valid) {
- em_format_format_secure (
- emf, stream, emfc->secured,
- camel_cipher_validity_clone (emfc->valid),
- cancellable);
- return;
- }
-
- mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part);
- if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) {
- em_format_format_error (
- emf, stream, _("Could not parse MIME message. "
- "Displaying as source."));
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- /* Currently we only handle RFC2015-style PGP encryption. */
- protocol = camel_content_type_param (
- ((CamelDataWrapper *)mpe)->mime_type, "protocol");
- if (protocol == NULL || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) {
- em_format_format_error (
- emf, stream, _("Unsupported encryption "
- "type for multipart/encrypted"));
- em_format_part_as (
- emf, stream, part,
- "multipart/mixed", cancellable);
- return;
- }
-
- emf->validity_found |=
- EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
- EM_FORMAT_VALIDITY_FOUND_PGP;
-
- context = camel_gpg_context_new (emf->session);
- opart = camel_mime_part_new ();
- valid = camel_cipher_context_decrypt_sync (
- context, part, opart, cancellable, &local_error);
- preserve_charset_in_content_type (part, opart);
- if (valid == NULL) {
- em_format_format_error (
- emf, stream, local_error->message ?
- _("Could not parse PGP/MIME message") :
- _("Could not parse PGP/MIME message: Unknown error"));
- if (local_error->message != NULL)
- em_format_format_error (
- emf, stream, "%s", local_error->message);
- g_clear_error (&local_error);
-
- em_format_part_as (
- emf, stream, part,
- "multipart/mixed", cancellable);
- } else {
- if (emfc == NULL)
- emfc = emf_insert_cache (emf, emf->part_id->str);
-
- emfc->valid = camel_cipher_validity_clone (valid);
- g_object_ref ((emfc->secured = opart));
-
- add_validity_found (emf, valid);
- em_format_format_secure (
- emf, stream, opart, valid, cancellable);
- }
-
- /* TODO: Make sure when we finalize this part, it is zero'd out */
- g_object_unref (opart);
- g_object_unref (context);
-}
-
-static CamelMimePart *
-get_related_display_part (CamelMimePart *part,
- gint *out_displayid)
-{
- CamelMultipart *mp;
- CamelMimePart *body_part, *display_part = NULL;
- CamelContentType *content_type;
- const gchar *start;
- gint i, nparts, displayid = 0;
-
- mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
- if (!CAMEL_IS_MULTIPART (mp))
- return NULL;
-
- nparts = camel_multipart_get_number (mp);
- content_type = camel_mime_part_get_content_type (part);
- start = camel_content_type_param (content_type, "start");
- if (start && strlen (start) > 2) {
- gint len;
- const gchar *cid;
-
- /* strip <>'s */
- len = strlen (start) - 2;
- start++;
-
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- cid = camel_mime_part_get_content_id (body_part);
-
- if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
- display_part = body_part;
- displayid = i;
- break;
- }
- }
- } else {
- display_part = camel_multipart_get_part (mp, 0);
- }
-
- if (out_displayid)
- *out_displayid = displayid;
-
- return display_part;
-}
-
-static gboolean
-related_display_part_is_attachment (EMFormat *emf,
- CamelMimePart *part)
-{
- CamelMimePart *display_part;
-
- display_part = get_related_display_part (part, NULL);
- return display_part && em_format_is_attachment (emf, display_part);
-}
-
-static void
-emf_write_related (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- em_format_format_content (emf, stream, puri->part, cancellable);
- camel_stream_close (stream, cancellable, NULL);
-}
-
-/* RFC 2387 */
-static void
-emf_multipart_related (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMultipart *mp;
- CamelMimePart *body_part, *display_part = NULL;
- gint i, nparts, partidlen, displayid = 0;
- gchar *oldpartid;
- GList *link;
-
- mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
-
- if (!CAMEL_IS_MULTIPART (mp)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- display_part = get_related_display_part (part, &displayid);
-
- if (display_part == NULL) {
- emf_multipart_mixed (
- emf, stream, part, info, cancellable, is_fallback);
- return;
- }
-
- em_format_push_level (emf);
-
- oldpartid = g_strdup (emf->part_id->str);
- partidlen = emf->part_id->len;
-
- /* queue up the parts for possible inclusion */
- nparts = camel_multipart_get_number (mp);
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- if (body_part != display_part) {
- /* set the partid since add_puri uses it */
- g_string_append_printf(emf->part_id, ".related.%d", i);
- em_format_add_puri (
- emf, sizeof (EMFormatPURI), NULL,
- body_part, emf_write_related);
- g_string_truncate (emf->part_id, partidlen);
- }
- }
-
- g_string_append_printf(emf->part_id, ".related.%d", displayid);
- em_format_part (emf, stream, display_part, cancellable);
- g_string_truncate (emf->part_id, partidlen);
- camel_stream_flush (stream, NULL, NULL);
-
- link = g_queue_peek_head_link (emf->pending_uri_level->data);
-
- while (link && link->next != NULL) {
- EMFormatPURI *puri = link->data;
-
- if (puri->use_count == 0) {
- if (puri->func == emf_write_related) {
- g_string_printf(emf->part_id, "%s", puri->part_id);
- em_format_part (
- emf, stream, puri->part, cancellable);
- }
- }
-
- link = g_list_next (link);
- }
-
- g_string_printf(emf->part_id, "%s", oldpartid);
- g_free (oldpartid);
-
- em_format_pull_level (emf);
-}
-
-static void
-emf_multipart_signed (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMimePart *cpart;
- CamelMultipartSigned *mps;
- CamelCipherContext *cipher = NULL;
- EMFormatCache *emfc;
-
- /* should this perhaps run off a key of ".secured" ? */
- emfc = g_hash_table_lookup (emf->inline_table, emf->part_id->str);
- if (emfc && emfc->valid) {
- em_format_format_secure (
- emf, stream, emfc->secured,
- camel_cipher_validity_clone (emfc->valid),
- cancellable);
- return;
- }
-
- mps = (CamelMultipartSigned *) camel_medium_get_content ((CamelMedium *) part);
- if (!CAMEL_IS_MULTIPART_SIGNED (mps)
- || (cpart = camel_multipart_get_part ((CamelMultipart *) mps,
- CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
- em_format_format_error (
- emf, stream, _("Could not parse MIME message. "
- "Displaying as source."));
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- /* FIXME: Should be done via a plugin interface */
- /* FIXME: duplicated in em-format-html-display.c */
- if (mps->protocol) {
-#ifdef ENABLE_SMIME
- if (g_ascii_strcasecmp ("application/x-pkcs7-signature", mps->protocol) == 0
- || g_ascii_strcasecmp ("application/pkcs7-signature", mps->protocol) == 0) {
- cipher = camel_smime_context_new (emf->session);
- emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SMIME;
- } else
-#endif
- if (g_ascii_strcasecmp ("application/pgp-signature", mps->protocol) == 0) {
- cipher = camel_gpg_context_new (emf->session);
- emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_PGP;
- }
- }
-
- emf->validity_found |= EM_FORMAT_VALIDITY_FOUND_SIGNED;
-
- if (cipher == NULL) {
- em_format_format_error(emf, stream, _("Unsupported signature format"));
- em_format_part_as (
- emf, stream, part,
- "multipart/mixed", cancellable);
- } else {
- CamelCipherValidity *valid;
- GError *local_error = NULL;
-
- valid = camel_cipher_context_verify_sync (
- cipher, part, cancellable, &local_error);
- if (valid == NULL) {
- em_format_format_error (
- emf, stream, local_error->message ?
- _("Error verifying signature") :
- _("Unknown error verifying signature"));
- if (local_error->message != NULL)
- em_format_format_error (
- emf, stream, "%s",
- local_error->message);
- g_clear_error (&local_error);
-
- em_format_part_as (
- emf, stream, part,
- "multipart/mixed", cancellable);
- } else {
- if (emfc == NULL)
- emfc = emf_insert_cache (emf, emf->part_id->str);
-
- emfc->valid = camel_cipher_validity_clone (valid);
- g_object_ref ((emfc->secured = cpart));
-
- add_validity_found (emf, valid);
- em_format_format_secure (
- emf, stream, cpart, valid, cancellable);
- }
-
- g_object_unref (cipher);
- }
-}
-
-/* RFC 4155 */
-static void
-emf_application_mbox (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- const EMFormatHandler *handle;
- CamelMimeParser *parser;
- CamelStream *mem_stream;
- camel_mime_parser_state_t state;
-
- /* Extract messages from the application/mbox part and
- * render them as a flat list of messages. */
-
- /* XXX If the mbox has multiple messages, maybe render them
- * as a multipart/digest so each message can be expanded
- * or collapsed individually.
- *
- * See attachment_handler_mail_x_uid_list() for example. */
-
- /* XXX This is based on em_utils_read_messages_from_stream().
- * Perhaps refactor that function to return an array of
- * messages instead of assuming we want to append them
- * to a folder? */
-
- handle = em_format_find_handler (emf, "x-evolution/message/rfc822");
- g_return_if_fail (handle != NULL);
-
- parser = camel_mime_parser_new ();
- camel_mime_parser_scan_from (parser, TRUE);
-
- mem_stream = camel_stream_mem_new ();
- camel_data_wrapper_decode_to_stream_sync (
- camel_medium_get_content (CAMEL_MEDIUM (mime_part)),
- mem_stream, NULL, NULL);
- g_seekable_seek (G_SEEKABLE (mem_stream), 0, G_SEEK_SET, NULL, NULL);
- camel_mime_parser_init_with_stream (parser, mem_stream, NULL);
- g_object_unref (mem_stream);
-
- /* Extract messages from the mbox. */
- state = camel_mime_parser_step (parser, NULL, NULL);
- while (state == CAMEL_MIME_PARSER_STATE_FROM) {
- CamelMimeMessage *message;
-
- message = camel_mime_message_new ();
- mime_part = CAMEL_MIME_PART (message);
-
- if (!camel_mime_part_construct_from_parser_sync (
- mime_part, parser, NULL, NULL)) {
- g_object_unref (message);
- break;
- }
-
- /* Render the message. */
- handle->handler (
- emf, stream, mime_part,
- handle, cancellable, FALSE);
-
- g_object_unref (message);
-
- /* Skip past CAMEL_MIME_PARSER_STATE_FROM_END. */
- camel_mime_parser_step (parser, NULL, NULL);
-
- state = camel_mime_parser_step (parser, NULL, NULL);
- }
-
- g_object_unref (parser);
-}
-
-static void
-emf_message_rfc822 (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+/**
+ * em_format_is_attachment:
+ * @emf:
+ * @part: Part to check.
+ *
+ * Returns true if the part is an attachment.
+ *
+ * A part is not considered an attachment if it is a
+ * multipart, or a text part with no filename. It is used
+ * to determine if an attachment header should be displayed for
+ * the part.
+ *
+ * Content-Disposition is not checked.
+ *
+ * Return value: TRUE/FALSE
+ **/
+gint
+em_format_is_attachment (EMFormat *emf,
+ CamelMimePart *part)
{
+ /*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) part);
- const EMFormatHandler *handle;
- gint len;
- gchar *parent_message_part_id;
-
- if (!CAMEL_IS_MIME_MESSAGE (dw)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- parent_message_part_id = emf->current_message_part_id;
- emf->current_message_part_id = g_strdup (emf->part_id->str);
-
- len = emf->part_id->len;
- g_string_append_printf(emf->part_id, ".rfc822");
-
- handle = em_format_find_handler(emf, "x-evolution/message/rfc822");
- if (handle)
- handle->handler (
- emf, stream, CAMEL_MIME_PART (dw),
- handle, cancellable, FALSE);
- g_string_truncate (emf->part_id, len);
-
- g_free (emf->current_message_part_id);
- emf->current_message_part_id = parent_message_part_id;
-}
-
-static void
-emf_message_deliverystatus (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- em_format_format_text (
- emf, stream, (CamelDataWrapper *) part, cancellable);
-}
-
-static void
-emf_inlinepgp_signed (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *ipart,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelStream *filtered_stream;
- CamelMimeFilterPgp *pgp_filter;
- CamelContentType *content_type;
- CamelCipherContext *cipher;
- CamelCipherValidity *valid;
- CamelDataWrapper *dw;
- CamelMimePart *opart;
- CamelStream *ostream;
- gchar *type;
- GError *local_error = NULL;
-
- if (!ipart) {
- em_format_format_error(emf, stream, _("Unknown error verifying signature"));
- return;
- }
-
- emf->validity_found |=
- EM_FORMAT_VALIDITY_FOUND_SIGNED |
- EM_FORMAT_VALIDITY_FOUND_PGP;
-
- cipher = camel_gpg_context_new (emf->session);
- /* Verify the signature of the message */
- valid = camel_cipher_context_verify_sync (
- cipher, ipart, cancellable, &local_error);
- if (!valid) {
- em_format_format_error (
- emf, stream, local_error->message ?
- _("Error verifying signature") :
- _("Unknown error verifying signature"));
- if (local_error->message)
- em_format_format_error (
- emf, stream, "%s", local_error->message);
- em_format_format_source (emf, stream, ipart, cancellable);
- /* XXX I think this will loop:
- * em_format_part_as(emf, stream, part, "text/plain"); */
- g_clear_error (&local_error);
- g_object_unref (cipher);
- return;
- }
-
- /* Setup output stream */
- ostream = camel_stream_mem_new ();
- filtered_stream = camel_stream_filter_new (ostream);
-
- /* Add PGP header / footer filter */
- pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new ();
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream),
- CAMEL_MIME_FILTER (pgp_filter));
- g_object_unref (pgp_filter);
-
- /* Pass through the filters that have been setup */
- dw = camel_medium_get_content ((CamelMedium *) ipart);
- camel_data_wrapper_decode_to_stream_sync (
- dw, (CamelStream *) filtered_stream, NULL, NULL);
- camel_stream_flush ((CamelStream *) filtered_stream, NULL, NULL);
- g_object_unref (filtered_stream);
-
- /* Create a new text/plain MIME part containing the signed
- * content preserving the original part's Content-Type params. */
- content_type = camel_mime_part_get_content_type (ipart);
- type = camel_content_type_format (content_type);
- content_type = camel_content_type_decode (type);
- g_free (type);
-
- g_free (content_type->type);
- content_type->type = g_strdup ("text");
- g_free (content_type->subtype);
- content_type->subtype = g_strdup ("plain");
- type = camel_content_type_format (content_type);
- camel_content_type_unref (content_type);
-
- dw = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream_sync (dw, ostream, NULL, NULL);
- camel_data_wrapper_set_mime_type (dw, type);
- g_free (type);
-
- opart = camel_mime_part_new ();
- camel_medium_set_content ((CamelMedium *) opart, dw);
- camel_data_wrapper_set_mime_type_field (
- (CamelDataWrapper *) opart, dw->mime_type);
-
- add_validity_found (emf, valid);
- /* Pass it off to the real formatter */
- em_format_format_secure (emf, stream, opart, valid, cancellable);
-
- /* Clean Up */
- g_object_unref (dw);
- g_object_unref (opart);
- g_object_unref (ostream);
- g_object_unref (cipher);
-}
-
-static void
-emf_inlinepgp_encrypted (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *ipart,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelCipherContext *cipher;
- CamelCipherValidity *valid;
- CamelMimePart *opart;
- CamelDataWrapper *dw;
- gchar *mime_type;
- GError *local_error = NULL;
-
- emf->validity_found |=
- EM_FORMAT_VALIDITY_FOUND_ENCRYPTED |
- EM_FORMAT_VALIDITY_FOUND_PGP;
-
- cipher = camel_gpg_context_new (emf->session);
- opart = camel_mime_part_new ();
-
- /* Decrypt the message */
- valid = camel_cipher_context_decrypt_sync (
- cipher, ipart, opart, cancellable, &local_error);
-
- if (!valid) {
- em_format_format_error (
- emf, stream, _("Could not parse PGP message: "));
- if (local_error->message != NULL)
- em_format_format_error (
- emf, stream, "%s", local_error->message);
- else
- em_format_format_error (
- emf, stream, _("Unknown error"));
- em_format_format_source (emf, stream, ipart, cancellable);
- /* XXX I think this will loop:
- * em_format_part_as(emf, stream, part, "text/plain"); */
-
- g_clear_error (&local_error);
- g_object_unref (cipher);
- g_object_unref (opart);
- return;
- }
-
- dw = camel_medium_get_content ((CamelMedium *) opart);
- mime_type = camel_data_wrapper_get_mime_type (dw);
-
- /* this ensures to show the 'opart' as inlined, if possible */
- if (mime_type != NULL && g_ascii_strcasecmp (
- mime_type, "application/octet-stream") == 0) {
- const gchar *snoop = em_format_snoop_type (opart);
-
- if (snoop)
- camel_data_wrapper_set_mime_type (dw, snoop);
- }
-
- preserve_charset_in_content_type (ipart, opart);
- g_free (mime_type);
-
- add_validity_found (emf, valid);
- /* Pass it off to the real formatter */
- em_format_format_secure (emf, stream, opart, valid, cancellable);
-
- /* Clean Up */
- g_object_unref (opart);
- g_object_unref (cipher);
-}
-
-static EMFormatHandler type_builtin_table[] = {
-#ifdef ENABLE_SMIME
- { (gchar *) "application/x-pkcs7-mime",
- emf_application_xpkcs7mime,
- EM_FORMAT_HANDLER_INLINE_DISPOSITION },
-#endif
- { (gchar *) "application/mbox", emf_application_mbox, EM_FORMAT_HANDLER_INLINE },
- { (gchar *) "multipart/alternative", emf_multipart_alternative },
- { (gchar *) "multipart/appledouble", emf_multipart_appledouble },
- { (gchar *) "multipart/encrypted", emf_multipart_encrypted },
- { (gchar *) "multipart/mixed", emf_multipart_mixed },
- { (gchar *) "multipart/signed", emf_multipart_signed },
- { (gchar *) "multipart/related", emf_multipart_related },
- { (gchar *) "multipart/*", emf_multipart_mixed },
- { (gchar *) "message/rfc822", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
- { (gchar *) "message/news", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
- { (gchar *) "message/delivery-status", emf_message_deliverystatus },
- { (gchar *) "message/*", emf_message_rfc822, EM_FORMAT_HANDLER_INLINE },
-
- /* Insert brokenly-named parts here */
-#ifdef ENABLE_SMIME
- { (gchar *) "application/pkcs7-mime",
- emf_application_xpkcs7mime,
- EM_FORMAT_HANDLER_INLINE_DISPOSITION },
-#endif
-
- /* internal types */
- { (gchar *) "application/x-inlinepgp-signed", emf_inlinepgp_signed },
- { (gchar *) "application/x-inlinepgp-encrypted", emf_inlinepgp_encrypted },
-};
-
-static void
-emf_builtin_init (EMFormatClass *class)
-{
- gint ii;
+ if (!dw)
+ return 0;
- for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
- g_hash_table_insert (
- class->type_handlers,
- type_builtin_table[ii].mime_type,
- &type_builtin_table[ii]);
+ d(printf("checking is attachment %s/%s\n", dw->mime_type->type, dw->mime_type->subtype));
+ return !(camel_content_type_is (dw->mime_type, "multipart", "*")
+ || camel_content_type_is (
+ dw->mime_type, "application", "x-pkcs7-mime")
+ || camel_content_type_is (
+ dw->mime_type, "application", "pkcs7-mime")
+ || camel_content_type_is (
+ dw->mime_type, "application", "x-inlinepgp-signed")
+ || camel_content_type_is (
+ dw->mime_type, "application", "x-inlinepgp-encrypted")
+ || camel_content_type_is (
+ dw->mime_type, "x-evolution", "evolution-rss-feed")
+ || camel_content_type_is (dw->mime_type, "text", "calendar")
+ || camel_content_type_is (dw->mime_type, "text", "x-calendar")
+ || (camel_content_type_is (dw->mime_type, "text", "*")
+ && camel_mime_part_get_filename (part) == NULL));
}
/**
@@ -2493,5 +2401,237 @@ em_format_snoop_type (CamelMimePart *part)
return res;
/* We used to load parts to check their type, we don't anymore,
- * see bug #11778 for some discussion */
+ * see bug #211778 for some discussion */
+}
+
+/**
+ * Construct a URI for message.
+ *
+ * The URI can contain multiple query parameters. The list of parameters must be
+ * NULL-terminated. Each query must contain name, GType of value and value.
+ *
+ * @param folder Folder wit the message
+ * @param message_uid ID of message within the \p folder
+ * @param first_param_name Name of first query parameter followed by GType of it's value and value.
+ */
+gchar *
+em_format_build_mail_uri (CamelFolder *folder,
+ const gchar *message_uid,
+ const gchar *first_param_name,
+ ...)
+{
+ CamelStore *store;
+ gchar *uri, *tmp;
+ va_list ap;
+ const gchar *name;
+ const gchar *service_uid, *folder_name;
+ gchar separator;
+
+ g_return_val_if_fail (message_uid && *message_uid, NULL);
+
+ if (!folder) {
+ folder_name = "generic";
+ service_uid = "generic";
+ } else {
+ folder_name = camel_folder_get_full_name (folder);
+ store = camel_folder_get_parent_store (folder);
+ if (store)
+ service_uid = camel_service_get_uid (CAMEL_SERVICE (store));
+ else
+ service_uid = "generic";
+ }
+
+ tmp = g_strdup_printf ("mail://%s/%s/%s",
+ service_uid,
+ folder_name,
+ message_uid);
+
+ va_start (ap, first_param_name);
+ name = first_param_name;
+ separator = '?';
+ while (name) {
+ gchar *tmp2;
+ gint type = va_arg (ap, gint);
+ switch (type) {
+ case G_TYPE_INT:
+ case G_TYPE_BOOLEAN: {
+ gint val = va_arg (ap, gint);
+ tmp2 = g_strdup_printf ("%s%c%s=%d", tmp,
+ separator, name, val);
+ break;
+ }
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE: {
+ gdouble val = va_arg (ap, double);
+ tmp2 = g_strdup_printf ("%s%c%s=%f", tmp,
+ separator, name, val);
+ break;
+ }
+ case G_TYPE_STRING: {
+ gchar *val = va_arg (ap, gchar *);
+ gchar *escaped = soup_uri_encode (val, NULL);
+ tmp2 = g_strdup_printf ("%s%c%s=%s", tmp,
+ separator, name, escaped);
+ g_free (escaped);
+ break;
+ }
+ default:
+ g_warning ("Invalid param type %s", g_type_name (type));
+ return NULL;
+ }
+
+ g_free (tmp);
+ tmp = tmp2;
+
+ if (separator == '?')
+ separator = '&';
+
+ name = va_arg (ap, gchar *);
+ }
+ va_end (ap);
+
+ uri = tmp;
+
+ /* For some reason, webkit won't accept URL with username, but
+ * without password (mail://store@host/folder/mail), so we
+ * will replace the '@' symbol by '/' to get URL like
+ * mail://store/host/folder/mail which is OK
+ */
+ tmp = strchr (tmp, '@');
+ if (tmp) {
+ tmp[0] = '/';
+ }
+
+ return uri;
+}
+
+void
+em_format_redraw (EMFormat *emf)
+{
+ g_return_if_fail (EM_IS_FORMAT (emf));
+
+ g_signal_emit (emf, signals[REDRAW_REQUESTED], 0);
+}
+
+/**************************************************************************/
+EMFormatPURI *
+em_format_puri_new (EMFormat *emf,
+ gsize puri_size,
+ CamelMimePart *part,
+ const gchar *uri)
+{
+ EMFormatPURI *puri;
+
+ g_return_val_if_fail (EM_IS_FORMAT (emf), NULL);
+ g_return_val_if_fail (puri_size >= sizeof (EMFormatPURI), NULL);
+
+ puri = (EMFormatPURI *) g_malloc0 (puri_size);
+ puri->emf = emf;
+
+ if (part)
+ puri->part = g_object_ref (part);
+
+ if (uri)
+ puri->uri = g_strdup (uri);
+
+ return puri;
+}
+
+void
+em_format_puri_free (EMFormatPURI *puri)
+{
+ g_return_if_fail (puri);
+
+ if (puri->part)
+ g_object_unref (puri->part);
+
+ if (puri->uri)
+ g_free (puri->uri);
+
+ if (puri->cid)
+ g_free (puri->cid);
+
+ if (puri->mime_type)
+ g_free (puri->mime_type);
+
+ if (puri->validity)
+ camel_cipher_validity_free (puri->validity);
+
+ if (puri->validity_parent)
+ camel_cipher_validity_free (puri->validity_parent);
+
+ if (puri->free)
+ puri->free (puri);
+
+ g_free (puri);
+}
+
+void
+em_format_puri_write (EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ g_return_if_fail (puri);
+ g_return_if_fail (CAMEL_IS_STREAM (stream));
+
+ if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) {
+ const EMFormatHandler *handler;
+ handler = em_format_find_handler (puri->emf, "x-evolution/message/source");
+ handler->write_func (puri->emf, puri, stream, info, cancellable);
+ return;
+ }
+
+ if (puri->write_func) {
+ puri->write_func (puri->emf, puri, stream, info, cancellable);
+ } else {
+ const EMFormatHandler *handler;
+ const gchar *mime_type;
+
+ if (puri->mime_type) {
+ mime_type = puri->mime_type;
+ } else {
+ mime_type = (gchar *) "plain/text";
+ }
+
+ handler = em_format_find_handler (puri->emf, mime_type);
+ if (handler && handler->write_func) {
+ handler->write_func (puri->emf,
+ puri, stream, info, cancellable);
+ }
+ }
+}
+
+EMFormatHeader *
+em_format_header_new (const gchar *name,
+ const gchar *value)
+{
+ EMFormatHeader *header;
+
+ g_return_val_if_fail (name && *name, NULL);
+
+ header = g_new0 (EMFormatHeader, 1);
+ header->name = g_strdup (name);
+ if (value && *value)
+ header->value = g_strdup (value);
+
+ return header;
+}
+
+void
+em_format_header_free (EMFormatHeader * header)
+{
+ g_return_if_fail (header != NULL);
+
+ if (header->name) {
+ g_free (header->name);
+ header->name = NULL;
+ }
+
+ if (header->value) {
+ g_free (header->value);
+ header->value = NULL;
+ }
+
+ g_free (header);
}
diff --git a/em-format/em-format.h b/em-format/em-format.h
index cf214d81a5..712b41bce1 100644
--- a/em-format/em-format.h
+++ b/em-format/em-format.h
@@ -21,14 +21,11 @@
*
*/
-/*
- Abstract class for formatting mime messages
-*/
-
#ifndef EM_FORMAT_H
#define EM_FORMAT_H
#include <camel/camel.h>
+#include <gtk/gtk.h>
/* Standard GObject macros */
#define EM_TYPE_FORMAT \
@@ -51,194 +48,139 @@
G_BEGIN_DECLS
+#define EM_FORMAT_HEADER_BOLD (1<<0)
+#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */
+
+#define EM_FORMAT_VALIDITY_FOUND_PGP (1<<0)
+#define EM_FORMAT_VALIDITY_FOUND_SMIME (1<<1)
+#define EM_FORMAT_VALIDITY_FOUND_SIGNED (1<<2)
+#define EM_FORMAT_VALIDITY_FOUND_ENCRYPTED (1<<3)
+
typedef struct _EMFormat EMFormat;
typedef struct _EMFormatClass EMFormatClass;
typedef struct _EMFormatPrivate EMFormatPrivate;
-typedef struct _EMFormatHandler EMFormatHandler;
+typedef struct _EMFormatPURI EMFormatPURI;
typedef struct _EMFormatHeader EMFormatHeader;
+typedef struct _EMFormatHandler EMFormatHandler;
+typedef struct _EMFormatParserInfo EMFormatParserInfo;
+typedef struct _EMFormatWriterInfo EMFormatWriterInfo;
+typedef struct _WebKitDOMElement WebKitDOMElement;
-typedef void (*EMFormatFunc) (EMFormat *emf,
+typedef void (*EMFormatParseFunc) (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable);
+typedef void (*EMFormatWriteFunc) (EMFormat *emf,
+ EMFormatPURI *puri,
CamelStream *stream,
- CamelMimePart *mime_part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback);
-
-typedef enum {
- EM_FORMAT_MODE_NORMAL,
- EM_FORMAT_MODE_ALLHEADERS,
- EM_FORMAT_MODE_SOURCE
-} EMFormatMode;
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable);
+typedef GtkWidget * (*EMFormatWidgetFunc) (EMFormat *emf,
+ EMFormatPURI *puri,
+ GCancellable *cancellable);
+typedef void (*EMailDisplayBindFunc) (WebKitDOMElement *root,
+ EMFormatPURI *puri);
-/**
- * EMFormatHandlerFlags - Format handler flags.
- *
- * @EM_FORMAT_HANDLER_INLINE: This type should be shown expanded
- * inline by default.
- * @EM_FORMAT_HANDLER_INLINE_DISPOSITION: This type should always be
- * shown inline, despite what the Content-Disposition suggests.
- *
- **/
typedef enum {
EM_FORMAT_HANDLER_INLINE = 1 << 0,
- EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1 << 1
+ EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1 << 1,
+ EM_FORMAT_HANDLER_COMPOUND_TYPE = 1 << 2
} EMFormatHandlerFlags;
-/**
- * struct _EMFormatHandler - MIME type handler.
- *
- * @mime_type: Type this handler handles.
- * @handler: The handler callback.
- * @flags: Handler flags
- * @old: The last handler set on this type. Allows overrides to
- * fallback to previous implementation.
- *
- **/
+typedef enum {
+ EM_FORMAT_WRITE_MODE_NORMAL= 1 << 0,
+ EM_FORMAT_WRITE_MODE_ALL_HEADERS = 1 << 1,
+ EM_FORMAT_WRITE_MODE_SOURCE = 1 << 2,
+ EM_FORMAT_WRITE_MODE_PRINTING = 1 << 3,
+ EM_FORMAT_WRITE_MODE_RAW = 1 << 4
+} EMFormatWriteMode;
+
struct _EMFormatHandler {
gchar *mime_type;
- EMFormatFunc handler;
+ EMFormatParseFunc parse_func;
+ EMFormatWriteFunc write_func;
EMFormatHandlerFlags flags;
EMFormatHandler *old;
};
-typedef struct _EMFormatPURI EMFormatPURI;
-typedef void (*EMFormatPURIFunc) (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable);
-
/**
- * struct _EMFormatPURI - Pending URI object.
- *
- * @free: May be set by allocator and will be called when no longer needed.
- * @format:
- * @uri: Calculated URI of the part, if the part has one in its
- * Content-Location field.
- * @cid: The RFC2046 Content-Id of the part. If none is present, a unique value
- * is calculated from @part_id.
- * @part_id: A unique identifier for each part.
- * @func: Callback for when the URI is requested. The callback writes
- * its data to the supplied stream.
- * @part:
- * @use_count:
- *
- * This is used for multipart/related, and other formatters which may
- * need to include a reference to out-of-band data in the content
- * stream.
- *
- * This object may be subclassed as a struct.
- **/
-struct _EMFormatPURI {
- void (*free)(EMFormatPURI *p); /* optional callback for freeing user-fields */
- EMFormat *format;
+ * Use this struct to pass additional information between
+ * EMFormatParseFunc's.
+ * Much cleaner then setting public property of EMFormat.
+ */
+struct _EMFormatParserInfo {
+ const EMFormatHandler *handler;
- gchar *uri; /* will be the location of the part, may be empty */
- gchar *cid; /* will always be set, a fake one created if needed */
- gchar *part_id; /* will always be set, emf->part_id->str for this part */
+ /* EM_FORMAT_VALIDITY_* flags */
+ guint32 validity_type;
+ CamelCipherValidity *validity;
- EMFormatPURIFunc func;
- CamelMimePart *part;
+ gint is_attachment : 1;
+ gint force_handler: 1;
+};
- guint use_count; /* used by multipart/related to see if it was accessed */
+struct _EMFormatWriterInfo {
+ EMFormatWriteMode mode;
+ gboolean headers_collapsable;
+ gboolean headers_collapsed;
};
struct _EMFormatHeader {
guint32 flags; /* E_FORMAT_HEADER_ * */
- gchar name[1];
+ gchar *name;
+ gchar *value;
};
#define EM_FORMAT_HEADER_BOLD (1<<0)
#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */
-#define EM_FORMAT_VALIDITY_FOUND_PGP (1<<0)
-#define EM_FORMAT_VALIDITY_FOUND_SMIME (1<<1)
-#define EM_FORMAT_VALIDITY_FOUND_SIGNED (1<<2)
-#define EM_FORMAT_VALIDITY_FOUND_ENCRYPTED (1<<3)
+struct _EMFormatPURI {
+ CamelMimePart *part;
+
+ EMFormat *emf;
+ EMFormatWriteFunc write_func;
+ EMFormatWidgetFunc widget_func;
+
+ /**
+ * Called by #EMailDisplay whenever document/frame is reloaded.
+ * Modules and plugins can create bindings to events of DOM
+ * objects they created.
+ */
+ EMailDisplayBindFunc bind_func;
+
+ gchar *uri;
+ gchar *cid;
+ gchar *mime_type;
+
+ /* EM_FORMAT_VALIDITY_* flags */
+ guint32 validity_type;
+ CamelCipherValidity *validity;
+ CamelCipherValidity *validity_parent;
+
+ gboolean is_attachment;
+
+ void (*free)(EMFormatPURI *puri); /* optional callback for freeing user-fields */
+};
-/**
- * struct _EMFormat - Mail formatter object.
- *
- * @parent:
- * @priv:
- * @message:
- * @folder:
- * @uid:
- * @part_id:
- * @header_list:
- * @session:
- * @base url:
- * @snoop_mime_type:
- * @valid:
- * @valid_parent:
- * @inline_table:
- * @pending_uri_table:
- * @pending_uri_tree:
- * @pending_uri_level:
- * @mode:
- * @charset:
- * @default_charset:
- *
- * Most fields are private or read-only.
- *
- * This is the base MIME formatter class. It provides no formatting
- * itself, but drives most of the basic types, including multipart / * types.
- **/
struct _EMFormat {
GObject parent;
EMFormatPrivate *priv;
- /* The current message */
CamelMimeMessage *message;
-
CamelFolder *folder;
- gchar *uid;
+ gchar *message_uid;
+ gchar *uri_base;
- /* Current part ID prefix for identifying parts directly. */
- GString *part_id;
- /* part_id of the currently processing message
- * (when the message has message-attachments) */
- gchar *current_message_part_id;
+ /* Defines order in which parts should be displayed */
+ GList *mail_part_list;
+ /* For quick search for parts by their URI/ID */
+ GHashTable *mail_part_table;
/* If empty, then all. */
GQueue header_list;
-
- /* Used for authentication when required. */
- CamelSession *session;
-
- /* Content-Base header or absolute Content-Location, for any part. */
- CamelURL *base;
-
- /* If we snooped an application/octet-stream, what we snooped. */
- const gchar *snoop_mime_type;
-
- /* For validity enveloping. */
- CamelCipherValidity *valid;
- CamelCipherValidity *valid_parent;
-
- /* For checking whether we found any signed or encrypted parts. */
- guint32 validity_found;
-
- /* For forcing inlining. */
- GHashTable *inline_table;
-
- /* Global URI lookup table for message. */
- GHashTable *pending_uri_table;
-
- /* This structure is used internally to form a visibility tree of
- * parts in the current formatting stream. This is to implement the
- * part resolution rules for RFC2387 to implement multipart/related. */
- GNode *pending_uri_tree;
-
- /* The current level to search from. */
- GNode *pending_uri_level;
-
- EMFormatMode mode; /* source/headers/etc */
- gchar *charset; /* charset override */
- gchar *default_charset; /* charset fallback */
- gboolean composer; /* formatting from composer? */
- gboolean print; /* formatting for printing? */
};
struct _EMFormatClass {
@@ -246,187 +188,161 @@ struct _EMFormatClass {
GHashTable *type_handlers;
- /* lookup handler, default falls back to hashtable above */
- const EMFormatHandler *
- (*find_handler) (EMFormat *emf,
- const gchar *mime_type);
-
- /* start formatting a message */
- void (*format_clone) (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *message,
- EMFormat *source,
- GCancellable *cancellable);
-
- /* some internel error/inconsistency */
- void (*format_error) (EMFormat *emf,
- CamelStream *stream,
- const gchar *errmsg);
-
- /* use for external structured parts */
- void (*format_attachment) (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- const gchar *mime_type,
- const EMFormatHandler *info,
- GCancellable *cancellable);
+ gboolean (*is_inline) (EMFormat *emf,
+ const gchar *part_id,
+ CamelMimePart *part,
+ const EMFormatHandler *handler);
- /* use for unparsable content */
- void (*format_source) (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- GCancellable *cancellable);
- /* for outputing secure(d) content */
- void (*format_secure) (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- CamelCipherValidity *validity,
- GCancellable *cancellable);
+ /* Write the entire message to stream */
+ void (*write) (EMFormat *emf,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable);
- /* returns true if the formatter is still busy with pending stuff */
- gboolean (*busy) (EMFormat *);
-
- /* Shows optional way to open messages */
- void (*format_optional) (EMFormat *emf,
- CamelStream *filter_stream,
- CamelMimePart *mime_part,
- CamelStream *mem_stream,
- GCancellable *cancellable);
-
- gboolean (*is_inline) (EMFormat *emf,
- const gchar *part_id,
- CamelMimePart *mime_part,
- const EMFormatHandler *handle);
+ void (*preparse) (EMFormat *emf);
/* signals */
- /* complete, alternative to polling busy, for asynchronous work */
- void (*complete) (EMFormat *emf);
-};
+ void (*redraw_requested) (EMFormat *emf);
-void em_format_set_mode (EMFormat *emf,
- EMFormatMode mode);
-void em_format_set_charset (EMFormat *emf,
- const gchar *charset);
-void em_format_set_default_charset (EMFormat *emf,
- const gchar *charset);
-
-/* also indicates to show all headers */
-void em_format_clear_headers (EMFormat *emf);
-
-void em_format_default_headers (EMFormat *emf);
-void em_format_add_header (EMFormat *emf,
- const gchar *name,
- guint32 flags);
-
-/* FIXME: Need a 'clone' api to copy details about the current view (inlines etc)
- * Or maybe it should live with sub-classes? */
-
-gint em_format_is_attachment (EMFormat *emf,
- CamelMimePart *part);
-
-gboolean em_format_is_inline (EMFormat *emf,
- const gchar *part_id,
- CamelMimePart *mime_part,
- const EMFormatHandler *handle);
-void em_format_set_inline (EMFormat *emf,
- const gchar *partid,
- gint state);
-
-gchar * em_format_describe_part (CamelMimePart *part,
- const gchar *mime_type);
-
-/* for implementers */
-GType em_format_get_type (void);
-
-void em_format_class_add_handler (EMFormatClass *emfc,
- EMFormatHandler *info);
-void em_format_class_remove_handler (EMFormatClass *emfc,
- EMFormatHandler *info);
-const EMFormatHandler *
- em_format_find_handler (EMFormat *emf,
- const gchar *mime_type);
-const EMFormatHandler *
- em_format_fallback_handler (EMFormat *emf,
- const gchar *mime_type);
-
-/* puri is short for pending uri ... really */
-EMFormatPURI * em_format_add_puri (EMFormat *emf,
- gsize size,
- const gchar *uri,
- CamelMimePart *part,
- EMFormatPURIFunc func);
-EMFormatPURI * em_format_find_visible_puri (EMFormat *emf,
- const gchar *uri);
-EMFormatPURI * em_format_find_puri (EMFormat *emf,
- const gchar *uri);
-void em_format_clear_puri_tree (EMFormat *emf);
-void em_format_push_level (EMFormat *emf);
-void em_format_pull_level (EMFormat *emf);
-
-/* clones inline state/view and format, or use to redraw */
-void em_format_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *message,
- EMFormat *source,
- GCancellable *cancellable);
-
-/* formats a new message */
-void em_format_format (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *message,
- GCancellable *cancellable);
-void em_format_queue_redraw (EMFormat *emf);
-void em_format_format_attachment (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- const gchar *mime_type,
- const EMFormatHandler *info,
- GCancellable *cancellable);
-void em_format_format_error (EMFormat *emf,
- CamelStream *stream,
- const gchar *format,
- ...) G_GNUC_PRINTF (3, 4);
-void em_format_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- CamelCipherValidity *valid,
- GCancellable *cancellable);
-void em_format_format_source (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- GCancellable *cancellable);
-
-gboolean em_format_busy (EMFormat *emf);
-
-/* raw content only */
-void em_format_format_content (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- GCancellable *cancellable);
-
-/* raw content text parts - should this just be checked/done by above? */
-void em_format_format_text (EMFormat *emf,
- CamelStream *stream,
- CamelDataWrapper *part,
- GCancellable *cancellable);
-
-void em_format_part_as (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const gchar *mime_type,
- GCancellable *cancellable);
-void em_format_part (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *mime_part,
- GCancellable *cancellable);
-void em_format_merge_handler (EMFormat *new,
- EMFormat *old);
-
-const gchar * em_format_snoop_type (CamelMimePart *part);
+};
-G_END_DECLS
+EMFormat * em_format_new (void);
+
+GType em_format_get_type (void);
+
+void em_format_set_charset (EMFormat *emf,
+ const gchar *charset);
+const gchar * em_format_get_charset (EMFormat *emf);
+
+void em_format_set_default_charset (EMFormat *emf,
+ const gchar *charset);
+const gchar * em_format_get_default_charset (EMFormat *emf);
+
+void em_format_set_composer (EMFormat *emf,
+ gboolean composer);
+gboolean em_format_get_composer (EMFormat *emf);
+
+void em_format_set_base_url (EMFormat *emf,
+ CamelURL *url);
+void em_format_set_base_url_string (EMFormat *emf,
+ const gchar *url_string);
+CamelURL * em_format_get_base_url (EMFormat *emf);
+
+void em_format_clear_headers (EMFormat *emf);
+
+void em_format_default_headers (EMFormat *emf);
+
+void em_format_add_header (EMFormat *emf,
+ const gchar *name,
+ const gchar *value,
+ guint32 flags);
+void em_format_add_header_struct (EMFormat *emf,
+ EMFormatHeader *header);
+void em_format_remove_header (EMFormat *emf,
+ const gchar *name,
+ const gchar *value);
+void em_format_remove_header_struct (EMFormat *emf,
+ const EMFormatHeader *header);
+
+void em_format_add_puri (EMFormat *emf,
+ EMFormatPURI *puri);
+EMFormatPURI * em_format_find_puri (EMFormat *emf,
+ const gchar *id);
+
+void em_format_class_add_handler (EMFormatClass *emfc,
+ EMFormatHandler *handler);
+void em_format_class_remove_handler (EMFormatClass *emfc,
+ EMFormatHandler *handler);
+
+const EMFormatHandler * em_format_find_handler (EMFormat *emf,
+ const gchar *mime_type);
+const EMFormatHandler * em_format_fallback_handler (EMFormat *emf,
+ const gchar *mime_type);
+
+void em_format_parse (EMFormat *emf,
+ CamelMimeMessage *message,
+ CamelFolder *folder,
+ GCancellable *cancellable);
+
+void em_format_write (EMFormat *emf,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable);
+
+void em_format_parse_async (EMFormat *emf,
+ CamelMimeMessage *message,
+ CamelFolder *folder,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void em_format_parse_part (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable);
+void em_format_parse_part_as (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ const gchar *mime_type,
+ GCancellable *cancellable);
+gboolean em_format_is_inline (EMFormat *emf,
+ const gchar *part_id,
+ CamelMimePart *part,
+ const EMFormatHandler *handler);
+
+gchar * em_format_get_error_id (EMFormat *emf);
+
+void em_format_format_error (EMFormat *emf,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+void em_format_format_text (EMFormat *emf,
+ CamelStream *stream,
+ CamelDataWrapper *dw,
+ GCancellable *cancellable);
+gchar * em_format_describe_part (CamelMimePart *part,
+ const gchar *mime_type);
+gint em_format_is_attachment (EMFormat *emf,
+ CamelMimePart *part);
+const gchar * em_format_snoop_type (CamelMimePart *part);
+
+gchar * em_format_build_mail_uri (CamelFolder *folder,
+ const gchar *message_uid,
+ const gchar *part_uid,
+ ...) G_GNUC_NULL_TERMINATED;
+
+/* EMFormatParseFunc that does nothing. Use it to disable
+ * parsing of a specific mime type parts */
+void em_format_empty_parser (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable);
+
+/* EMFormatWriteFunc that does nothing. Use it to disable
+ * writing of a specific mime type parts */
+void em_format_empty_writer (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable);
+
+void em_format_redraw (EMFormat *emf);
+
+EMFormatPURI * em_format_puri_new (EMFormat *emf,
+ gsize puri_size,
+ CamelMimePart *part,
+ const gchar *uri);
+void em_format_puri_free (EMFormatPURI *puri);
+
+void em_format_puri_write (EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable);
+
+EMFormatHeader * em_format_header_new (const gchar *name,
+ const gchar *value);
+void em_format_header_free (EMFormatHeader *header);
#endif /* EM_FORMAT_H */
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 3a13284668..e6eccc83fe 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -22,7 +22,9 @@ libevolution_mail_la_CPPFLAGS = \
$(CERT_UI_CFLAGS) \
$(CANBERRA_CFLAGS) \
$(CLUTTER_CFLAGS) \
- $(GTKHTML_CFLAGS) \
+ $(GTKHTML_CFLAGS) \
+ $(JAVASCRIPTCORE_CFLAGS) \
+ $(LIBSOUP_CFLAGS) \
-DEVOLUTION_DATADIR=\""$(datadir)"\" \
-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
-DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \
@@ -56,11 +58,13 @@ mailinclude_HEADERS = \
e-mail-migrate.h \
e-mail-notebook-view.h \
e-mail-paned-view.h \
+ e-mail-printer.h \
e-mail-reader-utils.h \
e-mail-reader.h \
- e-mail-ui-session.h \
+ e-mail-request.h \
e-mail-sidebar.h \
e-mail-tag-editor.h \
+ e-mail-ui-session.h \
e-mail-view.h \
em-account-editor.h \
em-composer-utils.h \
@@ -81,7 +85,6 @@ mailinclude_HEADERS = \
em-format-html-display.h \
em-format-html-print.h \
em-format-html.h \
- em-html-stream.h \
em-search-context.h \
em-subscription-editor.h \
em-sync-stream.h \
@@ -121,11 +124,13 @@ libevolution_mail_la_SOURCES = \
e-mail-migrate.c \
e-mail-notebook-view.c \
e-mail-paned-view.c \
+ e-mail-printer.c \
e-mail-reader-utils.c \
e-mail-reader.c \
- e-mail-ui-session.c \
+ e-mail-request.c \
e-mail-sidebar.c \
e-mail-tag-editor.c \
+ e-mail-ui-session.c \
e-mail-view.c \
em-account-editor.c \
em-composer-utils.c \
@@ -146,7 +151,6 @@ libevolution_mail_la_SOURCES = \
em-format-html-display.c \
em-format-html-print.c \
em-format-html.c \
- em-html-stream.c \
em-search-context.c \
em-subscription-editor.c \
em-sync-stream.c \
@@ -194,7 +198,11 @@ libevolution_mail_la_LIBADD = \
$(CANBERRA_LIBS) \
$(CLUTTER_LIBS) \
$(GTKHTML_LIBS) \
- $(SMIME_LIBS)
+ $(JAVASCRIPTCORE_CFLAGS) \
+ $(E_WIDGETS_LIBS) \
+ $(SMIME_LIBS) \
+ $(LIBSOUP_LIBS) \
+ $(GNOME_PLATFORM_LIBS)
libevolution_mail_la_LDFLAGS = -avoid-version $(NO_UNDEFINED)
diff --git a/mail/e-mail-attachment-bar.c b/mail/e-mail-attachment-bar.c
index 21a298c56d..7572c664cb 100644
--- a/mail/e-mail-attachment-bar.c
+++ b/mail/e-mail-attachment-bar.c
@@ -60,7 +60,8 @@ enum {
PROP_ACTIVE_VIEW,
PROP_DRAGGING,
PROP_EDITABLE,
- PROP_EXPANDED
+ PROP_EXPANDED,
+ PROP_STORE
};
/* Forward Declarations */
@@ -78,7 +79,6 @@ G_DEFINE_TYPE_WITH_CODE (
static void
mail_attachment_bar_update_status (EMailAttachmentBar *bar)
{
- EAttachmentView *view;
EAttachmentStore *store;
GtkActivatable *activatable;
GtkAction *action;
@@ -88,8 +88,7 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar)
gchar *display_size;
gchar *markup;
- view = E_ATTACHMENT_VIEW (bar);
- store = e_attachment_view_get_store (view);
+ store = E_ATTACHMENT_STORE (bar->priv->model);
label = GTK_LABEL (bar->priv->status_label);
num_attachments = e_attachment_store_get_num_attachments (store);
@@ -120,6 +119,31 @@ mail_attachment_bar_update_status (EMailAttachmentBar *bar)
}
static void
+mail_attachment_bar_set_store (EMailAttachmentBar *bar,
+ EAttachmentStore *store)
+{
+ g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
+
+ bar->priv->model = g_object_ref (store);
+
+ gtk_icon_view_set_model (GTK_ICON_VIEW (bar->priv->icon_view),
+ bar->priv->model);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (bar->priv->tree_view),
+ bar->priv->model);
+
+ g_signal_connect_swapped (
+ bar->priv->model, "notify::num-attachments",
+ G_CALLBACK (mail_attachment_bar_update_status), bar);
+
+ g_signal_connect_swapped (
+ bar->priv->model, "notify::total-size",
+ G_CALLBACK (mail_attachment_bar_update_status), bar);
+
+ /* Initialize */
+ mail_attachment_bar_update_status (bar);
+}
+
+static void
mail_attachment_bar_set_property (GObject *object,
guint property_id,
const GValue *value,
@@ -127,7 +151,7 @@ mail_attachment_bar_set_property (GObject *object,
{
switch (property_id) {
case PROP_ACTIVE_VIEW:
- e_mail_attachment_bar_set_active_view (
+ e_mail_attachment_bar_set_active_view (
E_MAIL_ATTACHMENT_BAR (object),
g_value_get_int (value));
return;
@@ -149,6 +173,11 @@ mail_attachment_bar_set_property (GObject *object,
E_MAIL_ATTACHMENT_BAR (object),
g_value_get_boolean (value));
return;
+ case PROP_STORE:
+ mail_attachment_bar_set_store (
+ E_MAIL_ATTACHMENT_BAR (object),
+ g_value_get_object (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -188,6 +217,11 @@ mail_attachment_bar_get_property (GObject *object,
e_mail_attachment_bar_get_expanded (
E_MAIL_ATTACHMENT_BAR (object)));
return;
+ case PROP_STORE:
+ g_value_set_object (
+ value,
+ e_mail_attachment_bar_get_store (
+ E_MAIL_ATTACHMENT_BAR (object)));
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -201,8 +235,6 @@ mail_attachment_bar_dispose (GObject *object)
priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (object);
if (priv->model != NULL) {
- e_attachment_store_remove_all (
- E_ATTACHMENT_STORE (priv->model));
g_object_unref (priv->model);
priv->model = NULL;
}
@@ -347,17 +379,6 @@ mail_attachment_bar_get_private (EAttachmentView *view)
return e_attachment_view_get_private (view);
}
-static EAttachmentStore *
-mail_attachment_bar_get_store (EAttachmentView *view)
-{
- EMailAttachmentBar *bar;
-
- bar = E_MAIL_ATTACHMENT_BAR (view);
- view = E_ATTACHMENT_VIEW (bar->priv->icon_view);
-
- return e_attachment_view_get_store (view);
-}
-
static GtkTreePath *
mail_attachment_bar_get_path_at_pos (EAttachmentView *view,
gint x,
@@ -488,6 +509,17 @@ e_mail_attachment_bar_class_init (EMailAttachmentBarClass *class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
+ g_object_class_install_property (
+ object_class,
+ PROP_STORE,
+ g_param_spec_object (
+ "store",
+ "Attachment Store",
+ NULL,
+ E_TYPE_ATTACHMENT_STORE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
g_object_class_override_property (
object_class, PROP_DRAGGING, "dragging");
@@ -499,7 +531,7 @@ static void
e_mail_attachment_bar_interface_init (EAttachmentViewInterface *interface)
{
interface->get_private = mail_attachment_bar_get_private;
- interface->get_store = mail_attachment_bar_get_store;
+ interface->get_store = e_mail_attachment_bar_get_store;
interface->get_path_at_pos = mail_attachment_bar_get_path_at_pos;
interface->get_selected_paths = mail_attachment_bar_get_selected_paths;
interface->path_is_selected = mail_attachment_bar_path_is_selected;
@@ -520,7 +552,6 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar)
GtkAction *action;
bar->priv = E_MAIL_ATTACHMENT_BAR_GET_PRIVATE (bar);
- bar->priv->model = e_attachment_store_new ();
gtk_box_set_spacing (GTK_BOX (bar), 6);
@@ -644,23 +675,18 @@ e_mail_attachment_bar_init (EMailAttachmentBar *bar)
bar->priv->status_label = g_object_ref (widget);
gtk_widget_show (widget);
- g_signal_connect_swapped (
- bar->priv->model, "notify::num-attachments",
- G_CALLBACK (mail_attachment_bar_update_status), bar);
-
- g_signal_connect_swapped (
- bar->priv->model, "notify::total-size",
- G_CALLBACK (mail_attachment_bar_update_status), bar);
-
g_object_unref (size_group);
}
GtkWidget *
-e_mail_attachment_bar_new (void)
+e_mail_attachment_bar_new (EAttachmentStore *store)
{
+ g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
+
return g_object_new (
E_TYPE_MAIL_ATTACHMENT_BAR,
- "editable", FALSE, NULL);
+ "editable", FALSE,
+ "store", store, NULL);
}
gint
@@ -729,3 +755,11 @@ e_mail_attachment_bar_set_expanded (EMailAttachmentBar *bar,
g_object_notify (G_OBJECT (bar), "expanded");
}
+
+EAttachmentStore *
+e_mail_attachment_bar_get_store (EMailAttachmentBar *bar)
+{
+ g_return_val_if_fail (E_IS_MAIL_ATTACHMENT_BAR (bar), NULL);
+
+ return E_ATTACHMENT_STORE (bar->priv->model);
+}
diff --git a/mail/e-mail-attachment-bar.h b/mail/e-mail-attachment-bar.h
index 93c1b89261..b83d9733e0 100644
--- a/mail/e-mail-attachment-bar.h
+++ b/mail/e-mail-attachment-bar.h
@@ -60,7 +60,7 @@ struct _EMailAttachmentBarClass {
};
GType e_mail_attachment_bar_get_type (void);
-GtkWidget * e_mail_attachment_bar_new (void);
+GtkWidget * e_mail_attachment_bar_new (EAttachmentStore *store);
gint e_mail_attachment_bar_get_active_view
(EMailAttachmentBar *bar);
void e_mail_attachment_bar_set_active_view
@@ -71,6 +71,8 @@ gboolean e_mail_attachment_bar_get_expanded
void e_mail_attachment_bar_set_expanded
(EMailAttachmentBar *bar,
gboolean expanded);
+EAttachmentStore *
+ e_mail_attachment_bar_get_store (EMailAttachmentBar *bar);
G_END_DECLS
diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c
index 0dbb3d01e3..806980d602 100644
--- a/mail/e-mail-browser.c
+++ b/mail/e-mail-browser.c
@@ -55,7 +55,8 @@ struct _EMailBrowserPrivate {
EMailBackend *backend;
GtkUIManager *ui_manager;
EFocusTracker *focus_tracker;
- EMFormatHTMLDisplay *formatter;
+
+ EMFormatWriteMode mode;
GtkWidget *main_menu;
GtkWidget *main_toolbar;
@@ -74,7 +75,8 @@ enum {
PROP_GROUP_BY_THREADS,
PROP_SHOW_DELETED,
PROP_REPLY_STYLE,
- PROP_UI_MANAGER
+ PROP_UI_MANAGER,
+ PROP_DISPLAY_MODE,
};
static gpointer parent_class;
@@ -260,11 +262,10 @@ static void
mail_browser_message_selected_cb (EMailBrowser *browser,
const gchar *uid)
{
- EMFormatHTML *formatter;
CamelMessageInfo *info;
CamelFolder *folder;
EMailReader *reader;
- EWebView *web_view;
+ EMailDisplay *display;
const gchar *title;
guint32 state;
@@ -276,8 +277,7 @@ mail_browser_message_selected_cb (EMailBrowser *browser,
return;
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ display = e_mail_reader_get_mail_display (reader);
info = camel_folder_get_message_info (folder, uid);
@@ -289,7 +289,7 @@ mail_browser_message_selected_cb (EMailBrowser *browser,
title = _("(No Subject)");
gtk_window_set_title (GTK_WINDOW (browser), title);
- gtk_widget_grab_focus (GTK_WIDGET (web_view));
+ gtk_widget_grab_focus (GTK_WIDGET (display));
camel_message_info_set_flags (
info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
@@ -319,7 +319,6 @@ mail_browser_popup_event_cb (EMailBrowser *browser,
GdkEventButton *event,
const gchar *uri)
{
- EMFormatHTML *formatter;
EMailReader *reader;
EWebView *web_view;
GtkMenu *menu;
@@ -329,8 +328,7 @@ mail_browser_popup_event_cb (EMailBrowser *browser,
return FALSE;
reader = E_MAIL_READER (browser);
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
if (e_web_view_get_cursor_image (web_view) != NULL)
return FALSE;
@@ -415,6 +413,11 @@ mail_browser_set_property (GObject *object,
E_MAIL_BROWSER (object),
g_value_get_boolean (value));
return;
+
+ case PROP_DISPLAY_MODE:
+ E_MAIL_BROWSER (object)->priv->mode =
+ g_value_get_int (value);
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -468,6 +471,11 @@ mail_browser_get_property (GObject *object,
value, e_mail_browser_get_ui_manager (
E_MAIL_BROWSER (object)));
return;
+
+ case PROP_DISPLAY_MODE:
+ g_value_set_int (
+ value, E_MAIL_BROWSER (object)->priv->mode);
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -495,11 +503,6 @@ mail_browser_dispose (GObject *object)
priv->focus_tracker = NULL;
}
- if (priv->formatter != NULL) {
- g_object_unref (priv->formatter);
- priv->formatter = NULL;
- }
-
if (priv->main_menu != NULL) {
g_object_unref (priv->main_menu);
priv->main_menu = NULL;
@@ -534,14 +537,13 @@ static void
mail_browser_constructed (GObject *object)
{
EMailBrowser *browser;
- EMFormatHTML *formatter;
EMailReader *reader;
EMailBackend *backend;
EMailSession *session;
+ EMailDisplay *display;
EShellBackend *shell_backend;
EShell *shell;
EFocusTracker *focus_tracker;
- ESearchBar *search_bar;
GSettings *settings;
GtkAccelGroup *accel_group;
GtkActionGroup *action_group;
@@ -549,7 +551,6 @@ mail_browser_constructed (GObject *object)
GtkUIManager *ui_manager;
GtkWidget *container;
GtkWidget *widget;
- EWebView *web_view;
const gchar *domain;
const gchar *id;
guint merge_id;
@@ -558,7 +559,6 @@ mail_browser_constructed (GObject *object)
G_OBJECT_CLASS (parent_class)->constructed (object);
browser = E_MAIL_BROWSER (object);
-
reader = E_MAIL_READER (object);
backend = e_mail_reader_get_backend (reader);
session = e_mail_backend_get_session (backend);
@@ -575,9 +575,6 @@ mail_browser_constructed (GObject *object)
gtk_application_add_window (
GTK_APPLICATION (shell), GTK_WINDOW (object));
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
-
/* The message list is a widget, but it is not shown in the browser.
* Unfortunately, the widget is inseparable from its model, and the
* model is all we need. */
@@ -592,15 +589,20 @@ mail_browser_constructed (GObject *object)
browser->priv->message_list, "message-list-built",
G_CALLBACK (mail_browser_message_list_built_cb), object);
+ display = g_object_new (E_TYPE_MAIL_DISPLAY,
+ "mode", E_MAIL_BROWSER (object)->priv->mode, NULL);
+
g_signal_connect_swapped (
- web_view, "popup-event",
+ display, "popup-event",
G_CALLBACK (mail_browser_popup_event_cb), object);
g_signal_connect_swapped (
- web_view, "status-message",
+ display, "status-message",
G_CALLBACK (mail_browser_status_message_cb), object);
- /* Add action groups before initializing the reader interface. */
+ widget = e_preview_pane_new (E_WEB_VIEW (display));
+ browser->priv->preview_pane = g_object_ref (widget);
+ gtk_widget_show (widget);
action_group = gtk_action_group_new (ACTION_GROUP_STANDARD);
gtk_action_group_set_translation_domain (action_group, domain);
@@ -613,6 +615,7 @@ mail_browser_constructed (GObject *object)
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
/* For easy access. Takes ownership of the reference. */
+
g_object_set_data_full (
object, ACTION_GROUP_STANDARD,
action_group, (GDestroyNotify) g_object_unref);
@@ -664,7 +667,6 @@ mail_browser_constructed (GObject *object)
container = widget;
- /* Create the status bar before connecting proxy widgets. */
widget = gtk_statusbar_new ();
gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
browser->priv->statusbar = g_object_ref (widget);
@@ -682,21 +684,9 @@ mail_browser_constructed (GObject *object)
gtk_style_context_add_class (
gtk_widget_get_style_context (widget),
- GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
-
- gtk_widget_show (GTK_WIDGET (web_view));
+ GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
- widget = e_preview_pane_new (web_view);
- gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
- browser->priv->preview_pane = g_object_ref (widget);
- gtk_widget_show (widget);
-
- search_bar = e_preview_pane_get_search_bar (E_PREVIEW_PANE (widget));
-
- g_signal_connect_swapped (
- search_bar, "changed",
- G_CALLBACK (em_format_queue_redraw),
- browser->priv->formatter);
+ gtk_container_add (GTK_CONTAINER (container), browser->priv->preview_pane);
/* Bind GObject properties to GSettings keys. */
@@ -713,8 +703,6 @@ mail_browser_constructed (GObject *object)
e_plugin_ui_register_manager (ui_manager, id, object);
e_plugin_ui_enable_manager (ui_manager, id);
- e_mail_reader_connect_headers (E_MAIL_READER (reader));
-
e_extensible_load_extensions (E_EXTENSIBLE (object));
}
@@ -772,14 +760,15 @@ mail_browser_get_hide_deleted (EMailReader *reader)
return !e_mail_browser_get_show_deleted (browser);
}
-static EMFormatHTML *
-mail_browser_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_browser_get_mail_display (EMailReader *reader)
{
- EMailBrowser *browser;
+ EMailBrowserPrivate *priv;
- browser = E_MAIL_BROWSER (reader);
+ priv = E_MAIL_BROWSER_GET_PRIVATE (E_MAIL_BROWSER (reader));
- return EM_FORMAT_HTML (browser->priv->formatter);
+ return E_MAIL_DISPLAY (e_preview_pane_get_web_view (
+ E_PREVIEW_PANE (priv->preview_pane)));
}
static GtkWidget *
@@ -916,6 +905,18 @@ e_mail_browser_class_init (EMailBrowserClass *class)
"Show deleted messages",
FALSE,
G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DISPLAY_MODE,
+ g_param_spec_int (
+ "display-mode",
+ "Display Mode",
+ NULL,
+ 0,
+ G_MAXINT,
+ EM_FORMAT_WRITE_MODE_NORMAL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
@@ -923,7 +924,7 @@ e_mail_browser_reader_init (EMailReaderInterface *interface)
{
interface->get_action_group = mail_browser_get_action_group;
interface->get_backend = mail_browser_get_backend;
- interface->get_formatter = mail_browser_get_formatter;
+ interface->get_mail_display = mail_browser_get_mail_display;
interface->get_hide_deleted = mail_browser_get_hide_deleted;
interface->get_message_list = mail_browser_get_message_list;
interface->get_popup_menu = mail_browser_get_popup_menu;
@@ -936,7 +937,6 @@ static void
e_mail_browser_init (EMailBrowser *browser)
{
browser->priv = E_MAIL_BROWSER_GET_PRIVATE (browser);
- browser->priv->formatter = em_format_html_display_new ();
gtk_window_set_title (GTK_WINDOW (browser), _("Evolution"));
gtk_window_set_default_size (GTK_WINDOW (browser), 600, 400);
@@ -948,13 +948,22 @@ e_mail_browser_init (EMailBrowser *browser)
}
GtkWidget *
-e_mail_browser_new (EMailBackend *backend)
+e_mail_browser_new (EMailBackend *backend,
+ CamelFolder *folder,
+ const gchar *msg_uid,
+ EMFormatWriteMode mode)
{
+ GtkWidget *widget;
+
g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
- return g_object_new (
+ widget= g_object_new (
E_TYPE_MAIL_BROWSER,
- "backend", backend, NULL);
+ "backend", backend,
+ "display-mode", mode,
+ NULL);
+
+ return widget;
}
void
diff --git a/mail/e-mail-browser.h b/mail/e-mail-browser.h
index c09c85b1c8..88f8174a9d 100644
--- a/mail/e-mail-browser.h
+++ b/mail/e-mail-browser.h
@@ -24,6 +24,7 @@
#include <mail/e-mail-backend.h>
#include <misc/e-focus-tracker.h>
+#include <mail/e-mail-display.h>
/* Standard GObject macros */
#define E_TYPE_MAIL_BROWSER \
@@ -60,7 +61,10 @@ struct _EMailBrowserClass {
};
GType e_mail_browser_get_type (void);
-GtkWidget * e_mail_browser_new (EMailBackend *backend);
+GtkWidget * e_mail_browser_new (EMailBackend *backend,
+ CamelFolder *folder,
+ const gchar *message_uid,
+ EMFormatWriteMode mode);
void e_mail_browser_close (EMailBrowser *browser);
gboolean e_mail_browser_get_show_deleted (EMailBrowser *browser);
void e_mail_browser_set_show_deleted (EMailBrowser *browser,
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 7461e595f3..07f45ad461 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -23,14 +23,33 @@
#include <config.h>
#endif
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
#include "e-mail-display.h"
#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include "e-util/e-marshal.h"
#include "e-util/e-util.h"
#include "e-util/e-plugin-ui.h"
#include "mail/em-composer-utils.h"
#include "mail/em-utils.h"
+#include "mail/e-mail-request.h"
+#include "mail/em-format-html-display.h"
+#include "mail/e-mail-attachment-bar.h"
+#include "widgets/misc/e-attachment-button.h"
+
+#include <camel/camel.h>
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-requester.h>
+
+#include <JavaScriptCore/JavaScript.h>
+
+#define d(x)
+
+G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW)
#define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -38,13 +57,29 @@
struct _EMailDisplayPrivate {
EMFormatHTML *formatter;
+
+ EMFormatWriteMode mode;
+ gboolean headers_collapsable;
+ gboolean headers_collapsed;
+
+ GtkActionGroup *mailto_actions;
+ GtkActionGroup *images_actions;
+
+ gint force_image_load: 1;
};
enum {
PROP_0,
- PROP_FORMATTER
+ PROP_FORMATTER,
+ PROP_MODE,
+ PROP_HEADERS_COLLAPSABLE,
+ PROP_HEADERS_COLLAPSED,
};
+static gpointer parent_class;
+
+static CamelDataCache *emd_global_http_cache = 0;
+
static const gchar *ui =
"<ui>"
" <popup name='context'>"
@@ -61,6 +96,15 @@ static const gchar *ui =
" </popup>"
"</ui>";
+static const gchar *image_ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='custom-actions-2'>"
+" <menuitem action='image-save'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
static GtkActionEntry mailto_entries[] = {
{ "add-to-address-book",
@@ -88,7 +132,7 @@ static GtkActionEntry mailto_entries[] = {
NULL,
N_("Send _Reply To..."),
NULL,
- N_("Send a reply message to this address"),
+ N_("Send a reply message to this address"),
NULL /* Handled by EMailReader */ },
/*** Menus ***/
@@ -101,7 +145,54 @@ static GtkActionEntry mailto_entries[] = {
NULL }
};
-G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW)
+static GtkActionEntry image_entries[] = {
+
+ { "image-save",
+ GTK_STOCK_SAVE,
+ N_("Save _Image..."),
+ NULL,
+ N_("Save the image to a file"),
+ NULL /* Handled by EMailReader */ },
+
+};
+
+static void
+mail_display_webview_update_actions (EWebView *web_view,
+ gpointer user_data)
+{
+ const gchar *image_src;
+ gboolean visible;
+ GtkAction *action;
+
+ g_return_if_fail (web_view != NULL);
+
+ image_src = e_web_view_get_cursor_image_src (web_view);
+ visible = image_src && g_str_has_prefix (image_src, "cid:");
+ if (!visible && image_src) {
+ CamelStream *image_stream;
+
+ image_stream = camel_data_cache_get (emd_global_http_cache, "http", image_src, NULL);
+
+ visible = image_stream != NULL;
+
+ if (image_stream)
+ g_object_unref (image_stream);
+ }
+
+ action = e_web_view_get_action (web_view, "image-save");
+ if (action)
+ gtk_action_set_visible (action, visible);
+}
+
+static void
+formatter_image_loading_policy_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ EMailDisplay *display = user_data;
+
+ e_mail_display_load_images (display);
+}
static void
mail_display_update_formatter_colors (EMailDisplay *display)
@@ -115,6 +206,9 @@ mail_display_update_formatter_colors (EMailDisplay *display)
state = gtk_widget_get_state (GTK_WIDGET (display));
formatter = display->priv->formatter;
+ if (!display->priv->formatter)
+ return;
+
style = gtk_widget_get_style (GTK_WIDGET (display));
if (style == NULL)
return;
@@ -156,6 +250,21 @@ mail_display_set_property (GObject *object,
E_MAIL_DISPLAY (object),
g_value_get_object (value));
return;
+ case PROP_MODE:
+ e_mail_display_set_mode (
+ E_MAIL_DISPLAY (object),
+ g_value_get_int (value));
+ return;
+ case PROP_HEADERS_COLLAPSABLE:
+ e_mail_display_set_headers_collapsable (
+ E_MAIL_DISPLAY (object),
+ g_value_get_boolean (value));
+ return;
+ case PROP_HEADERS_COLLAPSED:
+ e_mail_display_set_headers_collapsed (
+ E_MAIL_DISPLAY (object),
+ g_value_get_boolean (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -173,6 +282,21 @@ mail_display_get_property (GObject *object,
value, e_mail_display_get_formatter (
E_MAIL_DISPLAY (object)));
return;
+ case PROP_MODE:
+ g_value_set_int (
+ value, e_mail_display_get_mode (
+ E_MAIL_DISPLAY (object)));
+ return;
+ case PROP_HEADERS_COLLAPSABLE:
+ g_value_set_boolean (
+ value, e_mail_display_get_headers_collapsable (
+ E_MAIL_DISPLAY (object)));
+ return;
+ case PROP_HEADERS_COLLAPSED:
+ g_value_set_boolean (
+ value, e_mail_display_get_headers_collapsed (
+ E_MAIL_DISPLAY (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -191,14 +315,14 @@ mail_display_dispose (GObject *object)
}
/* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object);
+ G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
mail_display_realize (GtkWidget *widget)
{
/* Chain up to parent's realize() method. */
- GTK_WIDGET_CLASS (e_mail_display_parent_class)->realize (widget);
+ GTK_WIDGET_CLASS (parent_class)->realize (widget);
mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
}
@@ -207,62 +331,28 @@ static void
mail_display_style_set (GtkWidget *widget,
GtkStyle *previous_style)
{
- EMailDisplayPrivate *priv;
+ EMailDisplay *display = E_MAIL_DISPLAY (widget);
- priv = E_MAIL_DISPLAY_GET_PRIVATE (widget);
+ mail_display_update_formatter_colors (display);
/* Chain up to parent's style_set() method. */
- GTK_WIDGET_CLASS (e_mail_display_parent_class)->
- style_set (widget, previous_style);
-
- mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
- em_format_queue_redraw (EM_FORMAT (priv->formatter));
-}
-
-static void
-mail_display_load_string (EWebView *web_view,
- const gchar *string)
-{
- EMailDisplayPrivate *priv;
-
- priv = E_MAIL_DISPLAY_GET_PRIVATE (web_view);
- g_return_if_fail (priv->formatter != NULL);
-
- if (em_format_busy (EM_FORMAT (priv->formatter)))
- return;
-
- /* Chain up to parent's load_string() method. */
- E_WEB_VIEW_CLASS (e_mail_display_parent_class)->
- load_string (web_view, string);
-}
-
-static void
-mail_display_url_requested (GtkHTML *html,
- const gchar *uri,
- GtkHTMLStream *stream)
-{
- /* XXX Sadly, we must block the default method
- * until EMFormatHTML is made asynchronous. */
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
}
static gboolean
mail_display_process_mailto (EWebView *web_view,
- const gchar *mailto_uri)
+ const gchar *mailto_uri,
+ gpointer user_data)
{
- g_return_val_if_fail (web_view != NULL, FALSE);
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
g_return_val_if_fail (mailto_uri != NULL, FALSE);
- g_return_val_if_fail (E_IS_MAIL_DISPLAY (web_view), FALSE);
if (g_ascii_strncasecmp (mailto_uri, "mailto:", 7) == 0) {
- EMailDisplayPrivate *priv;
EMFormat *format;
CamelFolder *folder = NULL;
EShell *shell;
- priv = E_MAIL_DISPLAY_GET_PRIVATE (web_view);
- g_return_val_if_fail (priv->formatter != NULL, FALSE);
-
- format = EM_FORMAT (priv->formatter);
+ format = (EMFormat *) E_MAIL_DISPLAY (web_view)->priv->formatter;
if (format != NULL && format->folder != NULL)
folder = format->folder;
@@ -277,79 +367,825 @@ mail_display_process_mailto (EWebView *web_view,
return FALSE;
}
-static void
-mail_display_link_clicked (GtkHTML *html,
- const gchar *uri)
+static gboolean
+mail_display_link_clicked (WebKitWebView *web_view,
+ WebKitWebFrame *frame,
+ WebKitNetworkRequest *request,
+ WebKitWebNavigationAction *navigation_action,
+ WebKitWebPolicyDecision *policy_decision,
+ gpointer user_data)
{
- EMailDisplayPrivate *priv;
+ EMailDisplay *display;
+ const gchar *uri = webkit_network_request_get_uri (request);
- priv = E_MAIL_DISPLAY_GET_PRIVATE (html);
- g_return_if_fail (priv->formatter != NULL);
-
- if (g_str_has_prefix (uri, "##")) {
- guint32 flags;
-
- flags = priv->formatter->header_wrap_flags;
-
- if (strcmp (uri, "##TO##") == 0) {
- if (!(flags & EM_FORMAT_HTML_HEADER_TO))
- flags |= EM_FORMAT_HTML_HEADER_TO;
- else
- flags &= ~EM_FORMAT_HTML_HEADER_TO;
- } else if (strcmp (uri, "##CC##") == 0) {
- if (!(flags & EM_FORMAT_HTML_HEADER_CC))
- flags |= EM_FORMAT_HTML_HEADER_CC;
- else
- flags &= ~EM_FORMAT_HTML_HEADER_CC;
- } else if (strcmp (uri, "##BCC##") == 0) {
- if (!(flags & EM_FORMAT_HTML_HEADER_BCC))
- flags |= EM_FORMAT_HTML_HEADER_BCC;
- else
- flags &= ~EM_FORMAT_HTML_HEADER_BCC;
- } else if (strcmp (uri, "##HEADERS##") == 0) {
- EMFormatHTMLHeadersState state;
-
- state = em_format_html_get_headers_state (
- priv->formatter);
-
- if (state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED)
- state = EM_FORMAT_HTML_HEADERS_STATE_EXPANDED;
- else
- state = EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED;
-
- em_format_html_set_headers_state (
- priv->formatter, state);
- }
+ display = E_MAIL_DISPLAY (web_view);
+ if (display->priv->formatter == NULL)
+ return FALSE;
- priv->formatter->header_wrap_flags = flags;
- em_format_queue_redraw (EM_FORMAT (priv->formatter));
-
- } else if (mail_display_process_mailto (E_WEB_VIEW (html), uri)) {
+ if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) {
/* do nothing, function handled the "mailto:" uri already */
- } else if (*uri == '#')
- gtk_html_jump_to_anchor (html, uri + 1);
+ webkit_web_policy_decision_ignore (policy_decision);
+ return TRUE;
- else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0)
+ } else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
/* ignore */ ;
+ webkit_web_policy_decision_ignore (policy_decision);
+ return TRUE;
- else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0)
+ } else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
/* ignore */ ;
+ webkit_web_policy_decision_ignore (policy_decision);
+ return TRUE;
+
+ }
+
+ /* Let webkit handle it */
+ return FALSE;
+}
+
+static void
+webkit_request_load_from_file (WebKitNetworkRequest *request,
+ const gchar *path)
+{
+ gchar *data = NULL;
+ gsize length = 0;
+ gboolean status;
+ gchar *b64, *new_uri;
+ gchar *ct;
+
+ status = g_file_get_contents (path, &data, &length, NULL);
+ if (!status)
+ return;
+
+ b64 = g_base64_encode ((guchar *) data, length);
+ ct = g_content_type_guess (path, NULL, 0, NULL);
+
+ new_uri = g_strdup_printf ("data:%s;base64,%s", ct, b64);
+ webkit_network_request_set_uri (request, new_uri);
+
+ g_free (b64);
+ g_free (new_uri);
+ g_free (ct);
+ g_free (data);
+}
+
+static void
+mail_display_resource_requested (WebKitWebView *web_view,
+ WebKitWebFrame *frame,
+ WebKitWebResource *resource,
+ WebKitNetworkRequest *request,
+ WebKitNetworkResponse *response,
+ gpointer user_data)
+{
+ EMailDisplay *display = E_MAIL_DISPLAY (web_view);
+ EMFormat *formatter = EM_FORMAT (display->priv->formatter);
+ const gchar *uri = webkit_network_request_get_uri (request);
+
+ if (!formatter) {
+ webkit_network_request_set_uri (request, "invalid://uri");
+ return;
+ }
+
+ /* Redirect cid:part_id to mail://mail_id/cid:part_id */
+ if (g_str_has_prefix (uri, "cid:")) {
+
+ /* Always write raw content of CID object */
+ gchar *new_uri = em_format_build_mail_uri (formatter->folder,
+ formatter->message_uid,
+ "part_id", G_TYPE_STRING, uri,
+ "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW, NULL);
+
+ webkit_network_request_set_uri (request, new_uri);
+
+ g_free (new_uri);
+
+ /* WebKit won't allow to load a local file when displaing "remote" mail://,
+ protocol, so we need to handle this manually */
+ } else if (g_str_has_prefix (uri, "file:")) {
+ gchar *path;
+
+ path = g_filename_from_uri (uri, NULL, NULL);
+ if (!path)
+ return;
+
+ webkit_request_load_from_file (request, path);
+
+ g_free (path);
+
+ /* Redirect http(s) request to evo-http(s) protocol. See EMailRequest for
+ * further details about this. */
+ } else if (g_str_has_prefix (uri, "http:") || g_str_has_prefix (uri, "https")) {
+
+ gchar *new_uri, *mail_uri, *enc;
+ SoupURI *soup_uri;
+ GHashTable *query;
+ gchar *uri_md5;
+ CamelStream *stream;
+
+ /* Open Evolution's cache */
+ uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+ stream = camel_data_cache_get (
+ emd_global_http_cache, "http", uri_md5, NULL);
+ g_free (uri_md5);
+
+ /* If the URI is not cached and we are not allowed to load it
+ * then redirect to invalid URI, so that webkit would display
+ * a native placeholder for it. */
+ if (!stream && !display->priv->force_image_load &&
+ !em_format_html_can_load_images (display->priv->formatter)) {
+ webkit_network_request_set_uri (request, "invalid://protocol");
+ return;
+ }
+
+ new_uri = g_strconcat ("evo-", uri, NULL);
+ mail_uri = em_format_build_mail_uri (formatter->folder,
+ formatter->message_uid, NULL, NULL);
+
+ soup_uri = soup_uri_new (new_uri);
+ if (soup_uri->query) {
+ query = soup_form_decode (soup_uri->query);
+ } else {
+ query = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+ }
+ enc = soup_uri_encode (mail_uri, NULL);
+ g_hash_table_insert (query, g_strdup ("__evo-mail"), enc);
+
+ if (display->priv->force_image_load) {
+ g_hash_table_insert (query,
+ g_strdup ("__evo-load-images"),
+ g_strdup ("true"));
+ }
+
+ g_free (mail_uri);
+
+ soup_uri_set_query_from_form (soup_uri, query);
+ g_free (new_uri);
+
+ new_uri = soup_uri_to_string (soup_uri, FALSE);
+ webkit_network_request_set_uri (request, new_uri);
+
+ g_free (new_uri);
+ soup_uri_free (soup_uri);
+ g_hash_table_unref (query);
+ }
+}
+
+static WebKitDOMElement *
+find_element_by_id (WebKitDOMDocument *document,
+ const gchar *id)
+{
+ WebKitDOMNodeList *frames;
+ WebKitDOMElement *element;
+ gulong i, length;
+
+ /* Try to look up the element in this DOM document */
+ element = webkit_dom_document_get_element_by_id (document, id);
+ if (element)
+ return element;
+
+ /* If the element is not here then recursively scan all frames */
+ frames = webkit_dom_document_get_elements_by_tag_name(document, "iframe");
+ length = webkit_dom_node_list_get_length (frames);
+ for (i = 0; i < length; i++) {
+
+ WebKitDOMHTMLIFrameElement *iframe =
+ WEBKIT_DOM_HTML_IFRAME_ELEMENT (
+ webkit_dom_node_list_item (frames, i));
+
+ WebKitDOMDocument *frame_doc =
+ webkit_dom_html_iframe_element_get_content_document (iframe);
+
+ WebKitDOMElement *el =
+ find_element_by_id (frame_doc, id);
+
+ if (el)
+ return el;
+ }
+
+ return NULL;
+}
+
+static void
+mail_display_plugin_widget_resize (GObject *object,
+ gpointer dummy,
+ EMailDisplay *display)
+{
+ GtkWidget *widget;
+ WebKitDOMElement *parent_element;
+ gchar *dim;
+ gint height;
+
+ widget = GTK_WIDGET (object);
+ gtk_widget_get_preferred_height (widget, &height, NULL);
+ parent_element = g_object_get_data (object, "parent_element");
+
+ if (!parent_element || !WEBKIT_DOM_IS_ELEMENT (parent_element)) {
+ d(printf("%s: %s does not have (valid) parent element!\n",
+ G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
+ return;
+ }
+
+ /* Int -> Str */
+ dim = g_strdup_printf ("%d", height);
+
+ /* Set height of the containment <object> to match height of the
+ * GtkWidget it contains */
+ webkit_dom_html_object_element_set_height (
+ WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim);
+ g_free (dim);
+}
+
+static void
+mail_display_plugin_widget_realize_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ /* Initial resize of the <object> element when the widget
+ * is displayed for the first time. */
+ mail_display_plugin_widget_resize (G_OBJECT (widget), NULL, user_data);
+}
+
+static void
+plugin_widget_set_parent_element (GtkWidget *widget,
+ EMailDisplay *display)
+{
+ const gchar *uri;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *element;
+
+ uri = g_object_get_data (G_OBJECT (widget), "uri");
+ if (!uri || !*uri)
+ return;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
+ element = find_element_by_id (document, uri);
+
+ if (!element || !WEBKIT_DOM_IS_ELEMENT (element)) {
+ g_warning ("Failed to find parent <object> for '%s' - no ID set?", uri);
+ return;
+ }
+
+ /* Assign the WebKitDOMElement to "parent_element" data of the GtkWidget
+ * and the GtkWidget to "widget" data of the DOM Element */
+ g_object_set_data (G_OBJECT (widget), "parent_element", element);
+ g_object_set_data (G_OBJECT (element), "widget", widget);
+}
+
+static void
+attachment_button_expanded (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ EAttachmentButton *button = E_ATTACHMENT_BUTTON (object);
+ WebKitDOMElement *attachment = user_data;
+ WebKitDOMCSSStyleDeclaration *css;
+ gboolean expanded;
+
+ d(printf("Attachment button %s (%p) expansion state toggled!\n",
+ (gchar *) g_object_get_data (object, "uri"), object));
+
+ expanded = e_attachment_button_get_expanded (button) &&
+ gtk_widget_get_visible (GTK_WIDGET (button));
+
+ if (!WEBKIT_DOM_IS_ELEMENT (attachment)) {
+ d(printf("%s: Parent element for button %s does not exist!\n",
+ G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
+ return;
+ }
+
+ /* Show or hide the DIV which contains the attachment (iframe, image...) */
+ css = webkit_dom_element_get_style (attachment);
+ webkit_dom_css_style_declaration_set_property (
+ css, "display", expanded ? "block" : "none", "", NULL);
+}
+
+static void
+constraint_widget_visibility (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (object);
+ EAttachmentButton *button = user_data;
+
+ gboolean can_show = e_attachment_button_get_expanded (button);
+ gboolean is_visible = gtk_widget_get_visible (widget);
+
+ if (is_visible && !can_show)
+ gtk_widget_hide (widget);
+ else if (!is_visible && can_show)
+ gtk_widget_show (widget);
+
+ /* Otherwise it's OK */
+}
+
+static void
+bind_iframe_content_visibility (EAttachmentButton *button,
+ WebKitDOMElement *iframe)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *nodes;
+ gulong i, length;
+
+ if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe))
+ return;
+
+ document = webkit_dom_html_iframe_element_get_content_document (
+ WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
+ nodes = webkit_dom_document_get_elements_by_tag_name (document, "object");
+ length = webkit_dom_node_list_get_length (nodes);
- else {
- /* Chain up to parent's link_clicked() method. */
- GTK_HTML_CLASS (e_mail_display_parent_class)->
- link_clicked (html, uri);
+ d(printf("Found %ld objects within iframe %s\n", length,
+ webkit_dom_html_iframe_element_get_name (
+ WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe))));
+
+ /* Iterate through all <object>s and bind visibility of their widget
+ * with expanded-state of related attachment button */
+ for (i = 0; i < length; i++) {
+
+ WebKitDOMNode *node = webkit_dom_node_list_item (nodes, i);
+ GtkWidget *widget;
+
+ widget = g_object_get_data (G_OBJECT (node), "widget");
+ if (!widget)
+ continue;
+
+ d(printf("Binding visibility of widget %s (%p) with button %s (%p)\n",
+ (gchar *) g_object_get_data (G_OBJECT (widget), "uri"), widget,
+ (gchar *) g_object_get_data (G_OBJECT (button), "uri"), button));
+
+ g_object_bind_property (
+ button, "expanded",
+ widget, "visible",
+ G_BINDING_SYNC_CREATE);
+
+ /* Ensure that someone won't attempt to _show() the widget when
+ * it is supposed to be hidden and vice versa. */
+ g_signal_connect (widget, "notify::visible",
+ G_CALLBACK (constraint_widget_visibility), button);
}
}
static void
+bind_attachment_iframe_visibility (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitWebFrame *webframe;
+ const gchar *frame_name;
+ gchar *button_uri;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *attachment;
+ WebKitDOMElement *button_element;
+ WebKitDOMNodeList *nodes;
+ gulong i, length;
+ GtkWidget *button;
+
+ /* Whenever an <iframe> is loaded, bind visibility of all GtkWidgets
+ * the document within the <iframe> contains with "expanded" property
+ * of the EAttachmentButton */
+
+ webframe = WEBKIT_WEB_FRAME (object);
+ if (webkit_web_frame_get_load_status (webframe) != WEBKIT_LOAD_FINISHED)
+ return;
+
+ frame_name = webkit_web_frame_get_name (webframe);
+
+ d(printf("Rebinding visibility of frame %s because it's URL changed\n",
+ frame_name));
+
+ /* Get DOMDocument of the main document */
+ document = webkit_web_view_get_dom_document (
+ webkit_web_frame_get_web_view (webframe));
+ if (!document)
+ return;
+
+ /* Find the <DIV> containing the <iframe> and related EAttachmentButton
+ * within the DOM */
+ attachment = find_element_by_id (document, frame_name);
+ if (!attachment)
+ return;
+
+ button_uri = g_strconcat (frame_name, ".attachment_button", NULL);
+ button_element = find_element_by_id (document, button_uri);
+ g_free (button_uri);
+ if (!button_element)
+ return;
+
+ button = g_object_get_data (G_OBJECT (button_element), "widget");
+
+ /* Get <iframe> representing the attachment content */
+ nodes = webkit_dom_element_get_elements_by_tag_name (attachment, "iframe");
+ length = webkit_dom_node_list_get_length (nodes);
+ for (i = 0; i < length; i++) {
+
+ WebKitDOMNode *node =
+ webkit_dom_node_list_item (nodes, i);
+
+ if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node))
+ continue;
+
+ /* Bind visibility of all GtkWidget within the
+ * iframe with "expanded" property of the button */
+ bind_iframe_content_visibility (
+ E_ATTACHMENT_BUTTON (button),
+ WEBKIT_DOM_ELEMENT (node));
+ }
+}
+
+static GtkWidget *
+mail_display_plugin_widget_requested (WebKitWebView *web_view,
+ gchar *mime_type,
+ gchar *uri,
+ GHashTable *param,
+ gpointer user_data)
+{
+ EMFormat *emf;
+ EMailDisplay *display;
+ EMFormatPURI *puri;
+ GtkWidget *widget;
+ gchar *puri_uri;
+
+ puri_uri = g_hash_table_lookup (param, "data");
+ if (!puri_uri || !g_str_has_prefix (uri, "mail://"))
+ return NULL;
+
+ display = E_MAIL_DISPLAY (web_view);
+ emf = (EMFormat *) display->priv->formatter;
+
+ puri = em_format_find_puri (emf, puri_uri);
+ if (!puri) {
+ return NULL;
+ }
+
+ if (puri->widget_func)
+ widget = puri->widget_func (emf, puri, NULL);
+ else
+ widget = NULL;
+
+ if (!widget)
+ return NULL;
+
+ if (E_IS_ATTACHMENT_BUTTON (widget)) {
+ /* Attachment button has URI different then the actual PURI because
+ * that URI identifies the attachment itself */
+ gchar *button_uri = g_strconcat (puri_uri, ".attachment_button", NULL);
+ g_object_set_data_full (G_OBJECT (widget), "uri",
+ button_uri, (GDestroyNotify) g_free);
+ } else {
+ g_object_set_data_full (G_OBJECT (widget), "uri",
+ g_strdup (puri_uri), (GDestroyNotify) g_free);
+ }
+
+ /* Set widget's <object> container as GObject data "parent_element" */
+ plugin_widget_set_parent_element (widget, display);
+
+ /* Resizing a GtkWidget requires changing size of parent
+ * <object> HTML element in DOM. */
+ g_signal_connect (widget, "realize",
+ G_CALLBACK (mail_display_plugin_widget_realize_cb), display);
+ g_signal_connect (widget, "size-allocate",
+ G_CALLBACK (mail_display_plugin_widget_resize), display);
+
+ /* Embed the attachment bar into the GtkBox before we do anything
+ * further with the widget. */
+ if (E_IS_MAIL_ATTACHMENT_BAR (widget)) {
+
+ /* When EMailAttachmentBar is expanded/collapsed it does not
+ * emit size-allocate signal despite it changes it's height. */
+ GtkWidget *box = NULL;
+
+ /* Only when packed in box, EMailAttachmentBar reports correct
+ * height */
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (widget, "notify::expanded",
+ G_CALLBACK (mail_display_plugin_widget_resize), display);
+ g_signal_connect (widget, "notify::active-view",
+ G_CALLBACK (mail_display_plugin_widget_resize), display);
+
+ /* Show the EAttachmentBar but not the containing layout */
+ gtk_widget_show (widget);
+
+ widget = box;
+
+ } else if (E_IS_ATTACHMENT_BUTTON (widget)) {
+
+ /* Bind visibility of DOM element containing related
+ * attachment with 'expanded' property of this
+ * attachment button. */
+ WebKitDOMElement *attachment;
+ WebKitDOMDocument *document;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
+ attachment = find_element_by_id (document, puri_uri);
+ if (!attachment) {
+ e_attachment_button_set_expandable (
+ E_ATTACHMENT_BUTTON (widget), FALSE);
+ } else {
+ const CamelContentDisposition *disposition;
+ WebKitDOMNodeList *nodes;
+ gulong i, length;
+
+ /* Show/hide the attachment when the EAttachmentButton
+ * is expanded/collapsed or shown/hidden */
+ g_signal_connect_data (widget, "notify::expanded",
+ G_CALLBACK (attachment_button_expanded),
+ g_object_ref (attachment), (GClosureNotify) g_object_unref, 0);
+ g_signal_connect_data (widget, "notify::visible",
+ G_CALLBACK (attachment_button_expanded),
+ g_object_ref (attachment), (GClosureNotify) g_object_unref, 0);
+ /* Initial synchronization */
+ attachment_button_expanded (G_OBJECT (widget),
+ NULL, attachment);
+
+ /* Find all <iframes> within the attachment and bind
+ * it's visiblity to expanded state of the attachment btn */
+ nodes = webkit_dom_element_get_elements_by_tag_name (
+ attachment, "iframe");
+ length = webkit_dom_node_list_get_length (nodes);
+ for (i = 0; i < length; i++) {
+
+ WebKitDOMNode *node =
+ webkit_dom_node_list_item (nodes, i);
+
+ if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node))
+ continue;
+
+ bind_iframe_content_visibility (
+ E_ATTACHMENT_BUTTON (widget),
+ WEBKIT_DOM_ELEMENT (node));
+ }
+
+ /* Expand inlined attachments */
+ disposition =
+ camel_mime_part_get_content_disposition (puri->part);
+ if (disposition &&
+ g_ascii_strncasecmp (
+ disposition->disposition, "inline", 6) == 0) {
+
+ e_attachment_button_set_expanded (
+ E_ATTACHMENT_BUTTON (widget), TRUE);
+ }
+ }
+ }
+
+ d(printf("Created widget %s (%p)\n", puri_uri, widget));
+ return widget;
+}
+
+static void
+toggle_headers_visibility (WebKitDOMElement *button,
+ WebKitDOMEvent *event,
+ WebKitWebView *web_view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMElement *short_headers, *full_headers;
+ WebKitDOMCSSStyleDeclaration *css_short, *css_full;
+ gboolean expanded;
+ const gchar *path;
+
+ document = webkit_web_view_get_dom_document (web_view);
+
+ short_headers = webkit_dom_document_get_element_by_id (
+ document, "__evo-short-headers");
+ if (!short_headers)
+ return;
+
+ css_short = webkit_dom_element_get_style (short_headers);
+
+ full_headers = webkit_dom_document_get_element_by_id (
+ document, "__evo-full-headers");
+ if (!full_headers)
+ return;
+
+ css_full = webkit_dom_element_get_style (full_headers);
+
+ expanded = (g_strcmp0 (webkit_dom_css_style_declaration_get_property_value (
+ css_full, "display"), "block") == 0);
+
+ webkit_dom_css_style_declaration_set_property (css_full, "display",
+ expanded ? "none" : "block", "", NULL);
+ webkit_dom_css_style_declaration_set_property (css_short, "display",
+ expanded ? "block" : "none", "", NULL);
+
+ if (expanded)
+ path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
+ else
+ path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+
+ e_mail_display_set_headers_collapsed (E_MAIL_DISPLAY (web_view), expanded);
+
+ d(printf("Headers %s!\n", expanded ? "collapsed" : "expanded"));
+}
+
+static const gchar* addresses[] = { "to", "cc", "bcc" };
+
+static void
+toggle_address_visibility (WebKitDOMElement *button,
+ WebKitDOMEvent *event,
+ const gchar *address)
+{
+ WebKitDOMElement *full_addr, *ellipsis;
+ WebKitDOMCSSStyleDeclaration *css_full, *css_ellipsis;
+ WebKitDOMDocument *document;
+ gchar *id;
+ const gchar *path;
+ gboolean expanded;
+
+ document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (button));
+
+ id = g_strconcat ("__evo-moreaddr-", address, NULL);
+ full_addr = webkit_dom_document_get_element_by_id (document, id);
+ g_free (id);
+
+ if (!full_addr)
+ return;
+
+ css_full = webkit_dom_element_get_style (full_addr);
+
+ id = g_strconcat ("__evo-moreaddr-ellipsis-", address, NULL);
+ ellipsis = webkit_dom_document_get_element_by_id (document, id);
+ g_free (id);
+
+ if (!ellipsis)
+ return;
+
+ css_ellipsis = webkit_dom_element_get_style (ellipsis);
+
+ expanded = (g_strcmp0 (
+ webkit_dom_css_style_declaration_get_property_value (
+ css_full, "display"), "inline") == 0);
+
+ webkit_dom_css_style_declaration_set_property (
+ css_full, "display", (expanded ? "none" : "inline"), "", NULL);
+ webkit_dom_css_style_declaration_set_property (
+ css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL);
+
+ if (expanded) {
+ path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
+ } else {
+ path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
+ }
+
+ if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) {
+ id = g_strconcat ("__evo-moreaddr-img-", address, NULL);
+ button = webkit_dom_document_get_element_by_id (document, id);
+ g_free (id);
+
+ if (!button)
+ return;
+ }
+
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
+
+}
+
+static void
+setup_DOM_bindings (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitWebView *web_view;
+ WebKitWebFrame *frame;
+ WebKitLoadStatus load_status;
+ WebKitDOMDocument *document;
+ WebKitDOMElement *button;
+ gint i = 0;
+
+ frame = WEBKIT_WEB_FRAME (object);
+ load_status = webkit_web_frame_get_load_status (frame);
+ if (load_status != WEBKIT_LOAD_FINISHED)
+ return;
+
+ web_view = webkit_web_frame_get_web_view (frame);
+ document = webkit_web_view_get_dom_document (web_view);
+
+ button = webkit_dom_document_get_element_by_id (
+ document, "__evo-collapse-headers-img");
+ if (!button)
+ return;
+
+ d(printf("Conntecting to __evo-collapsable-headers-img::click event\n"));
+
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (button), "click",
+ G_CALLBACK (toggle_headers_visibility), FALSE, web_view);
+
+ for (i = 0; i < 3; i++) {
+ gchar *id;
+ id = g_strconcat ("__evo-moreaddr-img-", addresses[i], NULL);
+ button = webkit_dom_document_get_element_by_id (document, id);
+ g_free (id);
+
+ if (!button)
+ continue;
+
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (button), "click",
+ G_CALLBACK (toggle_address_visibility), FALSE,
+ (gpointer) addresses[i]);
+
+ id = g_strconcat ("__evo-moreaddr-ellipsis-", addresses[i], NULL);
+ button = webkit_dom_document_get_element_by_id (document, id);
+ g_free (id);
+
+ if (!button)
+ continue;
+
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (button), "click",
+ G_CALLBACK (toggle_address_visibility), FALSE,
+ (gpointer) addresses[i]);
+ }
+}
+
+static void
+puri_bind_dom (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitWebFrame *frame;
+ WebKitLoadStatus load_status;
+ WebKitWebView *web_view;
+ WebKitDOMDocument *document;
+ EMailDisplay *display;
+ GList *iter;
+ EMFormat *emf;
+ const gchar *frame_puri;
+
+ frame = WEBKIT_WEB_FRAME (object);
+ load_status = webkit_web_frame_get_load_status (frame);
+
+ if (load_status != WEBKIT_LOAD_FINISHED)
+ return;
+
+ frame_puri = webkit_web_frame_get_name (frame);
+ web_view = webkit_web_frame_get_web_view (frame);
+ display = E_MAIL_DISPLAY (web_view);
+
+ emf = EM_FORMAT (display->priv->formatter);
+ if (!emf)
+ return;
+
+ iter = g_hash_table_lookup (
+ emf->mail_part_table,
+ webkit_web_frame_get_name (frame));
+
+ document = webkit_web_view_get_dom_document (web_view);
+
+ while (iter) {
+
+ EMFormatPURI *puri = iter->data;
+
+ if (!puri)
+ continue;
+
+ /* Iterate only the PURI rendered in the frame and all it's "subPURIs" */
+ if (!g_str_has_prefix (puri->uri, frame_puri))
+ break;
+
+ if (puri->bind_func) {
+ WebKitDOMElement *el = find_element_by_id (document, puri->uri);
+ if (el) {
+ d(printf("bind_func for %s\n", puri->uri));
+ puri->bind_func (el, puri);
+ }
+ }
+
+ iter = iter->next;
+ }
+}
+
+static void
+mail_display_frame_created (WebKitWebView *web_view,
+ WebKitWebFrame *frame,
+ gpointer user_data)
+{
+ d(printf("Frame %s created!\n", webkit_web_frame_get_name (frame)));
+
+ /* Re-bind visibility of this newly created <iframe> with
+ * related EAttachmentButton whenever content of this <iframe> is
+ * (re)loaded */
+ g_signal_connect (frame, "notify::load-status",
+ G_CALLBACK (bind_attachment_iframe_visibility), NULL);
+
+ /* Call bind_func of all PURIs written in this frame */
+ g_signal_connect (frame, "notify::load-status",
+ G_CALLBACK (puri_bind_dom), NULL);
+}
+
+static void
e_mail_display_class_init (EMailDisplayClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
- EWebViewClass *web_view_class;
- GtkHTMLClass *html_class;
+ parent_class = g_type_class_peek_parent (class);
g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
object_class = G_OBJECT_CLASS (class);
@@ -361,14 +1197,6 @@ e_mail_display_class_init (EMailDisplayClass *class)
widget_class->realize = mail_display_realize;
widget_class->style_set = mail_display_style_set;
- web_view_class = E_WEB_VIEW_CLASS (class);
- web_view_class->load_string = mail_display_load_string;
- web_view_class->process_mailto = mail_display_process_mailto;
-
- html_class = GTK_HTML_CLASS (class);
- html_class->url_requested = mail_display_url_requested;
- html_class->link_clicked = mail_display_link_clicked;
-
g_object_class_install_property (
object_class,
PROP_FORMATTER,
@@ -378,38 +1206,122 @@ e_mail_display_class_init (EMailDisplayClass *class)
NULL,
EM_TYPE_FORMAT_HTML,
G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MODE,
+ g_param_spec_int (
+ "mode",
+ "Display Mode",
+ NULL,
+ 0,
+ G_MAXINT,
+ EM_FORMAT_WRITE_MODE_NORMAL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HEADERS_COLLAPSABLE,
+ g_param_spec_boolean (
+ "headers-collapsable",
+ "Headers Collapsable",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_HEADERS_COLLAPSED,
+ g_param_spec_boolean (
+ "headers-collapsed",
+ "Headers Collapsed",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
}
static void
e_mail_display_init (EMailDisplay *display)
{
- EWebView *web_view;
GtkUIManager *ui_manager;
- GtkActionGroup *action_group;
GError *error = NULL;
-
- web_view = E_WEB_VIEW (display);
+ SoupSession *session;
+ SoupSessionFeature *feature;
+ const gchar *user_cache_dir;
+ WebKitWebSettings *settings;
+ WebKitWebFrame *main_frame;
display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
- /* EWebView's action groups are added during its instance
- * initialization function (like what we're in now), so it
- * is safe to fetch them this early in construction. */
- action_group = e_web_view_get_action_group (web_view, "mailto");
-
- /* We don't actually handle the actions we're adding.
- * EMailReader handles them. How devious is that? */
- gtk_action_group_add_actions (
- action_group, mailto_entries,
- G_N_ELEMENTS (mailto_entries), display);
-
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- ui_manager = e_web_view_get_ui_manager (web_view);
+ display->priv->force_image_load = FALSE;
+ display->priv->mailto_actions = gtk_action_group_new ("mailto");
+ gtk_action_group_add_actions (display->priv->mailto_actions, mailto_entries,
+ G_N_ELEMENTS (mailto_entries), NULL);
+
+ display->priv->images_actions = gtk_action_group_new ("image");
+ gtk_action_group_add_actions (display->priv->images_actions, image_entries,
+ G_N_ELEMENTS (image_entries), NULL);
+
+ webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (display), TRUE);
+
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (display));
+ g_object_set (settings, "enable-frame-flattening", TRUE, NULL);
+
+ g_signal_connect (display, "navigation-policy-decision-requested",
+ G_CALLBACK (mail_display_link_clicked), NULL);
+ g_signal_connect (display, "resource-request-starting",
+ G_CALLBACK (mail_display_resource_requested), NULL);
+ g_signal_connect (display, "process-mailto",
+ G_CALLBACK (mail_display_process_mailto), NULL);
+ g_signal_connect (display, "update-actions",
+ G_CALLBACK (mail_display_webview_update_actions), NULL);
+ g_signal_connect (display, "create-plugin-widget",
+ G_CALLBACK (mail_display_plugin_widget_requested), NULL);
+ g_signal_connect (display, "frame-created",
+ G_CALLBACK (mail_display_frame_created), NULL);
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
+ g_signal_connect (main_frame, "notify::load-status",
+ G_CALLBACK (setup_DOM_bindings), NULL);
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
+ g_signal_connect (main_frame, "notify::load-status",
+ G_CALLBACK (puri_bind_dom), NULL);
+
+ /* Because we are loading from a hard-coded string, there is
+ * no chance of I/O errors. Failure here implies a malformed
+ * UI definition. Full stop. */
+ ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display));
+ gtk_ui_manager_insert_action_group (ui_manager, display->priv->mailto_actions, 0);
gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
+
+ if (error != NULL) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ }
+
+ error = NULL;
+ gtk_ui_manager_insert_action_group (ui_manager, display->priv->images_actions, 0);
+ gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, &error);
+
+ if (error != NULL) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ }
+
+ /* Register our own handler for our own mail:// protocol */
+ session = webkit_get_default_session ();
+ feature = SOUP_SESSION_FEATURE (soup_requester_new ());
+ soup_session_feature_add_feature (feature, E_TYPE_MAIL_REQUEST);
+ soup_session_add_feature (session, feature);
+ g_object_unref (feature);
+
+ /* cache expiry - 2 hour access, 1 day max */
+ user_cache_dir = e_get_user_cache_dir ();
+ emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL);
+ if (emd_global_http_cache) {
+ camel_data_cache_set_expire_age (emd_global_http_cache, 24 * 60 * 60);
+ camel_data_cache_set_expire_access (emd_global_http_cache, 2 * 60 * 60);
+ }
}
EMFormatHTML *
@@ -427,10 +1339,264 @@ e_mail_display_set_formatter (EMailDisplay *display,
g_return_if_fail (E_IS_MAIL_DISPLAY (display));
g_return_if_fail (EM_IS_FORMAT_HTML (formatter));
- if (display->priv->formatter != NULL)
+ g_object_ref (formatter);
+
+ if (display->priv->formatter != NULL) {
+ /* The formatter might still exist after unrefing it, so
+ * we need to stop listening to it's request for redrawing */
+ g_signal_handlers_disconnect_by_func (
+ display->priv->formatter, e_mail_display_reload, display);
g_object_unref (display->priv->formatter);
+ }
+
+ display->priv->formatter = formatter;
+
+ mail_display_update_formatter_colors (display);
- display->priv->formatter = g_object_ref (formatter);
+ g_signal_connect (formatter, "notify::image-loading-policy",
+ G_CALLBACK (formatter_image_loading_policy_changed_cb), display);
+ g_signal_connect_swapped (formatter, "redraw-requested",
+ G_CALLBACK (e_mail_display_reload), display);
+ g_signal_connect_swapped (formatter, "notify::charset",
+ G_CALLBACK (e_mail_display_reload), display);
g_object_notify (G_OBJECT (display), "formatter");
}
+
+EMFormatWriteMode
+e_mail_display_get_mode (EMailDisplay *display)
+{
+ g_return_val_if_fail (E_IS_MAIL_DISPLAY (display),
+ EM_FORMAT_WRITE_MODE_NORMAL);
+
+ return display->priv->mode;
+}
+
+void
+e_mail_display_set_mode (EMailDisplay *display,
+ EMFormatWriteMode mode)
+{
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ if (display->priv->mode == mode)
+ return;
+
+ display->priv->mode = mode;
+
+ e_mail_display_reload (display);
+
+ g_object_notify (G_OBJECT (display), "mode");
+}
+
+gboolean
+e_mail_display_get_headers_collapsable (EMailDisplay *display)
+{
+ g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
+
+ return display->priv->headers_collapsable;
+}
+
+void
+e_mail_display_set_headers_collapsable (EMailDisplay *display,
+ gboolean collapsable)
+{
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ if (display->priv->headers_collapsable == collapsable)
+ return;
+
+ display->priv->headers_collapsable = collapsable;
+ e_mail_display_reload (display);
+
+ g_object_notify (G_OBJECT (display), "headers-collapsable");
+}
+
+gboolean
+e_mail_display_get_headers_collapsed (EMailDisplay *display)
+{
+ g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
+
+ if (display->priv->headers_collapsable)
+ return display->priv->headers_collapsed;
+
+ return FALSE;
+}
+
+void
+e_mail_display_set_headers_collapsed (EMailDisplay *display,
+ gboolean collapsed)
+{
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ if (display->priv->headers_collapsed == collapsed)
+ return;
+
+ display->priv->headers_collapsed = collapsed;
+
+ g_object_notify (G_OBJECT (display), "headers-collapsed");
+}
+
+void
+e_mail_display_load (EMailDisplay *display,
+ const gchar *msg_uri)
+{
+ EMFormat *emf;
+ gchar *uri;
+
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ display->priv->force_image_load = FALSE;
+
+ emf = EM_FORMAT (display->priv->formatter);
+
+ uri = em_format_build_mail_uri (emf->folder, emf->message_uid,
+ "mode", G_TYPE_INT, display->priv->mode,
+ "headers_collapsable", G_TYPE_BOOLEAN, display->priv->headers_collapsable,
+ "headers_collapsed", G_TYPE_BOOLEAN, display->priv->headers_collapsed,
+ NULL);
+
+ e_web_view_load_uri (E_WEB_VIEW (display), uri);
+
+ g_free (uri);
+}
+
+void
+e_mail_display_reload (EMailDisplay *display)
+{
+ EWebView *web_view;
+ const gchar *uri;
+ gchar *base;
+ GString *new_uri;
+ GHashTable *table;
+ GHashTableIter table_iter;
+ gpointer key, val;
+ gchar separator;
+
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ web_view = E_WEB_VIEW (display);
+ uri = e_web_view_get_uri (web_view);
+
+ if (!uri || !*uri)
+ return;
+
+ if (strstr(uri, "?") == NULL) {
+ e_web_view_reload (web_view);
+ return;
+ }
+
+ base = g_strndup (uri, strstr (uri, "?") - uri);
+ new_uri = g_string_new (base);
+ g_free (base);
+
+ table = soup_form_decode (strstr (uri, "?") + 1);
+ g_hash_table_insert (table, g_strdup ("mode"), g_strdup_printf ("%d", display->priv->mode));
+ g_hash_table_insert (table, g_strdup ("headers_collapsable"), g_strdup_printf ("%d", display->priv->headers_collapsable));
+ g_hash_table_insert (table, g_strdup ("headers_collapsed"), g_strdup_printf ("%d", display->priv->headers_collapsed));
+
+ g_hash_table_iter_init (&table_iter, table);
+ separator = '?';
+ while (g_hash_table_iter_next (&table_iter, &key, &val)) {
+ g_string_append_printf (new_uri, "%c%s=%s", separator,
+ (gchar *) key, (gchar *) val);
+
+ if (separator == '?')
+ separator = '&';
+ }
+
+ e_web_view_load_uri (web_view, new_uri->str);
+
+ g_string_free (new_uri, TRUE);
+ g_hash_table_destroy (table);
+}
+
+GtkAction *
+e_mail_display_get_action (EMailDisplay *display,
+ const gchar *action_name)
+{
+ GtkAction *action;
+
+ g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ action = gtk_action_group_get_action (display->priv->mailto_actions, action_name);
+ if (!action)
+ action = gtk_action_group_get_action (display->priv->images_actions, action_name);
+
+ return action;
+}
+
+void
+e_mail_display_set_status (EMailDisplay *display,
+ const gchar *status)
+{
+ gchar *str;
+
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ str = g_strdup_printf (
+ "<!DOCTYPE>"
+ "<html>"
+ "<head><title>Evolution Mail Display</title></head>"
+ "<body>"
+ "<table border=\"0\" width=\"100%%\" height=\"100%%\">"
+ "<tr height=\"100%%\" valign=\"middle\">"
+ "<td width=\"100%%\" align=\"center\">"
+ "<strong>%s</strong>"
+ "</td>"
+ "</tr>"
+ "</table>"
+ "</body>"
+ "</html>", status);
+
+ e_web_view_load_string (E_WEB_VIEW (display), str);
+ g_free (str);
+
+ gtk_widget_show_all (GTK_WIDGET (display));
+}
+
+gchar *
+e_mail_display_get_selection_plain_text (EMailDisplay *display,
+ gint *len)
+{
+ EWebView *web_view;
+ WebKitWebFrame *frame;
+ const gchar *frame_name;
+ GValue value = {0};
+ GType type;
+ const gchar *str;
+
+ g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
+
+ web_view = E_WEB_VIEW (display);
+ frame = webkit_web_view_get_focused_frame (WEBKIT_WEB_VIEW (web_view));
+ frame_name = webkit_web_frame_get_name (frame);
+
+ type = e_web_view_frame_exec_script (web_view, frame_name, "window.getSelection().toString()", &value);
+ g_return_val_if_fail (type == G_TYPE_STRING, NULL);
+
+ str = g_value_get_string (&value);
+
+ if (len)
+ *len = strlen (str);
+
+ return g_strdup (str);
+}
+
+void
+e_mail_display_load_images (EMailDisplay *display)
+{
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ display->priv->force_image_load = TRUE;
+ e_web_view_reload (E_WEB_VIEW (display));
+}
+
+void
+e_mail_display_set_force_load_images (EMailDisplay *display,
+ gboolean force_load_images)
+{
+ g_return_if_fail (E_IS_MAIL_DISPLAY (display));
+
+ display->priv->force_image_load = force_load_images;
+}
diff --git a/mail/e-mail-display.h b/mail/e-mail-display.h
index 1b71a9db7f..cbac1e37bb 100644
--- a/mail/e-mail-display.h
+++ b/mail/e-mail-display.h
@@ -22,8 +22,9 @@
#ifndef E_MAIL_DISPLAY_H
#define E_MAIL_DISPLAY_H
-#include <mail/em-format-html.h>
-#include <misc/e-web-view.h>
+#include <widgets/misc/e-web-view.h>
+#include <widgets/misc/e-search-bar.h>
+#include "em-format-html.h"
/* Standard GObject macros */
#define E_TYPE_MAIL_DISPLAY \
@@ -51,18 +52,53 @@ typedef struct _EMailDisplayClass EMailDisplayClass;
typedef struct _EMailDisplayPrivate EMailDisplayPrivate;
struct _EMailDisplay {
- EWebView parent;
+ EWebView web_view;
EMailDisplayPrivate *priv;
};
struct _EMailDisplayClass {
EWebViewClass parent_class;
+
};
-GType e_mail_display_get_type (void);
-EMFormatHTML * e_mail_display_get_formatter (EMailDisplay *display);
-void e_mail_display_set_formatter (EMailDisplay *display,
- EMFormatHTML *formatter);
+GType e_mail_display_get_type (void);
+EMFormatHTML * e_mail_display_get_formatter (EMailDisplay *display);
+void e_mail_display_set_formatter (EMailDisplay *display,
+ EMFormatHTML *formatter);
+
+void e_mail_display_set_mode (EMailDisplay *display,
+ EMFormatWriteMode mode);
+EMFormatWriteMode e_mail_display_get_mode (EMailDisplay *display);
+void e_mail_display_set_headers_collapsable
+ (EMailDisplay *display,
+ gboolean collapsable);
+gboolean e_mail_display_get_headers_collapsable
+ (EMailDisplay *display);
+void e_mail_display_set_headers_collapsed
+ (EMailDisplay *display,
+ gboolean collapsed);
+gboolean e_mail_display_get_headers_collapsed
+ (EMailDisplay *display);
+
+void e_mail_display_load (EMailDisplay *display,
+ const gchar *msg_uri);
+void e_mail_display_reload (EMailDisplay *display);
+
+GtkAction * e_mail_display_get_action (EMailDisplay *display,
+ const gchar *action_name);
+
+void e_mail_display_set_status (EMailDisplay *display,
+ const gchar *status);
+
+gchar * e_mail_display_get_selection_plain_text
+ (EMailDisplay *display,
+ gint *len);
+
+void e_mail_display_load_images (EMailDisplay *display);
+
+void e_mail_display_set_force_load_images
+ (EMailDisplay *display,
+ gboolean force_load_images);
G_END_DECLS
diff --git a/mail/e-mail-notebook-view.c b/mail/e-mail-notebook-view.c
index 64995bc8ef..86b47f6b92 100644
--- a/mail/e-mail-notebook-view.c
+++ b/mail/e-mail-notebook-view.c
@@ -900,21 +900,17 @@ mail_notebook_view_get_backend (EMailReader *reader)
return E_MAIL_BACKEND (shell_backend);
}
-static EMFormatHTML *
-mail_notebook_view_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_notebook_view_get_mail_display (EMailReader *reader)
{
- EMailNotebookView *notebook_view;
- EMailView *current_view;
+ EMailNotebookViewPrivate *priv;
- notebook_view = E_MAIL_NOTEBOOK_VIEW (reader);
- current_view = notebook_view->priv->current_view;
+ priv = E_MAIL_NOTEBOOK_VIEW (reader)->priv;
- if (current_view == NULL)
+ if (priv->current_view == NULL)
return NULL;
- reader = E_MAIL_READER (current_view);
-
- return e_mail_reader_get_formatter (reader);
+ return e_mail_reader_get_mail_display (E_MAIL_READER (priv->current_view));
}
static gboolean
@@ -1458,7 +1454,7 @@ e_mail_notebook_view_reader_init (EMailReaderInterface *interface)
interface->get_action_group = mail_notebook_view_get_action_group;
interface->get_alert_sink = mail_notebook_view_get_alert_sink;
interface->get_backend = mail_notebook_view_get_backend;
- interface->get_formatter = mail_notebook_view_get_formatter;
+ interface->get_mail_display = mail_notebook_view_get_mail_display;
interface->get_hide_deleted = mail_notebook_view_get_hide_deleted;
interface->get_message_list = mail_notebook_view_get_message_list;
interface->get_popup_menu = mail_notebook_view_get_popup_menu;
diff --git a/mail/e-mail-paned-view.c b/mail/e-mail-paned-view.c
index e1779a8c3e..5c7b356fca 100644
--- a/mail/e-mail-paned-view.c
+++ b/mail/e-mail-paned-view.c
@@ -59,8 +59,9 @@ struct _EMailPanedViewPrivate {
GtkWidget *scrolled_window;
GtkWidget *message_list;
GtkWidget *preview_pane;
+ GtkWidget *search_bar;
- EMFormatHTMLDisplay *formatter;
+ EMailDisplay *display;
GalViewInstance *view_instance;
/* ETable scrolling hack */
@@ -358,11 +359,6 @@ mail_paned_view_dispose (GObject *object)
priv->preview_pane = NULL;
}
- if (priv->formatter != NULL) {
- g_object_unref (priv->formatter);
- priv->formatter = NULL;
- }
-
if (priv->view_instance != NULL) {
g_object_unref (priv->view_instance);
priv->view_instance = NULL;
@@ -427,14 +423,14 @@ mail_paned_view_get_backend (EMailReader *reader)
return E_MAIL_BACKEND (shell_backend);
}
-static EMFormatHTML *
-mail_paned_view_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_paned_view_get_mail_display (EMailReader *reader)
{
- EMailPanedView *paned_view;
+ EMailPanedViewPrivate *priv;
- paned_view = E_MAIL_PANED_VIEW (reader);
+ priv = E_MAIL_PANED_VIEW (reader)->priv;
- return EM_FORMAT_HTML (paned_view->priv->formatter);
+ return priv->display;
}
static gboolean
@@ -622,7 +618,6 @@ mail_paned_view_constructed (GObject *object)
EShellView *shell_view;
EShell *shell;
EShellSettings *shell_settings;
- ESearchBar *search_bar;
EMailReader *reader;
EMailBackend *backend;
EMailSession *session;
@@ -630,10 +625,11 @@ mail_paned_view_constructed (GObject *object)
GtkWidget *message_list;
GtkWidget *container;
GtkWidget *widget;
- EWebView *web_view;
priv = E_MAIL_PANED_VIEW_GET_PRIVATE (object);
- priv->formatter = em_format_html_display_new ();
+
+ priv->display = g_object_new (E_TYPE_MAIL_DISPLAY,
+ "headers-collapsable", TRUE, NULL);
view = E_MAIL_VIEW (object);
shell_view = e_mail_view_get_shell_view (view);
@@ -645,17 +641,9 @@ mail_paned_view_constructed (GObject *object)
backend = E_MAIL_BACKEND (shell_backend);
session = e_mail_backend_get_session (backend);
- /* Make headers collapsable and store state of headers in config file */
- em_format_html_set_headers_collapsable (
- EM_FORMAT_HTML (priv->formatter), TRUE);
- g_object_bind_property (
- shell_settings, "paned-view-headers-state",
- priv->formatter, "headers-state",
- G_BINDING_BIDIRECTIONAL |
- G_BINDING_SYNC_CREATE);
-
- web_view = em_format_html_get_web_view (
- EM_FORMAT_HTML (priv->formatter));
+ g_object_bind_property (shell_settings, "paned-view-headers-state",
+ priv->display, "headers-collapsed",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
/* Build content widgets. */
@@ -692,11 +680,10 @@ mail_paned_view_constructed (GObject *object)
container = priv->paned;
- gtk_widget_show (GTK_WIDGET (web_view));
-
- widget = e_preview_pane_new (web_view);
+ widget = e_preview_pane_new (E_WEB_VIEW (priv->display));
gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE);
priv->preview_pane = g_object_ref (widget);
+ gtk_widget_show (GTK_WIDGET (priv->display));
gtk_widget_show (widget);
g_object_bind_property (
@@ -704,12 +691,6 @@ mail_paned_view_constructed (GObject *object)
widget, "visible",
G_BINDING_SYNC_CREATE);
- search_bar = e_preview_pane_get_search_bar (E_PREVIEW_PANE (widget));
-
- g_signal_connect_swapped (
- search_bar, "changed",
- G_CALLBACK (em_format_queue_redraw), priv->formatter);
-
/* Load the view instance. */
e_mail_view_update_view_instance (E_MAIL_VIEW (object));
@@ -733,8 +714,6 @@ mail_paned_view_constructed (GObject *object)
G_CALLBACK (mail_paned_view_restore_state_cb),
object);
- e_mail_reader_connect_headers (reader);
-
/* Do this after creating the message list. Our
* set_preview_visible() method relies on it. */
e_mail_view_set_preview_visible (view, TRUE);
@@ -749,26 +728,23 @@ static void
mail_paned_view_set_search_strings (EMailView *view,
GSList *search_strings)
{
+ EMailDisplay *display;
+ EWebView *web_view;
EMailReader *reader;
- EPreviewPane *preview_pane;
- ESearchBar *search_bar;
- ESearchingTokenizer *tokenizer;
reader = E_MAIL_READER (view);
- preview_pane = e_mail_reader_get_preview_pane (reader);
- search_bar = e_preview_pane_get_search_bar (preview_pane);
- tokenizer = e_search_bar_get_tokenizer (search_bar);
+ display = e_mail_reader_get_mail_display (reader);
+ if (!display)
+ return;
+
+ web_view = E_WEB_VIEW (display);
- e_searching_tokenizer_set_secondary_case_sensitivity (tokenizer, FALSE);
- e_searching_tokenizer_set_secondary_search_string (tokenizer, NULL);
+ e_web_view_clear_highlights (web_view);
while (search_strings != NULL) {
- e_searching_tokenizer_add_secondary_search_string (
- tokenizer, search_strings->data);
+ e_web_view_add_highlight (web_view, search_strings->data);
search_strings = g_slist_next (search_strings);
}
-
- e_search_bar_changed (search_bar);
}
static GalViewInstance *
@@ -1047,7 +1023,7 @@ e_mail_paned_view_reader_init (EMailReaderInterface *interface)
interface->get_action_group = mail_paned_view_get_action_group;
interface->get_alert_sink = mail_paned_view_get_alert_sink;
interface->get_backend = mail_paned_view_get_backend;
- interface->get_formatter = mail_paned_view_get_formatter;
+ interface->get_mail_display = mail_paned_view_get_mail_display;
interface->get_hide_deleted = mail_paned_view_get_hide_deleted;
interface->get_message_list = mail_paned_view_get_message_list;
interface->get_popup_menu = mail_paned_view_get_popup_menu;
@@ -1092,6 +1068,14 @@ e_mail_paned_view_hide_message_list_pane (EMailPanedView *view,
gtk_widget_hide (view->priv->scrolled_window);
}
+GtkWidget *
+e_mail_paned_view_get_preview (EMailPanedView *view)
+{
+ g_return_val_if_fail (E_IS_MAIL_PANED_VIEW (view), NULL);
+
+ return GTK_WIDGET (mail_paned_view_get_mail_display (E_MAIL_READER (view)));
+}
+
void
e_mail_paned_view_set_enable_show_folder (EMailPanedView *view,
gboolean set)
diff --git a/mail/e-mail-paned-view.h b/mail/e-mail-paned-view.h
index 3226b394f1..5e6879ae94 100644
--- a/mail/e-mail-paned-view.h
+++ b/mail/e-mail-paned-view.h
@@ -71,6 +71,7 @@ GtkWidget * e_mail_paned_view_new (EShellView *shell_view);
void e_mail_paned_view_hide_message_list_pane
(EMailPanedView *view,
gboolean visible);
+GtkWidget * e_mail_paned_view_get_preview (EMailPanedView *view);
void e_mail_paned_view_set_enable_show_folder
(EMailPanedView *view,
gboolean set);
diff --git a/mail/e-mail-printer.c b/mail/e-mail-printer.c
new file mode 100644
index 0000000000..f0fb8412a5
--- /dev/null
+++ b/mail/e-mail-printer.c
@@ -0,0 +1,859 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-print.h>
+#include <e-util/e-marshal.h>
+
+#include <webkit/webkitdom.h>
+
+#include "e-mail-printer.h"
+#include "em-format-html-print.h"
+#include "e-mail-display.h"
+
+static gpointer parent_class = NULL;
+
+enum {
+ BUTTON_SELECT_ALL,
+ BUTTON_SELECT_NONE,
+ BUTTON_TOP,
+ BUTTON_UP,
+ BUTTON_DOWN,
+ BUTTON_BOTTOM,
+ BUTTONS_COUNT
+};
+
+#define w(x)
+
+struct _EMailPrinterPrivate {
+ EMFormatHTMLPrint *efhp;
+
+ gboolean export_mode;
+
+ GtkListStore *headers;
+
+ WebKitWebView *webview; /* WebView to print from */
+ gchar *uri;
+ GtkWidget *buttons[BUTTONS_COUNT];
+ GtkWidget *treeview;
+
+ GtkPrintOperation *operation;
+};
+
+G_DEFINE_TYPE (
+ EMailPrinter,
+ e_mail_printer,
+ G_TYPE_OBJECT);
+
+enum {
+ PROP_0,
+ PROP_PRINT_FORMATTER
+};
+
+enum {
+ SIGNAL_DONE,
+ LAST_SIGNAL
+};
+
+enum {
+ COLUMN_ACTIVE,
+ COLUMN_HEADER_NAME,
+ COLUMN_HEADER_VALUE,
+ COLUMN_HEADER_STRUCT,
+ LAST_COLUMN
+};
+
+static guint signals[LAST_SIGNAL];
+
+static gint
+emp_header_name_equal (const EMFormatHeader *h1,
+ const EMFormatHeader *h2)
+{
+ if ((h2->value == NULL) || (h1->value == NULL)) {
+ return g_strcmp0 (h1->name, h2->name);
+ } else {
+ if ((g_strcmp0 (h1->name, h2->name) == 0) &&
+ (g_strcmp0 (h1->value, h2->value) == 0))
+ return 0;
+ else
+ return 1;
+ }
+}
+
+static void
+emp_draw_footer (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ gint n_pages;
+ gdouble width, height;
+ gchar *text;
+ cairo_t *cr;
+
+ cr = gtk_print_context_get_cairo_context (context);
+ width = gtk_print_context_get_width (context);
+ height = gtk_print_context_get_height (context);
+
+ g_object_get (operation, "n-pages", &n_pages, NULL);
+ text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
+
+ cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
+ cairo_fill (cr);
+
+ desc = pango_font_description_from_string ("Sans Regular 10");
+ layout = gtk_print_context_create_pango_layout (context);
+ pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_font_description (layout, desc);
+ pango_layout_set_text (layout, text, -1);
+ pango_layout_set_width (layout, width * PANGO_SCALE);
+ pango_font_description_free (desc);
+
+ cairo_move_to (cr, 0, height + 5);
+ pango_cairo_show_layout (cr, layout);
+
+ g_object_unref (layout);
+ g_free (text);
+}
+
+static void
+emp_printing_done (GtkPrintOperation *operation,
+ GtkPrintOperationResult result,
+ gpointer user_data)
+{
+ EMailPrinter *emp = user_data;
+
+ g_signal_emit (emp, signals[SIGNAL_DONE], 0, operation, result);
+}
+
+static void
+emp_start_printing (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitWebView *web_view;
+ WebKitWebFrame *frame;
+ WebKitLoadStatus load_status;
+ EMailPrinter *emp = user_data;
+
+ web_view = WEBKIT_WEB_VIEW (object);
+ load_status = webkit_web_view_get_load_status (web_view);
+
+ if (load_status != WEBKIT_LOAD_FINISHED)
+ return;
+
+ frame = webkit_web_view_get_main_frame (web_view);
+
+ if (emp->priv->export_mode) {
+ gtk_print_operation_set_export_filename (
+ emp->priv->operation,
+ emp->priv->efhp->export_filename);
+ webkit_web_frame_print_full (
+ frame, emp->priv->operation,
+ GTK_PRINT_OPERATION_ACTION_EXPORT, NULL);
+ } else {
+ webkit_web_frame_print_full
+ (frame, emp->priv->operation,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL);
+ }
+
+}
+
+static void
+emp_run_print_operation (EMailPrinter *emp)
+{
+ EMFormat *emf;
+ SoupSession *session;
+ GHashTable *formatters;
+ gchar *mail_uri;
+
+ emf = EM_FORMAT (emp->priv->efhp);
+ mail_uri = em_format_build_mail_uri (emf->folder, emf->message_uid, NULL, NULL);
+
+ /* It's safe to assume that session exists and contains formatters table,
+ * because at least the message we are about to print now must be already
+ * there */
+ session = webkit_get_default_session ();
+ formatters = g_object_get_data (G_OBJECT (session), "formatters");
+ g_hash_table_insert (formatters, g_strdup (mail_uri), emp->priv->efhp);
+
+ /* Print_layout is a special EMPart created by EMFormatHTMLPrint */
+ if (emp->priv->uri)
+ g_free (emp->priv->uri);
+
+ emp->priv->uri = g_strconcat (mail_uri, "?part_id=print_layout&__evo-load-images=1", NULL);
+
+ if (emp->priv->webview == NULL) {
+ emp->priv->webview = g_object_new (E_TYPE_MAIL_DISPLAY, NULL);
+ e_web_view_set_enable_frame_flattening (E_WEB_VIEW (emp->priv->webview), FALSE);
+ e_mail_display_set_force_load_images (
+ E_MAIL_DISPLAY (emp->priv->webview), TRUE);
+ g_object_ref_sink (emp->priv->webview);
+ g_signal_connect (emp->priv->webview, "notify::load-status",
+ G_CALLBACK (emp_start_printing), emp);
+
+ w ({
+ GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (window), sw);
+ gtk_container_add (GTK_CONTAINER (sw),
+ GTK_WIDGET (emp->priv->webview));
+ gtk_widget_show_all (window);
+ });
+ }
+
+ e_mail_display_set_formatter (E_MAIL_DISPLAY (emp->priv->webview),
+ (EMFormatHTML *) emp->priv->efhp);
+
+ webkit_web_view_load_uri (emp->priv->webview, emp->priv->uri);
+
+ g_free (mail_uri);
+}
+
+static void
+set_header_visible (EMailPrinter *emp,
+ EMFormatHeader *header,
+ gint index,
+ gboolean visible)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *headers;
+ WebKitDOMElement *element;
+ WebKitDOMCSSStyleDeclaration *style;
+
+ document = webkit_web_view_get_dom_document (emp->priv->webview);
+ headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
+
+ g_return_if_fail (index < webkit_dom_node_list_get_length (headers));
+
+ element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (headers, index));
+ style = webkit_dom_element_get_style (element);
+ webkit_dom_css_style_declaration_set_property (style,
+ "display", (visible ? "table-row" : "none"), "", NULL);
+}
+
+static void
+header_active_renderer_toggled_cb (GtkCellRendererToggle *renderer,
+ gchar *path,
+ EMailPrinter *emp)
+{
+ GtkTreeIter iter;
+ GtkTreePath *p;
+ gboolean active;
+ EMFormatHeader *header;
+ gint *indices;
+
+ gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (emp->priv->headers),
+ &iter, path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (emp->priv->headers), &iter,
+ COLUMN_ACTIVE, &active, -1);
+ gtk_tree_model_get (GTK_TREE_MODEL (emp->priv->headers), &iter,
+ COLUMN_HEADER_STRUCT, &header, -1);
+ gtk_list_store_set (GTK_LIST_STORE (emp->priv->headers), &iter,
+ COLUMN_ACTIVE, !active, -1);
+
+ p = gtk_tree_path_new_from_string (path);
+ indices = gtk_tree_path_get_indices (p);
+ set_header_visible (emp, header, indices[0], !active);
+ gtk_tree_path_free (p);
+}
+
+static void
+emp_headers_tab_toggle_selection (GtkWidget *button,
+ gpointer user_data)
+{
+ EMailPrinter *emp = user_data;
+ GtkTreeIter iter;
+ gboolean select;
+
+ if (button == emp->priv->buttons[BUTTON_SELECT_ALL])
+ select = TRUE;
+ else if (button == emp->priv->buttons[BUTTON_SELECT_NONE])
+ select = FALSE;
+ else
+ return;
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (emp->priv->headers), &iter))
+ return;
+
+ do {
+ EMFormatHeader *header;
+ GtkTreePath *path;
+ gint *indices;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (emp->priv->headers), &iter,
+ COLUMN_HEADER_STRUCT, &header, -1);
+ gtk_list_store_set (GTK_LIST_STORE (emp->priv->headers), &iter,
+ COLUMN_ACTIVE, select, -1);
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (emp->priv->headers), &iter);
+ indices = gtk_tree_path_get_indices (path);
+ set_header_visible (emp, header, indices[0], select);
+ gtk_tree_path_free (path);
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (emp->priv->headers), &iter));
+}
+
+static void
+emp_headers_tab_selection_changed (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ EMailPrinter *emp = user_data;
+ gboolean enabled;
+ GList *selected_rows;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ if (gtk_tree_selection_count_selected_rows (selection) == 0) {
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], FALSE);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], FALSE);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], FALSE);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], FALSE);
+
+ return;
+ }
+
+ model = GTK_TREE_MODEL (emp->priv->headers);
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ path = gtk_tree_path_copy (selected_rows->data);
+ enabled = gtk_tree_path_prev (path);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_TOP], enabled);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_UP], enabled);
+
+ gtk_tree_model_get_iter (model, &iter, g_list_last (selected_rows)->data);
+ enabled = gtk_tree_model_iter_next (model, &iter);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_DOWN], enabled);
+ gtk_widget_set_sensitive (emp->priv->buttons[BUTTON_BOTTOM], enabled);
+
+ g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected_rows);
+ gtk_tree_path_free (path);
+}
+
+static void
+emp_headers_tab_move (GtkWidget *button,
+ gpointer user_data)
+{
+ EMailPrinter *emp = user_data;
+ GtkTreeSelection *selection;
+ GList *selected_rows, *references, *l;
+ GtkTreePath *path;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeRowReference *selection_middle;
+ gint *indices;
+
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *headers;
+ WebKitDOMNode *header, *parent;
+
+ model = GTK_TREE_MODEL (emp->priv->headers);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (emp->priv->treeview));
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+ /* The order of header rows in the HMTL document should be in sync with
+ order of headers in the listview and in efhp->headers_list */
+ document = webkit_web_view_get_dom_document (emp->priv->webview);
+ headers = webkit_dom_document_get_elements_by_class_name (document, "header-item");
+
+ l = g_list_nth (selected_rows, g_list_length (selected_rows) / 2);
+ selection_middle = gtk_tree_row_reference_new (model, l->data);
+
+ references = NULL;
+ for (l = selected_rows; l; l = l->next) {
+ references = g_list_prepend (references,
+ gtk_tree_row_reference_new (model, l->data));
+ }
+
+ if (button == emp->priv->buttons[BUTTON_TOP]) {
+
+ for (l = references; l; l = l->next) {
+ /* Move the rows in the view */
+ path = gtk_tree_row_reference_get_path (l->data);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_list_store_move_after (emp->priv->headers, &iter, NULL);
+
+ /* Move the header row in HTML document */
+ indices = gtk_tree_path_get_indices (path);
+ header = webkit_dom_node_list_item (headers, indices[0]);
+ parent = webkit_dom_node_get_parent_node (header);
+ webkit_dom_node_remove_child (parent, header, NULL);
+ webkit_dom_node_insert_before (parent, header,
+ webkit_dom_node_get_first_child (parent), NULL);
+
+ gtk_tree_path_free (path);
+ }
+
+ } else if (button == emp->priv->buttons[BUTTON_UP]) {
+
+ GtkTreeIter *iter_prev;
+ WebKitDOMNode *node2;
+
+ references = g_list_reverse (references);
+
+ for (l = references; l; l = l->next) {
+
+ path = gtk_tree_row_reference_get_path (l->data);
+ gtk_tree_model_get_iter (model, &iter, path);
+ iter_prev = gtk_tree_iter_copy (&iter);
+ gtk_tree_model_iter_previous (model, iter_prev);
+
+ gtk_list_store_move_before (emp->priv->headers, &iter, iter_prev);
+
+ indices = gtk_tree_path_get_indices (path);
+ header = webkit_dom_node_list_item (headers, indices[0]);
+ node2 = webkit_dom_node_get_previous_sibling (header);
+ parent = webkit_dom_node_get_parent_node (header);
+
+ webkit_dom_node_remove_child (parent, header, NULL);
+ webkit_dom_node_insert_before (parent, header, node2, NULL);
+
+ gtk_tree_path_free (path);
+ gtk_tree_iter_free (iter_prev);
+ }
+
+ } else if (button == emp->priv->buttons[BUTTON_DOWN]) {
+
+ GtkTreeIter *iter_next;
+ WebKitDOMNode *node2;
+
+ for (l = references; l; l = l->next) {
+
+ path = gtk_tree_row_reference_get_path (l->data);
+ gtk_tree_model_get_iter (model, &iter, path);
+ iter_next = gtk_tree_iter_copy (&iter);
+ gtk_tree_model_iter_next (model, iter_next);
+
+ gtk_list_store_move_after (emp->priv->headers, &iter, iter_next);
+
+ indices = gtk_tree_path_get_indices (path);
+ header = webkit_dom_node_list_item (headers, indices[0]);
+ node2 = webkit_dom_node_get_next_sibling (header);
+ parent = webkit_dom_node_get_parent_node (header);
+
+ webkit_dom_node_remove_child (parent, header, NULL);
+ webkit_dom_node_insert_before (parent, header,
+ webkit_dom_node_get_next_sibling (node2), NULL);
+
+ gtk_tree_path_free (path);
+ gtk_tree_iter_free (iter_next);
+ }
+
+ } else if (button == emp->priv->buttons[BUTTON_BOTTOM]) {
+
+ references = g_list_reverse (references);
+
+ for (l = references; l; l = l->next) {
+ path = gtk_tree_row_reference_get_path (l->data);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_list_store_move_before (emp->priv->headers, &iter, NULL);
+
+ /* Move the header row in HTML document */
+ indices = gtk_tree_path_get_indices (path);
+ header = webkit_dom_node_list_item (headers, indices[0]);
+ parent = webkit_dom_node_get_parent_node (header);
+ webkit_dom_node_remove_child (parent, header, NULL);
+ webkit_dom_node_append_child (parent, header, NULL);
+
+ gtk_tree_path_free (path);
+ }
+ };
+
+ g_list_foreach (references, (GFunc) gtk_tree_row_reference_free, NULL);
+ g_list_free (references);
+
+ /* Keep the selection in middle of the screen */
+ path = gtk_tree_row_reference_get_path (selection_middle);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (emp->priv->treeview),
+ path, COLUMN_ACTIVE, TRUE, 0.5, 0.5);
+ gtk_tree_path_free (path);
+ gtk_tree_row_reference_free (selection_middle);
+
+ g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected_rows);
+
+ emp_headers_tab_selection_changed (selection, user_data);
+}
+
+static GtkWidget *
+emp_create_headers_tab (GtkPrintOperation *operation,
+ EMailPrinter *emp)
+{
+ GtkWidget *vbox, *hbox, *scw, *button;
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
+ gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
+
+ emp->priv->treeview = gtk_tree_view_new_with_model (
+ GTK_TREE_MODEL (emp->priv->headers));
+ view = GTK_TREE_VIEW (emp->priv->treeview);
+ selection = gtk_tree_view_get_selection (view);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (emp_headers_tab_selection_changed), emp);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (renderer, "toggled",
+ G_CALLBACK (header_active_renderer_toggled_cb), emp);
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Print"), renderer,
+ "active", COLUMN_ACTIVE, NULL);
+ gtk_tree_view_append_column (view, column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Header Name"), renderer,
+ "text", COLUMN_HEADER_NAME, NULL);
+ gtk_tree_view_append_column (view, column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Header Value"), renderer,
+ "text", COLUMN_HEADER_VALUE, NULL);
+ gtk_tree_view_append_column (view, column);
+
+ scw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (scw), GTK_WIDGET (view));
+ gtk_box_pack_start (GTK_BOX (hbox), scw, TRUE, TRUE, 0);
+
+ button = gtk_button_new_from_stock (GTK_STOCK_SELECT_ALL);
+ emp->priv->buttons[BUTTON_SELECT_ALL] = button;
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (emp_headers_tab_toggle_selection), emp);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+ button = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
+ emp->priv->buttons[BUTTON_SELECT_NONE] = button;
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (emp_headers_tab_toggle_selection), emp);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+ button = gtk_button_new_from_stock (GTK_STOCK_GOTO_TOP);
+ emp->priv->buttons[BUTTON_TOP] = button;
+ gtk_widget_set_sensitive (button, FALSE);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (emp_headers_tab_move), emp);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+ button = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
+ emp->priv->buttons[BUTTON_UP] = button;
+ gtk_widget_set_sensitive (button, FALSE);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (emp_headers_tab_move), emp);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+ button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
+ emp->priv->buttons[BUTTON_DOWN] = button;
+ gtk_widget_set_sensitive (button, FALSE);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (emp_headers_tab_move), emp);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+ button = gtk_button_new_from_stock (GTK_STOCK_GOTO_BOTTOM);
+ emp->priv->buttons[BUTTON_BOTTOM] = button;
+ gtk_widget_set_sensitive (button, FALSE);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (emp_headers_tab_move), emp);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 5);
+
+ gtk_print_operation_set_custom_tab_label (operation, _("Headers"));
+ gtk_widget_show_all (hbox);
+
+ return hbox;
+}
+
+static void
+emp_set_formatter (EMailPrinter *emp,
+ EMFormatHTMLPrint *formatter)
+{
+ EMFormat *emf = (EMFormat *) formatter;
+ CamelMediumHeader *header;
+ GArray *headers;
+ gint i;
+ GtkTreeIter last_known;
+
+ g_return_if_fail (EM_IS_FORMAT_HTML_PRINT (formatter));
+
+ g_object_ref (formatter);
+
+ if (emp->priv->efhp)
+ g_object_unref (emp->priv->efhp);
+
+ emp->priv->efhp = formatter;
+
+ if (emp->priv->headers)
+ g_object_unref (emp->priv->headers);
+ emp->priv->headers = gtk_list_store_new (5,
+ G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
+
+ headers = camel_medium_get_headers (CAMEL_MEDIUM (emf->message));
+ if (!headers)
+ return;
+
+ for (i = 0; i < headers->len; i++) {
+ GtkTreeIter iter;
+ GList *found_header;
+ EMFormatHeader *emfh;
+
+ header = &g_array_index (headers, CamelMediumHeader, i);
+ emfh = em_format_header_new (header->name, header->value);
+
+ found_header = g_queue_find_custom (&EM_FORMAT (formatter)->header_list,
+ emfh, (GCompareFunc) emp_header_name_equal);
+
+ if (!found_header) {
+ emfh->flags |= EM_FORMAT_HTML_HEADER_HIDDEN;
+ em_format_add_header_struct (EM_FORMAT (formatter), emfh);
+ gtk_list_store_append (emp->priv->headers, &iter);
+ } else {
+ if (gtk_list_store_iter_is_valid (emp->priv->headers, &last_known))
+ gtk_list_store_insert_after (emp->priv->headers, &iter, &last_known);
+ else
+ gtk_list_store_insert_after (emp->priv->headers, &iter, NULL);
+
+ last_known = iter;
+ }
+
+ gtk_list_store_set (emp->priv->headers, &iter,
+ COLUMN_ACTIVE, (found_header != NULL),
+ COLUMN_HEADER_NAME, emfh->name,
+ COLUMN_HEADER_VALUE, emfh->value,
+ COLUMN_HEADER_STRUCT, emfh, -1);
+ }
+
+ camel_medium_free_headers (CAMEL_MEDIUM (emf->message), headers);
+}
+
+static void
+emp_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EMailPrinter *emp = E_MAIL_PRINTER (object);
+
+ switch (property_id) {
+
+ case PROP_PRINT_FORMATTER:
+ emp_set_formatter (emp, g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emp_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EMailPrinter *emp = E_MAIL_PRINTER (object);
+
+ switch (property_id) {
+
+ case PROP_PRINT_FORMATTER:
+ g_value_set_object (value,
+ e_mail_printer_get_print_formatter (emp));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emp_finalize (GObject *object)
+{
+ EMailPrinterPrivate *priv = E_MAIL_PRINTER (object)->priv;
+
+ if (priv->efhp) {
+ g_object_unref (priv->efhp);
+ priv->efhp = NULL;
+ }
+
+ if (priv->headers) {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->headers), &iter)) {
+ do {
+ EMFormatHeader *header = NULL;
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->headers), &iter,
+ COLUMN_HEADER_STRUCT, &header, -1);
+ em_format_header_free (header);
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->headers), &iter));
+ }
+ g_object_unref (priv->headers);
+ priv->headers = NULL;
+ }
+
+ if (priv->webview) {
+ g_object_unref (priv->webview);
+ priv->webview = NULL;
+ }
+
+ if (priv->uri) {
+ g_free (priv->uri);
+ priv->uri = NULL;
+ }
+
+ if (priv->operation) {
+ g_object_unref (priv->operation);
+ priv->operation = NULL;
+ }
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+e_mail_printer_class_init (EMailPrinterClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (EMailPrinterPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = emp_set_property;
+ object_class->get_property = emp_get_property;
+ object_class->finalize = emp_finalize;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PRINT_FORMATTER,
+ g_param_spec_object (
+ "print-formatter",
+ NULL,
+ NULL,
+ EM_TYPE_FORMAT_HTML_PRINT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ signals[SIGNAL_DONE] = g_signal_new ("done",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EMailPrinterClass, done),
+ NULL, NULL,
+ e_marshal_VOID__OBJECT_INT,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_PRINT_OPERATION, G_TYPE_INT);
+}
+
+static void
+e_mail_printer_init (EMailPrinter *emp)
+{
+ emp->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ emp, E_TYPE_MAIL_PRINTER, EMailPrinterPrivate);
+
+ emp->priv->efhp = NULL;
+ emp->priv->headers = NULL;
+ emp->priv->webview = NULL;
+}
+
+EMailPrinter *
+e_mail_printer_new (EMFormatHTML * source)
+{
+ EMailPrinter *emp;
+ EMFormatHTMLPrint *efhp;
+
+ efhp = em_format_html_print_new (source);
+
+ emp = g_object_new (E_TYPE_MAIL_PRINTER,
+ "print-formatter", efhp, NULL);
+
+ g_object_unref (efhp);
+
+ return emp;
+}
+
+void
+e_mail_printer_print (EMailPrinter *emp,
+ gboolean export_mode,
+ GCancellable *cancellable)
+{
+ g_return_if_fail (E_IS_MAIL_PRINTER (emp));
+
+ if (emp->priv->operation)
+ g_object_unref (emp->priv->operation);
+ emp->priv->operation = e_print_operation_new ();
+ gtk_print_operation_set_unit (emp->priv->operation, GTK_UNIT_PIXEL);
+
+ gtk_print_operation_set_show_progress (emp->priv->operation, TRUE);
+ g_signal_connect (emp->priv->operation, "create-custom-widget",
+ G_CALLBACK (emp_create_headers_tab), emp);
+ g_signal_connect (emp->priv->operation, "done",
+ G_CALLBACK (emp_printing_done), emp);
+ g_signal_connect (emp->priv->operation, "draw-page",
+ G_CALLBACK (emp_draw_footer), NULL);
+
+ emp->priv->export_mode = export_mode;
+
+ if (cancellable)
+ g_signal_connect_swapped (cancellable, "cancelled",
+ G_CALLBACK (gtk_print_operation_cancel), emp->priv->operation);
+
+ emp_run_print_operation (emp);
+}
+
+const gchar *
+e_mail_printer_get_export_filename (EMailPrinter *printer)
+{
+ g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
+
+ if (!printer->priv->efhp)
+ return NULL;
+
+ return printer->priv->efhp->export_filename;
+}
+
+void
+e_mail_printer_set_export_filename (EMailPrinter *printer,
+ const gchar *filename)
+{
+ g_return_if_fail (E_IS_MAIL_PRINTER (printer));
+ g_return_if_fail (printer->priv->efhp != NULL);
+
+ if (printer->priv->efhp->export_filename && *printer->priv->efhp->export_filename)
+ g_free (printer->priv->efhp->export_filename);
+
+ printer->priv->efhp->export_filename = g_strdup (filename);
+}
+
+EMFormatHTMLPrint *
+e_mail_printer_get_print_formatter (EMailPrinter *emp)
+{
+ g_return_val_if_fail (E_IS_MAIL_PRINTER (emp), NULL);
+
+ return emp->priv->efhp;
+}
+
diff --git a/mail/e-mail-printer.h b/mail/e-mail-printer.h
new file mode 100644
index 0000000000..fcd163ec78
--- /dev/null
+++ b/mail/e-mail-printer.h
@@ -0,0 +1,85 @@
+/*
+ * Class for printing emails
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com>
+ */
+
+#ifndef E_MAIL_PRINTER_H
+#define E_MAIL_PRINTER_H
+
+#include "mail/em-format-html-print.h"
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_PRINTER \
+ (e_mail_printer_get_type ())
+#define E_MAIL_PRINTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MAIL_PRINTER, EMailPrinter))
+#define E_MAIL_PRINTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MAIL_PRINTER, EMailPrinterClass))
+#define E_IS_MAIL_PRINTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MAIL_PRINTER))
+#define E_IS_MAIL_PRINTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MAIL_PRINTER_CLASS))
+#define E_MAIL_PRINTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MAIL_PRINTER, EMailPrinterClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailPrinter EMailPrinter;
+typedef struct _EMailPrinterClass EMailPrinterClass;
+typedef struct _EMailPrinterPrivate EMailPrinterPrivate;
+
+struct _EMailPrinter {
+ GObject parent;
+ EMailPrinterPrivate *priv;
+};
+
+struct _EMailPrinterClass {
+ GObjectClass parent_class;
+
+ void (*done) (EMailPrinter *printer,
+ GtkPrintOperation *operation,
+ GtkPrintOperationResult *result,
+ gpointer user_data);
+};
+
+GType e_mail_printer_get_type (void);
+
+EMailPrinter * e_mail_printer_new (EMFormatHTML *source);
+
+void e_mail_printer_print (EMailPrinter *printer,
+ gboolean export,
+ GCancellable *cancellable);
+
+void e_mail_printer_set_export_filename
+ (EMailPrinter *printer,
+ const gchar *filename);
+
+const gchar * e_mail_printer_get_export_filename
+ (EMailPrinter *printer);
+
+EMFormatHTMLPrint *
+ e_mail_printer_get_print_formatter
+ (EMailPrinter *printer);
+
+G_END_DECLS
+
+#endif /* E_MAIL_PRINTER_H */
diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c
index bc87295f60..5956ab209d 100644
--- a/mail/e-mail-reader-utils.c
+++ b/mail/e-mail-reader-utils.c
@@ -44,6 +44,7 @@
#include "mail/e-mail-backend.h"
#include "mail/e-mail-browser.h"
+#include "mail/e-mail-printer.h"
#include "mail/em-composer-utils.h"
#include "mail/em-format-html-print.h"
#include "mail/em-utils.h"
@@ -412,7 +413,8 @@ e_mail_reader_open_selected (EMailReader *reader)
const gchar *uid = views->pdata[ii];
GtkWidget *browser;
- browser = e_mail_browser_new (backend);
+ browser = e_mail_browser_new (backend, folder, uid,
+ EM_FORMAT_WRITE_MODE_NORMAL);
e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
e_mail_reader_set_message (E_MAIL_READER (browser), uid);
copy_tree_state (reader, E_MAIL_READER (browser));
@@ -430,93 +432,74 @@ e_mail_reader_open_selected (EMailReader *reader)
return ii;
}
-/* Helper for e_mail_reader_print() */
-static void
-mail_reader_print_cb (CamelFolder *folder,
- GAsyncResult *result,
- AsyncContext *context)
+static gboolean
+destroy_printing_activity (EActivity *activity)
{
- EAlertSink *alert_sink;
- CamelMimeMessage *message;
- EMFormatHTML *formatter;
- EMFormatHTMLPrint *html_print;
- GError *error = NULL;
-
- alert_sink = e_activity_get_alert_sink (context->activity);
+ g_object_unref (activity);
- message = camel_folder_get_message_finish (folder, result, &error);
+ return FALSE;
+}
- if (e_activity_handle_cancellation (context->activity, error)) {
- g_warn_if_fail (message == NULL);
- async_context_free (context);
- g_error_free (error);
- return;
+static void
+printing_done_cb (EMailPrinter *printer,
+ GtkPrintOperation *operation,
+ GtkPrintOperationResult result,
+ gpointer user_data)
+{
+ EActivity *activity = user_data;
- } else if (error != NULL) {
- g_warn_if_fail (message == NULL);
- e_alert_submit (
- alert_sink, "mail:no-retrieve-message",
- error->message, NULL);
- async_context_free (context);
- g_error_free (error);
- return;
- }
+ if (result == GTK_PRINT_OPERATION_RESULT_ERROR) {
- g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+ EAlertSink *alert_sink;
+ GError *error = NULL;
- formatter = e_mail_reader_get_formatter (context->reader);
+ alert_sink = e_activity_get_alert_sink (activity);
+ gtk_print_operation_get_error (operation, &error);
- html_print = em_format_html_print_new (
- formatter, context->print_action);
- em_format_merge_handler (
- EM_FORMAT (html_print), EM_FORMAT (formatter));
- em_format_html_print_message (
- html_print, message, folder, context->message_uid);
- g_object_unref (html_print);
+ if (error != NULL) {
+ e_alert_submit (alert_sink, "mail:printing-failed",
+ error->message, NULL);
+ g_error_free (error);
+ }
- g_object_unref (message);
+ g_object_unref (activity);
+ g_object_unref (printer);
+ return;
+ }
- e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
+ /* Set activity as completed, and keep it displayed for a few seconds
+ * so that user can actually see the the printing was sucesfully finished. */
+ e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
+ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 3,
+ (GSourceFunc) destroy_printing_activity, activity, NULL);
- async_context_free (context);
+ g_object_unref (printer);
}
void
e_mail_reader_print (EMailReader *reader,
GtkPrintOperationAction action)
{
+ EMailDisplay *display;
+ EMailPrinter *printer;
+ EMFormatHTML *formatter;
EActivity *activity;
- AsyncContext *context;
GCancellable *cancellable;
- CamelFolder *folder;
- GPtrArray *uids;
- const gchar *message_uid;
g_return_if_fail (E_IS_MAIL_READER (reader));
- folder = e_mail_reader_get_folder (reader);
- g_return_if_fail (CAMEL_IS_FOLDER (folder));
-
- /* XXX Learn to handle len > 1. */
- uids = e_mail_reader_get_selected_uids (reader);
- g_return_if_fail (uids != NULL && uids->len == 1);
- message_uid = g_ptr_array_index (uids, 0);
+ display = e_mail_reader_get_mail_display (reader);
+ formatter = e_mail_display_get_formatter (display);
activity = e_mail_reader_new_activity (reader);
+ e_activity_set_text (activity, _("Printing"));
+ e_activity_set_state (activity, E_ACTIVITY_RUNNING);
cancellable = e_activity_get_cancellable (activity);
- context = g_slice_new0 (AsyncContext);
- context->activity = activity;
- context->reader = g_object_ref (reader);
- context->message_uid = g_strdup (message_uid);
- context->print_action = action;
-
- camel_folder_get_message (
- folder, message_uid, G_PRIORITY_DEFAULT,
- cancellable, (GAsyncReadyCallback)
- mail_reader_print_cb, context);
-
- em_utils_uids_free (uids);
+ printer = e_mail_printer_new (formatter);
+ g_signal_connect (printer, "done",
+ G_CALLBACK (printing_done_cb), activity);
+ e_mail_printer_print (printer, FALSE, cancellable);
}
static void
@@ -763,6 +746,7 @@ mail_reader_get_message_ready_cb (CamelFolder *folder,
EMailBackend *backend;
EAlertSink *alert_sink;
EMFormatHTML *formatter;
+ EMailDisplay *display;
CamelMimeMessage *message;
GError *error = NULL;
@@ -790,8 +774,8 @@ mail_reader_get_message_ready_cb (CamelFolder *folder,
backend = e_mail_reader_get_backend (context->reader);
shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
-
- formatter = e_mail_reader_get_formatter (context->reader);
+ display = e_mail_reader_get_mail_display (context->reader);
+ formatter = e_mail_display_get_formatter (display);
em_utils_reply_to_message (
shell, message,
@@ -814,6 +798,7 @@ e_mail_reader_reply_to_message (EMailReader *reader,
EShell *shell;
EMailBackend *backend;
EShellBackend *shell_backend;
+ EMailDisplay *display;
EMFormatHTML *formatter;
GtkWidget *message_list;
CamelMimeMessage *new_message;
@@ -834,14 +819,15 @@ e_mail_reader_reply_to_message (EMailReader *reader,
backend = e_mail_reader_get_backend (reader);
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
+ formatter = e_mail_display_get_formatter (display);
message_list = e_mail_reader_get_message_list (reader);
reply_style = e_mail_reader_get_reply_style (reader);
shell_backend = E_SHELL_BACKEND (backend);
shell = e_shell_backend_get_shell (shell_backend);
- web_view = em_format_html_get_web_view (formatter);
+ web_view = E_WEB_VIEW (display);
if (reply_type == E_MAIL_REPLY_TO_RECIPIENT) {
const gchar *uri;
@@ -885,7 +871,8 @@ e_mail_reader_reply_to_message (EMailReader *reader,
if (!e_web_view_is_selection_active (web_view))
goto whole_message;
- selection = gtk_html_get_selection_html (GTK_HTML (web_view), &length);
+ selection = e_web_view_get_selection_html (web_view);
+ length = strlen (selection);
if (selection == NULL || *selection == '\0')
goto whole_message;
@@ -1397,20 +1384,18 @@ static void
headers_changed_cb (GConfClient *client,
guint cnxn_id,
GConfEntry *entry,
- EMailReader *reader)
+ EMFormat *emf)
{
- EMFormatHTML *formatter;
GSList *header_config_list, *p;
g_return_if_fail (client != NULL);
- g_return_if_fail (reader != NULL);
-
- formatter = e_mail_reader_get_formatter (reader);
+ g_return_if_fail (EM_IS_FORMAT (emf));
header_config_list = gconf_client_get_list (
client, "/apps/evolution/mail/display/headers",
GCONF_VALUE_STRING, NULL);
- em_format_clear_headers (EM_FORMAT (formatter));
+
+ em_format_clear_headers (emf);
for (p = header_config_list; p; p = g_slist_next (p)) {
EMailReaderHeader *h;
gchar *xml = (gchar *) p->data;
@@ -1418,21 +1403,20 @@ headers_changed_cb (GConfClient *client,
h = e_mail_reader_header_from_xml (xml);
if (h && h->enabled)
em_format_add_header (
- EM_FORMAT (formatter),
- h->name, EM_FORMAT_HEADER_BOLD);
+ emf, h->name, NULL, EM_FORMAT_HEADER_BOLD);
e_mail_reader_header_free (h);
}
if (!header_config_list)
- em_format_default_headers (EM_FORMAT (formatter));
+ em_format_default_headers (emf);
g_slist_foreach (header_config_list, (GFunc) g_free, NULL);
g_slist_free (header_config_list);
/* force a redraw */
- if (EM_FORMAT (formatter)->message)
- em_format_queue_redraw (EM_FORMAT (formatter));
+ if (emf->message)
+ em_format_redraw (emf);
}
static void
@@ -1458,7 +1442,8 @@ remove_header_notify_cb (gpointer data)
* updates the EMFormat whenever it changes and on this call too.
**/
void
-e_mail_reader_connect_headers (EMailReader *reader)
+e_mail_reader_connect_headers (EMailReader *reader,
+ EMFormat *emf)
{
GConfClient *client;
guint notify_id;
@@ -1471,13 +1456,13 @@ e_mail_reader_connect_headers (EMailReader *reader)
notify_id = gconf_client_notify_add (
client, "/apps/evolution/mail/display/headers",
(GConfClientNotifyFunc) headers_changed_cb,
- reader, NULL, NULL);
+ emf, NULL, NULL);
g_object_set_data_full (
- G_OBJECT (reader), "reader-header-notify-id",
+ G_OBJECT (emf), "reader-header-notify-id",
GINT_TO_POINTER (notify_id), remove_header_notify_cb);
- headers_changed_cb (client, 0, NULL, reader);
+ headers_changed_cb (client, 0, NULL, emf);
g_object_unref (client);
}
diff --git a/mail/e-mail-reader-utils.h b/mail/e-mail-reader-utils.h
index bb4671ba21..6913d0964c 100644
--- a/mail/e-mail-reader-utils.h
+++ b/mail/e-mail-reader-utils.h
@@ -67,7 +67,8 @@ EMailReaderHeader *
gchar * e_mail_reader_header_to_xml (EMailReaderHeader *header);
void e_mail_reader_header_free (EMailReaderHeader *header);
-void e_mail_reader_connect_headers (EMailReader *reader);
+void e_mail_reader_connect_headers (EMailReader *reader,
+ EMFormat *emf);
G_END_DECLS
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 53d4c31b84..0106b6ed53 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -48,13 +48,13 @@
#include "mail/e-mail-backend.h"
#include "mail/e-mail-browser.h"
-#include "mail/e-mail-display.h"
#include "mail/e-mail-reader-utils.h"
#include "mail/e-mail-view.h"
#include "mail/em-composer-utils.h"
#include "mail/em-event.h"
#include "mail/em-folder-selector.h"
#include "mail/em-folder-tree.h"
+#include "mail/em-format-html-display.h"
#include "mail/em-utils.h"
#include "mail/mail-autofilter.h"
#include "mail/mail-vfolder-ui.h"
@@ -70,6 +70,8 @@
((EMailReaderPrivate *) g_object_get_qdata \
(G_OBJECT (obj), quark_private))
+#define d(x)
+
typedef struct _EMailReaderClosure EMailReaderClosure;
typedef struct _EMailReaderPrivate EMailReaderPrivate;
@@ -125,6 +127,13 @@ static guint signals[LAST_SIGNAL];
G_DEFINE_INTERFACE (EMailReader, e_mail_reader, G_TYPE_INITIALLY_UNOWNED)
static void
+mail_reader_set_display_formatter_for_message (EMailReader *reader,
+ EMailDisplay *display,
+ const gchar *message_uid,
+ CamelMimeMessage *message,
+ CamelFolder *folder);
+
+static void
mail_reader_closure_free (EMailReaderClosure *closure)
{
if (closure->reader != NULL)
@@ -201,7 +210,6 @@ action_add_to_address_book_cb (GtkAction *action,
EShell *shell;
EMailBackend *backend;
EShellBackend *shell_backend;
- EMFormatHTML *formatter;
CamelInternetAddress *cia;
EWebView *web_view;
CamelURL *curl;
@@ -211,9 +219,10 @@ action_add_to_address_book_cb (GtkAction *action,
/* This action is defined in EMailDisplay. */
backend = e_mail_reader_get_backend (reader);
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
+ if (!web_view)
+ return;
uri = e_web_view_get_selected_uri (web_view);
g_return_if_fail (uri != NULL);
@@ -247,21 +256,136 @@ exit:
}
static void
+attachment_load_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GFile *file)
+{
+ EShell *shell;
+ GtkWindow *parent;
+
+ e_attachment_load_finish (attachment, result, NULL);
+
+ shell = e_shell_get_default ();
+ parent = e_shell_get_active_window (shell);
+
+ e_attachment_save_async (
+ attachment, file, (GAsyncReadyCallback)
+ e_attachment_save_handle_error, parent);
+
+ g_object_unref (file);
+}
+
+static void
+action_mail_image_save_cb (GtkAction *action,
+ EMailReader *reader)
+{
+ EMailDisplay *display;
+ EWebView *web_view;
+ EMFormat *emf;
+ const gchar *image_src;
+ CamelMimePart *part;
+ EAttachment *attachment;
+ GFile *file;
+
+ display = e_mail_reader_get_mail_display (reader);
+ web_view = E_WEB_VIEW (display);
+
+ if (!E_IS_WEB_VIEW (web_view))
+ return;
+
+ image_src = e_web_view_get_cursor_image_src (web_view);
+ if (!image_src)
+ return;
+
+ emf = EM_FORMAT (e_mail_display_get_formatter (display));
+ g_return_if_fail (emf != NULL);
+ g_return_if_fail (emf->message != NULL);
+
+ if (g_str_has_prefix (image_src, "cid:")) {
+ part = camel_mime_message_get_part_by_content_id (
+ emf->message, image_src + 4);
+ g_return_if_fail (part != NULL);
+
+ g_object_ref (part);
+ } else {
+ CamelStream *image_stream;
+ CamelDataWrapper *dw;
+ CamelDataCache *cache;
+ const gchar *filename;
+ const gchar *user_cache_dir;
+
+ /* Open cache and find the file there */
+ user_cache_dir = e_get_user_cache_dir ();
+ cache = camel_data_cache_new (user_cache_dir, NULL);
+ image_stream = camel_data_cache_get (cache, "http", image_src, NULL);
+ if (!image_stream) {
+ g_object_unref (cache);
+ return;
+ }
+
+ filename = strrchr (image_src, '/');
+ if (filename && strchr (filename, '?'))
+ filename = NULL;
+ else if (filename)
+ filename = filename + 1;
+
+ part = camel_mime_part_new ();
+ if (filename)
+ camel_mime_part_set_filename (part, filename);
+
+ dw = camel_data_wrapper_new ();
+ camel_data_wrapper_set_mime_type (
+ dw, "application/octet-stream");
+ camel_data_wrapper_construct_from_stream_sync (
+ dw, image_stream, NULL, NULL);
+ camel_medium_set_content (CAMEL_MEDIUM (part), dw);
+ g_object_unref (dw);
+
+ camel_mime_part_set_encoding (
+ part, CAMEL_TRANSFER_ENCODING_BASE64);
+
+ g_object_unref (image_stream);
+ g_object_unref (cache);
+ }
+
+ file = e_shell_run_save_dialog (
+ e_shell_get_default (),
+ _("Save Image"), camel_mime_part_get_filename (part),
+ NULL, NULL, NULL);
+ if (file == NULL) {
+ g_object_unref (part);
+ return;
+ }
+
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, part);
+
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ attachment_load_finish, file);
+
+ g_object_unref (part);
+}
+
+static void
action_mail_charset_cb (GtkRadioAction *action,
GtkRadioAction *current,
EMailReader *reader)
{
+ EMailDisplay *display;
EMFormatHTML *formatter;
const gchar *charset;
if (action != current)
return;
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
+ formatter = e_mail_display_get_formatter (display);
charset = g_object_get_data (G_OBJECT (action), "charset");
/* Charset for "Default" action will be NULL. */
- em_format_set_charset (EM_FORMAT (formatter), charset);
+ if (formatter)
+ em_format_set_charset (EM_FORMAT (formatter), charset);
}
static void
@@ -438,38 +562,38 @@ static void
action_mail_flag_clear_cb (GtkAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
+ EMailDisplay *display;
CamelFolder *folder;
GtkWindow *window;
GPtrArray *uids;
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
uids = e_mail_reader_get_selected_uids (reader);
window = e_mail_reader_get_window (reader);
em_utils_flag_for_followup_clear (window, folder, uids);
- em_format_queue_redraw (EM_FORMAT (formatter));
+ e_mail_display_reload (display);
}
static void
action_mail_flag_completed_cb (GtkAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
+ EMailDisplay *display;
CamelFolder *folder;
GtkWindow *window;
GPtrArray *uids;
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
uids = e_mail_reader_get_selected_uids (reader);
window = e_mail_reader_get_window (reader);
em_utils_flag_for_followup_completed (window, folder, uids);
- em_format_queue_redraw (EM_FORMAT (formatter));
+ e_mail_display_reload (display);
}
static void
@@ -663,11 +787,11 @@ static void
action_mail_load_images_cb (GtkAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
+ EMailDisplay *display;
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
- em_format_html_load_images (formatter);
+ e_mail_display_load_images (display);
}
static void
@@ -1576,20 +1700,14 @@ static void
action_mail_show_all_headers_cb (GtkToggleAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
- EMFormatMode mode;
+ EMailDisplay *display;
- formatter = e_mail_reader_get_formatter (reader);
-
- if (!formatter)
- return;
+ display = e_mail_reader_get_mail_display (reader);
if (gtk_toggle_action_get_active (action))
- mode = EM_FORMAT_MODE_ALLHEADERS;
+ e_mail_display_set_mode (display, EM_FORMAT_WRITE_MODE_ALL_HEADERS);
else
- mode = EM_FORMAT_MODE_NORMAL;
-
- em_format_set_mode (EM_FORMAT (formatter), mode);
+ e_mail_display_set_mode (display, EM_FORMAT_WRITE_MODE_NORMAL);
}
static void
@@ -1597,8 +1715,9 @@ action_mail_show_source_cb (GtkAction *action,
EMailReader *reader)
{
EMailBackend *backend;
- EMFormatHTML *formatter;
+ EMailDisplay *display;
CamelFolder *folder;
+ CamelMimeMessage *message;
GtkWidget *browser;
GPtrArray *uids;
const gchar *message_uid;
@@ -1610,16 +1729,16 @@ action_mail_show_source_cb (GtkAction *action,
g_return_if_fail (uids != NULL && uids->len == 1);
message_uid = g_ptr_array_index (uids, 0);
- browser = e_mail_browser_new (backend);
- reader = E_MAIL_READER (browser);
- formatter = e_mail_reader_get_formatter (reader);
+ message = camel_folder_get_message_sync (folder, message_uid, NULL, NULL);
- if (formatter != NULL)
- em_format_set_mode (
- EM_FORMAT (formatter), EM_FORMAT_MODE_SOURCE);
+ browser = e_mail_browser_new (backend, NULL, NULL, EM_FORMAT_WRITE_MODE_SOURCE);
+ e_mail_reader_set_folder (E_MAIL_READER (browser), folder);
+ e_mail_reader_set_message (E_MAIL_READER (browser), message_uid);
+
+ display = e_mail_reader_get_mail_display (E_MAIL_READER (browser));
+ mail_reader_set_display_formatter_for_message (
+ E_MAIL_READER (browser), display, message_uid, message, folder);
- e_mail_reader_set_folder (reader, folder);
- e_mail_reader_set_message (reader, message_uid);
gtk_widget_show (browser);
em_utils_uids_free (uids);
@@ -1671,39 +1790,33 @@ static void
action_mail_zoom_100_cb (GtkAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
- EWebView *web_view;
+ EMailDisplay *display;
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ display = e_mail_reader_get_mail_display (reader);
- e_web_view_zoom_100 (web_view);
+ webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (display), 1.0);
}
static void
action_mail_zoom_in_cb (GtkAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
- EWebView *web_view;
+ EMailDisplay *display;
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ display = e_mail_reader_get_mail_display (reader);
- e_web_view_zoom_in (web_view);
+ webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (display));
}
static void
action_mail_zoom_out_cb (GtkAction *action,
EMailReader *reader)
{
- EMFormatHTML *formatter;
- EWebView *web_view;
+ EMailDisplay *display;
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ display = e_mail_reader_get_mail_display (reader);
- e_web_view_zoom_out (web_view);
+ webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (display));
}
static void
@@ -1712,7 +1825,6 @@ action_search_folder_recipient_cb (GtkAction *action,
{
EMailBackend *backend;
EMailSession *session;
- EMFormatHTML *formatter;
EWebView *web_view;
CamelFolder *folder;
CamelURL *curl;
@@ -1721,9 +1833,7 @@ action_search_folder_recipient_cb (GtkAction *action,
/* This action is defined in EMailDisplay. */
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
-
- web_view = em_format_html_get_web_view (formatter);
+ web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
uri = e_web_view_get_selected_uri (web_view);
g_return_if_fail (uri != NULL);
@@ -1753,7 +1863,6 @@ action_search_folder_sender_cb (GtkAction *action,
{
EMailBackend *backend;
EMailSession *session;
- EMFormatHTML *formatter;
EWebView *web_view;
CamelFolder *folder;
CamelURL *curl;
@@ -1762,9 +1871,7 @@ action_search_folder_sender_cb (GtkAction *action,
/* This action is defined in EMailDisplay. */
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
-
- web_view = em_format_html_get_web_view (formatter);
+ web_view = E_WEB_VIEW (e_mail_reader_get_mail_display (reader));
uri = e_web_view_get_selected_uri (web_view);
g_return_if_fail (uri != NULL);
@@ -2494,6 +2601,7 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure)
EMailReader *reader;
GtkWidget *message_list;
EMFormatHTML *formatter;
+ EMailDisplay *display;
CamelMimeMessage *message;
const gchar *current_uid;
const gchar *message_uid;
@@ -2502,19 +2610,20 @@ mail_reader_message_seen_cb (EMailReaderClosure *closure)
reader = closure->reader;
message_uid = closure->message_uid;
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
+ formatter = e_mail_display_get_formatter (display);
message_list = e_mail_reader_get_message_list (reader);
if (e_tree_is_dragging (E_TREE (message_list)))
return FALSE;
- current_uid = EM_FORMAT (formatter)->uid;
- uid_is_current &= (g_strcmp0 (current_uid, message_uid) == 0);
-
current_uid = MESSAGE_LIST (message_list)->cursor_uid;
uid_is_current &= (g_strcmp0 (current_uid, message_uid) == 0);
- message = EM_FORMAT (formatter)->message;
+ if (formatter)
+ message = EM_FORMAT (formatter)->message;
+ else
+ message = NULL;
if (uid_is_current && message != NULL)
g_signal_emit (
@@ -2535,7 +2644,6 @@ schedule_timeout_mark_seen (EMailReader *reader)
gboolean schedule_timeout;
gint timeout_interval;
const gchar *message_uid;
-
backend = e_mail_reader_get_backend (reader);
message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
shell_backend = E_SHELL_BACKEND (backend);
@@ -2669,56 +2777,47 @@ static gboolean
mail_reader_message_selected_timeout_cb (EMailReader *reader)
{
EMailReaderPrivate *priv;
- EMFormatHTML *formatter;
+ EMailDisplay *display;
GtkWidget *message_list;
- EPreviewPane *preview_pane;
CamelFolder *folder;
const gchar *cursor_uid;
const gchar *format_uid;
+ EMFormat *formatter;
priv = E_MAIL_READER_GET_PRIVATE (reader);
folder = e_mail_reader_get_folder (reader);
- formatter = e_mail_reader_get_formatter (reader);
message_list = e_mail_reader_get_message_list (reader);
- preview_pane = e_mail_reader_get_preview_pane (reader);
+ display = e_mail_reader_get_mail_display (reader);
+ formatter = EM_FORMAT (e_mail_display_get_formatter (display));
cursor_uid = MESSAGE_LIST (message_list)->cursor_uid;
- format_uid = EM_FORMAT (formatter)->uid;
-
- e_preview_pane_clear_alerts (preview_pane);
+ format_uid = formatter ? formatter->message_uid : NULL;
if (MESSAGE_LIST (message_list)->last_sel_single) {
- gboolean preview_visible;
+ GtkWidget *widget;
+ gboolean display_visible;
gboolean selected_uid_changed;
/* Decide whether to download the full message now. */
+ widget = GTK_WIDGET (display);
+ display_visible = gtk_widget_get_mapped (widget);
- preview_visible =
- gtk_widget_get_mapped (GTK_WIDGET (preview_pane));
- selected_uid_changed = g_strcmp0 (cursor_uid, format_uid);
+ selected_uid_changed = (g_strcmp0 (cursor_uid, format_uid) != 0);
- if (preview_visible && selected_uid_changed) {
+ if (display_visible && selected_uid_changed) {
EMailReaderClosure *closure;
GCancellable *cancellable;
EActivity *activity;
- EWebView *web_view;
gchar *string;
- web_view = e_preview_pane_get_web_view (preview_pane);
-
- string = g_strdup_printf (
- _("Retrieving message '%s'"), cursor_uid);
-#if HAVE_CLUTTER
- if (!e_shell_get_express_mode (e_shell_get_default ()))
- e_web_view_load_string (web_view, string);
-#else
- e_web_view_load_string (web_view, string);
-#endif
+ string = g_strdup_printf (_("Retrieving message '%s'"), cursor_uid);
+ e_mail_display_set_status (display, string);
g_free (string);
activity = e_mail_reader_new_activity (reader);
+ e_activity_set_text (activity, _("Retrieving message"));
cancellable = e_activity_get_cancellable (activity);
closure = g_slice_new0 (EMailReaderClosure);
@@ -2736,9 +2835,6 @@ mail_reader_message_selected_timeout_cb (EMailReader *reader)
priv->retrieving_message = g_object_ref (cancellable);
}
} else {
- /* FIXME Need to pass a GCancellable. */
- em_format_format (
- EM_FORMAT (formatter), NULL, NULL, NULL, NULL);
priv->restoring_message_selection = FALSE;
}
@@ -2869,7 +2965,7 @@ mail_reader_set_folder (EMailReader *reader,
CamelFolder *folder)
{
EMailReaderPrivate *priv;
- EMFormatHTML *formatter;
+ EMailDisplay *display;
CamelFolder *previous_folder;
GtkWidget *message_list;
EMailBackend *backend;
@@ -2879,7 +2975,7 @@ mail_reader_set_folder (EMailReader *reader,
priv = E_MAIL_READER_GET_PRIVATE (reader);
backend = e_mail_reader_get_backend (reader);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
message_list = e_mail_reader_get_message_list (reader);
previous_folder = e_mail_reader_get_folder (reader);
@@ -2899,8 +2995,7 @@ mail_reader_set_folder (EMailReader *reader,
em_utils_folder_is_outbox (folder) ||
em_utils_folder_is_sent (folder));
- /* FIXME Need to pass a GCancellable. */
- em_format_format (EM_FORMAT (formatter), NULL, NULL, NULL, NULL);
+ e_web_view_clear (E_WEB_VIEW (display));
priv->folder_was_just_selected = (folder != NULL);
@@ -2931,18 +3026,128 @@ mail_reader_folder_loaded (EMailReader *reader)
e_mail_reader_update_actions (reader, state);
}
+struct _formatter_weak_ref_closure {
+ GHashTable *formatters;
+ gchar *mail_uri;
+};
+
+static void
+formatter_weak_ref_cb (struct _formatter_weak_ref_closure *data,
+ EMFormat *formatter)
+{
+ /* When this callback is called, the formatter is being finalized
+ * so we only remove it from the formatters table. */
+ g_hash_table_remove (data->formatters,
+ data->mail_uri);
+
+ d(printf("Destroying formatter %p (%s)\n", formatter, data->mail_uri));
+
+ /* Destroying the formatter will prevent this callback
+ * being called, so we can remove the closure data as well */
+ g_hash_table_unref (data->formatters);
+ g_free (data->mail_uri);
+ g_free (data);
+}
+
+struct format_parser_async_closure_ {
+ EMailDisplay *display;
+ EActivity *activity;
+};
+
+static void
+format_parser_async_done_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EMFormat *emf = EM_FORMAT (source);
+ struct format_parser_async_closure_ *closure = user_data;
+
+ e_mail_display_set_formatter (closure->display, EM_FORMAT_HTML (emf));
+ e_mail_display_load (closure->display, emf->uri_base);
+
+ g_object_unref (closure->activity);
+ g_object_unref (closure->display);
+ g_free (closure);
+
+ g_object_unref (result);
+
+ /* Remove the reference added when formatter was created,
+ * so that only owners are EMailDisplays */
+ g_object_unref (emf);
+}
+
+static void
+mail_reader_set_display_formatter_for_message (EMailReader *reader,
+ EMailDisplay *display,
+ const gchar *message_uid,
+ CamelMimeMessage *message,
+ CamelFolder *folder)
+{
+ SoupSession *session;
+ GHashTable *formatters;
+ EMFormat *formatter;
+ gchar *mail_uri;
+
+ mail_uri = em_format_build_mail_uri (folder, message_uid, NULL, NULL);
+
+ session = webkit_get_default_session ();
+ formatters = g_object_get_data (G_OBJECT (session), "formatters");
+ if (!formatters) {
+ formatters = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, NULL);
+ g_object_set_data (G_OBJECT (session), "formatters", formatters);
+ }
+
+ if ((formatter = g_hash_table_lookup (formatters, mail_uri)) == NULL) {
+ struct _formatter_weak_ref_closure *formatter_data =
+ g_new0 (struct _formatter_weak_ref_closure, 1);
+
+ struct format_parser_async_closure_ *closure;
+
+ formatter_data->formatters = g_hash_table_ref (formatters);
+ formatter_data->mail_uri = g_strdup (mail_uri);
+
+ formatter = EM_FORMAT (em_format_html_display_new ());
+
+ /* When no EMailDisplay holds reference to the formatter, then
+ * the formatter can be destroyed. */
+ g_object_weak_ref (G_OBJECT (formatter),
+ (GWeakNotify) formatter_weak_ref_cb, formatter_data);
+
+ formatter->message_uid = g_strdup (message_uid);
+ formatter->uri_base = g_strdup (mail_uri);
+
+ e_mail_reader_connect_headers (reader, formatter);
+
+ closure = g_new0 (struct format_parser_async_closure_, 1);
+ closure->activity = e_mail_reader_new_activity (reader);
+ e_activity_set_text (closure->activity, _("Parsing message"));
+ closure->display = g_object_ref (display);
+
+ em_format_parse_async (formatter, message, folder,
+ e_activity_get_cancellable (closure->activity),
+ format_parser_async_done_cb, closure);
+
+ /* Don't free the mail_uri!! */
+ g_hash_table_insert (formatters, mail_uri, formatter);
+ } else {
+ e_mail_display_set_formatter (display, EM_FORMAT_HTML (formatter));
+ e_mail_display_load (display, formatter->uri_base);
+
+ g_free (mail_uri);
+ }
+}
+
static void
mail_reader_message_loaded (EMailReader *reader,
const gchar *message_uid,
CamelMimeMessage *message)
{
EMailReaderPrivate *priv;
- EMFormatHTML *formatter;
GtkWidget *message_list;
EMailBackend *backend;
CamelFolder *folder;
- EWebView *web_view;
- EPreviewPane *preview_pane;
+ EMailDisplay *display;
EShellBackend *shell_backend;
EShell *shell;
EMEvent *event;
@@ -2953,15 +3158,12 @@ mail_reader_message_loaded (EMailReader *reader,
folder = e_mail_reader_get_folder (reader);
backend = e_mail_reader_get_backend (reader);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
message_list = e_mail_reader_get_message_list (reader);
- preview_pane = e_mail_reader_get_preview_pane (reader);
shell_backend = E_SHELL_BACKEND (backend);
shell = e_shell_backend_get_shell (shell_backend);
- web_view = e_preview_pane_get_web_view (preview_pane);
-
/** @Event: message.reading
* @Title: Viewing a message
* @Target: EMEventTargetMessage
@@ -2975,10 +3177,8 @@ mail_reader_message_loaded (EMailReader *reader,
(EEvent *) event, "message.reading",
(EEventTarget *) target);
- /* FIXME Need to pass a GCancellable. */
- em_format_format (
- EM_FORMAT (formatter), folder,
- message_uid, message, NULL);
+ mail_reader_set_display_formatter_for_message (
+ reader, display, message_uid, message, folder);
/* Reset the shell view icon. */
e_shell_event (shell, "mail-icon", (gpointer) "evolution-mail");
@@ -2996,7 +3196,7 @@ mail_reader_message_loaded (EMailReader *reader,
g_clear_error (&error);
} else if (error != NULL) {
e_alert_submit (
- E_ALERT_SINK (web_view),
+ E_ALERT_SINK (display),
"mail:no-retrieve-message",
error->message, NULL);
g_error_free (error);
@@ -3597,14 +3797,13 @@ e_mail_reader_init (EMailReader *reader,
gboolean init_actions,
gboolean connect_signals)
{
- EMFormatHTML *formatter;
EMenuToolAction *menu_tool_action;
- EWebView *web_view;
GtkActionGroup *action_group;
GtkWidget *message_list;
GtkAction *action;
gboolean sensitive;
const gchar *action_name;
+ EMailDisplay *display;
#ifndef G_OS_WIN32
GSettings *settings;
@@ -3612,10 +3811,8 @@ e_mail_reader_init (EMailReader *reader,
g_return_if_fail (E_IS_MAIL_READER (reader));
- formatter = e_mail_reader_get_formatter (reader);
message_list = e_mail_reader_get_message_list (reader);
-
- web_view = em_format_html_get_web_view (formatter);
+ display = e_mail_reader_get_mail_display (reader);
if (!init_actions)
goto connect_signals;
@@ -3755,30 +3952,38 @@ e_mail_reader_init (EMailReader *reader,
gtk_action_set_is_important (action, TRUE);
gtk_action_set_short_label (action, _("Reply"));
+ display = e_mail_reader_get_mail_display (reader);
+
action_name = "add-to-address-book";
- action = e_web_view_get_action (web_view, action_name);
+ action = e_mail_display_get_action (display, action_name);
g_signal_connect (
action, "activate",
G_CALLBACK (action_add_to_address_book_cb), reader);
action_name = "send-reply";
- action = e_web_view_get_action (web_view, action_name);
+ action = e_mail_display_get_action (display, action_name);
g_signal_connect (
action, "activate",
G_CALLBACK (action_mail_reply_recipient_cb), reader);
action_name = "search-folder-recipient";
- action = e_web_view_get_action (web_view, action_name);
+ action = e_mail_display_get_action (display, action_name);
g_signal_connect (
action, "activate",
G_CALLBACK (action_search_folder_recipient_cb), reader);
action_name = "search-folder-sender";
- action = e_web_view_get_action (web_view, action_name);
+ action = e_mail_display_get_action (display, action_name);
g_signal_connect (
action, "activate",
G_CALLBACK (action_search_folder_sender_cb), reader);
+ action_name = "image-save";
+ action = e_mail_display_get_action (display, action_name);
+ g_signal_connect (
+ action, "activate",
+ G_CALLBACK (action_mail_image_save_cb), reader);
+
#ifndef G_OS_WIN32
/* Lockdown integration. */
@@ -3821,7 +4026,7 @@ e_mail_reader_init (EMailReader *reader,
g_object_bind_property (
action, "active",
- web_view, "caret-mode",
+ display, "caret-mode",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
@@ -3832,7 +4037,7 @@ connect_signals:
/* Connect signals. */
g_signal_connect_swapped (
- web_view, "key-press-event",
+ display, "key-press-event",
G_CALLBACK (mail_reader_key_press_event_cb), reader);
g_signal_connect_swapped (
@@ -4157,17 +4362,17 @@ e_mail_reader_get_backend (EMailReader *reader)
return interface->get_backend (reader);
}
-EMFormatHTML *
-e_mail_reader_get_formatter (EMailReader *reader)
+EMailDisplay *
+e_mail_reader_get_mail_display (EMailReader *reader)
{
EMailReaderInterface *interface;
g_return_val_if_fail (E_IS_MAIL_READER (reader), NULL);
interface = E_MAIL_READER_GET_INTERFACE (reader);
- g_return_val_if_fail (interface->get_formatter != NULL, NULL);
+ g_return_val_if_fail (interface->get_mail_display != NULL, NULL);
- return interface->get_formatter (reader);
+ return interface->get_mail_display (reader);
}
gboolean
diff --git a/mail/e-mail-reader.h b/mail/e-mail-reader.h
index 233e52b1f9..6f03fc0cfe 100644
--- a/mail/e-mail-reader.h
+++ b/mail/e-mail-reader.h
@@ -31,7 +31,7 @@
#include <camel/camel.h>
#include <libevolution-utils/e-alert-sink.h>
#include <mail/e-mail-backend.h>
-#include <mail/em-format-html.h>
+#include <mail/e-mail-display.h>
#include <misc/e-preview-pane.h>
/* Standard GObject macros */
@@ -96,7 +96,7 @@ struct _EMailReaderInterface {
EMailReaderActionGroup group);
EAlertSink * (*get_alert_sink) (EMailReader *reader);
EMailBackend * (*get_backend) (EMailReader *reader);
- EMFormatHTML * (*get_formatter) (EMailReader *reader);
+ EMailDisplay * (*get_mail_display) (EMailReader *reader);
gboolean (*get_hide_deleted) (EMailReader *reader);
GtkWidget * (*get_message_list) (EMailReader *reader);
GtkMenu * (*get_popup_menu) (EMailReader *reader);
@@ -141,7 +141,7 @@ GtkActionGroup *
EMailReaderActionGroup group);
EAlertSink * e_mail_reader_get_alert_sink (EMailReader *reader);
EMailBackend * e_mail_reader_get_backend (EMailReader *reader);
-EMFormatHTML * e_mail_reader_get_formatter (EMailReader *reader);
+EMailDisplay * e_mail_reader_get_mail_display (EMailReader *reader);
gboolean e_mail_reader_get_hide_deleted (EMailReader *reader);
GtkWidget * e_mail_reader_get_message_list (EMailReader *reader);
guint e_mail_reader_open_selected_mail
diff --git a/mail/e-mail-request.c b/mail/e-mail-request.c
new file mode 100644
index 0000000000..34f1845a3c
--- /dev/null
+++ b/mail/e-mail-request.c
@@ -0,0 +1,771 @@
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "e-mail-request.h"
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-requester.h>
+#include <libsoup/soup-request-http.h>
+
+#include <glib/gi18n.h>
+#include <camel/camel.h>
+
+#include "em-format-html.h"
+
+#include <e-util/e-icon-factory.h>
+#include <e-util/e-util.h>
+
+#define d(x)
+#define dd(x)
+
+G_DEFINE_TYPE (EMailRequest, e_mail_request, SOUP_TYPE_REQUEST)
+
+struct _EMailRequestPrivate {
+ EMFormatHTML *efh;
+
+ CamelStream *output_stream;
+ EMFormatPURI *puri;
+ gchar *mime_type;
+
+ gint content_length;
+
+ GHashTable *uri_query;
+
+ gchar *ret_mime_type;
+};
+
+static void
+handle_mail_request (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ EMailRequest *request = E_MAIL_REQUEST (object);
+ EMFormatHTML *efh = request->priv->efh;
+ EMFormat *emf = EM_FORMAT (efh);
+ GInputStream *stream;
+ GByteArray *ba;
+ gchar *part_id;
+ EMFormatWriterInfo info = {0};
+ gchar *val;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ if (request->priv->output_stream != NULL) {
+ g_object_unref (request->priv->output_stream);
+ }
+
+ request->priv->output_stream = camel_stream_mem_new ();
+
+ val = g_hash_table_lookup (request->priv->uri_query, "headers_collapsed");
+ if (val)
+ info.headers_collapsed = atoi (val);
+
+ val = g_hash_table_lookup (request->priv->uri_query, "headers_collapsable");
+ if (val)
+ info.headers_collapsable = atoi (val);
+
+ val = g_hash_table_lookup (request->priv->uri_query, "mode");
+ if (val)
+ info.mode = atoi (val);
+
+ part_id = g_hash_table_lookup (request->priv->uri_query, "part_id");
+ if (part_id) {
+ /* original part_id is owned by the GHashTable */
+ part_id = soup_uri_decode (part_id);
+ request->priv->puri = em_format_find_puri (emf, part_id);
+
+ if (request->priv->puri) {
+ em_format_puri_write (request->priv->puri,
+ request->priv->output_stream, &info, NULL);
+ } else {
+ g_warning ("Failed to lookup requested part '%s' - this should not happen!", part_id);
+ }
+
+ g_free (part_id);
+ } else {
+
+ em_format_write (emf, request->priv->output_stream, &info, NULL);
+ }
+
+ /* Convert the GString to GInputStream and send it back to WebKit */
+ ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (request->priv->output_stream));
+ if (!ba->data) {
+ gchar *data = g_strdup_printf(_("Failed to load part '%s'"), part_id);
+ dd(printf("%s", data));
+ g_byte_array_append (ba, (guchar *) data, strlen (data));
+ g_free (data);
+ } else {
+ dd ({
+ gchar *d = g_strndup ((gchar *) ba->data, ba->len);
+ printf("%s", d);
+ g_free (d);
+ });
+ }
+
+ stream = g_memory_input_stream_new_from_data (
+ (gchar *) ba->data, ba->len, NULL);
+ g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+}
+
+static void
+handle_file_request (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ EMailRequest *request = E_MAIL_REQUEST (object);
+ SoupURI *uri;
+ GInputStream *stream;
+ gchar *contents;
+ gsize length;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ uri = soup_request_get_uri (SOUP_REQUEST (request));
+
+ if (g_file_get_contents (uri->path, &contents, &length, NULL)) {
+
+ request->priv->mime_type = g_content_type_guess (uri->path, NULL, 0, NULL);
+ request->priv->content_length = length;
+
+ stream = g_memory_input_stream_new_from_data (
+ contents, length, (GDestroyNotify) g_free);
+ g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+ }
+}
+
+struct http_request_async_data {
+ GMainLoop *loop;
+ GCancellable *cancellable;
+ CamelDataCache *cache;
+ gchar *cache_key;
+
+ GInputStream *stream;
+ CamelStream *cache_stream;
+ gchar *content_type;
+ goffset content_length;
+
+ gchar *buff;
+};
+
+static void
+http_request_write_to_cache (GInputStream *stream,
+ GAsyncResult *res,
+ struct http_request_async_data *data)
+{
+ GError *error;
+ gssize len;
+
+ error = NULL;
+ len = g_input_stream_read_finish (stream, res, &error);
+
+ /* Error while reading data */
+ if (len == -1) {
+ g_message ("Error while reading input stream: %s",
+ error ? error->message : "Unknown error");
+ g_clear_error (&error);
+
+ g_main_loop_quit (data->loop);
+
+ if (data->buff)
+ g_free (data->buff);
+
+ /* Don't keep broken data in cache */
+ camel_data_cache_remove (data->cache, "http", data->cache_key, NULL);
+ return;
+ }
+
+ /* EOF */
+ if (len == 0) {
+ camel_stream_close (data->cache_stream, data->cancellable, NULL);
+
+ if (data->buff)
+ g_free (data->buff);
+
+ g_main_loop_quit (data->loop);
+ return;
+ }
+
+ if (!data->cache_stream) {
+
+ if (data->buff)
+ g_free (data->buff);
+
+ g_main_loop_quit (data->loop);
+ return;
+ }
+
+ /* Write chunk to cache and read another block of data. */
+ camel_stream_write (data->cache_stream, data->buff, len,
+ data->cancellable, NULL);
+
+ g_input_stream_read_async (stream, data->buff, 4096,
+ G_PRIORITY_DEFAULT, data->cancellable,
+ (GAsyncReadyCallback) http_request_write_to_cache, data);
+}
+
+static void
+http_request_finished (SoupRequest *request,
+ GAsyncResult *res,
+ struct http_request_async_data *data)
+{
+ GError *error;
+ SoupMessage *message;
+
+ error = NULL;
+ data->stream = soup_request_send_finish (request, res, &error);
+
+ if (!data->stream) {
+ g_warning("HTTP request failed: %s", error ? error->message: "Unknown error");
+ g_clear_error (&error);
+ g_main_loop_quit (data->loop);
+ return;
+ }
+
+ message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+ if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
+ g_warning ("HTTP request failed: HTTP code %d", message->status_code);
+ g_main_loop_quit (data->loop);
+ g_object_unref (message);
+ return;
+ }
+
+ g_object_unref (message);
+
+ data->content_length = soup_request_get_content_length (request);
+ data->content_type = g_strdup (soup_request_get_content_type (request));
+
+ if (!data->cache_stream) {
+ g_main_loop_quit (data->loop);
+ return;
+ }
+
+ data->buff = g_malloc (4096);
+ g_input_stream_read_async (data->stream, data->buff, 4096,
+ G_PRIORITY_DEFAULT, data->cancellable,
+ (GAsyncReadyCallback) http_request_write_to_cache, data);
+
+}
+
+static void
+handle_http_request (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ EMailRequest *request = E_MAIL_REQUEST (object);
+ SoupURI *soup_uri;
+ gchar *evo_uri, *uri;
+ GInputStream *stream;
+ gboolean force_load_images = FALSE;
+ gchar *uri_md5;
+
+ const gchar *user_cache_dir;
+ CamelDataCache *cache;
+ CamelStream *cache_stream;
+
+ gssize len;
+ gchar *buff;
+
+ GHashTable *query;
+
+ /* Remove the __evo-mail query */
+ soup_uri = soup_request_get_uri (SOUP_REQUEST (request));
+ query = soup_form_decode (soup_uri->query);
+ g_hash_table_remove (query, "__evo-mail");
+
+ /* Remove __evo-load-images if present (and in such case set
+ * force_load_images to TRUE) */
+ force_load_images = g_hash_table_remove (query, "__evo-load-images");
+
+ soup_uri_set_query_from_form (soup_uri, query);
+ g_hash_table_unref (query);
+
+ evo_uri = soup_uri_to_string (soup_uri, FALSE);
+
+ /* Remove the "evo-" prefix from scheme */
+ if (evo_uri && (strlen (evo_uri) > 5)) {
+ uri = g_strdup (&evo_uri[4]);
+ g_free (evo_uri);
+ }
+
+ g_return_if_fail (uri && *uri);
+
+ /* Use MD5 hash of the URI as a filname of the resourec cache file.
+ * We were previously using the URI as a filename but the URI is
+ * sometimes too long for a filename. */
+ uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+
+ /* Open Evolution's cache */
+ user_cache_dir = e_get_user_cache_dir ();
+ cache = camel_data_cache_new (user_cache_dir, NULL);
+ if (cache) {
+ camel_data_cache_set_expire_age (cache, 24 * 60 * 60);
+ camel_data_cache_set_expire_access (cache, 2 * 60 * 60);
+ }
+
+ /* Found item in cache! */
+ cache_stream = camel_data_cache_get (cache, "http", uri_md5, NULL);
+ if (cache_stream) {
+
+ stream = g_memory_input_stream_new ();
+
+ request->priv->content_length = 0;
+
+ buff = g_malloc (4096);
+ while ((len = camel_stream_read (cache_stream, buff, 4096,
+ cancellable, NULL)) > 0) {
+
+ g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
+ buff, len, g_free);
+ request->priv->content_length += len;
+
+ buff = g_malloc (4096);
+ }
+
+ g_object_unref (cache_stream);
+
+ /* When succesfully read some data from cache then
+ * get mimetype and return the stream to WebKit.
+ * Otherwise try to fetch the resource again from the network. */
+ if ((len != -1) && (request->priv->content_length > 0)) {
+ GFile *file;
+ GFileInfo *info;
+ gchar *path;
+
+ path = camel_data_cache_get_filename (cache, "http", uri_md5, NULL);
+ file = g_file_new_for_path (path);
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, cancellable, NULL);
+
+ request->priv->mime_type = g_strdup (
+ g_file_info_get_content_type (info));
+
+ d(printf ("'%s' found in cache (%d bytes, %s)\n",
+ uri, request->priv->content_length,
+ request->priv->mime_type));
+
+ g_object_unref (info);
+ g_object_unref (file);
+ g_free (path);
+
+ /* Set result and quit the thread */
+ g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+
+ goto cleanup;
+ } else {
+ d(printf("Failed to load '%s' from cache.\n", uri));
+ }
+ }
+
+ /* Item not found in cache, but image loading policy allows us to fetch
+ * it from the interwebs */
+ if (force_load_images || em_format_html_can_load_images (request->priv->efh)) {
+
+ SoupRequester *requester;
+ SoupRequest *http_request;
+ SoupSession *session;
+ GMainContext *context;
+ GError *error;
+
+ struct http_request_async_data data = { 0 };
+
+ context = g_main_context_get_thread_default ();
+ session = soup_session_async_new_with_options (
+ SOUP_SESSION_ASYNC_CONTEXT, context, NULL);
+
+ requester = soup_requester_new ();
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+
+ http_request = soup_requester_request (requester, uri, NULL);
+
+ error = NULL;
+ data.loop = g_main_loop_new (context, TRUE);
+ data.cancellable = cancellable;
+ data.cache = cache;
+ data.cache_key = uri_md5;
+ data.cache_stream = camel_data_cache_add (cache, "http", uri_md5, &error);
+
+ if (!data.cache_stream) {
+ g_warning ("Failed to create cache file for '%s': %s",
+ uri, error ? error->message : "Unknown error");
+ g_clear_error (&error);
+ }
+
+ /* Send the request and waint in mainloop until it's finished
+ * and copied to cache */
+ d(printf(" '%s' not in cache, sending HTTP request\n", uri));
+ soup_request_send_async (http_request, cancellable,
+ (GAsyncReadyCallback) http_request_finished, &data);
+
+ g_main_loop_run (data.loop);
+ d(printf (" '%s' fetched from internet and (hopefully) stored in"
+ " cache\n", uri));
+
+ g_main_loop_unref (data.loop);
+
+ g_object_unref (session);
+
+ g_object_unref (http_request);
+ g_object_unref (requester);
+
+ stream = data.stream;
+ if (!stream)
+ goto cleanup;
+
+ request->priv->content_length = data.content_length;
+ request->priv->mime_type = data.content_type;
+
+ g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+
+ goto cleanup;
+
+ }
+
+cleanup:
+ g_free (uri);
+ g_free (uri_md5);
+}
+
+static void
+handle_stock_request (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ EMailRequest *request;
+ SoupURI *uri;
+ GtkIconTheme *icon_theme;
+ GtkIconInfo *icon_info;
+ const gchar *file;
+ gchar *a_size;
+ gssize size;
+ gchar *buffer;
+ gsize buff_len;
+ GtkStyleContext *context;
+ GtkWidgetPath *path;
+ GtkIconSet *set;
+
+ request = E_MAIL_REQUEST (object);
+ uri = soup_request_get_uri (SOUP_REQUEST (object));
+
+ if (request->priv->uri_query) {
+ a_size = g_hash_table_lookup (request->priv->uri_query, "size");
+ } else {
+ a_size = NULL;
+ }
+
+ if (!a_size) {
+ size = GTK_ICON_SIZE_BUTTON;
+ } else {
+ size = atoi (a_size);
+ }
+
+ /* Try style context first */
+ context = gtk_style_context_new ();
+ path = gtk_widget_path_new ();
+ gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
+ gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
+ gtk_style_context_set_path (context, path);
+
+ set = gtk_style_context_lookup_icon_set (context, uri->host);
+ if (!set) {
+ /* Fallback to icon theme */
+ icon_theme = gtk_icon_theme_get_default ();
+ icon_info = gtk_icon_theme_lookup_icon (
+ icon_theme, uri->host, size,
+ GTK_ICON_LOOKUP_USE_BUILTIN);
+ if (!icon_info) {
+ gtk_widget_path_free (path);
+ g_object_unref (context);
+ return;
+ }
+
+ file = gtk_icon_info_get_filename (icon_info);
+ buffer = NULL;
+ if (file) {
+ if (g_file_get_contents (file, &buffer, &buff_len, NULL)) {
+
+ request->priv->mime_type =
+ g_content_type_guess (file, NULL, 0, NULL);
+ request->priv->content_length = buff_len;
+ }
+
+ } else {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gtk_icon_info_get_builtin_pixbuf (icon_info);
+ if (pixbuf) {
+ gdk_pixbuf_save_to_buffer (
+ pixbuf, &buffer,
+ &buff_len, "png", NULL, NULL);
+
+ request->priv->mime_type = g_strdup("image/png");
+ request->priv->content_length = buff_len;
+
+ g_object_unref (pixbuf);
+ }
+ }
+
+ gtk_icon_info_free (icon_info);
+
+ } else {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gtk_icon_set_render_icon_pixbuf (set, context, size);
+ gdk_pixbuf_save_to_buffer (
+ pixbuf, &buffer,
+ &buff_len, "png", NULL, NULL);
+
+ request->priv->mime_type = g_strdup("image/png");
+ request->priv->content_length = buff_len;
+
+ g_object_unref (pixbuf);
+ }
+
+ if (buffer) {
+ GInputStream *stream;
+ stream = g_memory_input_stream_new_from_data (
+ buffer, buff_len, (GDestroyNotify) g_free);
+ g_simple_async_result_set_op_res_gpointer (res, stream, NULL);
+ }
+
+ gtk_widget_path_free (path);
+ g_object_unref (context);
+
+}
+
+static void
+e_mail_request_init (EMailRequest *request)
+{
+ request->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ request, E_TYPE_MAIL_REQUEST, EMailRequestPrivate);
+
+ request->priv->efh = NULL;
+ request->priv->output_stream = NULL;
+ request->priv->uri_query = NULL;
+ request->priv->puri = NULL;
+ request->priv->mime_type = NULL;
+ request->priv->content_length = 0;
+}
+
+static void
+mail_request_finalize (GObject *object)
+{
+ EMailRequest *request = E_MAIL_REQUEST (object);
+
+ if (request->priv->output_stream) {
+ g_object_unref (request->priv->output_stream);
+ request->priv->output_stream = NULL;
+ }
+
+ if (request->priv->mime_type) {
+ g_free (request->priv->mime_type);
+ request->priv->mime_type = NULL;
+ }
+
+ if (request->priv->uri_query) {
+ g_hash_table_destroy (request->priv->uri_query);
+ request->priv->uri_query = NULL;
+ }
+
+ if (request->priv->ret_mime_type) {
+ g_free (request->priv->ret_mime_type);
+ request->priv->ret_mime_type = NULL;
+ }
+
+ G_OBJECT_CLASS (e_mail_request_parent_class)->finalize (object);
+}
+
+static gboolean
+mail_request_check_uri (SoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ return ((strcmp (uri->scheme, "mail") == 0) ||
+ (strcmp (uri->scheme, "evo-file") == 0) ||
+ (strcmp (uri->scheme, "evo-http") == 0) ||
+ (strcmp (uri->scheme, "evo-https") == 0) ||
+ (strcmp (uri->scheme, "gtk-stock") == 0));
+}
+
+static void
+mail_request_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupSession *session;
+ EMailRequest *emr = E_MAIL_REQUEST (request);
+ GSimpleAsyncResult *result;
+ SoupURI *uri;
+ GHashTable *formatters;
+
+ session = soup_request_get_session (request);
+ uri = soup_request_get_uri (request);
+
+ d(printf("received request for %s\n", soup_uri_to_string (uri, FALSE)));
+
+ /* WebKit won't allow us to load data through local file:// protocol
+ * when using "remote" mail:// protocol, so we have evo-file://
+ * which WebKit thinks it's remote, but in fact it behaves like
+ * oridnary file:// */
+ if (g_strcmp0 (uri->scheme, "evo-file") == 0) {
+
+ result = g_simple_async_result_new (G_OBJECT (request), callback,
+ user_data, mail_request_send_async);
+ g_simple_async_result_run_in_thread (result, handle_file_request,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ return;
+ }
+
+ if (uri->query) {
+ emr->priv->uri_query = soup_form_decode (uri->query);
+ } else {
+ emr->priv->uri_query = NULL;
+ }
+
+ formatters = g_object_get_data (G_OBJECT (session), "formatters");
+ g_return_if_fail (formatters != NULL);
+
+ /* Get HTML content of given PURI part */
+ if (g_strcmp0 (uri->scheme, "mail") == 0) {
+ gchar *uri_str;
+
+ uri_str = g_strdup_printf ("%s://%s%s", uri->scheme, uri->host, uri->path);
+ emr->priv->efh = g_hash_table_lookup (formatters, uri_str);
+ g_free (uri_str);
+
+ g_return_if_fail (emr->priv->efh);
+
+ result = g_simple_async_result_new (G_OBJECT (request), callback,
+ user_data, mail_request_send_async);
+ g_simple_async_result_run_in_thread (result, handle_mail_request,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ return;
+
+ /* For http and https requests we have this evo-http(s) protocol.
+ * We first try to lookup the data in local cache and when not found,
+ * we send standard http(s) request to fetch them. But only when image
+ * loading policy allows us. */
+ } else if ((g_strcmp0 (uri->scheme, "evo-http") == 0) ||
+ (g_strcmp0 (uri->scheme, "evo-https") == 0)) {
+
+ gchar *mail_uri;
+ const gchar *enc = g_hash_table_lookup (emr->priv->uri_query,
+ "__evo-mail");
+
+ g_return_if_fail (enc && *enc);
+
+ mail_uri = soup_uri_decode (enc);
+
+ emr->priv->efh = g_hash_table_lookup (formatters, mail_uri);
+ g_free (mail_uri);
+
+ g_return_if_fail (emr->priv->efh);
+
+ result = g_simple_async_result_new (G_OBJECT (request), callback,
+ user_data, mail_request_send_async);
+ g_simple_async_result_run_in_thread (result, handle_http_request,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ return;
+
+ } else if ((g_strcmp0 (uri->scheme, "gtk-stock") == 0)) {
+
+ result = g_simple_async_result_new (G_OBJECT (request), callback,
+ user_data, mail_request_send_async);
+ g_simple_async_result_run_in_thread (result, handle_stock_request,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ return;
+ }
+}
+
+static GInputStream *
+mail_request_send_finish (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ GInputStream *stream;
+
+ stream = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+ g_object_unref (result);
+
+ /* Reset the stream before passing it back to webkit */
+ if (stream && G_IS_SEEKABLE (stream))
+ g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
+ else /* We must always return something */
+ stream = g_memory_input_stream_new ();
+
+ return stream;
+}
+
+static goffset
+mail_request_get_content_length (SoupRequest *request)
+{
+ EMailRequest *emr = E_MAIL_REQUEST (request);
+ GByteArray *ba;
+ gint content_length = 0;
+
+ if (emr->priv->content_length > 0)
+ content_length = emr->priv->content_length;
+ else if (emr->priv->output_stream) {
+ ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (emr->priv->output_stream));
+ if (ba) {
+ content_length = ba->len;
+ }
+ }
+
+ d(printf("Content-Length: %d bytes\n", content_length));
+ return content_length;
+}
+
+static const gchar *
+mail_request_get_content_type (SoupRequest *request)
+{
+ EMailRequest *emr = E_MAIL_REQUEST (request);
+ gchar *mime_type;
+
+ if (emr->priv->mime_type) {
+ mime_type = g_strdup (emr->priv->mime_type);
+ } else if (!emr->priv->puri) {
+ mime_type = g_strdup ("text/html");
+ } else if (!emr->priv->puri->mime_type) {
+ CamelContentType *ct = camel_mime_part_get_content_type (emr->priv->puri->part);
+ mime_type = camel_content_type_simple (ct);
+ } else {
+ mime_type = g_strdup (emr->priv->puri->mime_type);
+ }
+
+ if (g_strcmp0 (mime_type, "text/html") == 0) {
+ emr->priv->ret_mime_type = g_strconcat (mime_type, "; charset=\"UTF-8\"", NULL);
+ g_free (mime_type);
+ } else {
+ emr->priv->ret_mime_type = mime_type;
+ }
+
+ d(printf("Content-Type: %s\n", emr->priv->ret_mime_type));
+
+ return emr->priv->ret_mime_type;
+}
+
+static const char *data_schemes[] = { "mail", "evo-file", "evo-http", "evo-https", "gtk-stock", NULL };
+
+static void
+e_mail_request_class_init (EMailRequestClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ SoupRequestClass *request_class = SOUP_REQUEST_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (EMailRequestPrivate));
+
+ object_class->finalize = mail_request_finalize;
+
+ request_class->schemes = data_schemes;
+ request_class->send_async = mail_request_send_async;
+ request_class->send_finish = mail_request_send_finish;
+ request_class->get_content_type = mail_request_get_content_type;
+ request_class->get_content_length = mail_request_get_content_length;
+ request_class->check_uri = mail_request_check_uri;
+}
diff --git a/mail/e-mail-request.h b/mail/e-mail-request.h
new file mode 100644
index 0000000000..923362101e
--- /dev/null
+++ b/mail/e-mail-request.h
@@ -0,0 +1,36 @@
+#ifndef E_MAIL_REQUEST_H
+#define E_MAIL_REQUEST_H
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-request.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_MAIL_REQUEST (e_mail_request_get_type ())
+#define E_MAIL_REQUEST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), E_TYPE_MAIL_REQUEST, EMailRequest))
+#define E_MAIL_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_MAIL_REQUEST, EMailRequestClass))
+#define E_IS_MAIL_REQUEST(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), E_TYPE_MAIL_REQUEST))
+#define E_IS_MAIL_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_MAIL_REQUEST))
+#define E_MAIL_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_MAIL_REQUEST, EMailRequestClass))
+
+typedef struct _EMailRequest EMailRequest;
+typedef struct _EMailRequestClass EMailRequestClass;
+typedef struct _EMailRequestPrivate EMailRequestPrivate;
+
+struct _EMailRequest {
+ SoupRequest parent;
+
+ EMailRequestPrivate *priv;
+};
+
+struct _EMailRequestClass {
+ SoupRequestClass parent;
+};
+
+GType e_mail_request_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_MAIL_REQUEST_H */
diff --git a/mail/em-account-editor.c b/mail/em-account-editor.c
index d53289d3c5..74a3d5bc92 100644
--- a/mail/em-account-editor.c
+++ b/mail/em-account-editor.c
@@ -2523,9 +2523,9 @@ emae_refresh_providers (EMAccountEditor *emae,
GTK_COMBO_BOX (combo_box), service->protocol);
/* make sure at least something is selected;
- this applies for cases when user changed from provider which was
- store and transport together, to a store provider only (like from
- exchange to imap provider), which left unselected transport type
+ * this applies for cases when user changed from provider which was
+ * store and transport together, to a store provider only (like from
+ * exchange to imap provider), which left unselected transport type
*/
if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) == -1)
gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index 745609fbc0..f4b8560f81 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -51,12 +51,14 @@
#include <composer/e-composer-actions.h>
#include <composer/e-composer-post-header.h>
+#include "e-mail-printer.h"
#include "em-utils.h"
#include "em-composer-utils.h"
#include "em-folder-selector.h"
#include "em-folder-tree.h"
#include "em-format-html.h"
#include "em-format-html-print.h"
+#include "em-format-html-display.h"
#include "em-format-quote.h"
#include "em-event.h"
#include "mail-send-recv.h"
@@ -929,17 +931,38 @@ em_utils_composer_save_to_outbox_cb (EMsgComposer *composer,
}
static void
+composer_print_done_cb (EMailPrinter *emp,
+ GtkPrintOperation *operation,
+ GtkPrintOperationResult result,
+ gpointer user_data)
+{
+ EMFormat *emf = user_data;
+ g_object_unref (emf);
+ g_object_unref (emp);
+}
+
+static void
em_utils_composer_print_cb (EMsgComposer *composer,
GtkPrintOperationAction action,
CamelMimeMessage *message,
EActivity *activity,
EMailSession *session)
{
- EMFormatHTMLPrint *efhp;
+ EMailPrinter *emp;
+ EMFormatHTMLDisplay *efhd;
+
+ efhd = em_format_html_display_new ();
+ ((EMFormat *) efhd)->message_uid = g_strdup (camel_mime_message_get_message_id (message));
+
+ /* Parse the message */
+ em_format_parse ((EMFormat *) efhd, message, NULL, NULL);
+
+ /* Use EMailPrinter and WebKit to print the message */
+ emp = e_mail_printer_new ((EMFormatHTML *) efhd);
+ g_signal_connect (emp, "done",
+ G_CALLBACK (composer_print_done_cb), efhd);
- efhp = em_format_html_print_new (NULL, action);
- em_format_html_print_message (efhp, message, NULL, NULL);
- g_object_unref (efhp);
+ e_mail_printer_print (emp, FALSE, NULL);
}
/* Composing messages... */
diff --git a/mail/em-format-hook.c b/mail/em-format-hook.c
index 233805561b..7ceeb20e4d 100644
--- a/mail/em-format-hook.c
+++ b/mail/em-format-hook.c
@@ -61,24 +61,23 @@ static const EPluginHookTargetKey emfh_flag_map[] = {
G_DEFINE_TYPE (EMFormatHook, em_format_hook, E_TYPE_PLUGIN_HOOK)
static void
-emfh_format_format (EMFormat *md,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+emfh_parse_part (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- struct _EMFormatHookItem *item = (EMFormatHookItem *) info;
+ struct _EMFormatHookItem *item = (EMFormatHookItem *) info->handler;
if (item->hook->hook.plugin->enabled) {
EMFormatHookTarget target = {
- md, stream, part, item
+ emf, part, part_id, info
};
e_plugin_invoke (item->hook->hook.plugin, item->format, &target);
- } else if (info->old) {
- info->old->handler (
- md, stream, part, info->old, cancellable, FALSE);
+ } else if (info->handler->old) {
+ info->handler->old->parse_func (
+ emf, part, part_id, info, cancellable);
}
}
@@ -116,7 +115,7 @@ emfh_construct_item (EPluginHook *eph,
item->handler.flags = e_plugin_hook_mask(root, emfh_flag_map, "flags");
item->format = e_plugin_xml_prop(root, "format");
- item->handler.handler = emfh_format_format;
+ item->handler.parse_func = emfh_parse_part;
item->hook = emfh;
if (item->handler.mime_type == NULL || item->format == NULL)
diff --git a/mail/em-format-hook.h b/mail/em-format-hook.h
index ad0745bc2e..09076c2f48 100644
--- a/mail/em-format-hook.h
+++ b/mail/em-format-hook.h
@@ -40,9 +40,9 @@ typedef void (*EMFormatHookFunc)(struct _EPlugin *plugin, EMFormatHookTarget *da
struct _EMFormatHookTarget {
struct _EMFormat *format;
- CamelStream *stream;
CamelMimePart *part;
- struct _EMFormatHookItem *item;
+ GString *part_id;
+ EMFormatParserInfo *info;
};
struct _EMFormatHookItem {
diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c
index 013e1ac731..8f20169636 100644
--- a/mail/em-format-html-display.c
+++ b/mail/em-format-html-display.c
@@ -38,9 +38,6 @@
#undef interface
#endif
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-embedded.h>
-
#include <glib/gi18n.h>
#include <e-util/e-util.h>
@@ -53,6 +50,8 @@
#include <shell/e-shell.h>
#include <shell/e-shell-utils.h>
+#include <libedataserver/e-flag.h>
+
#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
#include "certificate-viewer.h"
#include "e-cert-db.h"
@@ -65,6 +64,8 @@
#include "widgets/misc/e-attachment.h"
#include "widgets/misc/e-attachment-button.h"
#include "widgets/misc/e-attachment-view.h"
+#include "shell/e-shell.h"
+#include "shell/e-shell-window.h"
#define EM_FORMAT_HTML_DISPLAY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -77,16 +78,9 @@
((obj), EM_TYPE_FORMAT_HTML_DISPLAY, EMFormatHTMLDisplayPrivate))
struct _EMFormatHTMLDisplayPrivate {
- GHashTable *attachment_views; /* weak reference; message_part_id->EAttachmentView */
- gboolean attachment_expanded;
-};
-struct _smime_pobject {
- EMFormatHTMLPObject object;
+ EAttachmentView *last_view;
- gint signature;
- CamelCipherValidity *valid;
- GtkWidget *widget;
};
/* TODO: move the dialogue elsehwere */
@@ -111,78 +105,100 @@ static const struct {
{ "stock_lock-ok", N_("Encrypted, strong"), N_("This message is encrypted, with a strong encryption algorithm. It would be very difficult for an outsider to view the content of this message in a practical amount of time.") },
};
-static const gchar *smime_sign_colour[5] = {
- "", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\"",""
+static const GdkRGBA smime_sign_colour[5] = {
+ { 0 }, { 0.53, 0.73, 0.53, 1 }, { 0.73, 0.53, 0.53, 1 }, { 0.91, 0.82, 0.13, 1 }, { 0 },
};
-static void efhd_attachment_frame (EMFormat *emf, CamelStream *stream, EMFormatPURI *puri, GCancellable *cancellable);
-static void efhd_message_add_bar (EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);
-static gboolean efhd_attachment_button (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject);
-static gboolean efhd_attachment_optional (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *object);
+static void efhd_message_prefix (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_message_add_bar (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_parse_attachment (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_parse_secure (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efhd_parse_optional (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+
+static GtkWidget * efhd_attachment_bar (EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable);
+static GtkWidget * efhd_attachment_button (EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable);
+static GtkWidget * efhd_attachment_optional (EMFormat *emf, EMFormatPURI *puri, GCancellable *cancellable);
+
+static void efhd_write_attachment_bar (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhd_write_attachment (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhd_write_secure_button (EMFormat *emf, EMFormatPURI *emp, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
static void efhd_free_attach_puri_data (EMFormatPURI *puri);
-struct _attach_puri {
- EMFormatPURI puri;
+static void efhd_builtin_init (EMFormatHTMLDisplayClass *efhc);
+
+static gpointer parent_class;
- const EMFormatHandler *handle;
+static EAttachmentStore *
+find_parent_attachment_store (EMFormatHTMLDisplay *efhd,
+ const gchar *part_id)
+{
+ EMFormat *emf = (EMFormat *) efhd;
+ EMFormatAttachmentBarPURI *abp;
+ gchar *tmp, *pos;
+ GList *item;
- const gchar *snoop_mime_type;
+ tmp = g_strdup (part_id);
- /* for the > and V buttons */
- GtkWidget *forward, *down;
- /* currently no way to correlate this data to the frame :( */
- GtkHTML *frame;
- guint shown : 1;
+ do {
+ gchar *id;
- /* Embedded Frame */
- GtkHTMLEmbedded *html;
+ pos = g_strrstr (tmp, ".");
+ if (!pos)
+ break;
- /* Attachment */
- EAttachment *attachment;
- gchar *attachment_view_part_id;
+ g_free (tmp);
+ tmp = g_strndup (part_id, pos - tmp);
+ id = g_strdup_printf ("%s.attachment-bar", tmp);
- /* image stuff */
- gint fit_width;
- gint fit_height;
- GtkImage *image;
- GtkWidget *event_box;
+ item = g_hash_table_lookup (emf->mail_part_table, id);
- /* Optional Text Mem Stream */
- CamelStreamMem *mstream;
+ g_free (id);
- /* Signed / Encrypted */
- camel_cipher_validity_sign_t sign;
- camel_cipher_validity_encrypt_t encrypt;
-};
+ } while (pos && !item);
-static void efhd_message_prefix (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback);
+ g_free (tmp);
-static void efhd_builtin_init (EMFormatHTMLDisplayClass *efhc);
+ abp = (EMFormatAttachmentBarPURI *) item->data;
-G_DEFINE_TYPE (
- EMFormatHTMLDisplay,
- em_format_html_display,
- EM_TYPE_FORMAT_HTML)
+ if (abp)
+ return abp->store;
+ else
+ return NULL;
+}
static void
-efhd_xpkcs7mime_free (EMFormatHTMLPObject *o)
+efhd_attachment_bar_puri_free (EMFormatPURI *puri)
{
- struct _smime_pobject *po = (struct _smime_pobject *) o;
+ EMFormatAttachmentBarPURI *abp;
- if (po->widget)
- gtk_widget_destroy (po->widget);
- camel_cipher_validity_free (po->valid);
+ abp = (EMFormatAttachmentBarPURI *) puri;
+
+ if (abp->store) {
+ g_object_unref (abp->store);
+ abp->store = NULL;
+ }
+}
+
+static void
+efhd_xpkcs7mime_free (EMFormatPURI *puri)
+{
+ EMFormatSMIMEPURI *sp = (EMFormatSMIMEPURI *) puri;
+
+ if (sp->widget)
+ gtk_widget_destroy (sp->widget);
+
+ if (sp->description)
+ g_free (sp->description);
+
+ if (sp->valid)
+ camel_cipher_validity_free (sp->valid);
}
static void
efhd_xpkcs7mime_info_response (GtkWidget *widget,
guint button,
- struct _smime_pobject *po)
+ EMFormatSMIMEPURI *po)
{
gtk_widget_destroy (widget);
po->widget = NULL;
@@ -191,7 +207,7 @@ efhd_xpkcs7mime_info_response (GtkWidget *widget,
#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
static void
efhd_xpkcs7mime_viewcert_clicked (GtkWidget *button,
- struct _smime_pobject *po)
+ EMFormatSMIMEPURI *po)
{
CamelCipherCertInfo *info = g_object_get_data((GObject *)button, "e-cert-info");
ECert *ec = NULL;
@@ -221,7 +237,7 @@ efhd_xpkcs7mime_viewcert_clicked (GtkWidget *button,
static void
efhd_xpkcs7mime_add_cert_table (GtkWidget *grid,
GQueue *certlist,
- struct _smime_pobject *po)
+ EMFormatSMIMEPURI *po)
{
GList *head, *link;
GtkTable *table;
@@ -283,9 +299,9 @@ efhd_xpkcs7mime_add_cert_table (GtkWidget *grid,
static void
efhd_xpkcs7mime_validity_clicked (GtkWidget *button,
- EMFormatHTMLPObject *pobject)
+ EMFormatPURI *puri)
{
- struct _smime_pobject *po = (struct _smime_pobject *) pobject;
+ EMFormatSMIMEPURI *po = (EMFormatSMIMEPURI *) puri;
GtkBuilder *builder;
GtkWidget *grid, *w;
@@ -367,20 +383,19 @@ efhd_xpkcs7mime_validity_clicked (GtkWidget *button,
g_object_unref (builder);
g_signal_connect (
- po->widget, "response",
+ po->widget, "response",
G_CALLBACK (efhd_xpkcs7mime_info_response), po);
gtk_widget_show (po->widget);
}
-static gboolean
-efhd_xpkcs7mime_button (EMFormatHTML *efh,
- GtkHTMLEmbedded *eb,
- EMFormatHTMLPObject *pobject)
+static GtkWidget *
+efhd_xpkcs7mime_button (EMFormat *emf,
+ EMFormatPURI *puri,
+ GCancellable *cancellable)
{
- GtkWidget *container;
- GtkWidget *widget;
- struct _smime_pobject *po = (struct _smime_pobject *) pobject;
+ GtkWidget *box, *button, *layout, *widget;
+ EMFormatSMIMEPURI *po = (EMFormatSMIMEPURI *) puri;
const gchar *icon_name;
/* FIXME: need to have it based on encryption and signing too */
@@ -389,450 +404,436 @@ efhd_xpkcs7mime_button (EMFormatHTML *efh,
else
icon_name = smime_encrypt_table[po->valid->encrypt.status].icon;
- container = GTK_WIDGET (eb);
+ box = gtk_event_box_new ();
+ if (po->valid->sign.status != 0)
+ gtk_widget_override_background_color (box, GTK_STATE_FLAG_NORMAL,
+ &smime_sign_colour[po->valid->sign.status]);
- widget = gtk_button_new ();
- g_signal_connect (
- widget, "clicked",
- G_CALLBACK (efhd_xpkcs7mime_validity_clicked), pobject);
- gtk_container_add (GTK_CONTAINER (container), widget);
- gtk_widget_show (widget);
+ layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_container_add (GTK_CONTAINER (box), layout);
- container = widget;
+ button = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (efhd_xpkcs7mime_validity_clicked), puri);
widget = gtk_image_new_from_icon_name (
- icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
- gtk_container_add (GTK_CONTAINER (container), widget);
- gtk_widget_show (widget);
+ icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_button_set_image (GTK_BUTTON (button), widget);
- return TRUE;
-}
+ widget = gtk_label_new (po->description);
+ gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
-static gboolean
-remove_attachment_view_cb (gpointer message_part_id,
- gpointer attachment_view,
- gpointer gone_attachment_view)
-{
- return attachment_view == gone_attachment_view;
+ gtk_widget_show_all (box);
+
+ return box;
}
+struct attachment_load_data {
+ EAttachment *attachment;
+ EFlag *flag;
+};
+
static void
-efhd_attachment_view_gone_cb (gpointer efh,
- GObject *gone_attachment_view)
+attachment_loaded (EAttachment *attachment,
+ GAsyncResult *res,
+ gpointer user_data)
{
- EMFormatHTMLDisplay *efhd = EM_FORMAT_HTML_DISPLAY (efh);
+ struct attachment_load_data *data = user_data;
+ EShell *shell;
+ GtkWindow *window;
- g_return_if_fail (efhd != NULL);
+ shell = e_shell_get_default ();
+ window = e_shell_get_active_window (shell);
+ if (!E_IS_SHELL_WINDOW (window))
+ window = NULL;
- g_hash_table_foreach_remove (
- efhd->priv->attachment_views,
- remove_attachment_view_cb,
- gone_attachment_view);
-}
+ e_attachment_load_handle_error (data->attachment, res, window);
-static void
-weak_unref_attachment_view_cb (gpointer message_part_id,
- gpointer attachment_view,
- gpointer efh)
-{
- g_object_weak_unref (
- G_OBJECT (attachment_view),
- efhd_attachment_view_gone_cb, efh);
+ e_flag_set (data->flag);
}
-static void
-efhd_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormat *src,
- GCancellable *cancellable)
+/* Idle callback */
+static gboolean
+load_attachment_idle (struct attachment_load_data *data)
{
- EMFormatHTMLDisplay *efhd;
-
- efhd = EM_FORMAT_HTML_DISPLAY (emf);
- g_return_if_fail (efhd != NULL);
+ e_attachment_load_async (data->attachment,
+ (GAsyncReadyCallback) attachment_loaded, data);
- g_hash_table_foreach (efhd->priv->attachment_views, weak_unref_attachment_view_cb, efhd);
- g_hash_table_remove_all (efhd->priv->attachment_views);
-
- if (emf != src)
- EM_FORMAT_HTML (emf)->header_wrap_flags = 0;
-
- /* Chain up to parent's format_clone() method. */
- EM_FORMAT_CLASS (em_format_html_display_parent_class)->
- format_clone (emf, folder, uid, msg, src, cancellable);
+ return FALSE;
}
static void
-efhd_format_attachment (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const gchar *mime_type,
- const EMFormatHandler *handle,
- GCancellable *cancellable)
+efhd_parse_attachment (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- GString *buffer;
- gchar *classid, *text, *html;
- struct _attach_puri *info;
-
- classid = g_strdup_printf ("attachment%s", emf->part_id->str);
- info = (struct _attach_puri *) em_format_add_puri (
- emf, sizeof (*info), classid, part, efhd_attachment_frame);
- info->puri.free = efhd_free_attach_puri_data;
- info->attachment_view_part_id = g_strdup (emf->current_message_part_id);
- em_format_html_add_pobject (
- EM_FORMAT_HTML (emf), sizeof (EMFormatHTMLPObject),
- classid, part, efhd_attachment_button);
- info->handle = handle;
- info->shown = em_format_is_inline (
- emf, info->puri.part_id, info->puri.part, handle);
- info->snoop_mime_type = emf->snoop_mime_type;
- info->attachment = e_attachment_new ();
- e_attachment_set_mime_part (info->attachment, info->puri.part);
-
- if (emf->valid) {
- info->sign = emf->valid->sign.status;
- info->encrypt = emf->valid->encrypt.status;
+ gchar *text, *html;
+ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
+ EMFormatAttachmentPURI *puri;
+ EAttachmentStore *store;
+ const EMFormatHandler *handler;
+ CamelContentType *ct;
+ gchar *mime_type;
+ gint len;
+ const gchar *cid;
+ guint32 size;
+ struct attachment_load_data *load_data;
+ gboolean can_show = FALSE;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ len = part_id->len;
+ g_string_append (part_id, ".attachment");
+
+ /* Try to find handler for the mime part */
+ ct = camel_mime_part_get_content_type (part);
+ if (ct) {
+ mime_type = camel_content_type_simple (ct);
+ handler = em_format_find_handler (emf, mime_type);
}
- buffer = g_string_sized_new (1024);
-
- g_string_append_printf (
- buffer, EM_FORMAT_HTML_VPAD
- "<table cellspacing=0 cellpadding=0>"
- "<tr><td>"
- "<table width=10 cellspacing=0 cellpadding=0>"
- "<tr><td></td><tr>"
- "</table>"
- "</td>"
- "<td><object classid=\"%s\"></object></td>"
- "<td><table width=3 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr>"
- "</table></td>"
- "<td><font size=-1>",
- classid);
-
- /* output some info about it */
/* FIXME: should we look up mime_type from object again? */
text = em_format_describe_part (part, mime_type);
html = camel_text_to_html (
text, EM_FORMAT_HTML (emf)->text_html_flags &
CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- g_string_append (buffer, html);
- g_free (html);
g_free (text);
+ g_free (mime_type);
+
+ puri = (EMFormatAttachmentPURI *) em_format_puri_new (
+ emf, sizeof (EMFormatAttachmentPURI), part, part_id->str);
+ puri->puri.free = efhd_free_attach_puri_data;
+ puri->puri.write_func = efhd_write_attachment;
+ puri->puri.widget_func = efhd_attachment_button;
+ puri->shown = (handler && em_format_is_inline (emf, part_id->str, part, handler));
+ puri->snoop_mime_type = em_format_snoop_type (part);
+ puri->attachment = e_attachment_new ();
+ puri->attachment_view_part_id = NULL;
+ puri->description = html;
+ puri->handle = handler;
+ if (info->validity)
+ puri->puri.validity = camel_cipher_validity_clone (info->validity);
+
+ cid = camel_mime_part_get_content_id (part);
+ if (cid)
+ puri->puri.cid = g_strdup_printf ("cid:%s", cid);
+
+ if (handler) {
+ CamelContentType *ct;
+
+ /* This mime_type is important for WebKit to determine content type.
+ * We have converted text/ * to text/html, other (binary) formats remained
+ * untouched. */
+ ct = camel_content_type_decode (handler->mime_type);
+ if (g_strcmp0 (ct->type, "text") == 0)
+ puri->puri.mime_type = g_strdup ("text/html");
+ else
+ puri->puri.mime_type = camel_content_type_simple (ct);
+ camel_content_type_unref (ct);
+ }
- g_string_append (
- buffer,
- "</font></td>"
- "</tr><tr></table>\n"
- EM_FORMAT_HTML_VPAD);
-
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
+ em_format_add_puri (emf, (EMFormatPURI *) puri);
+
+ /* Though it is an attachment, we still might be able to parse it and
+ * so discover some parts that we might be even able to display. */
+ if (handler && handler->parse_func && (handler->parse_func != efhd_parse_attachment) &&
+ ((handler->flags & EM_FORMAT_HANDLER_COMPOUND_TYPE) ||
+ (handler->flags & EM_FORMAT_HANDLER_INLINE_DISPOSITION))) {
+ GList *i;
+ EMFormatParserInfo attachment_info = { .handler = handler,
+ .is_attachment = TRUE };
+ handler->parse_func (emf, puri->puri.part, part_id, &attachment_info, cancellable);
+
+ i = g_hash_table_lookup (emf->mail_part_table, part_id->str);
+ if (i->next && i->next->data) {
+ EMFormatPURI *p = i->next->data;
+ puri->attachment_view_part_id = g_strdup (p->uri);
+ can_show = TRUE;
+ }
+ }
- if (handle && info->shown)
- handle->handler (
- emf, stream, part, handle, cancellable, FALSE);
+ e_attachment_set_mime_part (puri->attachment, part);
+ e_attachment_set_shown (puri->attachment, puri->shown);
+ if (puri->puri.validity) {
+ e_attachment_set_signed (puri->attachment, puri->puri.validity->sign.status);
+ e_attachment_set_encrypted (puri->attachment, puri->puri.validity->encrypt.status);
+ }
+ e_attachment_set_can_show (puri->attachment,
+ can_show || (puri->handle && puri->handle->write_func));
- g_free (classid);
-}
+ store = find_parent_attachment_store (efhd, part_id->str);
+ e_attachment_store_add_attachment (store, puri->attachment);
-static void
-efhd_format_optional (EMFormat *emf,
- CamelStream *fstream,
- CamelMimePart *part,
- CamelStream *mstream,
- GCancellable *cancellable)
-{
- gchar *classid, *html;
- struct _attach_puri *info;
- CamelStream *stream = NULL;
- GString *buffer;
+ if (emf->folder && emf->folder->summary && emf->message_uid) {
+ CamelDataWrapper *dw = camel_medium_get_content (CAMEL_MEDIUM (puri->puri.part));
+ GByteArray *ba;
+ ba = camel_data_wrapper_get_byte_array (dw);
+ if (ba) {
+ size = ba->len;
- if (CAMEL_IS_STREAM_FILTER (fstream))
- stream = camel_stream_filter_get_source (
- CAMEL_STREAM_FILTER (fstream));
- if (stream == NULL)
- stream = fstream;
-
- classid = g_strdup_printf ("optional%s", emf->part_id->str);
- info = (struct _attach_puri *) em_format_add_puri (
- emf, sizeof (*info), classid, part, efhd_attachment_frame);
- info->puri.free = efhd_free_attach_puri_data;
- info->attachment_view_part_id = g_strdup (emf->current_message_part_id);
- em_format_html_add_pobject (
- EM_FORMAT_HTML (emf), sizeof (EMFormatHTMLPObject),
- classid, part, efhd_attachment_optional);
- info->handle = em_format_find_handler (emf, "text/plain");
- info->shown = FALSE;
- info->snoop_mime_type = "text/plain";
- info->attachment = e_attachment_new ();
- e_attachment_set_mime_part (info->attachment, info->puri.part);
- info->mstream = (CamelStreamMem *) g_object_ref (mstream);
- if (emf->valid) {
- info->sign = emf->valid->sign.status;
- info->encrypt = emf->valid->encrypt.status;
+ if (camel_mime_part_get_encoding (puri->puri.part) == CAMEL_TRANSFER_ENCODING_BASE64)
+ size = size / 1.37;
+ }
}
- buffer = g_string_sized_new (1024);
+ load_data = g_new0 (struct attachment_load_data, 1);
+ load_data->attachment = g_object_ref (puri->attachment);
+ load_data->flag = e_flag_new ();
- g_string_append (
- buffer, EM_FORMAT_HTML_VPAD
- "<table cellspacing=0 cellpadding=0><tr><td>"
- "<h3><font size=-1 color=red>");
+ e_flag_clear (load_data->flag);
- html = camel_text_to_html (
- _("Evolution cannot render this email as it is too "
- "large to process. You can view it unformatted or "
- "with an external text editor."),
- EM_FORMAT_HTML (emf)->text_html_flags &
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- g_string_append (buffer, html);
- g_free (html);
+ /* e_attachment_load_async must be called from main thread */
+ g_idle_add ((GSourceFunc) load_attachment_idle, load_data);
- g_string_append_printf (
- buffer,
- "</font></h3></td></tr></table>\n"
- "<table cellspacing=0 cellpadding=0><tr>"
- "<td><object classid=\"%s\"></object>"
- "</td></tr></table>" EM_FORMAT_HTML_VPAD,
- classid);
+ e_flag_wait (load_data->flag);
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
+ e_flag_free (load_data->flag);
+ g_object_unref (load_data->attachment);
+ g_free (load_data);
- g_string_free (buffer, TRUE);
+ if (size != 0) {
+ GFileInfo *fileinfo;
+
+ fileinfo = e_attachment_get_file_info (puri->attachment);
+ g_file_info_set_size (fileinfo, size);
+ e_attachment_set_file_info (puri->attachment, fileinfo);
+ }
- g_free (classid);
+ g_string_truncate (part_id, len);
}
static void
-efhd_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- CamelCipherValidity *valid,
- GCancellable *cancellable)
+efhd_parse_optional (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatClass *format_class;
+ EMFormatAttachmentPURI *puri;
+ gint len;
+
+ len = part_id->len;
+ g_string_append (part_id, ".optional");
+
+ puri = (EMFormatAttachmentPURI *) em_format_puri_new (
+ emf, sizeof (EMFormatAttachmentPURI), part, part_id->str);
+ puri->puri.free = efhd_free_attach_puri_data;
+ puri->puri.write_func = efhd_write_attachment;
+ puri->puri.widget_func = efhd_attachment_optional;
+ puri->attachment_view_part_id = g_strdup (part_id->str);
+ puri->handle = em_format_find_handler (emf, "text/plain");
+ puri->shown = FALSE;
+ puri->snoop_mime_type = "text/plain";
+ puri->attachment = e_attachment_new ();
+ e_attachment_set_mime_part (puri->attachment, puri->puri.part);
+ puri->description = g_strdup(_("Evolution cannot render this email as it is too "
+ "large to process. You can view it unformatted or "
+ "with an external text editor."));
+
+ puri->mstream = CAMEL_STREAM_MEM (camel_stream_mem_new ());
+ camel_data_wrapper_decode_to_stream_sync ((CamelDataWrapper *) part,
+ (CamelStream *) puri->mstream, cancellable, NULL);
+
+ if (info->validity) {
+ puri->puri.validity = camel_cipher_validity_clone (info->validity);
+ }
- format_class = g_type_class_peek (EM_TYPE_FORMAT);
- format_class->format_secure (emf, stream, part, valid, cancellable);
+ em_format_add_puri (emf, (EMFormatPURI *) puri);
- if (emf->valid == valid
- && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
- || valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
+ g_string_truncate (part_id, len);
+}
+
+static void
+efhd_parse_secure (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ if (info->validity
+ && (info->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
+ || info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
GString *buffer;
- gchar *classid;
- struct _smime_pobject *pobj;
+ EMFormatSMIMEPURI *pobj;
- buffer = g_string_sized_new (1024);
+ pobj = (EMFormatSMIMEPURI *) em_format_puri_new (
+ emf, sizeof (EMFormatSMIMEPURI), part, part_id->str);
+ pobj->puri.free = efhd_xpkcs7mime_free;
+ pobj->valid = camel_cipher_validity_clone (info->validity);
+ pobj->puri.widget_func = efhd_xpkcs7mime_button;
+ pobj->puri.write_func = efhd_write_secure_button;
- g_string_append_printf (
- buffer,
- "<table border=0 width=\"100%%\" "
- "cellpadding=3 cellspacing=0%s><tr>",
- smime_sign_colour[valid->sign.status]);
+ em_format_add_puri (emf, (EMFormatPURI *) pobj);
- classid = g_strdup_printf (
- "smime:///em-format-html/%s/icon/signed",
- emf->part_id->str);
- pobj = (struct _smime_pobject *) em_format_html_add_pobject (
- EM_FORMAT_HTML (emf), sizeof (*pobj),
- classid, part, efhd_xpkcs7mime_button);
- pobj->valid = camel_cipher_validity_clone (valid);
- pobj->object.free = efhd_xpkcs7mime_free;
- g_string_append_printf (
- buffer,
- "<td valign=center><object classid=\"%s\">"
- "</object></td><td width=100%% valign=center>",
- classid);
- g_free (classid);
+ buffer = g_string_new ("");
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
+ if (info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
const gchar *desc;
gint status;
- status = valid->sign.status;
+ status = info->validity->sign.status;
desc = smime_sign_table[status].shortdesc;
g_string_append (buffer, gettext (desc));
em_format_html_format_cert_infos (
- &valid->sign.signers, buffer);
+ &info->validity->sign.signers, buffer);
}
- if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
+ if (info->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
const gchar *desc;
gint status;
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
- g_string_append (buffer, "<br>");
+ if (info->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
+ g_string_append (buffer, "\n");
- status = valid->encrypt.status;
+ status = info->validity->encrypt.status;
desc = smime_encrypt_table[status].shortdesc;
g_string_append (buffer, gettext (desc));
}
- g_string_append (buffer, "</td></tr></table>");
-
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
+ pobj->description = g_string_free (buffer, FALSE);
}
}
+/******************************************************************************/
static void
-attachment_load_finish (EAttachment *attachment,
- GAsyncResult *result,
- GFile *file)
+efhd_write_attachment_bar (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- EShell *shell;
- GtkWindow *parent;
+ EMFormatAttachmentBarPURI *efab = (EMFormatAttachmentBarPURI *) puri;
+ gchar *str;
- e_attachment_load_finish (attachment, result, NULL);
+ if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING)
+ return;
- shell = e_shell_get_default ();
- parent = e_shell_get_active_window (shell);
+ if (e_attachment_store_get_num_attachments (efab->store) == 0)
+ return;
- e_attachment_save_async (
- attachment, file, (GAsyncReadyCallback)
- e_attachment_save_handle_error, parent);
+ str = g_strdup_printf (
+ "<object type=\"application/x-attachment-bar\" "
+ "height=\"20\" width=\"100%%\" "
+ "id=\"%s\"data=\"%s\"></object>", puri->uri, puri->uri);
- g_object_unref (file);
+ camel_stream_write_string (stream, str, cancellable, NULL);
+
+ g_free (str);
}
static void
-action_image_save_cb (GtkAction *action,
- EMFormatHTMLDisplay *efhd)
+efhd_write_attachment (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- EWebView *web_view;
- EMFormat *emf;
- const gchar *image_src;
- CamelMimePart *part;
- EAttachment *attachment;
- GFile *file;
+ gchar *str, *desc;
+ const gchar *mime_type;
+ gchar *button_id;
- web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhd));
- g_return_if_fail (web_view != NULL);
+ EMFormatAttachmentPURI *efa = (EMFormatAttachmentPURI *) puri;
- image_src = e_web_view_get_cursor_image_src (web_view);
- if (!image_src)
- return;
+ /* If the attachment is requested as RAW, then call the handler directly
+ * and do not append any other code. */
+ if ((info->mode == EM_FORMAT_WRITE_MODE_RAW) &&
+ efa->handle && efa->handle->write_func) {
- emf = EM_FORMAT (efhd);
- g_return_if_fail (emf != NULL);
- g_return_if_fail (emf->message != NULL);
+ efa->handle->write_func (emf, puri, stream, info, cancellable);
+ return;
+ }
- if (g_str_has_prefix (image_src, "cid:")) {
- part = camel_mime_message_get_part_by_content_id (
- emf->message, image_src + 4);
- g_return_if_fail (part != NULL);
+ if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) {
- g_object_ref (part);
- } else {
- CamelStream *image_stream;
- CamelDataWrapper *dw;
- const gchar *filename;
-
- image_stream = em_format_html_get_cached_image (
- EM_FORMAT_HTML (efhd), image_src);
- if (!image_stream)
- return;
-
- filename = strrchr (image_src, '/');
- if (filename && strchr (filename, '?'))
- filename = NULL;
- else if (filename)
- filename = filename + 1;
-
- part = camel_mime_part_new ();
- if (filename)
- camel_mime_part_set_filename (part, filename);
-
- dw = camel_data_wrapper_new ();
- camel_data_wrapper_set_mime_type (
- dw, "application/octet-stream");
- camel_data_wrapper_construct_from_stream_sync (
- dw, image_stream, NULL, NULL);
- camel_medium_set_content (CAMEL_MEDIUM (part), dw);
- g_object_unref (dw);
-
- camel_mime_part_set_encoding (
- part, CAMEL_TRANSFER_ENCODING_BASE64);
-
- g_object_unref (image_stream);
- }
+ if (efa->handle && efa->handle->write_func)
+ efa->handle->write_func (emf, puri, stream, info, cancellable);
- file = e_shell_run_save_dialog (
- e_shell_get_default (),
- _("Save Image"), camel_mime_part_get_filename (part),
- NULL, NULL, NULL);
- if (file == NULL) {
- g_object_unref (part);
return;
}
- attachment = e_attachment_new ();
- e_attachment_set_mime_part (attachment, part);
+ if (efa->handle)
+ mime_type = efa->handle->mime_type;
+ else
+ mime_type = efa->snoop_mime_type;
+
+ button_id = g_strconcat (puri->uri, ".attachment_button", NULL);
+
+ desc = em_format_describe_part (puri->part, mime_type);
+ str = g_strdup_printf (
+ "<div class=\"attachment\">"
+ "<table width=\"100%%\" border=\"0\">"
+ "<tr valign=\"middle\">"
+ "<td align=\"left\" width=\"100\">"
+ "<object type=\"application/x-attachment-button\" "
+ "height=\"20\" width=\"100\" data=\"%s\" id=\"%s\"></object>"
+ "</td>"
+ "<td align=\"left\">%s</td>"
+ "</tr>", puri->uri, button_id, desc);
+
+ camel_stream_write_string (stream, str, cancellable, NULL);
+ g_free (desc);
+ g_free (button_id);
+ g_free (str);
+
+ /* If we know how to write the attachment, then do it */
+ if ((efa->handle && efa->handle->write_func) ||
+ (efa->attachment_view_part_id)) {
+
+ str = g_strdup_printf (
+ "<tr><td colspan=\"2\">"
+ "<div class=\"attachment-wrapper\" id=\"%s\">",
+ puri->uri);
+
+ camel_stream_write_string (stream, str, cancellable, NULL);
+ g_free (str);
+
+ if (efa->handle->write_func) {
+ efa->handle->write_func (
+ emf, puri, stream, info, cancellable);
+ } else if (efa->attachment_view_part_id) {
+ EMFormatPURI *p;
+
+ p = em_format_find_puri (
+ emf, efa->attachment_view_part_id);
+ if (p && p->write_func)
+ p->write_func (emf, p, stream, info, cancellable);
+ }
- e_attachment_load_async (
- attachment, (GAsyncReadyCallback)
- attachment_load_finish, file);
+ camel_stream_write_string (stream, "</div></td></tr>", cancellable, NULL);
+ }
- g_object_unref (part);
+ camel_stream_write_string (stream, "</table></div>", cancellable, NULL);
}
static void
-efhd_web_view_update_actions_cb (EWebView *web_view,
- EMFormatHTMLDisplay *efhd)
+efhd_write_secure_button (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- const gchar *image_src;
- gboolean visible;
- GtkAction *action;
-
- g_return_if_fail (web_view != NULL);
+ gchar *str;
- image_src = e_web_view_get_cursor_image_src (web_view);
- visible = image_src && g_str_has_prefix (image_src, "cid:");
- if (!visible && image_src) {
- CamelStream *image_stream;
+ if ((info->mode != EM_FORMAT_WRITE_MODE_NORMAL) &&
+ (info->mode != EM_FORMAT_WRITE_MODE_RAW))
+ return;
- image_stream = em_format_html_get_cached_image (
- EM_FORMAT_HTML (efhd), image_src);
- visible = image_stream != NULL;
+ str = g_strdup_printf (
+ "<object type=\"application/x-secure-button\" "
+ "height=\"20\" width=\"100%%\" "
+ "data=\"%s\" id=\"%s\"></object>", puri->uri, puri->uri);
- if (image_stream)
- g_object_unref (image_stream);
- }
+ camel_stream_write_string (stream, str, cancellable, NULL);
- action = e_web_view_get_action (web_view, "efhd-image-save");
- if (action)
- gtk_action_set_visible (action, visible);
+ g_free (str);
}
-static GtkActionEntry image_entries[] = {
- { "efhd-image-save",
- GTK_STOCK_SAVE,
- N_("Save _Image..."),
- NULL,
- N_("Save the image to a file"),
- G_CALLBACK (action_image_save_cb) }
-};
-
-static const gchar *image_ui =
- "<ui>"
- " <popup name='context'>"
- " <placeholder name='custom-actions-2'>"
- " <menuitem action='efhd-image-save'/>"
- " </placeholder>"
- " </popup>"
- "</ui>";
-
static void
efhd_finalize (GObject *object)
{
@@ -841,84 +842,77 @@ efhd_finalize (GObject *object)
efhd = EM_FORMAT_HTML_DISPLAY (object);
g_return_if_fail (efhd != NULL);
- if (efhd->priv->attachment_views) {
- g_hash_table_foreach (
- efhd->priv->attachment_views,
- weak_unref_attachment_view_cb, efhd);
- g_hash_table_destroy (efhd->priv->attachment_views);
- efhd->priv->attachment_views = NULL;
- }
-
/* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (em_format_html_display_parent_class)->
- finalize (object);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
-em_format_html_display_class_init (EMFormatHTMLDisplayClass *class)
+efhd_preparse (EMFormat *emf)
+{
+ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) emf;
+
+ efhd->priv->last_view = NULL;
+}
+
+static void
+efhd_class_init (EMFormatHTMLDisplayClass *class)
{
GObjectClass *object_class;
- EMFormatClass *format_class;
EMFormatHTMLClass *format_html_class;
+ EMFormatClass *format_class;
+ parent_class = g_type_class_peek_parent (class);
g_type_class_add_private (class, sizeof (EMFormatHTMLDisplayPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->finalize = efhd_finalize;
- format_class = EM_FORMAT_CLASS (class);
- format_class->format_clone = efhd_format_clone;
- format_class->format_attachment = efhd_format_attachment;
- format_class->format_optional = efhd_format_optional;
- format_class->format_secure = efhd_format_secure;
-
format_html_class = EM_FORMAT_HTML_CLASS (class);
format_html_class->html_widget_type = E_TYPE_MAIL_DISPLAY;
+ format_class = EM_FORMAT_CLASS (class);
+ format_class->preparse = efhd_preparse;
+
efhd_builtin_init (class);
}
static void
-em_format_html_display_init (EMFormatHTMLDisplay *efhd)
+efhd_init (EMFormatHTMLDisplay *efhd)
{
- EWebView *web_view;
- GtkActionGroup *image_actions;
- GtkUIManager *ui_manager;
- GError *error = NULL;
-
- web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhd));
-
efhd->priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efhd);
- efhd->priv->attachment_views = g_hash_table_new_full (
- g_str_hash, g_str_equal, g_free, NULL);
- efhd->priv->attachment_expanded = FALSE;
-
- e_mail_display_set_formatter (
- E_MAIL_DISPLAY (web_view), EM_FORMAT_HTML (efhd));
- /* we want to convert url's etc */
+ /* we want to convert url's etc */
EM_FORMAT_HTML (efhd)->text_html_flags |=
CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
- image_actions = e_web_view_get_action_group (web_view, "image");
- g_return_if_fail (image_actions != NULL);
-
- gtk_action_group_add_actions (
- image_actions, image_entries,
- G_N_ELEMENTS (image_entries), efhd);
+}
- /* Because we are loading from a hard-coded string, there is
- * no chance of I/O errors. Failure here implies a malformed
- * UI definition. Full stop. */
- ui_manager = e_web_view_get_ui_manager (web_view);
- gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, &error);
- if (error != NULL)
- g_error ("%s", error->message);
+GType
+em_format_html_display_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EMFormatHTMLDisplayClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) efhd_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMFormatHTMLDisplay),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) efhd_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ EM_TYPE_FORMAT_HTML, "EMFormatHTMLDisplay",
+ &type_info, 0);
+ }
- g_signal_connect (
- web_view, "update-actions",
- G_CALLBACK (efhd_web_view_update_actions_cb), efhd);
+ return type;
}
EMFormatHTMLDisplay *
@@ -930,8 +924,11 @@ em_format_html_display_new (void)
/* ********************************************************************** */
static EMFormatHandler type_builtin_table[] = {
- { (gchar *) "x-evolution/message/prefix", efhd_message_prefix },
- { (gchar *) "x-evolution/message/post-header", (EMFormatFunc)efhd_message_add_bar }
+ { (gchar *) "x-evolution/message/prefix", efhd_message_prefix, },
+ { (gchar *) "x-evolution/message/attachment-bar", (EMFormatParseFunc) efhd_message_add_bar, efhd_write_attachment_bar, },
+ { (gchar *) "x-evolution/message/attachment", efhd_parse_attachment, efhd_write_attachment, },
+ { (gchar *) "x-evolution/message/x-secure-button", efhd_parse_secure, efhd_write_secure_button, },
+ { (gchar *) "x-evolution/message/optional", efhd_parse_optional, },
};
static void
@@ -939,81 +936,51 @@ efhd_builtin_init (EMFormatHTMLDisplayClass *efhc)
{
gint i;
- for (i = 0; i < G_N_ELEMENTS (type_builtin_table); i++)
- em_format_class_add_handler ((EMFormatClass *) efhc, &type_builtin_table[i]);
-}
-
-static void
-efhd_write_image (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
+ EMFormatClass *emfc = (EMFormatClass *) efhc;
- /* TODO: identical to efh_write_image */
- d(printf("writing image '%s'\n", puri->cid));
- camel_data_wrapper_decode_to_stream_sync (
- dw, stream, cancellable, NULL);
- camel_stream_close (stream, cancellable, NULL);
+ for (i = 0; i < G_N_ELEMENTS (type_builtin_table); i++)
+ em_format_class_add_handler (emfc, &type_builtin_table[i]);
}
static void
efhd_message_prefix (EMFormat *emf,
- CamelStream *stream,
CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
const gchar *flag, *comp, *due;
time_t date;
gchar *iconpath, *due_date_str;
GString *buffer;
+ EMFormatAttachmentPURI *puri;
- if (emf->folder == NULL || emf->uid == NULL
- || (flag = camel_folder_get_message_user_tag(emf->folder, emf->uid, "follow-up")) == NULL
+ if (emf->folder == NULL || emf->message_uid == NULL
+ || (flag = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "follow-up")) == NULL
|| flag[0] == 0)
return;
- buffer = g_string_sized_new (1024);
+ puri = (EMFormatAttachmentPURI *) em_format_puri_new (
+ emf, sizeof (EMFormatAttachmentPURI), part, ".message_prefix");
- /* header displayed for message-flags in mail display */
- g_string_append (
- buffer,
- "<table border=1 width=\"100%%\" "
- "cellspacing=2 cellpadding=2><tr>");
+ puri->attachment_view_part_id = g_strdup (part_id->str);
- comp = camel_folder_get_message_user_tag(emf->folder, emf->uid, "completed-on");
+ comp = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "completed-on");
iconpath = e_icon_factory_get_icon_filename (comp && comp[0] ? "stock_mail-flag-for-followup-done" : "stock_mail-flag-for-followup", GTK_ICON_SIZE_MENU);
if (iconpath) {
- CamelMimePart *iconpart;
-
- iconpart = em_format_html_file_part (
- (EMFormatHTML *)emf, "image/png",
- iconpath, cancellable);
- g_free (iconpath);
- if (iconpart) {
- gchar *classid;
-
- classid = g_strdup_printf (
- "icon:///em-format-html-display/%s/%s",
- emf->part_id->str,
- comp && comp[0] ? "comp" : "uncomp");
- g_string_append_printf (
- buffer,
- "<td align=\"left\">"
- "<img src=\"%s\"></td>",
- classid);
- (void) em_format_add_puri (
- emf, sizeof (EMFormatPURI),
- classid, iconpart, efhd_write_image);
- g_free (classid);
- g_object_unref (iconpart);
- }
+ gchar *classid;
+
+ classid = g_strdup_printf (
+ "icon:///em-format-html-display/%s/%s",
+ part_id->str,
+ comp && comp[0] ? "comp" : "uncomp");
+
+ puri->puri.uri = classid;
+
+ g_free (classid);
}
- g_string_append (buffer, "<td align=\"left\" width=\"100%%\">");
+ buffer = g_string_new ("");
if (comp && comp[0]) {
date = camel_header_decode_date (comp, NULL);
@@ -1024,7 +991,7 @@ efhd_message_prefix (EMFormat *emf,
flag, _("Completed on"),
due_date_str ? due_date_str : "???");
g_free (due_date_str);
- } else if ((due = camel_folder_get_message_user_tag(emf->folder, emf->uid, "due-by")) != NULL && due[0]) {
+ } else if ((due = camel_folder_get_message_user_tag(emf->folder, emf->message_uid, "due-by")) != NULL && due[0]) {
time_t now;
date = camel_header_decode_date (due, NULL);
@@ -1032,7 +999,7 @@ efhd_message_prefix (EMFormat *emf,
if (now > date)
g_string_append_printf (
buffer,
- "<b>%s</b>&nbsp;",
+ "<b>%s</b> ",
_("Overdue:"));
due_date_str = e_datetime_format_format (
@@ -1048,270 +1015,84 @@ efhd_message_prefix (EMFormat *emf,
g_string_append (buffer, flag);
}
- g_string_append (buffer, "</td></tr></table>");
-
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
-}
-
-/* ********************************************************************** */
-
-static void
-efhd_attachment_button_expanded (EAttachmentButton *button,
- GParamSpec *pspec,
- struct _attach_puri *info)
-{
- EMFormatHTML *efh;
- EMFormatHTMLDisplay *efhd;
-
- /* FIXME The PURI struct seems to have some lifecycle issues,
- * because casting info->puri.format to an EMFormatHTML
- * can lead to crashes. So we hack around it. */
- efh = g_object_get_data (G_OBJECT (button), "efh");
- g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
- if (efh->state == EM_FORMAT_HTML_STATE_RENDERING)
- return;
-
- info->shown = e_attachment_button_get_expanded (button);
-
- em_format_set_inline (
- info->puri.format, info->puri.part_id, info->shown);
-
- efhd = (EMFormatHTMLDisplay *) efh;
- g_return_if_fail (EM_IS_FORMAT_HTML_DISPLAY (efhd));
-
- efhd->priv->attachment_expanded = TRUE;
-}
-
-/* ********************************************************************** */
-
-static void
-attachment_button_realized (GtkWidget *widget)
-{
- EMFormatHTML *efh = g_object_get_data (G_OBJECT (widget), "efh");
- EMFormatHTMLDisplay *efhd;
- g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
- efhd = (EMFormatHTMLDisplay *) efh;
- g_return_if_fail (EM_IS_FORMAT_HTML_DISPLAY (efhd));
-
- gtk_widget_grab_focus (widget);
- efhd->priv->attachment_expanded = FALSE;
+ puri->description = g_string_free (buffer, FALSE);
}
/* ********************************************************************** */
/* attachment button callback */
-static gboolean
-efhd_attachment_button (EMFormatHTML *efh,
- GtkHTMLEmbedded *eb,
- EMFormatHTMLPObject *pobject)
+static GtkWidget *
+efhd_attachment_button (EMFormat *emf,
+ EMFormatPURI *puri,
+ GCancellable *cancellable)
{
- EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *) efh;
- struct _attach_puri *info;
- EAttachmentView *view;
- EAttachmentStore *store;
- EAttachment *attachment;
- EWebView *web_view;
+ EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri;
GtkWidget *widget;
- gpointer parent;
- EMFormat *emf = (EMFormat *) efh;
- guint32 size = 0;
/* FIXME: handle default shown case */
d(printf("adding attachment button/content\n"));
- if (emf->folder && emf->folder->summary && emf->uid) {
- CamelMessageInfo *mi;
-
- mi = camel_folder_summary_get (emf->folder->summary, emf->uid);
- if (mi) {
- const CamelMessageContentInfo *ci;
-
- ci = camel_folder_summary_guess_content_info (mi, camel_mime_part_get_content_type (pobject->part));
- if (ci) {
- size = ci->size;
- /* what if its not encoded in base64 ? is it a case to consider? */
- if (ci->encoding && !g_ascii_strcasecmp (ci->encoding, "base64"))
- size = size / 1.37;
- }
- camel_message_info_free (mi);
- }
- }
-
- info = (struct _attach_puri *) em_format_find_puri ((EMFormat *) efh, pobject->classid);
+ if (g_cancellable_is_cancelled (cancellable))
+ return NULL;
if (!info || info->forward) {
g_warning ("unable to expand the attachment\n");
- return TRUE;
+ return NULL;
}
- attachment = info->attachment;
- e_attachment_set_shown (attachment, info->shown);
- e_attachment_set_signed (attachment, info->sign);
- e_attachment_set_encrypted (attachment, info->encrypt);
- e_attachment_set_can_show (attachment, info->handle != NULL);
-
- web_view = em_format_html_get_web_view (efh);
- g_return_val_if_fail (web_view != NULL, TRUE);
- parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
- parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
-
- view = em_format_html_display_get_attachment_view (efhd, info->attachment_view_part_id);
- g_return_val_if_fail (view != NULL, TRUE);
- gtk_widget_show (GTK_WIDGET (view));
-
- store = e_attachment_view_get_store (view);
- e_attachment_store_add_attachment (store, info->attachment);
-
- e_attachment_load_async (
- info->attachment, (GAsyncReadyCallback)
- e_attachment_load_handle_error, parent);
- if (size != 0) {
- GFileInfo *fileinfo;
-
- fileinfo = e_attachment_get_file_info (info->attachment);
- g_file_info_set_size (fileinfo, size);
- e_attachment_set_file_info (info->attachment, fileinfo);
- }
-
- widget = e_attachment_button_new (view);
+ widget = e_attachment_button_new ();
+ g_object_set_data (G_OBJECT (widget), "uri", puri->uri);
e_attachment_button_set_attachment (
- E_ATTACHMENT_BUTTON (widget), attachment);
+ E_ATTACHMENT_BUTTON (widget), info->attachment);
+ e_attachment_button_set_view (
+ E_ATTACHMENT_BUTTON (widget),
+ EM_FORMAT_HTML_DISPLAY (emf)->priv->last_view);
+
gtk_widget_set_can_focus (widget, TRUE);
- gtk_container_add (GTK_CONTAINER (eb), widget);
gtk_widget_show (widget);
- /* FIXME Not sure why the expanded callback can't just use
- * info->puri.format, but there seems to be lifecycle
- * issues with the PURI struct. Maybe it should have
- * a reference count? */
- g_object_set_data (G_OBJECT (widget), "efh", efh);
-
- g_signal_connect (
- widget, "notify::expanded",
- G_CALLBACK (efhd_attachment_button_expanded), info);
-
- /* If the button is created, then give it focus after
- * it is realized, so that user can use arrow keys to scroll
- * message */
- if (efhd->priv->attachment_expanded) {
- g_signal_connect (
- widget, "realize",
- G_CALLBACK (attachment_button_realized), NULL);
- }
-
- return TRUE;
-}
-
-static void
-efhd_attachment_frame (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- struct _attach_puri *info = (struct _attach_puri *) puri;
-
- if (info->shown)
- info->handle->handler (
- emf, stream, info->puri.part,
- info->handle, cancellable, FALSE);
-
- camel_stream_close (stream, cancellable, NULL);
-}
-
-static void
-set_size_request_cb (gpointer message_part_id,
- gpointer widget,
- gpointer width)
-{
- gtk_widget_set_size_request (widget, GPOINTER_TO_INT (width), -1);
+ return widget;
}
-static void
-efhd_bar_resize (EMFormatHTML *efh,
- GtkAllocation *event)
+static GtkWidget *
+efhd_attachment_bar (EMFormat *emf,
+ EMFormatPURI *puri,
+ GCancellable *cancellable)
{
- EMFormatHTMLDisplayPrivate *priv;
- GtkAllocation allocation;
- EWebView *web_view;
+ EMFormatAttachmentBarPURI *abp = (EMFormatAttachmentBarPURI *) puri;
GtkWidget *widget;
- gint width;
- priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efh);
+ widget = e_mail_attachment_bar_new (abp->store);
+ EM_FORMAT_HTML_DISPLAY (emf)->priv->last_view = (EAttachmentView *) widget;
- web_view = em_format_html_get_web_view (efh);
-
- widget = GTK_WIDGET (web_view);
- gtk_widget_get_allocation (widget, &allocation);
- width = allocation.width - 12;
-
- if (width > 0) {
- g_hash_table_foreach (priv->attachment_views, set_size_request_cb, GINT_TO_POINTER (width));
- }
+ return widget;
}
-static gboolean
-efhd_add_bar (EMFormatHTML *efh,
- GtkHTMLEmbedded *eb,
- EMFormatHTMLPObject *pobject)
+static void
+efhd_message_add_bar (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormatHTMLDisplayPrivate *priv;
- GtkWidget *widget;
-
- /* XXX See note in efhd_message_add_bar(). */
- if (!EM_IS_FORMAT_HTML_DISPLAY (efh))
- return FALSE;
-
- g_return_val_if_fail (pobject != NULL && pobject->classid != NULL, FALSE);
- g_return_val_if_fail (g_str_has_prefix (pobject->classid, "attachment-bar:"), FALSE);
-
- priv = EM_FORMAT_HTML_DISPLAY_GET_PRIVATE (efh);
+ EMFormatAttachmentBarPURI *puri;
+ gint len;
- widget = e_mail_attachment_bar_new ();
- gtk_container_add (GTK_CONTAINER (eb), widget);
-
- g_hash_table_insert (priv->attachment_views, g_strdup (strchr (pobject->classid, ':') + 1), widget);
- g_object_weak_ref (G_OBJECT (widget), efhd_attachment_view_gone_cb, efh);
- gtk_widget_hide (widget);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- g_signal_connect_swapped (
- eb, "size-allocate",
- G_CALLBACK (efhd_bar_resize), efh);
+ len = part_id->len;
+ g_string_append (part_id, ".attachment-bar");
+ puri = (EMFormatAttachmentBarPURI *) em_format_puri_new (
+ emf, sizeof (EMFormatAttachmentBarPURI), part, part_id->str);
+ puri->puri.write_func = efhd_write_attachment_bar;
+ puri->puri.widget_func = efhd_attachment_bar;
+ puri->puri.free = efhd_attachment_bar_puri_free;
+ puri->store = E_ATTACHMENT_STORE (e_attachment_store_new ());
- return TRUE;
-}
+ em_format_add_puri (emf, (EMFormatPURI *) puri);
-static void
-efhd_message_add_bar (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info)
-{
- gchar *classid;
- gchar *content;
-
- classid = g_strdup_printf (
- "attachment-bar:%s", emf->current_message_part_id);
-
- /* XXX Apparently this installs the callback for -all-
- * EMFormatHTML subclasses, not just this subclass.
- * Bad idea. So we have to filter out other types
- * in the callback. */
- em_format_html_add_pobject (
- EM_FORMAT_HTML (emf),
- sizeof (EMFormatHTMLPObject),
- classid, part, efhd_add_bar);
-
- content = g_strdup_printf (
- "<td><object classid=\"%s\"></object></td>", classid);
- camel_stream_write_string (stream, content, NULL, NULL);
- g_free (content);
-
- g_free (classid);
+ g_string_truncate (part_id, len);
}
static void
@@ -1329,41 +1110,28 @@ efhd_optional_button_show (GtkWidget *widget,
}
}
-static void
-efhd_resize (GtkWidget *w,
- GtkAllocation *event,
- EMFormatHTML *efh)
-{
- EWebView *web_view;
- GtkAllocation allocation;
-
- web_view = em_format_html_get_web_view (efh);
- gtk_widget_get_allocation (GTK_WIDGET (web_view), &allocation);
- gtk_widget_set_size_request (w, allocation.width - 48, 250);
-}
-
/* optional render attachment button callback */
-static gboolean
-efhd_attachment_optional (EMFormatHTML *efh,
- GtkHTMLEmbedded *eb,
- EMFormatHTMLPObject *pobject)
+static GtkWidget *
+efhd_attachment_optional (EMFormat *efh,
+ EMFormatPURI *puri,
+ GCancellable *cancellable)
{
- struct _attach_puri *info;
GtkWidget *hbox, *vbox, *button, *mainbox, *scroll, *label, *img;
AtkObject *a11y;
GtkWidget *view;
- GtkAllocation allocation;
GtkTextBuffer *buffer;
GByteArray *byte_array;
- EWebView *web_view;
+ EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return NULL;
/* FIXME: handle default shown case */
d(printf("adding attachment button/content for optional rendering\n"));
- info = (struct _attach_puri *) em_format_find_puri ((EMFormat *) efh, pobject->classid);
if (!info || info->forward) {
g_warning ("unable to expand the attachment\n");
- return TRUE;
+ return NULL;
}
scroll = gtk_scrolled_window_new (NULL, NULL);
@@ -1428,28 +1196,19 @@ efhd_attachment_optional (EMFormatHTML *efh,
gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 6);
gtk_widget_show (GTK_WIDGET (view));
- web_view = em_format_html_get_web_view (efh);
- gtk_widget_get_allocation (GTK_WIDGET (web_view), &allocation);
- gtk_widget_set_size_request (scroll, allocation.width - 48, 250);
- g_signal_connect (
- scroll, "size_allocate",
- G_CALLBACK (efhd_resize), efh);
- gtk_widget_show (scroll);
-
if (!info->shown)
gtk_widget_hide (scroll);
gtk_widget_show (vbox);
- gtk_container_add (GTK_CONTAINER (eb), vbox);
info->handle = NULL;
- return TRUE;
+ return view;
}
static void
efhd_free_attach_puri_data (EMFormatPURI *puri)
{
- struct _attach_puri *info = (struct _attach_puri *) puri;
+ EMFormatAttachmentPURI *info = (EMFormatAttachmentPURI *) puri;
g_return_if_fail (puri != NULL);
@@ -1458,23 +1217,18 @@ efhd_free_attach_puri_data (EMFormatPURI *puri)
info->attachment = NULL;
}
- g_free (info->attachment_view_part_id);
- info->attachment_view_part_id = NULL;
-}
-
-/* returned object owned by html_display, thus do not unref it */
-EAttachmentView *
-em_format_html_display_get_attachment_view (EMFormatHTMLDisplay *html_display,
- const gchar *message_part_id)
-{
- gpointer aview;
-
- g_return_val_if_fail (EM_IS_FORMAT_HTML_DISPLAY (html_display), NULL);
- g_return_val_if_fail (message_part_id != NULL, NULL);
+ if (info->description) {
+ g_free (info->description);
+ info->description = NULL;
+ }
- /* it should be added in efhd_add_bar() with this message_part_id */
- aview = g_hash_table_lookup (html_display->priv->attachment_views, message_part_id);
- g_return_val_if_fail (aview != NULL, NULL);
+ if (info->attachment_view_part_id) {
+ g_free (info->attachment_view_part_id);
+ info->attachment_view_part_id = NULL;
+ }
- return E_ATTACHMENT_VIEW (aview);
+ if (info->mstream) {
+ g_object_unref (info->mstream);
+ info->mstream = NULL;
+ }
}
diff --git a/mail/em-format-html-display.h b/mail/em-format-html-display.h
index ec29698d46..c1b22eec58 100644
--- a/mail/em-format-html-display.h
+++ b/mail/em-format-html-display.h
@@ -52,6 +52,51 @@ G_BEGIN_DECLS
typedef struct _EMFormatHTMLDisplay EMFormatHTMLDisplay;
typedef struct _EMFormatHTMLDisplayClass EMFormatHTMLDisplayClass;
typedef struct _EMFormatHTMLDisplayPrivate EMFormatHTMLDisplayPrivate;
+typedef struct _EMFormatAttachmentBarPURI EMFormatAttachmentBarPURI;
+typedef struct _EMFormatAttachmentPURI EMFormatAttachmentPURI;
+typedef struct _EMFormatSMIMEPURI EMFormatSMIMEPURI;
+
+struct _EMFormatAttachmentBarPURI {
+ EMFormatPURI puri;
+
+ EAttachmentStore *store;
+};
+
+struct _EMFormatAttachmentPURI {
+ EMFormatPURI puri;
+
+ const EMFormatHandler *handle;
+
+ const gchar *snoop_mime_type;
+
+ /* for the > and V buttons */
+ GtkWidget *forward, *down;
+ guint shown : 1;
+
+ /* Attachment */
+ EAttachment *attachment;
+ gchar *attachment_view_part_id;
+ gchar *description;
+
+ /* image stuff */
+ gint fit_width;
+ gint fit_height;
+ GtkImage *image;
+ GtkWidget *event_box;
+
+ /* Optional Text Mem Stream */
+ CamelStreamMem *mstream;
+};
+
+struct _EMFormatSMIMEPURI {
+ EMFormatPURI puri;
+
+ gchar *description;
+
+ gint signature;
+ CamelCipherValidity *valid;
+ GtkWidget *widget;
+};
struct _EMFormatHTMLDisplay {
EMFormatHTML parent;
@@ -67,10 +112,6 @@ struct _EMFormatHTMLDisplayClass {
GType em_format_html_display_get_type (void);
EMFormatHTMLDisplay *
em_format_html_display_new (void);
-EAttachmentView *
- em_format_html_display_get_attachment_view
- (EMFormatHTMLDisplay *html_display,
- const gchar *message_part_id);
G_END_DECLS
#endif /* EM_FORMAT_HTML_DISPLAY_H */
diff --git a/mail/em-format-html-print.c b/mail/em-format-html-print.c
index 0de9e2303d..2f9e556d9e 100644
--- a/mail/em-format-html-print.c
+++ b/mail/em-format-html-print.c
@@ -28,33 +28,411 @@
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml.h>
+#include "em-format-html-print.h"
+#include "em-format-html-display.h"
+#include "e-mail-attachment-bar.h"
#include <e-util/e-print.h>
-
-#include <libemail-utils/mail-mt.h>
+#include <e-util/e-util.h>
+#include <widgets/misc/e-attachment-store.h>
#include <libemail-engine/mail-ops.h>
#include "em-format-html-print.h"
-G_DEFINE_TYPE (
- EMFormatHTMLPrint,
- em_format_html_print,
- EM_TYPE_FORMAT_HTML)
+#define d(x)
+
+static gpointer parent_class = NULL;
+
+struct _EMFormatHTMLPrintPrivate {
+
+ EMFormatHTML *original_formatter;
+ EMFormatPURI *top_level_puri;
+
+ /* List of attachment PURIs */
+ GList *attachments;
+
+};
+
+enum {
+ PROP_0,
+ PROP_ORIGINAL_FORMATTER
+};
+
+static void efhp_write_print_layout (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhp_write_headers (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efhp_write_inline_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
+static void
+efhp_write_attachments_list (EMFormatHTMLPrint *efhp,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ GString *str;
+ GList *iter;
+
+ if (!efhp->priv->attachments)
+ return;
+
+ str = g_string_new (
+ "<table border=\"0\" cellspacing=\"5\" cellpadding=\"0\" "
+ "class=\"attachments-list\" >\n");
+ g_string_append_printf (str,
+ "<tr><th colspan=\"2\"><h1>%s</h1></td></tr>\n"
+ "<tr><th>%s</th><th>%s</th></tr>\n",
+ _("Attachments"), _("Name"), _("Size"));
+
+ for (iter = efhp->priv->attachments; iter; iter = iter->next) {
+ EMFormatPURI *puri = iter->data;
+ EAttachment *attachment;
+ GFileInfo *fi;
+ gchar *name, *size;
+ GByteArray *ba;
+ CamelDataWrapper *dw;
+
+ attachment = ((EMFormatAttachmentPURI *) puri)->attachment;
+ fi = e_attachment_get_file_info (attachment);
+ if (!fi)
+ continue;
+
+ if (e_attachment_get_description (attachment) &&
+ *e_attachment_get_description (attachment)) {
+ name = g_strdup_printf ("%s (%s)",
+ e_attachment_get_description (attachment),
+ g_file_info_get_display_name (fi));
+ } else {
+ name = g_strdup (g_file_info_get_display_name (fi));
+ }
+
+ dw = camel_medium_get_content ((CamelMedium *) puri->part);
+ ba = camel_data_wrapper_get_byte_array (dw);
+ size = g_format_size (ba->len);
+
+ g_string_append_printf (str, "<tr><td>%s</td><td>%s</td></tr>\n",
+ name, size);
+
+ g_free (name);
+ g_free (size);
+ }
+
+ g_string_append (str, "</table>\n");
+
+ camel_stream_write_string (stream, str->str, cancellable, NULL);
+ g_string_free (str, TRUE);
+}
+
+static void
+efhp_write_headers (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ struct _camel_header_raw raw_header;
+ GString *str, *tmp;
+ gchar *subject;
+ const gchar *buf;
+ EMFormatPURI *p;
+ GList *iter;
+ gint attachments_count;
+ gchar *puri_prefix;
+
+ buf = camel_medium_get_header (CAMEL_MEDIUM (puri->part), "subject");
+ subject = camel_header_decode_string (buf, "UTF-8");
+ str = g_string_new ("<table border=\"0\" cellspacing=\"5\" " \
+ "cellpadding=\"0\" class=\"printing-header\">\n");
+ g_string_append_printf (
+ str,
+ "<tr class=\"header-item\">"
+ "<td colspan=\"2\"><h1>%s</h1></td>"
+ "</tr>\n",
+ subject);
+ g_free (subject);
+
+ for (iter = g_queue_peek_head_link (&emf->header_list); iter; iter = iter->next) {
+
+ EMFormatHeader *header = iter->data;
+ raw_header.name = header->name;
+
+ /* Skip 'Subject' header, it's already displayed. */
+ if (g_ascii_strncasecmp (header->name, "Subject", 7) == 0)
+ continue;
+
+ if (header->value && *header->value) {
+ raw_header.value = header->value;
+ em_format_html_format_header (emf, str,
+ CAMEL_MEDIUM (puri->part), &raw_header,
+ header->flags | EM_FORMAT_HTML_HEADER_NOLINKS,
+ "UTF-8");
+ } else {
+ raw_header.value = g_strdup (camel_medium_get_header (
+ CAMEL_MEDIUM (emf->message), header->name));
+
+ if (raw_header.value && *raw_header.value) {
+ em_format_html_format_header (emf, str,
+ CAMEL_MEDIUM (puri->part), &raw_header,
+ header->flags | EM_FORMAT_HTML_HEADER_NOLINKS,
+ "UTF-8");
+ }
+
+ if (raw_header.value)
+ g_free (raw_header.value);
+ }
+ }
+
+ /* Get prefix of this PURI */
+ puri_prefix = g_strndup (puri->uri, g_strrstr (puri->uri, ".") - puri->uri);
+
+ /* Add encryption/signature header */
+ raw_header.name = _("Security");
+ tmp = g_string_new ("");
+ /* Find first secured part. */
+ for (iter = emf->mail_part_list, puri; iter; iter = iter->next) {
+
+ p = iter->data;
+
+ if (p->validity_type == 0)
+ continue;
+
+ if (!g_str_has_prefix (p->uri, puri_prefix))
+ continue;
+
+ if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) &&
+ (p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) {
+ g_string_append (tmp, _("GPG signed"));
+ }
+ if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) &&
+ (p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) {
+ if (tmp->len > 0) g_string_append (tmp, ", ");
+ g_string_append (tmp, _("GPG encrpyted"));
+ }
+ if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) &&
+ (p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) {
+
+ if (tmp->len > 0) g_string_append (tmp, ", ");
+ g_string_append (tmp, _("S/MIME signed"));
+ }
+ if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) &&
+ (p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) {
+
+ if (tmp->len > 0) g_string_append (tmp, ", ");
+ g_string_append (tmp, _("S/MIME encrpyted"));
+ }
+
+ break;
+ }
+
+ if (tmp->len > 0) {
+ raw_header.value = tmp->str;
+ em_format_html_format_header (emf, str, CAMEL_MEDIUM (p->part),
+ &raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8");
+ }
+ g_string_free (tmp, TRUE);
+
+ /* Count attachments and display the number as a header */
+ attachments_count = 0;
+
+ for (iter = emf->mail_part_list; iter; iter = iter ? iter->next : iter) {
+
+ p = iter->data;
+
+ if (!g_str_has_prefix (p->uri, puri_prefix))
+ continue;
+
+ if ((p->is_attachment || g_str_has_suffix(p->uri, ".attachment")) &&
+ (!p->cid)) {
+ attachments_count++;
+ /* EFHD sometimes creates two PURIs per attachment! */
+ if (iter->next && iter->next->data) {
+ EMFormatPURI *p2 = iter->next->data;
+ if (g_str_has_prefix (p2->uri, p->uri))
+ iter = iter->next;
+ }
+ }
+ }
+ if (attachments_count > 0) {
+ raw_header.name = _("Attachments");
+ raw_header.value = g_strdup_printf ("%d", attachments_count);
+ em_format_html_format_header (emf, str, CAMEL_MEDIUM (puri->part),
+ &raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8");
+ g_free (raw_header.value);
+ }
+
+ g_string_append (str, "</table>");
+
+ camel_stream_write_string (stream, str->str, cancellable, NULL);
+ g_string_free (str, TRUE);
+ g_free (puri_prefix);
+}
+
+static void
+efhp_write_inline_attachment (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ gchar *name;
+ EMFormatAttachmentPURI *att_puri = (EMFormatAttachmentPURI *) puri;
+ EAttachment *attachment;
+ GFileInfo *fi;
+
+ attachment = att_puri->attachment;
+ fi = e_attachment_get_file_info (attachment);
+
+ if (e_attachment_get_description (attachment) &&
+ *e_attachment_get_description (attachment)) {
+ name = g_strdup_printf ("<h2>Attachment: %s (%s)</h2>\n",
+ e_attachment_get_description (attachment),
+ g_file_info_get_display_name (fi));
+ } else {
+ name = g_strdup_printf ("<h2>Attachment: %s</h2>\n",
+ g_file_info_get_display_name (fi));
+ }
+
+ camel_stream_write_string (stream, name, cancellable, NULL);
+ g_free (name);
+}
+
+static void
+efhp_write_print_layout (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ GList *iter;
+ EMFormatWriterInfo print_info = {
+ EM_FORMAT_WRITE_MODE_PRINTING, FALSE, FALSE };
+ EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (emf);
+
+ g_list_free (efhp->priv->attachments);
+ efhp->priv->attachments = NULL;
+
+ camel_stream_write_string (stream,
+ "<!DOCTYPE HTML>\n<html>\n"
+ "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\" />\n"
+ "<title>Evolution Mail Display</title>\n"
+ "<link type=\"text/css\" rel=\"stylesheet\" media=\"print\" "
+ "href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview-print.css\" />\n"
+ "</head>\n"
+ "<body style=\"background: #FFF; color: #000;\">",
+ cancellable, NULL);
+
+ for (iter = emf->mail_part_list; iter != NULL; iter = iter ? iter->next : iter) {
+
+ EMFormatPURI *puri = iter->data;
+
+ if (g_str_has_suffix (puri->uri, "print_layout"))
+ continue;
+
+ /* To late to change .headers writer_func, do it manually. */
+ if (g_str_has_suffix (puri->uri, ".headers")) {
+ efhp_write_headers (emf, puri, stream, &print_info, cancellable);
+ continue;
+ }
+
+ if (g_str_has_suffix (puri->uri, ".rfc822")) {
+
+ puri->write_func (emf, puri, stream, &print_info, cancellable);
+
+ while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) {
+
+ iter = iter->next;
+ if (iter)
+ puri = iter->data;
+ }
+
+ if (!iter)
+ break;
+
+ continue;
+
+ }
+
+ if (puri->is_attachment || g_str_has_suffix (puri->uri, ".attachment")) {
+ const EMFormatHandler *handler;
+ CamelContentType *ct;
+ gchar *mime_type;
+
+ if (puri->cid && g_ascii_strncasecmp (puri->cid, "cid:", 4) == 0)
+ continue;
+
+ ct = camel_mime_part_get_content_type (puri->part);
+ mime_type = camel_content_type_simple (ct);
+
+ handler = em_format_find_handler (puri->emf, mime_type);
+ d(printf("Handler for PURI %s (%s): %s\n", puri->uri, mime_type,
+ handler ? handler->mime_type : "(null)"));
+ g_free (mime_type);
+
+ efhp->priv->attachments =
+ g_list_append (efhp->priv->attachments, puri);
+
+ /* If we can't inline this attachment, skip it */
+ if (handler && puri->write_func) {
+ efhp_write_inline_attachment (puri->emf, puri,
+ stream, &print_info, cancellable);
+
+ if (iter->next && iter->next->data) {
+ EMFormatPURI *p;
+ p = iter->next->data;
+
+ /* Has the next PURI the same prefix? */
+ if (p->write_func &&
+ g_str_has_prefix (p->uri, puri->uri)) {
+ p->write_func (emf, p, stream,
+ &print_info, cancellable);
+ iter = iter->next;
+ } else {
+ if (puri->write_func) {
+ puri->write_func (emf, puri,
+ stream, &print_info,
+ cancellable);
+ }
+ }
+ }
+ }
+
+ continue;
+ }
+
+ /* Ignore widget parts and unwritable non-attachment parts */
+ if (puri->write_func == NULL)
+ continue;
+
+ /* Passed all tests, probably a regular part - display it */
+ puri->write_func (puri->emf, puri, stream, &print_info, cancellable);
+
+ }
+
+ efhp_write_attachments_list (efhp, stream, &print_info, cancellable);
+
+ camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
+}
static void
efhp_finalize (GObject *object)
{
- EMFormatHTMLPrint *efhp = (EMFormatHTMLPrint *) object;
+ EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (object);
- g_free (efhp->export_filename);
- efhp->export_filename = NULL;
- gtk_widget_destroy (efhp->window);
- if (efhp->source != NULL)
- g_object_unref (efhp->source);
+ if (efhp->priv->original_formatter) {
+ g_object_unref (efhp->priv->original_formatter);
+ efhp->priv->original_formatter = NULL;
+ }
+
+ if (efhp->priv->top_level_puri) {
+ em_format_puri_free (efhp->priv->top_level_puri);
+ efhp->priv->top_level_puri = NULL;
+ }
+
+ if (efhp->priv->attachments) {
+ g_list_free (efhp->priv->attachments);
+ efhp->priv->attachments = NULL;
+ }
/* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (em_format_html_print_parent_class)->finalize (object);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
@@ -68,173 +446,182 @@ efhp_is_inline (EMFormat *emf,
}
static void
-em_format_html_print_class_init (EMFormatHTMLPrintClass *class)
+efhp_set_orig_formatter (EMFormatHTMLPrint *efhp,
+ EMFormat *formatter)
{
- GObjectClass *object_class;
- EMFormatClass *format_class;
-
- object_class = G_OBJECT_CLASS (class);
- object_class->finalize = efhp_finalize;
-
- format_class = EM_FORMAT_CLASS (class);
- format_class->is_inline = efhp_is_inline;
+ EMFormat *emfp, *emfs;
+ EMFormatPURI *puri;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ efhp->priv->original_formatter = g_object_ref (formatter);
+
+ emfp = EM_FORMAT (efhp);
+ emfs = EM_FORMAT (formatter);
+
+ emfp->mail_part_list = g_list_copy (emfs->mail_part_list);
+
+ /* Make a shallow copy of the table. This table will NOT destroy
+ * the PURIs when free'd! */
+ if (emfp->mail_part_table)
+ g_hash_table_unref (emfp->mail_part_table);
+
+ emfp->mail_part_table = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_iter_init (&iter, emfs->mail_part_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_hash_table_insert (emfp->mail_part_table, key, value);
+
+ if (emfs->folder)
+ emfp->folder = g_object_ref (emfs->folder);
+ emfp->message_uid = g_strdup (emfs->message_uid);
+ emfp->message = g_object_ref (emfs->message);
+
+ /* Add a generic PURI that will write a HTML layout
+ * for all the parts */
+ puri = em_format_puri_new (EM_FORMAT (efhp),
+ sizeof (EMFormatPURI), NULL, "print_layout");
+ puri->write_func = efhp_write_print_layout;
+ puri->mime_type = g_strdup ("text/html");
+ em_format_add_puri (EM_FORMAT (efhp), puri);
+ efhp->priv->top_level_puri = puri;
}
+static EMFormatHandler type_builtin_table[] = {
+ { (gchar *) "x-evolution/message/headers", 0, efhp_write_headers, },
+};
+
static void
-em_format_html_print_init (EMFormatHTMLPrint *efhp)
+efhp_builtin_init (EMFormatHTMLPrintClass *efhc)
{
- EWebView *web_view;
+ EMFormatClass *emfc;
+ gint ii;
- web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhp));
+ emfc = (EMFormatClass *) efhc;
- /* gtk widgets don't like to be realized outside top level widget
- * so we put new html widget into gtk window */
- efhp->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_container_add (GTK_CONTAINER (efhp->window), GTK_WIDGET (web_view));
- gtk_widget_realize (GTK_WIDGET (web_view));
- efhp->parent.show_icon = FALSE;
- ((EMFormat *) efhp)->print = TRUE;
-
- efhp->export_filename = NULL;
- efhp->async = TRUE;
+ for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
+ em_format_class_add_handler (
+ emfc, &type_builtin_table[ii]);
}
-EMFormatHTMLPrint *
-em_format_html_print_new (EMFormatHTML *source,
- GtkPrintOperationAction action)
+static void
+efhp_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- EMFormatHTMLPrint *efhp;
+ switch (prop_id) {
- efhp = g_object_new (EM_TYPE_FORMAT_HTML_PRINT, NULL);
- if (source != NULL)
- efhp->source = g_object_ref (source);
- efhp->action = action;
+ case PROP_ORIGINAL_FORMATTER:
+ efhp_set_orig_formatter (
+ EM_FORMAT_HTML_PRINT (object),
+ (EMFormat *) g_value_get_object (value));
+ return;
+ }
- return efhp;
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
-static gint
-efhp_calc_footer_height (GtkHTML *html,
- GtkPrintOperation *operation,
- GtkPrintContext *context)
+static void
+efhp_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- PangoContext *pango_context;
- PangoFontDescription *desc;
- PangoFontMetrics *metrics;
- gint footer_height;
+ EMFormatHTMLPrintPrivate *priv;
- pango_context = gtk_print_context_create_pango_context (context);
- desc = pango_font_description_from_string ("Sans Regular 10");
+ priv = EM_FORMAT_HTML_PRINT (object)->priv;
- metrics = pango_context_get_metrics (
- pango_context, desc, pango_language_get_default ());
- footer_height =
- pango_font_metrics_get_ascent (metrics) +
- pango_font_metrics_get_descent (metrics);
- pango_font_metrics_unref (metrics);
+ switch (prop_id) {
- pango_font_description_free (desc);
- g_object_unref (pango_context);
+ case PROP_ORIGINAL_FORMATTER:
+ g_value_set_pointer (value,
+ priv->original_formatter);
+ return;
+ }
- return footer_height;
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static void
-efhp_draw_footer (GtkHTML *html,
- GtkPrintOperation *operation,
- GtkPrintContext *context,
- gint page_nr,
- PangoRectangle *rec)
+em_format_html_print_base_init (EMFormatHTMLPrintClass *klass)
{
- PangoFontDescription *desc;
- PangoLayout *layout;
- gdouble x, y;
- gint n_pages;
- gchar *text;
- cairo_t *cr;
-
- g_object_get (operation, "n-pages", &n_pages, NULL);
- text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
-
- desc = pango_font_description_from_string ("Sans Regular 10");
- layout = gtk_print_context_create_pango_layout (context);
- pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
- pango_layout_set_font_description (layout, desc);
- pango_layout_set_text (layout, text, -1);
- pango_layout_set_width (layout, rec->width);
-
- x = pango_units_to_double (rec->x);
- y = pango_units_to_double (rec->y);
-
- cr = gtk_print_context_get_cairo_context (context);
-
- cairo_save (cr);
- cairo_set_source_rgb (cr, .0, .0, .0);
- cairo_move_to (cr, x, y);
- pango_cairo_show_layout (cr, layout);
- cairo_restore (cr);
-
- g_object_unref (layout);
- pango_font_description_free (desc);
-
- g_free (text);
+ efhp_builtin_init (klass);
}
static void
-emfhp_complete (EMFormatHTMLPrint *efhp)
+em_format_html_print_class_init (EMFormatHTMLPrintClass *klass)
{
- GtkPrintOperation *operation;
- EWebView *web_view;
- GError *error = NULL;
+ GObjectClass *object_class;
+ EMFormatClass *format_class;
- web_view = em_format_html_get_web_view (EM_FORMAT_HTML (efhp));
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (EMFormatHTMLPrintPrivate));
- operation = e_print_operation_new ();
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = efhp_finalize;
+ object_class->set_property = efhp_set_property;
+ object_class->get_property = efhp_get_property;
- if (efhp->action == GTK_PRINT_OPERATION_ACTION_EXPORT)
- gtk_print_operation_set_export_filename (operation, efhp->export_filename);
+ format_class = EM_FORMAT_CLASS (klass);
+ format_class->is_inline = efhp_is_inline;
- gtk_html_print_operation_run (
- GTK_HTML (web_view),
- operation, efhp->action, NULL,
- (GtkHTMLPrintCalcHeight) NULL,
- (GtkHTMLPrintCalcHeight) efhp_calc_footer_height,
- (GtkHTMLPrintDrawFunc) NULL,
- (GtkHTMLPrintDrawFunc) efhp_draw_footer,
- NULL, &error);
+ g_object_class_install_property (
+ object_class,
+ PROP_ORIGINAL_FORMATTER,
+ g_param_spec_object (
+ "original-formatter",
+ NULL,
+ NULL,
+ EM_TYPE_FORMAT,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
- g_object_unref (operation);
+static void
+em_format_html_print_init (EMFormatHTMLPrint *efhp)
+{
+ efhp->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ efhp, EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrintPrivate);
+
+ efhp->priv->attachments = NULL;
+ efhp->export_filename = NULL;
}
-void
-em_format_html_print_message (EMFormatHTMLPrint *efhp,
- CamelMimeMessage *message,
- CamelFolder *folder,
- const gchar *message_uid)
+GType
+em_format_html_print_get_type (void)
{
- g_return_if_fail (EM_IS_FORMAT_HTML_PRINT (efhp));
- g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
-
- /* Wrap flags to display all entries by default.*/
- EM_FORMAT_HTML (efhp)->header_wrap_flags |=
- EM_FORMAT_HTML_HEADER_TO |
- EM_FORMAT_HTML_HEADER_CC |
- EM_FORMAT_HTML_HEADER_BCC;
-
- if (efhp->async) {
- g_signal_connect (
- efhp, "complete", G_CALLBACK (emfhp_complete), efhp);
-
- /* FIXME Not passing a GCancellable here. */
- em_format_format_clone (
- (EMFormat *) efhp,
- folder, message_uid, message,
- (EMFormat *) efhp->source, NULL);
- } else {
- em_format_html_clone_sync (
- folder, message_uid, message,
- (EMFormatHTML *) efhp,
- (EMFormat *) efhp->source);
- emfhp_complete (efhp);
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ static const GTypeInfo type_info = {
+ sizeof (EMFormatHTMLPrintClass),
+ (GBaseInitFunc) em_format_html_print_base_init,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) em_format_html_print_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (EMFormatHTMLPrint),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) em_format_html_print_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (
+ em_format_html_get_type(), "EMFormatHTMLPrint",
+ &type_info, 0);
}
+
+ return type;
+}
+
+EMFormatHTMLPrint *
+em_format_html_print_new (EMFormatHTML *source)
+{
+ EMFormatHTMLPrint *efhp;
+
+ efhp = g_object_new (EM_TYPE_FORMAT_HTML_PRINT,
+ "original-formatter", source,
+ NULL);
+
+ return efhp;
}
diff --git a/mail/em-format-html-print.h b/mail/em-format-html-print.h
index 5f08b6ef82..d9fe1ff6d4 100644
--- a/mail/em-format-html-print.h
+++ b/mail/em-format-html-print.h
@@ -45,20 +45,12 @@ G_BEGIN_DECLS
typedef struct _EMFormatHTMLPrint EMFormatHTMLPrint;
typedef struct _EMFormatHTMLPrintClass EMFormatHTMLPrintClass;
+typedef struct _EMFormatHTMLPrintPrivate EMFormatHTMLPrintPrivate;
struct _EMFormatHTMLPrint {
EMFormatHTML parent;
-
- /* Used to realize the gtkhtml in a toplevel. */
- GtkWidget *window;
-
- /* Used for print_message. */
- EMFormatHTML *source;
-
- GtkPrintOperationAction action;
+ EMFormatHTMLPrintPrivate *priv;
gchar *export_filename;
-
- gboolean async;
};
struct _EMFormatHTMLPrintClass {
@@ -67,12 +59,7 @@ struct _EMFormatHTMLPrintClass {
GType em_format_html_print_get_type (void);
EMFormatHTMLPrint *
- em_format_html_print_new (EMFormatHTML *source,
- GtkPrintOperationAction action);
-void em_format_html_print_message (EMFormatHTMLPrint *efhp,
- CamelMimeMessage *message,
- CamelFolder *folder,
- const gchar *uid);
+ em_format_html_print_new (EMFormatHTML *source);
G_END_DECLS
diff --git a/mail/em-format-html.c b/mail/em-format-html.c
index 3c130e63d4..c481693eed 100644
--- a/mail/em-format-html.c
+++ b/mail/em-format-html.c
@@ -24,6 +24,8 @@
#include <config.h>
#endif
+#define _GNU_SOURCE /* Enable strcasestr in string.h */
+
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
@@ -52,20 +54,20 @@
#include <shell/e-shell.h>
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-stream.h>
-
#include <glib/gi18n.h>
-#include <libemail-utils/mail-mt.h>
+#include <JavaScriptCore/JavaScript.h>
+#include <webkit/webkit.h>
+#include <libemail-utils/mail-mt.h>
#include <libemail-engine/e-mail-enumtypes.h>
#include <libemail-engine/e-mail-utils.h>
#include <libemail-engine/mail-config.h>
#include "em-format-html.h"
-#include "em-html-stream.h"
#include "em-utils.h"
+#include "e-mail-display.h"
+#include <em-format/em-inline-filter.h>
#define EM_FORMAT_HTML_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -73,45 +75,19 @@
#define d(x)
-#define EM_FORMAT_HTML_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), EM_TYPE_FORMAT_HTML, EMFormatHTMLPrivate))
-
-#define EFM_MESSAGE_START_ANAME "evolution#message#start"
-#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>"
-
-struct _EMFormatHTMLCache {
- CamelMultipart *textmp;
-
- gchar partid[1];
-};
-
struct _EMFormatHTMLPrivate {
- EWebView *web_view;
-
- CamelMimeMessage *last_part; /* not reffed, DO NOT dereference */
- volatile gint format_id; /* format thread id */
- guint format_timeout_id;
- struct _format_msg *format_timeout_msg;
-
- /* Table that re-maps text parts into a mutlipart/mixed, EMFormatHTMLCache * */
- GHashTable *text_inline_parts;
-
- GQueue pending_jobs;
- GMutex *lock;
-
GdkColor colors[EM_FORMAT_HTML_NUM_COLOR_TYPES];
EMailImageLoadingPolicy image_loading_policy;
- EMFormatHTMLHeadersState headers_state;
- gboolean headers_collapsable;
-
- guint load_images_now : 1;
+ guint can_load_images : 1;
guint only_local_photos : 1;
guint show_sender_photo : 1;
guint show_real_date : 1;
+ guint animate_images : 1;
};
+static gpointer parent_class;
+
enum {
PROP_0,
PROP_BODY_COLOR,
@@ -125,345 +101,1214 @@ enum {
PROP_SHOW_SENDER_PHOTO,
PROP_SHOW_REAL_DATE,
PROP_TEXT_COLOR,
- PROP_WEB_VIEW,
- PROP_HEADERS_STATE,
- PROP_HEADERS_COLLAPSABLE
+ PROP_ANIMATE_IMAGES
};
-static void efh_url_requested (GtkHTML *html, const gchar *url, GtkHTMLStream *handle, EMFormatHTML *efh);
-static gboolean efh_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTML *efh);
-static void efh_gtkhtml_destroy (GtkHTML *html, EMFormatHTML *efh);
+#define EFM_MESSAGE_START_ANAME "evolution_message_start"
+#define EFH_MESSAGE_START "<A name=\"" EFM_MESSAGE_START_ANAME "\"></A>"
-static void efh_format_message (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback);
+static void efh_parse_image (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efh_parse_text_enriched (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efh_parse_text_plain (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efh_parse_text_html (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efh_parse_message_external (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efh_parse_message_deliverystatus (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+static void efh_parse_message_rfc822 (EMFormat *emf, CamelMimePart *part, GString *part_id, EMFormatParserInfo *info, GCancellable *cancellable);
+
+static void efh_write_image (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_text_enriched (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_text_plain (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_text_html (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_source (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_headers (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_error (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+static void efh_write_message_rfc822 (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
+static void efh_format_full_headers (EMFormatHTML *efh, GString *buffer, CamelMedium *part, gboolean all_headers, gboolean visible, GCancellable *cancellable);
+static void efh_format_short_headers (EMFormatHTML *efh, GString *buffer, CamelMedium *part, gboolean visible, GCancellable *cancellable);
+
+static void efh_write_message (EMFormat *emf, GList *puris, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
+
+/*****************************************************************************/
+static void
+efh_parse_image (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatPURI *puri;
+ const gchar *tmp;
+ gchar *cid;
+ gint len;
-static void efh_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- CamelCipherValidity *valid,
- GCancellable *cancellable);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
-static void efh_builtin_init (EMFormatHTMLClass *efhc);
+ tmp = camel_mime_part_get_content_id (part);
+ if (!tmp) {
+ em_format_parse_part_as (emf, part, part_id, info,
+ "x-evolution/message/attachment", cancellable);
+ return;
+ }
-static void efh_write_image (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable);
+ cid = g_strdup_printf ("cid:%s", tmp);
+ len = part_id->len;
+ g_string_append (part_id, ".image");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->cid = cid;
+ puri->write_func = efh_write_image;
+ puri->mime_type = g_strdup (info->handler->mime_type);
+ puri->is_attachment = TRUE;
+ puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+ puri->validity_type = info->validity_type;
-static gpointer parent_class;
-static CamelDataCache *emfh_http_cache;
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+}
-#define EMFH_HTTP_CACHE_PATH "http"
+static void
+efh_parse_text_enriched (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatPURI *puri;
+ const gchar *tmp;
+ gchar *cid;
+ gint len;
-/* Sigh, this is so we have a cancellable, async rendering thread */
-struct _format_msg {
- MailMsg base;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- EMFormatHTML *format;
- EMFormat *format_source;
- EMHTMLStream *estream;
- CamelFolder *folder;
- gchar *uid;
- CamelMimeMessage *message;
- gboolean cancelled;
-};
+ tmp = camel_mime_part_get_content_id (part);
+ if (!tmp) {
+ cid = g_strdup_printf ("em-no-cid:%s", part_id->str);
+ } else {
+ cid = g_strdup_printf ("cid:%s", tmp);
+ }
-static gchar *
-efh_format_desc (struct _format_msg *m)
+ len = part_id->len;
+ g_string_append (part_id, ".text_enriched");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->cid = cid;
+ puri->mime_type = g_strdup (info->handler->mime_type);
+ puri->write_func = efh_write_text_enriched;
+ puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+ puri->validity_type = info->validity_type;
+ puri->is_attachment = info->is_attachment;
+
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+}
+
+static void
+efh_parse_text_plain (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- return g_strdup(_("Formatting message"));
+ EMFormatPURI *puri;
+ CamelStream *filtered_stream, *null;
+ CamelMultipart *mp;
+ CamelDataWrapper *dw;
+ CamelContentType *type;
+ gint i, count, len;
+ EMInlineFilter *inline_filter;
+ gboolean charset_added = FALSE;
+ const gchar *snoop_type = NULL;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ dw = camel_medium_get_content ((CamelMedium *) part);
+ if (!dw)
+ return;
+
+ /* This scans the text part for inline-encoded data, creates
+ * a multipart of all the parts inside it. */
+
+ /* FIXME: We should discard this multipart if it only contains
+ * the original text, but it makes this hash lookup more complex */
+
+ /* TODO: We could probably put this in the superclass, since
+ * no knowledge of html is required - but this messes with
+ * filters a bit. Perhaps the superclass should just deal with
+ * html anyway and be done with it ... */
+
+ if (!dw->mime_type)
+ snoop_type = em_format_snoop_type (part);
+
+ /* if we had to snoop the part type to get here, then
+ * use that as the base type, yuck */
+ if (snoop_type == NULL
+ || (type = camel_content_type_decode (snoop_type)) == NULL) {
+ type = dw->mime_type;
+ camel_content_type_ref (type);
+ }
+
+ if (dw->mime_type && type != dw->mime_type && camel_content_type_param (dw->mime_type, "charset")) {
+ camel_content_type_set_param (type, "charset", camel_content_type_param (dw->mime_type, "charset"));
+ charset_added = TRUE;
+ }
+
+ null = camel_stream_null_new ();
+ filtered_stream = camel_stream_filter_new (null);
+ g_object_unref (null);
+ inline_filter = em_inline_filter_new (camel_mime_part_get_encoding (part), type);
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream),
+ CAMEL_MIME_FILTER (inline_filter));
+ camel_data_wrapper_decode_to_stream_sync (
+ dw, (CamelStream *) filtered_stream, cancellable, NULL);
+ camel_stream_close ((CamelStream *) filtered_stream, cancellable, NULL);
+ g_object_unref (filtered_stream);
+
+ mp = em_inline_filter_get_multipart (inline_filter);
+
+ if (charset_added) {
+ camel_content_type_set_param (type, "charset", NULL);
+ }
+
+ g_object_unref (inline_filter);
+ camel_content_type_unref (type);
+
+ /* We handle our made-up multipart here, so we don't recursively call ourselves */
+ len = part_id->len;
+ count = camel_multipart_get_number (mp);
+ for (i = 0; i < count; i++) {
+ CamelMimePart *newpart = camel_multipart_get_part (mp, i);
+
+ if (!newpart)
+ continue;
+
+ type = camel_mime_part_get_content_type (newpart);
+ if (camel_content_type_is (type, "text", "*") && (!camel_content_type_is (type, "text", "calendar"))) {
+ gint s_len = part_id->len;
+
+ g_string_append (part_id, ".plain_text");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), newpart, part_id->str);
+ puri->write_func = efh_write_text_plain;
+ puri->mime_type = g_strdup ("text/html");
+ puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+ puri->validity_type = info->validity_type;
+ puri->is_attachment = info->is_attachment;
+ g_string_truncate (part_id, s_len);
+ em_format_add_puri (emf, puri);
+ } else {
+ g_string_append_printf (part_id, ".inline.%d", i);
+ em_format_parse_part (emf, CAMEL_MIME_PART (newpart), part_id, info, cancellable);
+ g_string_truncate (part_id, len);
+ }
+ }
+
+ g_object_unref (mp);
}
static void
-efh_format_exec (struct _format_msg *m,
- GCancellable *cancellable,
- GError **error)
+efh_parse_text_html (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
{
- EMFormat *format;
- CamelStream *stream;
- struct _EMFormatHTMLJob *job;
- GNode *puri_level;
+ EMFormatPURI *puri;
+ const gchar *location;
+ gchar *cid = NULL;
CamelURL *base;
+ gint len;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ base = em_format_get_base_url (emf);
+ location = camel_mime_part_get_content_location (part);
+ if (location == NULL) {
+ if (base)
+ cid = camel_url_to_string (base, 0);
+ else
+ cid = g_strdup (part_id->str);
+ } else {
+ if (strchr (location, ':') == NULL && base != NULL) {
+ CamelURL *uri;
+
+ uri = camel_url_new_with_base (base, location);
+ cid = camel_url_to_string (uri, 0);
+ camel_url_free (uri);
+ } else {
+ cid = g_strdup (location);
+ }
+ }
+
+ len = part_id->len;
+ g_string_append (part_id, ".text_html");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = efh_write_text_html;
+ puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+ puri->validity_type = info->validity_type;
+ puri->is_attachment = info->is_attachment;
+
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+
+ if (cid)
+ g_free (cid);
+}
+
+static void
+efh_parse_message_external (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatPURI *puri;
+ CamelMimePart *newpart;
+ CamelContentType *type;
+ const gchar *access_type;
+ gchar *url = NULL, *desc = NULL;
gchar *content;
+ gint len;
- if (m->format->priv->web_view == NULL) {
- m->cancelled = TRUE;
+ if (g_cancellable_is_cancelled (cancellable))
return;
+
+ newpart = camel_mime_part_new ();
+
+ /* needs to be cleaner */
+ type = camel_mime_part_get_content_type (part);
+ access_type = camel_content_type_param (type, "access-type");
+ if (!access_type) {
+ const gchar *msg = _("Malformed external-body part");
+ camel_mime_part_set_content (newpart, msg, strlen (msg),
+ "text/plain");
+ goto addPart;
}
- format = EM_FORMAT (m->format);
- stream = CAMEL_STREAM (m->estream);
+ if (!g_ascii_strcasecmp(access_type, "ftp") ||
+ !g_ascii_strcasecmp(access_type, "anon-ftp")) {
+ const gchar *name, *site, *dir, *mode;
+ gchar *path;
+ gchar ftype[16];
+
+ name = camel_content_type_param (type, "name");
+ site = camel_content_type_param (type, "site");
+ dir = camel_content_type_param (type, "directory");
+ mode = camel_content_type_param (type, "mode");
+ if (name == NULL || site == NULL)
+ goto fail;
+
+ /* Generate the path. */
+ if (dir)
+ path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name);
+ else
+ path = g_strdup_printf("/%s", *name=='/'?name+1:name);
+
+ if (mode && *mode)
+ sprintf(ftype, ";type=%c", *mode);
+ else
+ ftype[0] = 0;
+
+ url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype);
+ g_free (path);
+ desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url);
+ } else if (!g_ascii_strcasecmp (access_type, "local-file")) {
+ const gchar *name, *site;
+
+ name = camel_content_type_param (type, "name");
+ site = camel_content_type_param (type, "site");
+ if (name == NULL)
+ goto fail;
+ url = g_filename_to_uri (name, NULL, NULL);
+ if (site)
+ desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site);
+ else
+ desc = g_strdup_printf(_("Pointer to local file (%s)"), name);
+ } else if (!g_ascii_strcasecmp (access_type, "URL")) {
+ const gchar *urlparam;
+ gchar *s, *d;
+
+ /* RFC 2017 */
+ urlparam = camel_content_type_param (type, "url");
+ if (urlparam == NULL)
+ goto fail;
+
+ /* For obscure MIMEy reasons, the URL may be split into words */
+ url = g_strdup (urlparam);
+ s = d = url;
+ while (*s) {
+ if (!isspace ((guchar) * s))
+ *d++ = *s;
+ s++;
+ }
+ *d = 0;
+ desc = g_strdup_printf (_("Pointer to remote data (%s)"), url);
+ } else
+ goto fail;
+
+ content = g_strdup_printf ("<a href=\"%s\">%s</a>", url, desc);
+ camel_mime_part_set_content (newpart, content, strlen (content), "text/html");
+ g_free (content);
+
+ g_free (url);
+ g_free (desc);
+
+fail:
content = g_strdup_printf (
- "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
- "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
- "<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n",
- e_color_to_value (
- &m->format->priv->colors[
- EM_FORMAT_HTML_COLOR_BODY]),
- e_color_to_value (
- &m->format->priv->colors[
- EM_FORMAT_HTML_COLOR_HEADER]));
- camel_stream_write_string (stream, content, cancellable, NULL);
+ _("Pointer to unknown external data (\"%s\" type)"),
+ access_type);
+ camel_mime_part_set_content (newpart, content, strlen (content), "text/plain");
g_free (content);
- /* <insert top-header stuff here> */
+addPart:
+ len = part_id->len;
+ g_string_append (part_id, ".msg_external");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = efh_write_text_html;
+ puri->mime_type = g_strdup ("text/html");
- if (format->mode == EM_FORMAT_MODE_SOURCE) {
- em_format_format_source (
- format, stream,
- (CamelMimePart *) m->message, cancellable);
- } else {
- const EMFormatHandler *handle;
- const gchar *mime_type;
-
- mime_type = "x-evolution/message/prefix";
- handle = em_format_find_handler (format, mime_type);
-
- if (handle != NULL)
- handle->handler (
- format, stream,
- CAMEL_MIME_PART (m->message), handle,
- cancellable, FALSE);
-
- mime_type = "x-evolution/message/rfc822";
- handle = em_format_find_handler (format, mime_type);
-
- if (handle != NULL)
- handle->handler (
- format, stream,
- CAMEL_MIME_PART (m->message), handle,
- cancellable, FALSE);
- }
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+}
- camel_stream_flush (stream, cancellable, NULL);
+static void
+efh_parse_message_deliverystatus (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatPURI *puri;
+ gint len;
- puri_level = format->pending_uri_level;
- base = format->base;
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
- do {
- /* now dispatch any added tasks ... */
- g_mutex_lock (m->format->priv->lock);
- while ((job = g_queue_pop_head (&m->format->priv->pending_jobs))) {
- g_mutex_unlock (m->format->priv->lock);
-
- /* This is an implicit check to see if the gtkhtml has been destroyed */
- if (m->format->priv->web_view == NULL)
- g_cancellable_cancel (cancellable);
-
- /* call jobs even if cancelled, so they can clean up resources */
- format->pending_uri_level = job->puri_level;
- if (job->base)
- format->base = job->base;
- job->callback (job, cancellable);
- format->base = base;
-
- /* clean up the job */
- g_object_unref (job->stream);
- if (job->base)
- camel_url_free (job->base);
- g_free (job);
-
- g_mutex_lock (m->format->priv->lock);
- }
- g_mutex_unlock (m->format->priv->lock);
-
- if (m->estream) {
- /* Closing this base stream can queue more jobs, so we need
- * to check the list again after we've finished */
- d(printf("out of jobs, closing root stream\n"));
- camel_stream_write_string (
- (CamelStream *) m->estream,
- "</body>\n</html>\n", cancellable, NULL);
- camel_stream_close ((CamelStream *) m->estream, cancellable, NULL);
- if (g_cancellable_is_cancelled (cancellable)) {
- m->cancelled = TRUE;
- m->estream->sync.cancel = TRUE;
- }
- g_object_unref (m->estream);
- m->estream = NULL;
- }
+ len = part_id->len;
+ g_string_append (part_id, ".deliverystatus");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = efh_write_source;
+ puri->mime_type = g_strdup ("text/html");
+ puri->validity = info->validity ? camel_cipher_validity_clone (info->validity) : NULL;
+ puri->validity_type = info->validity_type;
+ puri->is_attachment = info->is_attachment;
+
+ em_format_add_puri (emf, puri);
+ g_string_truncate (part_id, len);
+}
+
+static void
+efh_parse_message_rfc822 (EMFormat *emf,
+ CamelMimePart *part,
+ GString *part_id,
+ EMFormatParserInfo *info,
+ GCancellable *cancellable)
+{
+ CamelDataWrapper *dw;
+ CamelMimePart *opart;
+ CamelStream *stream;
+ CamelMimeParser *parser;
+ gint len;
+ EMFormatParserInfo oinfo = *info;
+ EMFormatPURI *puri;
+
+ len = part_id->len;
+ g_string_append (part_id, ".rfc822");
+
+ /* Create an empty PURI that will represent start of the RFC message */
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), part, part_id->str);
+ puri->write_func = efh_write_message_rfc822;
+ puri->mime_type = g_strdup ("text/html");
+ puri->is_attachment = info->is_attachment;
+ em_format_add_puri (emf, puri);
+
+ /* Now parse the message, creating multiple sub-PURIs */
+ stream = camel_stream_mem_new ();
+ dw = camel_medium_get_content ((CamelMedium *) part);
+ camel_data_wrapper_write_to_stream_sync (dw, stream, cancellable, NULL);
+ g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, cancellable, NULL);
+
+ parser = camel_mime_parser_new ();
+ camel_mime_parser_init_with_stream (parser, stream, NULL);
- } while (!g_queue_is_empty (&m->format->priv->pending_jobs));
+ opart = camel_mime_part_new ();
+ camel_mime_part_construct_from_parser_sync (opart, parser, cancellable, NULL);
- d(printf("out of jobs, done\n"));
+ em_format_parse_part_as (emf, opart, part_id, &oinfo,
+ "x-evolution/message", cancellable);
- format->pending_uri_level = puri_level;
- m->cancelled = m->cancelled || g_cancellable_is_cancelled (cancellable);
+ /* Add another generic PURI that represents end of the RFC message.
+ * The em_format_write() function will skip all PURIs between the ".rfc822"
+ * PURI and ".rfc822.end" PURI as they will be rendered in an <iframe> */
+ g_string_append (part_id, ".end");
+ puri = em_format_puri_new (emf, sizeof (EMFormatPURI), NULL, part_id->str);
+ em_format_add_puri (emf, puri);
+
+ g_string_truncate (part_id, len);
+
+ g_object_unref (opart);
+ g_object_unref (parser);
+ g_object_unref (stream);
}
+/*****************************************************************************/
+
static void
-efh_format_done (struct _format_msg *m)
+efh_write_image (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- d(printf("formatting finished\n"));
+ gchar *content;
+ EMFormatHTML *efh;
+ CamelDataWrapper *dw;
+ GByteArray *ba;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ efh = (EMFormatHTML *) emf;
+
+ dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part));
+ g_return_if_fail (dw);
+
+ ba = camel_data_wrapper_get_byte_array (dw);
+
+ if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
- m->format->priv->format_id = -1;
- m->format->priv->load_images_now = FALSE;
- m->format->state = EM_FORMAT_HTML_STATE_NONE;
- g_signal_emit_by_name(m->format, "complete");
+ if (!efh->priv->animate_images) {
+
+ gchar *buff;
+ gsize len;
+ gchar *data;
+ GByteArray anim;
+
+ data = g_strndup ((gchar *) ba->data, (gsize) ba->len);
+ anim.data = g_base64_decode (data, (gsize *) &(anim.len));
+ g_free (data);
+
+ em_format_html_animation_extract_frame (&anim, &buff, &len);
+
+ camel_stream_write (stream, buff, len, cancellable, NULL);
+
+ g_free (buff);
+ g_free (anim.data);
+
+ } else {
+ CamelStream *stream_filter;
+ CamelMimeFilter *filter;
+
+ stream_filter = camel_stream_filter_new (stream);
+ filter = camel_mime_filter_basic_new (
+ CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
+
+ camel_stream_write (
+ stream_filter,
+ (gchar *) ba->data, ba->len,
+ cancellable, NULL);
+ g_object_unref (stream_filter);
+ g_object_unref (filter);
+ }
+
+ } else {
+
+ gchar *buffer;
+
+ if (!efh->priv->animate_images) {
+
+ gchar *buff;
+ gsize len;
+ gchar *data;
+ GByteArray raw_data;
+
+ data = g_strndup ((gchar *) ba->data, ba->len);
+ raw_data.data = (guint8 *) g_base64_decode (
+ data, (gsize *) &(raw_data.len));
+ g_free (data);
+
+ em_format_html_animation_extract_frame (&raw_data, &buff, &len);
+
+ content = g_base64_encode ((guchar *) buff, len);
+ g_free (buff);
+ g_free (raw_data.data);
+
+ } else {
+ content = g_strndup ((gchar *) ba->data, ba->len);
+ }
+
+ /* The image is already base64-encrypted so we can directly
+ * paste it to the output */
+ buffer = g_strdup_printf (
+ "<img src=\"data:%s;base64,%s\" style=\"max-width: 100%%;\" />",
+ puri->mime_type ? puri->mime_type : "image/*", content);
+
+ camel_stream_write_string (stream, buffer, cancellable, NULL);
+ g_free (buffer);
+ g_free (content);
+ }
}
static void
-efh_format_free (struct _format_msg *m)
+efh_write_text_enriched (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- d(printf("formatter freed\n"));
- g_object_unref (m->format);
- if (m->estream) {
- camel_stream_close ((CamelStream *) m->estream, NULL, NULL);
- if (m->cancelled)
- m->estream->sync.cancel = TRUE;
- g_object_unref (m->estream);
+ EMFormatHTML *efh = EM_FORMAT_HTML (emf);
+ CamelStream *filtered_stream;
+ CamelMimeFilter *enriched;
+ guint32 flags = 0;
+ GString *buffer;
+ CamelContentType *ct;
+ gchar *mime_type = NULL;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ ct = camel_mime_part_get_content_type (puri->part);
+ if (ct) {
+ mime_type = camel_content_type_simple (ct);
}
- if (m->folder)
- g_object_unref (m->folder);
- g_free (m->uid);
- if (m->message)
- g_object_unref (m->message);
- if (m->format_source)
- g_object_unref (m->format_source);
+
+ if (!g_strcmp0(mime_type, "text/richtext")) {
+ flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
+ camel_stream_write_string (
+ stream, "\n<!-- text/richtext -->\n",
+ cancellable, NULL);
+ } else {
+ camel_stream_write_string (
+ stream, "\n<!-- text/enriched -->\n",
+ cancellable, NULL);
+ }
+
+ if (mime_type)
+ g_free (mime_type);
+
+ enriched = camel_mime_filter_enriched_new (flags);
+ filtered_stream = camel_stream_filter_new (stream);
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream), enriched);
+ g_object_unref (enriched);
+
+ buffer = g_string_new ("");
+
+ g_string_append_printf (buffer,
+ "<div class=\"part-container\" style=\"border-color: #%06x; "
+ "background-color: #%06x; color: #%06x;\">"
+ "<div class=\"part-container-inner-margin\">\n",
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_FRAME]),
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_CONTENT]),
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_TEXT]));
+
+ camel_stream_write_string (stream, buffer->str, cancellable, NULL);
+ g_string_free (buffer, TRUE);
+
+ em_format_format_text (
+ emf, (CamelStream *) filtered_stream,
+ (CamelDataWrapper *) puri->part, cancellable);
+
+ g_object_unref (filtered_stream);
+ camel_stream_write_string (stream, "</div></div>", cancellable, NULL);
}
-static MailMsgInfo efh_format_info = {
- sizeof (struct _format_msg),
- (MailMsgDescFunc) efh_format_desc,
- (MailMsgExecFunc) efh_format_exec,
- (MailMsgDoneFunc) efh_format_done,
- (MailMsgFreeFunc) efh_format_free
-};
+static void
+efh_write_text_plain (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ CamelDataWrapper *dw;
+ CamelStream *filtered_stream;
+ CamelMimeFilter *html_filter;
+ EMFormatHTML *efh = (EMFormatHTML *) emf;
+ gchar *content;
+ const gchar *format;
+ guint32 flags;
+ guint32 rgb;
+
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ flags = efh->text_html_flags;
+
+ dw = camel_medium_get_content (CAMEL_MEDIUM (puri->part));
+
+ /* Check for RFC 2646 flowed text. */
+ if (camel_content_type_is(dw->mime_type, "text", "plain")
+ && (format = camel_content_type_param(dw->mime_type, "format"))
+ && !g_ascii_strcasecmp(format, "flowed"))
+ flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
+
+ rgb = e_color_to_value (
+ &efh->priv->colors[EM_FORMAT_HTML_COLOR_CITATION]);
+ filtered_stream = camel_stream_filter_new (stream);
+ html_filter = camel_mime_filter_tohtml_new (flags, rgb);
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream), html_filter);
+ g_object_unref (html_filter);
+
+ content = g_strdup_printf (
+ "<div class=\"part-container\" style=\"border-color: #%06x; "
+ "background-color: #%06x; color: #%06x;\">"
+ "<div class=\"part-container-inner-margin\">\n",
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_FRAME]),
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_CONTENT]),
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_TEXT]));
+
+ camel_stream_write_string (stream, content, cancellable, NULL);
+ em_format_format_text (emf, filtered_stream, (CamelDataWrapper *) puri->part, cancellable);
+
+ g_object_unref (filtered_stream);
+ g_free (content);
-static gboolean
-efh_format_helper (struct _format_msg *m,
- gboolean async)
+ camel_stream_write_string (stream, "</div></div>\n", cancellable, NULL);
+}
+
+static gchar *
+get_tag (const gchar *tag_name,
+ gchar *opening,
+ gchar *closing)
{
- GtkHTMLStream *hstream;
- EMFormatHTML *efh = m->format;
- struct _EMFormatHTMLPrivate *p = efh->priv;
- EWebView *web_view;
+ gchar *t;
+ gboolean has_end;
+
+ for (t = closing - 1; t != opening; t--) {
+ if (*t != ' ')
+ break;
+ }
- web_view = em_format_html_get_web_view (m->format);
+ /* Not a pair tag */
+ if (*t == '/')
+ return g_strndup (opening, closing - opening + 1);
- if (web_view == NULL) {
- mail_msg_unref (m);
- return FALSE;
+ for (t = closing; t && *t; t++) {
+ if (*t == '<')
+ break;
}
- if (async) {
- d(printf("timeout called ...\n"));
- if (p->format_id != -1) {
- d(printf(" still waiting for cancellation to take effect, waiting ...\n"));
- return TRUE;
+ do {
+ if (*t == '/') {
+ has_end = TRUE;
+ break;
}
- }
- g_return_val_if_fail (g_queue_is_empty (&p->pending_jobs), FALSE);
+ if (*t == '>') {
+ has_end = FALSE;
+ break;
+ }
+
+ t++;
+
+ } while (t && *t);
+
+ /* Broken HTML? */
+ if (!has_end)
+ return g_strndup (opening, closing - opening + 1);
+
+ do {
+ if ((*t != ' ') && (*t != '/'))
+ break;
+
+ t++;
+ } while (t && *t);
- d(printf(" ready to go, firing off format thread\n"));
+ if (g_strncasecmp (t, tag_name, strlen (tag_name)) == 0) {
- /* call super-class to kick it off */
- /* FIXME Not passing a GCancellable here. */
- EM_FORMAT_CLASS (parent_class)->format_clone (
- EM_FORMAT (efh), m->folder, m->uid,
- m->message, m->format_source, NULL);
- em_format_html_clear_pobject (m->format);
+ closing = strstr (t, ">");
- /* FIXME: method off EMFormat? */
- if (((EMFormat *) efh)->valid) {
- camel_cipher_validity_free (((EMFormat *) efh)->valid);
- ((EMFormat *) efh)->valid = NULL;
- ((EMFormat *) efh)->valid_parent = NULL;
+ return g_strndup (opening, closing - opening + strlen (tag_name));
}
- if (m->message == NULL) {
- hstream = gtk_html_begin (GTK_HTML (web_view));
- gtk_html_stream_close (hstream, GTK_HTML_STREAM_OK);
- mail_msg_unref (m);
- p->last_part = NULL;
- } else {
- efh->state = EM_FORMAT_HTML_STATE_RENDERING;
-#if HAVE_CLUTTER
- if (p->last_part != m->message && !e_shell_get_express_mode (e_shell_get_default ())) {
-#else
- if (p->last_part != m->message) {
-#endif
- hstream = gtk_html_begin (GTK_HTML (web_view));
- gtk_html_stream_printf (hstream, "<h5>%s</h5>", _("Formatting Message..."));
- gtk_html_stream_close (hstream, GTK_HTML_STREAM_OK);
- }
+ /* Broken HTML? */
+ return g_strndup (opening, closing - opening + 1);
+}
- hstream = NULL;
- m->estream = (EMHTMLStream *) em_html_stream_new (
- GTK_HTML (web_view), hstream);
+static void
+efh_write_text_html (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ EMFormatHTML *efh = (EMFormatHTML *) emf;
- if (p->last_part == m->message) {
- em_html_stream_set_flags (m->estream,
- GTK_HTML_BEGIN_KEEP_SCROLL | GTK_HTML_BEGIN_KEEP_IMAGES
- | GTK_HTML_BEGIN_BLOCK_UPDATES | GTK_HTML_BEGIN_BLOCK_IMAGES);
- } else {
- /* clear cache of inline-scanned text parts */
- g_hash_table_remove_all (p->text_inline_parts);
+ if (g_cancellable_is_cancelled (cancellable))
+ return;
+
+ if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
+ em_format_format_text (emf, stream,
+ (CamelDataWrapper *) puri->part, cancellable);
+
+ } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) {
+ GString *string;
+ GByteArray *ba;
+ gchar *pos;
+ GList *tags, *iter;
+ gboolean valid;
+ gchar *tag;
+ const gchar *document_end;
+ gint length;
+ gint i;
+ CamelStream *decoded_stream;
+
+ decoded_stream = camel_stream_mem_new ();
+ em_format_format_text (emf, decoded_stream,
+ (CamelDataWrapper *) puri->part, cancellable);
+ g_seekable_seek (G_SEEKABLE (decoded_stream), 0, G_SEEK_SET, cancellable, NULL);
+
+ ba = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (decoded_stream));
+ string = g_string_new_len ((gchar *) ba->data, ba->len);
+
+ g_object_unref (decoded_stream);
+
+ tags = NULL;
+ pos = string->str;
+ valid = FALSE;
+ do {
+ gchar *closing;
+ gchar *opening;
+
+ pos = strstr (pos + 1, "<");
+ if (!pos)
+ break;
+
+ opening = pos;
+ closing = strstr (pos, ">");
+
+ /* Find where the actual tag name begins */
+ for (tag = pos + 1; tag && *tag; tag++) {
+ if (*tag != ' ')
+ break;
+ }
+
+ if (g_ascii_strncasecmp (tag, "style", 5) == 0) {
+ tags = g_list_append (
+ tags,
+ get_tag ("style", opening, closing));
+ } else if (g_ascii_strncasecmp (tag, "script", 6) == 0) {
+ tags = g_list_append (
+ tags,
+ get_tag ("script", opening, closing));
+ } else if (g_ascii_strncasecmp (tag, "link", 4) == 0) {
+ tags = g_list_append (
+ tags,
+ get_tag ("link", opening, closing));
+ } else if (g_ascii_strncasecmp (tag, "body", 4) == 0) {
+ valid = TRUE;
+ break;
+ }
+
+ } while (TRUE);
- p->last_part = m->message;
+ /* Something's wrong, let's write the entire HTML and hope
+ * that WebKit can handle it */
+ if (!valid) {
+ EMFormatWriterInfo i = *info;
+ i.mode = EM_FORMAT_WRITE_MODE_RAW;
+ efh_write_text_html (emf, puri, stream, &i, cancellable);
+ return;
}
- efh->priv->format_id = m->base.seq;
+ /* include the "body" as well -----v */
+ g_string_erase (string, 0, tag - string->str + 4);
+ g_string_prepend (string, "<div ");
- if (async) {
- mail_msg_unordered_push (m);
- } else {
- efh_format_exec (m, NULL, NULL);
+ for (iter = tags; iter; iter = iter->next) {
+ g_string_prepend (string, iter->data);
}
- }
- efh->priv->format_timeout_id = 0;
- efh->priv->format_timeout_msg = NULL;
+ g_list_free_full (tags, g_free);
+
+ /* that's reversed </body></html>... */
+ document_end = ">lmth/<>ydob/<";
+ length = strlen (document_end);
+ tag = string->str + string->len - 1;
+ i = 0;
+ valid = FALSE;
+ while (i < length - 1) {
+ gchar c;
- return FALSE;
+ if (g_ascii_isspace (*tag)) {
+ tag--;
+ continue;
+ }
+
+ if ((*tag >= 'A') && (*tag <= 'Z'))
+ c = *tag + 32;
+ else
+ c = *tag;
+
+ if (c == document_end[i]) {
+ tag--;
+ i++;
+ valid = TRUE;
+ continue;
+ }
+
+ valid = FALSE;
+ }
+
+ if (valid)
+ g_string_truncate (string, tag - string->str);
+
+ camel_stream_write_string (stream, string->str, cancellable, NULL);
+
+ g_string_free (string, TRUE);
+ } else {
+ gchar *str;
+ gchar *uri;
+
+ uri = em_format_build_mail_uri (
+ emf->folder, emf->message_uid,
+ "part_id", G_TYPE_STRING, puri->uri,
+ "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW,
+ NULL);
+
+ str = g_strdup_printf (
+ "<div class=\"part-container\" style=\"border-color: #%06x; "
+ "background-color: #%06x;\">"
+ "<div class=\"part-container-inner-margin\">\n"
+ "<iframe width=\"100%%\" height=\"auto\""
+ " frameborder=\"0\" src=\"%s\"></iframe>"
+ "</div></div>",
+ e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]),
+ e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]),
+ uri);
+
+ camel_stream_write_string (stream, str, cancellable, NULL);
+
+ g_free (str);
+ g_free (uri);
+ }
}
static void
-efh_free_cache (struct _EMFormatHTMLCache *efhc)
+efh_write_source (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- if (efhc->textmp)
- g_object_unref (efhc->textmp);
- g_free (efhc);
+ EMFormatHTML *efh = (EMFormatHTML *) emf;
+ GString *buffer;
+ CamelStream *filtered_stream;
+ CamelMimeFilter *filter;
+ CamelDataWrapper *dw = (CamelDataWrapper *) puri->part;
+
+ filtered_stream = camel_stream_filter_new (stream);
+
+ filter = camel_mime_filter_tohtml_new (
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
+ CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream), filter);
+ g_object_unref (filter);
+
+ buffer = g_string_new ("");
+
+ g_string_append_printf (
+ buffer, "<div class=\"part-container\" style=\"background: #%06x; color: #%06x;\" >",
+ e_color_to_value (
+ &efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_BODY]),
+ e_color_to_value (
+ &efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_HEADER]));
+
+ camel_stream_write_string (
+ stream, buffer->str, cancellable, NULL);
+ camel_stream_write_string (
+ stream, "<code class=\"pre\">", cancellable, NULL);
+ camel_data_wrapper_write_to_stream_sync (dw, filtered_stream,
+ cancellable, NULL);
+ camel_stream_write_string (
+ stream, "</code>", cancellable, NULL);
+
+ g_object_unref (filtered_stream);
+ g_string_free (buffer, TRUE);
}
static void
-efh_gtkhtml_destroy (GtkHTML *html,
- EMFormatHTML *efh)
+efh_write_headers (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- if (efh->priv->format_timeout_id != 0) {
- g_source_remove (efh->priv->format_timeout_id);
- efh->priv->format_timeout_id = 0;
- mail_msg_unref (efh->priv->format_timeout_msg);
- efh->priv->format_timeout_msg = NULL;
+ GString *buffer;
+ EMFormatHTML *efh = (EMFormatHTML *) emf;
+ gint bg_color;
+
+ if (!puri->part)
+ return;
+
+ buffer = g_string_new ("");
+
+ if (info->mode & EM_FORMAT_WRITE_MODE_PRINTING) {
+ GdkColor white = { 0, G_MAXUINT16, G_MAXUINT16, G_MAXUINT16 };
+ bg_color = e_color_to_value (&white);
+ } else {
+ bg_color = e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY]);
+ }
+
+ g_string_append_printf (
+ buffer,
+ "<div class=\"headers\" style=\"background: #%06x;\">"
+ "<table border=\"0\" width=\"100%%\" style=\"color: #%06x;\">\n"
+ "<tr><td valign=\"top\" width=\"16\">\n",
+ bg_color,
+ e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_HEADER]));
+
+ if (info->headers_collapsable) {
+ g_string_append_printf (buffer,
+ "<img src=\"evo-file://%s/%s\" class=\"navigable\" "
+ "id=\"__evo-collapse-headers-img\" />"
+ "</td><td>",
+ EVOLUTION_IMAGESDIR,
+ (info->headers_collapsed) ? "plus.png" : "minus.png");
+
+ efh_format_short_headers (efh, buffer, (CamelMedium *) puri->part,
+ info->headers_collapsed,
+ cancellable);
}
- /* This probably works ... */
- if (efh->priv->format_id != -1)
- mail_msg_cancel (efh->priv->format_id);
+ efh_format_full_headers (efh, buffer, (CamelMedium *) puri->part,
+ (info->mode == EM_FORMAT_WRITE_MODE_ALL_HEADERS),
+ !info->headers_collapsed,
+ cancellable);
+
+ g_string_append (buffer, "</td></tr></table></div>");
+
+ camel_stream_write_string (stream, buffer->str, cancellable, NULL);
- if (efh->priv->web_view != NULL) {
- g_object_unref (efh->priv->web_view);
- efh->priv->web_view = NULL;
+ g_string_free (buffer, true);
+}
+
+static void
+efh_write_error (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ CamelStream *filtered_stream;
+ CamelMimeFilter *filter;
+ CamelDataWrapper *dw;
+
+ dw = camel_medium_get_content ((CamelMedium *) puri->part);
+
+ camel_stream_write_string (stream, "<em><font color=\"red\">", cancellable, NULL);
+
+ filtered_stream = camel_stream_filter_new (stream);
+ filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered_stream), filter);
+ g_object_unref (filter);
+
+ camel_data_wrapper_decode_to_stream_sync (dw, filtered_stream, cancellable, NULL);
+
+ g_object_unref (filtered_stream);
+
+ camel_stream_write_string (stream, "</font></em><br>", cancellable, NULL);
+}
+
+static void
+efh_write_message_rfc822 (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ if (info->mode == EM_FORMAT_WRITE_MODE_RAW) {
+
+ GList *puris;
+ GList *iter;
+
+ /* Create a new fake list of PURIs which will contain only
+ * PURIs from this message. */
+ iter = g_hash_table_lookup (emf->mail_part_table, puri->uri);
+ if (!iter || !iter->next)
+ return;
+
+ iter = iter->next;
+ puris = NULL;
+ while (iter) {
+
+ EMFormatPURI *p;
+ p = iter->data;
+
+ if (g_str_has_suffix (p->uri, ".rfc822.end"))
+ break;
+
+ puris = g_list_append (puris, p);
+ iter = iter->next;
+
+ };
+
+ efh_write_message (emf, puris, stream, info, cancellable);
+
+ g_list_free (puris);
+
+ } else if (info->mode == EM_FORMAT_WRITE_MODE_PRINTING) {
+
+ GList *iter;
+ gboolean can_write = FALSE;
+
+ iter = g_hash_table_lookup (emf->mail_part_table, puri->uri);
+ if (!iter || !iter->next)
+ return;
+
+ /* Skip everything before attachment bar, inclusive */\
+ iter = iter->next;
+ while (iter) {
+
+ EMFormatPURI *p = iter->data;
+
+ /* EMFormatHTMLPrint has registered a special writer
+ * for headers, try to find it and use it. */
+ if (g_str_has_suffix (p->uri, ".headers")) {
+
+ const EMFormatHandler *handler;
+
+ handler = em_format_find_handler (
+ emf, "x-evolution/message/headers");
+ if (handler && handler->write_func)
+ handler->write_func (emf, p, stream, info, cancellable);
+
+ iter = iter->next;
+ continue;
+ }
+
+ if (g_str_has_suffix (p->uri, ".rfc822.end"))
+ break;
+
+ if (g_str_has_suffix (p->uri, ".attachment-bar"))
+ can_write = TRUE;
+
+ if (can_write && p->write_func) {
+ p->write_func (
+ emf, p, stream, info, cancellable);
+ }
+
+ iter = iter->next;
+ }
+
+ } else {
+ gchar *str;
+ gchar *uri;
+
+ EMFormatHTML *efh = (EMFormatHTML *) emf;
+ EMFormatPURI *p;
+ GList *iter;
+
+ iter = g_hash_table_lookup (emf->mail_part_table, puri->uri);
+ if (!iter || !iter->next)
+ return;
+
+ iter = iter->next;
+ p = iter->data;
+
+ uri = em_format_build_mail_uri (emf->folder, emf->message_uid,
+ "part_id", G_TYPE_STRING, p->uri,
+ "mode", G_TYPE_INT, EM_FORMAT_WRITE_MODE_RAW,
+ NULL);
+
+ str = g_strdup_printf (
+ "<div class=\"part-container\" style=\"border-color: #%06x; "
+ "background-color: #%06x;\">"
+ "<div class=\"part-container-inner-margin\">\n"
+ "<iframe width=\"100%%\" height=\"auto\""
+ " frameborder=\"0\" src=\"%s\" name=\"%s\"></iframe>"
+ "</div></div>",
+ e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_FRAME]),
+ e_color_to_value (&efh->priv->colors[EM_FORMAT_HTML_COLOR_CONTENT]),
+ uri, puri->uri);
+
+ camel_stream_write_string (stream, str, cancellable, NULL);
+
+ g_free (str);
+ g_free (uri);
}
+
}
-static struct _EMFormatHTMLCache *
-efh_insert_cache (EMFormatHTML *efh,
- const gchar *partid)
+/*****************************************************************************/
+
+/* Notes:
+ *
+ * image/tiff is omitted because it's a multi-page image format, but
+ * gdk-pixbuf unconditionally renders the first page only, and doesn't
+ * even indicate through meta-data whether multiple pages are present
+ * (see bug 335959). Therefore, make no attempt to render TIFF images
+ * inline and defer to an application that can handle multi-page TIFF
+ * files properly like Evince or Gimp. Once the referenced bug is
+ * fixed we can reevaluate this policy.
+ */
+static EMFormatHandler type_builtin_table[] = {
+ { (gchar *) "image/gif", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/jpeg", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/png", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-png", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-bmp", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/bmp", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/svg", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-cmu-raster", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-ico", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-portable-anymap", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-portable-bitmap", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-portable-graymap", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-portable-pixmap", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/x-xpixmap", efh_parse_image, efh_write_image, },
+ { (gchar *) "text/enriched", efh_parse_text_enriched, efh_write_text_enriched, },
+ { (gchar *) "text/plain", efh_parse_text_plain, efh_write_text_plain, },
+ { (gchar *) "text/html", efh_parse_text_html, efh_write_text_html, },
+ { (gchar *) "text/richtext", efh_parse_text_enriched, efh_write_text_enriched, },
+ { (gchar *) "text/*", efh_parse_text_plain, efh_write_text_plain, },
+ { (gchar *) "message/rfc822", efh_parse_message_rfc822, efh_write_message_rfc822, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE },
+ { (gchar *) "message/news", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE | EM_FORMAT_HANDLER_COMPOUND_TYPE },
+ { (gchar *) "message/delivery-status", efh_parse_message_deliverystatus, efh_write_text_plain, },
+ { (gchar *) "message/external-body", efh_parse_message_external, efh_write_text_plain, },
+ { (gchar *) "message/*", efh_parse_message_rfc822, 0, EM_FORMAT_HANDLER_INLINE },
+
+ /* This is where one adds those busted, non-registered types,
+ * that some idiot mailer writers out there decide to pull out
+ * of their proverbials at random. */
+ { (gchar *) "image/jpg", efh_parse_image, efh_write_image, },
+ { (gchar *) "image/pjpeg", efh_parse_image, efh_write_image, },
+
+ /* special internal types */
+ { (gchar *) "x-evolution/message/rfc822", 0, efh_write_text_plain, },
+ { (gchar *) "x-evolution/message/headers", 0, efh_write_headers, },
+ { (gchar *) "x-evolution/message/source", 0, efh_write_source, },
+ { (gchar *) "x-evolution/message/attachment", 0, efh_write_attachment, },
+ { (gchar *) "x-evolution/message/error", 0, efh_write_error, },
+};
+
+static void
+efh_builtin_init (EMFormatHTMLClass *efhc)
{
- struct _EMFormatHTMLCache *efhc;
+ EMFormatClass *emfc;
+ gint ii;
- efhc = g_malloc0 (sizeof (*efh) + strlen (partid));
- strcpy (efhc->partid, partid);
- g_hash_table_insert (efh->priv->text_inline_parts, efhc->partid, efhc);
+ emfc = (EMFormatClass *) efhc;
- return efhc;
+ for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
+ em_format_class_add_handler (
+ emfc, &type_builtin_table[ii]);
}
static void
@@ -544,15 +1389,12 @@ efh_set_property (GObject *object,
EM_FORMAT_HTML_COLOR_TEXT,
g_value_get_boxed (value));
return;
- case PROP_HEADERS_STATE:
- em_format_html_set_headers_state (
- EM_FORMAT_HTML (object),
- g_value_get_int (value));
- return;
- case PROP_HEADERS_COLLAPSABLE:
- em_format_html_set_headers_collapsable (
+
+ case PROP_ANIMATE_IMAGES:
+ em_format_html_set_animate_images (
EM_FORMAT_HTML (object),
g_value_get_boolean (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -645,21 +1487,11 @@ efh_get_property (GObject *object,
&color);
g_value_set_boxed (value, &color);
return;
-
- case PROP_WEB_VIEW:
- g_value_set_object (
- value, em_format_html_get_web_view (
- EM_FORMAT_HTML (object)));
- return;
- case PROP_HEADERS_STATE:
- g_value_set_int (
- value, em_format_html_get_headers_state (
- EM_FORMAT_HTML (object)));
- return;
- case PROP_HEADERS_COLLAPSABLE:
+ case PROP_ANIMATE_IMAGES:
g_value_set_boolean (
- value, em_format_html_get_headers_collapsable (
+ value, em_format_html_get_animate_images (
EM_FORMAT_HTML (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -668,230 +1500,198 @@ efh_get_property (GObject *object,
static void
efh_finalize (GObject *object)
{
- EMFormatHTML *efh = EM_FORMAT_HTML (object);
-
- em_format_html_clear_pobject (efh);
- efh_gtkhtml_destroy (GTK_HTML (efh->priv->web_view), efh);
-
- g_hash_table_destroy (efh->priv->text_inline_parts);
-
- g_mutex_free (efh->priv->lock);
-
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
-static gboolean
-efh_format_timeout (struct _format_msg *m)
-{
- return efh_format_helper (m, TRUE);
-}
-
-void
-em_format_html_clone_sync (CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormatHTML *efh,
- EMFormat *source)
-{
- struct _format_msg *m;
-
- m = mail_msg_new (&efh_format_info);
- m->format = g_object_ref (efh);
- if (source)
- m->format_source = g_object_ref (source);
- m->folder = g_object_ref (folder);
- m->uid = g_strdup (uid);
- m->message = g_object_ref (msg);
-
- efh_format_helper (m, FALSE);
- efh_format_free (m);
-}
-
static void
-efh_format_clone (EMFormat *emf,
- CamelFolder *folder,
- const gchar *uid,
- CamelMimeMessage *msg,
- EMFormat *emfsource,
- GCancellable *cancellable)
+efh_write_attachment (EMFormat *emf,
+ EMFormatPURI *puri,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- struct _format_msg *m;
-
- /* How to sub-class ? Might need to adjust api ... */
-
- if (efh->priv->web_view == NULL)
- return;
+ gchar *text, *html;
+ CamelContentType *ct;
+ gchar *mime_type;
+ const EMFormatHandler *handler;
- d(printf("efh_format called\n"));
- if (efh->priv->format_timeout_id != 0) {
- d(printf(" timeout for last still active, removing ...\n"));
- g_source_remove (efh->priv->format_timeout_id);
- efh->priv->format_timeout_id = 0;
- mail_msg_unref (efh->priv->format_timeout_msg);
- efh->priv->format_timeout_msg = NULL;
- }
+ /* we display all inlined attachments only */
- if (emfsource != NULL)
- g_object_ref (emfsource);
+ /* this could probably be cleaned up ... */
+ camel_stream_write_string (
+ stream,
+ "<table border=1 cellspacing=0 cellpadding=0><tr><td>"
+ "<table width=10 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td>"
+ "<td><table width=3 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td><td><font size=-1>\n",
+ cancellable, NULL);
- if (folder != NULL)
- g_object_ref (folder);
+ ct = camel_mime_part_get_content_type (puri->part);
+ mime_type = camel_content_type_simple (ct);
- if (msg != NULL)
- g_object_ref (msg);
+ /* output some info about it */
+ text = em_format_describe_part (puri->part, mime_type);
+ html = camel_text_to_html (
+ text, ((EMFormatHTML *) emf)->text_html_flags &
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+ camel_stream_write_string (stream, html, cancellable, NULL);
+ g_free (html);
+ g_free (text);
- m = mail_msg_new (&efh_format_info);
- m->format = g_object_ref (emf);
- m->format_source = emfsource;
- m->folder = folder;
- m->uid = g_strdup (uid);
- m->message = msg;
+ camel_stream_write_string (
+ stream, "</font></td></tr><tr></table>", cancellable, NULL);
- if (efh->priv->format_id == -1) {
- d(printf(" idle, forcing format\n"));
- efh_format_timeout (m);
- } else {
- d(printf(" still busy, cancelling and queuing wait\n"));
- /* cancel and poll for completion */
- mail_msg_cancel (efh->priv->format_id);
- efh->priv->format_timeout_msg = m;
- efh->priv->format_timeout_id = g_timeout_add (
- 100, (GSourceFunc) efh_format_timeout, m);
+ handler = em_format_find_handler (emf, mime_type);
+ if (handler && handler->write_func && handler->write_func != efh_write_attachment) {
+ if (em_format_is_inline (emf, puri->uri, puri->part, handler))
+ handler->write_func (emf, puri, stream, info, cancellable);
}
+
+ g_free (mime_type);
}
static void
-efh_format_error (EMFormat *emf,
- CamelStream *stream,
- const gchar *txt)
+efh_preparse (EMFormat *emf)
{
- GString *buffer;
- gchar *html;
-
- buffer = g_string_new ("<em><font color=\"red\">");
-
- html = camel_text_to_html (
- txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- g_string_append (buffer, html);
- g_free (html);
+ CamelInternetAddress *addr;
- g_string_append (buffer, "</font></em><br>");
+ EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- camel_stream_write (stream, buffer->str, buffer->len, NULL, NULL);
+ if (!emf->message) {
+ efh->priv->can_load_images = FALSE;
+ return;
+ }
- g_string_free (buffer, TRUE);
+ addr = camel_mime_message_get_from (emf->message);
+ efh->priv->can_load_images = em_utils_in_addressbook (addr, FALSE);
}
static void
-efh_format_source (EMFormat *emf,
+efh_write_message (EMFormat *emf,
+ GList *puris,
CamelStream *stream,
- CamelMimePart *part,
+ EMFormatWriterInfo *info,
GCancellable *cancellable)
{
- CamelStream *filtered_stream;
- CamelMimeFilter *filter;
- CamelDataWrapper *dw = (CamelDataWrapper *) part;
+ GList *iter;
+ EMFormatHTML *efh;
+ gchar *header;
- filtered_stream = camel_stream_filter_new (stream);
+ efh = (EMFormatHTML *) emf;
- filter = camel_mime_filter_tohtml_new (
- CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
- CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream), filter);
- g_object_unref (filter);
+ header = g_strdup_printf (
+ "<!DOCTYPE HTML>\n<html>\n"
+ "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\" />\n"
+ "<title>Evolution Mail Display</title>\n"
+ "<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\" />\n"
+ "<style type=\"text/css\">\n"
+ " table th { color: #000; font-weight: bold; }\n"
+ "</style>\n"
+ "</head><body bgcolor=\"#%06x\">",
+ e_color_to_value (&efh->priv->colors[
+ EM_FORMAT_HTML_COLOR_BODY]));
- camel_stream_write_string (
- stream, "<table><tr><td><tt>", cancellable, NULL);
- em_format_format_text (emf, filtered_stream, dw, cancellable);
- camel_stream_write_string (
- stream, "</tt></td></tr></table>", cancellable, NULL);
+ camel_stream_write_string (stream, header, cancellable, NULL);
+ g_free (header);
- g_object_unref (filtered_stream);
-}
+ if (info->mode == EM_FORMAT_WRITE_MODE_SOURCE) {
-static void
-efh_format_attachment (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const gchar *mime_type,
- const EMFormatHandler *handle,
- GCancellable *cancellable)
-{
- gchar *text, *html;
+ efh_write_source (emf, emf->mail_part_list->data,
+ stream, info, cancellable);
- /* we display all inlined attachments only */
+ camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
+ return;
+ }
- /* this could probably be cleaned up ... */
- camel_stream_write_string (
- stream,
- "<table border=1 cellspacing=0 cellpadding=0><tr><td>"
- "<table width=10 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr></table></td>"
- "<td><table width=3 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr></table></td><td><font size=-1>\n",
- cancellable, NULL);
+ for (iter = puris; iter; iter = iter->next) {
- /* output some info about it */
- text = em_format_describe_part (part, mime_type);
- html = camel_text_to_html (
- text, ((EMFormatHTML *) emf)->text_html_flags &
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- camel_stream_write_string (stream, html, cancellable, NULL);
- g_free (html);
- g_free (text);
+ EMFormatPURI *puri = iter->data;
- camel_stream_write_string (
- stream, "</font></td></tr><tr></table>", cancellable, NULL);
+ if (!puri)
+ continue;
- if (handle && em_format_is_inline (emf, emf->part_id->str, part, handle))
- handle->handler (emf, stream, part, handle, cancellable, FALSE);
-}
+ /* If current PURI has suffix .rfc822 then iterate through all
+ * subsequent PURIs until PURI with suffix .rfc822.end is found.
+ * These skipped PURIs contain entire RFC message which will
+ * be written in <iframe> as attachment.
+ */
+ if (g_str_has_suffix (puri->uri, ".rfc822")) {
+
+ /* If the PURI is not an attachment, then we must
+ * inline it here otherwise it would not be displayed. */
+ if (!puri->is_attachment && puri->write_func) {
+ /* efh_write_message_rfc822 starts parsing _after_
+ * the passed PURI, so we must give it previous PURI here */
+ EMFormatPURI *p;
+ if (!iter->prev)
+ continue;
-static gboolean
-efh_busy (EMFormat *emf)
-{
- EMFormatHTMLPrivate *priv;
+ p = iter->prev->data;
+ puri->write_func (emf, p, stream, info, cancellable);
+ }
+
+ while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) {
+
+ iter = iter->next;
+ if (iter)
+ puri = iter->data;
- priv = EM_FORMAT_HTML_GET_PRIVATE (emf);
+ d(printf(".rfc822 - skipping %s\n", puri->uri));
+ }
+
+ /* Skip the .rfc822.end PURI as well. */
+ if (!iter)
+ break;
- return (priv->format_id != -1);
+ continue;
+ }
+
+ if (puri->write_func && !puri->is_attachment) {
+ puri->write_func (emf, puri, stream, info, cancellable);
+ d(printf("Writing PURI %s\n", puri->uri));
+ } else {
+ d(printf("Skipping PURI %s\n", puri->uri));
+ }
+ }
+
+ camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
+}
+
+static void
+efh_write (EMFormat *emf,
+ CamelStream *stream,
+ EMFormatWriterInfo *info,
+ GCancellable *cancellable)
+{
+ efh_write_message (emf, emf->mail_part_list, stream, info, cancellable);
}
+
static void
-efh_base_init (EMFormatHTMLClass *class)
+efh_base_init (EMFormatHTMLClass *klass)
{
- efh_builtin_init (class);
+ efh_builtin_init (klass);
}
static void
-efh_class_init (EMFormatHTMLClass *class)
+efh_class_init (EMFormatHTMLClass *klass)
{
GObjectClass *object_class;
- EMFormatClass *format_class;
- const gchar *user_cache_dir;
+ EMFormatClass *emf_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (EMFormatHTMLPrivate));
- parent_class = g_type_class_peek_parent (class);
- g_type_class_add_private (class, sizeof (EMFormatHTMLPrivate));
+ emf_class = EM_FORMAT_CLASS (klass);
+ emf_class->preparse = efh_preparse;
+ emf_class->write = efh_write;
- object_class = G_OBJECT_CLASS (class);
+ object_class = G_OBJECT_CLASS (klass);
object_class->set_property = efh_set_property;
object_class->get_property = efh_get_property;
object_class->finalize = efh_finalize;
- format_class = EM_FORMAT_CLASS (class);
- format_class->format_clone = efh_format_clone;
- format_class->format_error = efh_format_error;
- format_class->format_source = efh_format_source;
- format_class->format_attachment = efh_format_attachment;
- format_class->format_secure = efh_format_secure;
- format_class->busy = efh_busy;
-
- class->html_widget_type = E_TYPE_WEB_VIEW;
-
g_object_class_install_property (
object_class,
PROP_BODY_COLOR,
@@ -982,7 +1782,7 @@ efh_class_init (EMFormatHTMLClass *class)
"show-sender-photo",
"Show Sender Photo",
NULL,
- TRUE,
+ FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
@@ -1009,79 +1809,24 @@ efh_class_init (EMFormatHTMLClass *class)
g_object_class_install_property (
object_class,
- PROP_WEB_VIEW,
- g_param_spec_object (
- "web-view",
- "Web View",
- NULL,
- E_TYPE_WEB_VIEW,
- G_PARAM_READABLE));
-
- g_object_class_install_property (
- object_class,
- PROP_HEADERS_STATE,
- g_param_spec_int (
- "headers-state",
- "Headers state",
- NULL,
- EM_FORMAT_HTML_HEADERS_STATE_EXPANDED,
- EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED,
- EM_FORMAT_HTML_HEADERS_STATE_EXPANDED,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
- PROP_HEADERS_STATE,
+ PROP_ANIMATE_IMAGES,
g_param_spec_boolean (
- "headers-collapsable",
- NULL,
+ "animate-images",
+ "Animate images",
NULL,
FALSE,
G_PARAM_READWRITE));
-
- /* cache expiry - 2 hour access, 1 day max */
- user_cache_dir = e_get_user_cache_dir ();
- emfh_http_cache = camel_data_cache_new (user_cache_dir, NULL);
- if (emfh_http_cache) {
- camel_data_cache_set_expire_age (emfh_http_cache, 24 *60 *60);
- camel_data_cache_set_expire_access (emfh_http_cache, 2 *60 *60);
- }
}
static void
efh_init (EMFormatHTML *efh,
- EMFormatHTMLClass *class)
+ EMFormatHTMLClass *klass)
{
- EWebView *web_view;
GdkColor *color;
efh->priv = EM_FORMAT_HTML_GET_PRIVATE (efh);
g_queue_init (&efh->pending_object_list);
- g_queue_init (&efh->priv->pending_jobs);
- efh->priv->lock = g_mutex_new ();
- efh->priv->format_id = -1;
- efh->priv->text_inline_parts = g_hash_table_new_full (
- g_str_hash, g_str_equal,
- (GDestroyNotify) NULL,
- (GDestroyNotify) efh_free_cache);
-
- web_view = g_object_new (class->html_widget_type, NULL);
- efh->priv->web_view = g_object_ref_sink (web_view);
-
- gtk_html_set_blocking (GTK_HTML (web_view), FALSE);
- gtk_html_set_caret_first_focus_anchor (
- GTK_HTML (web_view), EFM_MESSAGE_START_ANAME);
- gtk_html_set_default_content_type (
- GTK_HTML (web_view), "text/html; charset=utf-8");
- e_web_view_set_editable (web_view, FALSE);
-
- g_signal_connect (
- web_view, "url-requested",
- G_CALLBACK (efh_url_requested), efh);
- g_signal_connect (
- web_view, "object-requested",
- G_CALLBACK (efh_object_requested), efh);
color = &efh->priv->colors[EM_FORMAT_HTML_COLOR_BODY];
gdk_color_parse ("#eeeeee", color);
@@ -1103,7 +1848,6 @@ efh_init (EMFormatHTML *efh,
CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
efh->show_icon = TRUE;
- efh->state = EM_FORMAT_HTML_STATE_NONE;
e_extensible_load_extensions (E_EXTENSIBLE (efh));
}
@@ -1144,28 +1888,7 @@ em_format_html_get_type (void)
return type;
}
-EWebView *
-em_format_html_get_web_view (EMFormatHTML *efh)
-{
- g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), NULL);
-
- return efh->priv->web_view;
-}
-
-void
-em_format_html_load_images (EMFormatHTML *efh)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
- if (efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS)
- return;
-
- /* This will remain set while we're still
- * rendering the same message, then it wont be. */
- efh->priv->load_images_now = TRUE;
- em_format_queue_redraw (EM_FORMAT (efh));
-}
-
+/*****************************************************************************/
void
em_format_html_get_color (EMFormatHTML *efh,
EMFormatHTMLColorType type,
@@ -1338,42 +2061,23 @@ em_format_html_set_show_real_date (EMFormatHTML *efh,
g_object_notify (G_OBJECT (efh), "show-real-date");
}
-EMFormatHTMLHeadersState
-em_format_html_get_headers_state (EMFormatHTML *efh)
-{
- g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), EM_FORMAT_HTML_HEADERS_STATE_EXPANDED);
-
- return efh->priv->headers_state;
-}
-
-void
-em_format_html_set_headers_state (EMFormatHTML *efh,
- EMFormatHTMLHeadersState state)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (efh));
-
- efh->priv->headers_state = state;
-
- g_object_notify (G_OBJECT (efh), "headers-state");
-}
-
gboolean
-em_format_html_get_headers_collapsable (EMFormatHTML *efh)
+em_format_html_get_animate_images (EMFormatHTML *efh)
{
g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
- return efh->priv->headers_collapsable;
+ return efh->priv->animate_images;
}
void
-em_format_html_set_headers_collapsable (EMFormatHTML *efh,
- gboolean collapsable)
+em_format_html_set_animate_images (EMFormatHTML *efh,
+ gboolean animate_images)
{
g_return_if_fail (EM_IS_FORMAT_HTML (efh));
- efh->priv->headers_collapsable = collapsable;
+ efh->priv->animate_images = animate_images;
- g_object_notify (G_OBJECT (efh), "headers-collapsable");
+ g_object_notify (G_OBJECT (efh), "animate-images");
}
CamelMimePart *
@@ -1407,1184 +2111,60 @@ em_format_html_file_part (EMFormatHTML *efh,
return part;
}
-/* all this api is a pain in the bum ... */
-
-EMFormatHTMLPObject *
-em_format_html_add_pobject (EMFormatHTML *efh,
- gsize size,
- const gchar *classid,
- CamelMimePart *part,
- EMFormatHTMLPObjectFunc func)
-{
- EMFormatHTMLPObject *pobj;
-
- if (size < sizeof (EMFormatHTMLPObject)) {
- g_warning ("size is less than the size of EMFormatHTMLPObject\n");
- size = sizeof (EMFormatHTMLPObject);
- }
-
- pobj = g_malloc0 (size);
- if (classid)
- pobj->classid = g_strdup (classid);
- else
- pobj->classid = g_strdup_printf("e-object:///%s", ((EMFormat *)efh)->part_id->str);
-
- pobj->format = efh;
- pobj->func = func;
- pobj->part = part;
-
- g_queue_push_tail (&efh->pending_object_list, pobj);
-
- return pobj;
-}
-
-EMFormatHTMLPObject *
-em_format_html_find_pobject (EMFormatHTML *emf,
- const gchar *classid)
-{
- GList *link;
-
- g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL);
- g_return_val_if_fail (classid != NULL, NULL);
-
- link = g_queue_peek_head_link (&emf->pending_object_list);
-
- while (link != NULL) {
- EMFormatHTMLPObject *pw = link->data;
-
- if (!strcmp (pw->classid, classid))
- return pw;
-
- link = g_list_next (link);
- }
-
- return NULL;
-}
-
-EMFormatHTMLPObject *
-em_format_html_find_pobject_func (EMFormatHTML *emf,
- CamelMimePart *part,
- EMFormatHTMLPObjectFunc func)
-{
- GList *link;
-
- g_return_val_if_fail (EM_IS_FORMAT_HTML (emf), NULL);
-
- link = g_queue_peek_head_link (&emf->pending_object_list);
-
- while (link != NULL) {
- EMFormatHTMLPObject *pw = link->data;
-
- if (pw->func == func && pw->part == part)
- return pw;
-
- link = g_list_next (link);
- }
-
- return NULL;
-}
-
-void
-em_format_html_remove_pobject (EMFormatHTML *emf,
- EMFormatHTMLPObject *pobject)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (emf));
- g_return_if_fail (pobject != NULL);
-
- g_queue_remove (&emf->pending_object_list, pobject);
-
- if (pobject->free != NULL)
- pobject->free (pobject);
-
- g_free (pobject->classid);
- g_free (pobject);
-}
-
-void
-em_format_html_clear_pobject (EMFormatHTML *emf)
-{
- g_return_if_fail (EM_IS_FORMAT_HTML (emf));
-
- while (!g_queue_is_empty (&emf->pending_object_list)) {
- EMFormatHTMLPObject *pobj;
-
- pobj = g_queue_pop_head (&emf->pending_object_list);
- em_format_html_remove_pobject (emf, pobj);
- }
-}
-
-struct _EMFormatHTMLJob *
-em_format_html_job_new (EMFormatHTML *emfh,
- void (*callback) (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable),
- gpointer data)
-{
- struct _EMFormatHTMLJob *job = g_malloc0 (sizeof (*job));
-
- job->format = emfh;
- job->puri_level = ((EMFormat *) emfh)->pending_uri_level;
- job->callback = callback;
- job->u.data = data;
- if (((EMFormat *) emfh)->base)
- job->base = camel_url_copy (((EMFormat *) emfh)->base);
-
- return job;
-}
-
void
-em_format_html_job_queue (EMFormatHTML *emfh,
- struct _EMFormatHTMLJob *job)
-{
- g_mutex_lock (emfh->priv->lock);
- g_queue_push_tail (&emfh->priv->pending_jobs, job);
- g_mutex_unlock (emfh->priv->lock);
-}
-
-/* ********************************************************************** */
-
-static void
-emfh_getpuri (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable)
-{
- d(printf(" running getpuri task\n"));
- if (!g_cancellable_is_cancelled (cancellable))
- job->u.puri->func (
- EM_FORMAT (job->format), job->stream,
- job->u.puri, cancellable);
-}
-
-static void
-emfh_configure_stream_for_proxy (CamelHttpStream *stream,
- const gchar *uri)
-{
- EProxy *proxy;
- SoupURI *proxy_uri;
- gchar *basic;
- gchar *basic64;
- const gchar *user = "";
- const gchar *password = "";
-
- proxy = em_utils_get_proxy ();
-
- if (!e_proxy_require_proxy_for_uri (proxy, uri))
- return;
-
- proxy_uri = e_proxy_peek_uri_for (proxy, uri);
-
- if (proxy_uri == NULL)
- return;
-
- if (proxy_uri->user != NULL)
- user = proxy_uri->user;
-
- if (proxy_uri->password != NULL)
- password = proxy_uri->password;
-
- if (*user == '\0' && *password == '\0')
- return;
-
- basic = g_strdup_printf ("%s:%s", user, password);
- basic64 = g_base64_encode ((guchar *) basic, strlen (basic));
- camel_http_stream_set_proxy_authpass (stream, basic64);
- g_free (basic64);
- g_free (basic);
-}
-
-static void
-emfh_gethttp (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable)
-{
- CamelStream *cistream = NULL, *costream = NULL, *instream = NULL;
- CamelURL *url;
- CamelHttpStream *tmp_stream;
- gssize n, total = 0, pc_complete = 0, nread = 0;
- gchar buffer[1500];
- const gchar *length;
-
- if (g_cancellable_is_cancelled (cancellable)
- || (url = camel_url_new (job->u.uri, NULL)) == NULL)
- goto badurl;
-
- d(printf(" running load uri task: %s\n", job->u.uri));
-
- if (emfh_http_cache)
- instream = cistream = camel_data_cache_get (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
-
- if (instream == NULL) {
- EMailImageLoadingPolicy policy;
-
- policy = em_format_html_get_image_loading_policy (job->format);
-
- if (!(job->format->priv->load_images_now
- || policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS
- || (policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES
- && em_utils_in_addressbook ((CamelInternetAddress *) camel_mime_message_get_from (job->format->parent.message), FALSE)))) {
- /* TODO: Ideally we would put the http requests into
- * another queue and only send them out if the user
- * selects 'load images', when they do. The problem
- * is how to maintain this state with multiple
- * renderings, and how to adjust the thread
- * dispatch/setup routine to handle it */
- camel_url_free (url);
- goto done;
- }
-
- instream = camel_http_stream_new (CAMEL_HTTP_METHOD_GET, ((EMFormat *) job->format)->session, url);
- camel_http_stream_set_user_agent((CamelHttpStream *) instream, "CamelHttpStream/1.0 Evolution/" VERSION);
- emfh_configure_stream_for_proxy ((CamelHttpStream *) instream, job->u.uri);
-
- camel_operation_push_message (
- cancellable, _("Retrieving '%s'"), job->u.uri);
- tmp_stream = (CamelHttpStream *) instream;
- if (camel_stream_read (CAMEL_STREAM (instream), NULL, 0, cancellable, NULL) == 0) {
- CamelContentType *content_type;
-
- content_type = camel_http_stream_get_content_type (tmp_stream);
- length = camel_header_raw_find(&tmp_stream->headers, "Content-Length", NULL);
- d(printf(" Content-Length: %s\n", length));
- if (length != NULL)
- total = atoi (length);
- camel_content_type_unref (content_type);
- }
- } else
- camel_operation_push_message (
- cancellable, _("Retrieving '%s'"), job->u.uri);
-
- camel_url_free (url);
-
- if (instream == NULL)
- goto done;
-
- if (emfh_http_cache != NULL && cistream == NULL)
- costream = camel_data_cache_add (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
-
- do {
- if (g_cancellable_is_cancelled (cancellable)) {
- n = -1;
- break;
- }
- /* FIXME: progress reporting in percentage, can we get the length always? do we care? */
- n = camel_stream_read (instream, buffer, sizeof (buffer), cancellable, NULL);
- if (n > 0) {
- nread += n;
- /* If we didn't get a valid Content-Length header, do not try to calculate percentage */
- if (total != 0) {
- pc_complete = ((nread * 100) / total);
- camel_operation_progress (cancellable, pc_complete);
- }
- d(printf(" read %d bytes\n", n));
- if (costream && camel_stream_write (costream, buffer, n, cancellable, NULL) == -1) {
- n = -1;
- break;
- }
-
- camel_stream_write (job->stream, buffer, n, cancellable, NULL);
- }
- } while (n > 0);
-
- /* indicates success */
- if (n == 0)
- camel_stream_close (job->stream, cancellable, NULL);
-
- if (costream) {
- /* do not store broken files in a cache */
- if (n != 0)
- camel_data_cache_remove (emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
- g_object_unref (costream);
- }
-
- g_object_unref (instream);
-done:
- camel_operation_pop_message (cancellable);
-badurl:
- g_free (job->u.uri);
-}
-
-/* ********************************************************************** */
-
-static void
-efh_url_requested (GtkHTML *html,
- const gchar *url,
- GtkHTMLStream *handle,
- EMFormatHTML *efh)
-{
- EMFormatPURI *puri;
- struct _EMFormatHTMLJob *job = NULL;
-
- d(printf("url requested, html = %p, url '%s'\n", html, url));
-
- puri = em_format_find_visible_puri ((EMFormat *) efh, url);
- if (puri) {
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
- CamelContentType *ct = dw ? dw->mime_type : NULL;
-
- /* GtkHTML only handles text and images.
- * application/octet-stream parts are the only ones
- * which are snooped for other content. So only try
- * to pass these to it - any other types are badly
- * formed or intentionally malicious emails. They
- * will still show as attachments anyway */
-
- if (ct && (camel_content_type_is(ct, "text", "*")
- || camel_content_type_is(ct, "image", "*")
- || camel_content_type_is(ct, "application", "octet-stream"))) {
- puri->use_count++;
-
- d(printf(" adding puri job\n"));
- job = em_format_html_job_new (efh, emfh_getpuri, puri);
- } else {
- d(printf(" part is unknown type '%s', not using\n", ct?camel_content_type_format(ct):"<unset>"));
- gtk_html_stream_close (handle, GTK_HTML_STREAM_ERROR);
- }
- } else if (g_ascii_strncasecmp(url, "http:", 5) == 0 || g_ascii_strncasecmp(url, "https:", 6) == 0) {
- d(printf(" adding job, get %s\n", url));
- job = em_format_html_job_new (efh, emfh_gethttp, g_strdup (url));
- } else if (g_str_has_prefix (url, "file://")) {
- gchar *data = NULL;
- gsize length = 0;
- gboolean status;
- gchar *path;
-
- path = g_filename_from_uri (url, NULL, NULL);
- g_return_if_fail (path != NULL);
-
- status = g_file_get_contents (path, &data, &length, NULL);
- if (status)
- gtk_html_stream_write (handle, data, length);
-
- gtk_html_stream_close (handle, status ? GTK_HTML_STREAM_OK : GTK_HTML_STREAM_ERROR);
- g_free (data);
- g_free (path);
- } else {
- d(printf("HTML Includes reference to unknown uri '%s'\n", url));
- gtk_html_stream_close (handle, GTK_HTML_STREAM_ERROR);
- }
-
- if (job) {
- job->stream = em_html_stream_new (html, handle);
- em_format_html_job_queue (efh, job);
- }
-
- g_signal_stop_emission_by_name (html, "url-requested");
-}
-
-static gboolean
-efh_object_requested (GtkHTML *html,
- GtkHTMLEmbedded *eb,
- EMFormatHTML *efh)
-{
- EMFormatHTMLPObject *pobject;
- gint res = FALSE;
-
- if (eb->classid == NULL)
- return FALSE;
-
- pobject = em_format_html_find_pobject (efh, eb->classid);
- if (pobject) {
- /* This stops recursion of the part */
- g_queue_remove (&efh->pending_object_list, pobject);
- res = pobject->func (efh, eb, pobject);
- g_queue_push_head (&efh->pending_object_list, pobject);
- } else {
- d(printf("HTML Includes reference to unknown object '%s'\n", eb->classid));
- }
-
- return res;
-}
-
-/* ********************************************************************** */
-#include "em-format/em-inline-filter.h"
-
-/* FIXME: This is duplicated in em-format-html-display, should be exported or in security module */
-static const struct {
- const gchar *icon, *shortdesc;
-} smime_sign_table[5] = {
- { "stock_signature-bad", N_("Unsigned") },
- { "stock_signature-ok", N_("Valid signature") },
- { "stock_signature-bad", N_("Invalid signature") },
- { "stock_signature", N_("Valid signature, but cannot verify sender") },
- { "stock_signature-bad", N_("Signature exists, but need public key") },
-};
-
-static const struct {
- const gchar *icon, *shortdesc;
-} smime_encrypt_table[4] = {
- { "stock_lock-broken", N_("Unencrypted") },
- { "stock_lock", N_("Encrypted, weak"),},
- { "stock_lock-ok", N_("Encrypted") },
- { "stock_lock-ok", N_("Encrypted, strong") },
-};
-
-static const gchar *smime_sign_colour[4] = {
- "", " bgcolor=\"#88bb88\"", " bgcolor=\"#bb8888\"", " bgcolor=\"#e8d122\""
-};
-
-/* TODO: this could probably be virtual on em-format-html
- * then we only need one version of each type handler */
-static void
-efh_format_secure (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- CamelCipherValidity *valid,
- GCancellable *cancellable)
-{
- EMFormatClass *format_class;
-
- format_class = EM_FORMAT_CLASS (parent_class);
- g_return_if_fail (format_class->format_secure != NULL);
- format_class->format_secure (emf, stream, part, valid, cancellable);
-
- /* To explain, if the validity is the same, then we are the
- * base validity and now have a combined sign/encrypt validity
- * we can display. Primarily a new verification context is
- * created when we have an embeded message. */
- if (emf->valid == valid
- && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
- || valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) {
- gchar *classid, *iconpath;
- const gchar *icon;
- CamelMimePart *iconpart;
- GString *buffer;
-
- buffer = g_string_sized_new (1024);
-
- g_string_append_printf (
- buffer,
- "<table border=0 width=\"100%%\" "
- "cellpadding=3 cellspacing=0%s><tr>",
- smime_sign_colour[valid->sign.status]);
-
- classid = g_strdup_printf (
- "smime:///em-format-html/%s/icon/signed",
- emf->part_id->str);
- g_string_append_printf (
- buffer,
- "<td valign=\"top\"><img src=\"%s\"></td>"
- "<td valign=\"top\" width=\"100%%\">", classid);
-
- if (valid->sign.status != 0)
- icon = smime_sign_table[valid->sign.status].icon;
- else
- icon = smime_encrypt_table[valid->encrypt.status].icon;
- iconpath = e_icon_factory_get_icon_filename (icon, GTK_ICON_SIZE_DIALOG);
- iconpart = em_format_html_file_part((EMFormatHTML *)emf, "image/png", iconpath, cancellable);
- if (iconpart) {
- (void) em_format_add_puri (emf, sizeof (EMFormatPURI), classid, iconpart, efh_write_image);
- g_object_unref (iconpart);
- }
- g_free (iconpath);
- g_free (classid);
-
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
- g_string_append (
- buffer, _(smime_sign_table[valid->sign.status].shortdesc));
-
- em_format_html_format_cert_infos (
- &valid->sign.signers, buffer);
- }
-
- if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
- if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
- g_string_append (buffer, "<br>");
-
- g_string_append (
- buffer, _(smime_encrypt_table[valid->encrypt.status].shortdesc));
- }
-
- g_string_append (buffer, "</td></tr></table>");
-
- camel_stream_write (
- stream, buffer->str,
- buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
- }
-}
-
-static void
-efh_text_plain (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- CamelStream *filtered_stream;
- CamelMimeFilter *html_filter;
- CamelMultipart *mp;
- CamelDataWrapper *dw;
- CamelContentType *type;
- const gchar *format;
- guint32 flags;
- guint32 rgb;
- gint i, count, len;
- struct _EMFormatHTMLCache *efhc;
-
- flags = efh->text_html_flags;
-
- dw = camel_medium_get_content ((CamelMedium *) part);
- if (!dw)
- return;
-
- /* Check for RFC 2646 flowed text. */
- if (camel_content_type_is(dw->mime_type, "text", "plain")
- && (format = camel_content_type_param(dw->mime_type, "format"))
- && !g_ascii_strcasecmp(format, "flowed"))
- flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
-
- /* This scans the text part for inline-encoded data, creates
- * a multipart of all the parts inside it. */
-
- /* FIXME: We should discard this multipart if it only contains
- * the original text, but it makes this hash lookup more complex */
-
- /* TODO: We could probably put this in the superclass, since
- * no knowledge of html is required - but this messes with
- * filters a bit. Perhaps the superclass should just deal with
- * html anyway and be done with it ... */
-
- efhc = g_hash_table_lookup (
- efh->priv->text_inline_parts,
- emf->part_id->str);
-
- if (efhc == NULL || (mp = efhc->textmp) == NULL) {
- EMInlineFilter *inline_filter;
- CamelStream *null;
- CamelContentType *ct;
- gboolean charset_added = FALSE;
-
- /* if we had to snoop the part type to get here, then
- * use that as the base type, yuck */
- if (emf->snoop_mime_type == NULL
- || (ct = camel_content_type_decode (emf->snoop_mime_type)) == NULL) {
- ct = dw->mime_type;
- camel_content_type_ref (ct);
- }
-
- if (dw->mime_type && ct != dw->mime_type && camel_content_type_param (dw->mime_type, "charset")) {
- camel_content_type_set_param (ct, "charset", camel_content_type_param (dw->mime_type, "charset"));
- charset_added = TRUE;
- }
-
- null = camel_stream_null_new ();
- filtered_stream = camel_stream_filter_new (null);
- g_object_unref (null);
- inline_filter = em_inline_filter_new (camel_mime_part_get_encoding (part), ct);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream),
- CAMEL_MIME_FILTER (inline_filter));
- camel_data_wrapper_decode_to_stream_sync (
- dw, (CamelStream *) filtered_stream, cancellable, NULL);
- camel_stream_close ((CamelStream *) filtered_stream, cancellable, NULL);
- g_object_unref (filtered_stream);
-
- mp = em_inline_filter_get_multipart (inline_filter);
- if (efhc == NULL)
- efhc = efh_insert_cache (efh, emf->part_id->str);
- efhc->textmp = mp;
-
- if (charset_added) {
- camel_content_type_set_param (ct, "charset", NULL);
- }
-
- g_object_unref (inline_filter);
- camel_content_type_unref (ct);
- }
-
- rgb = e_color_to_value (
- &efh->priv->colors[EM_FORMAT_HTML_COLOR_CITATION]);
- filtered_stream = camel_stream_filter_new (stream);
- html_filter = camel_mime_filter_tohtml_new (flags, rgb);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream), html_filter);
- g_object_unref (html_filter);
-
- /* We handle our made-up multipart here, so we don't recursively call ourselves */
-
- len = emf->part_id->len;
- count = camel_multipart_get_number (mp);
- for (i = 0; i < count; i++) {
- CamelMimePart *newpart = camel_multipart_get_part (mp, i);
-
- if (!newpart)
- continue;
-
- type = camel_mime_part_get_content_type (newpart);
- if (camel_content_type_is (type, "text", "*") && (is_fallback || !camel_content_type_is (type, "text", "calendar"))) {
- gchar *content;
-
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; padding: 10px; "
- "color: #%06x;\">\n<tt>\n" EFH_MESSAGE_START,
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_FRAME]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_CONTENT]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_TEXT]));
- camel_stream_write_string (
- stream, content, cancellable, NULL);
- g_free (content);
-
- em_format_format_text (
- emf, filtered_stream,
- (CamelDataWrapper *) newpart,
- cancellable);
- camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL);
- camel_stream_write_string (stream, "</tt>\n", cancellable, NULL);
- camel_stream_write_string (stream, "</div>\n", cancellable, NULL);
- } else {
- g_string_append_printf (emf->part_id, ".inline.%d", i);
- em_format_part (
- emf, stream, newpart, cancellable);
- g_string_truncate (emf->part_id, len);
- }
- }
-
- g_object_unref (filtered_stream);
-}
-
-static void
-efh_text_enriched (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- CamelStream *filtered_stream;
- CamelMimeFilter *enriched;
- guint32 flags = 0;
- gchar *content;
-
- if (!strcmp(info->mime_type, "text/richtext")) {
- flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
- camel_stream_write_string (
- stream, "\n<!-- text/richtext -->\n",
- cancellable, NULL);
- } else {
- camel_stream_write_string (
- stream, "\n<!-- text/enriched -->\n",
- cancellable, NULL);
- }
-
- enriched = camel_mime_filter_enriched_new (flags);
- filtered_stream = camel_stream_filter_new (stream);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream), enriched);
- g_object_unref (enriched);
-
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; padding: 10px; "
- "color: #%06x;\">\n" EFH_MESSAGE_START,
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_FRAME]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_CONTENT]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_TEXT]));
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-
- em_format_format_text (
- emf, (CamelStream *) filtered_stream,
- (CamelDataWrapper *) part, cancellable);
-
- g_object_unref (filtered_stream);
- camel_stream_write_string (stream, "</div>", cancellable, NULL);
-}
-
-static void
-efh_write_text_html (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
+em_format_html_format_cert_infos (GQueue *cert_infos,
+ GString *output_buffer)
{
-#if d(!)0
- CamelStream *out;
- gint fd;
- CamelDataWrapper *dw;
-
- fd = dup (STDOUT_FILENO);
- out = camel_stream_fs_new_with_fd (fd);
- printf("writing text content to frame '%s'\n", puri->cid);
- dw = camel_medium_get_content (puri->part);
- if (dw)
- camel_data_wrapper_write_to_stream (dw, out);
- g_object_unref (out);
-#endif
- em_format_format_text (
- emf, stream, (CamelDataWrapper *) puri->part, cancellable);
-}
+ GQueue valid = G_QUEUE_INIT;
+ GList *head, *link;
-static void
-efh_text_html (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- const gchar *location;
- gchar *cid = NULL;
- gchar *content;
+ g_return_if_fail (cert_infos != NULL);
+ g_return_if_fail (output_buffer != NULL);
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; color: #%06x;\">\n"
- "<!-- text/html -->\n" EFH_MESSAGE_START,
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_FRAME]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_CONTENT]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_TEXT]));
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
+ head = g_queue_peek_head_link (cert_infos);
- /* TODO: perhaps we don't need to calculate this anymore now base is handled better */
- /* calculate our own location string so add_puri doesn't do it
- * for us. our iframes are special cases, we need to use the
- * proper base url to access them, but other children parts
- * shouldn't blindly inherit the container's location. */
- location = camel_mime_part_get_content_location (part);
- if (location == NULL) {
- if (emf->base)
- cid = camel_url_to_string (emf->base, 0);
- else
- cid = g_strdup (emf->part_id->str);
- } else {
- if (strchr (location, ':') == NULL && emf->base != NULL) {
- CamelURL *uri;
+ /* Make sure we have a valid CamelCipherCertInfo before
+ * appending anything to the output buffer, so we don't
+ * end up with "()". */
+ for (link = head; link != NULL; link = g_list_next (link)) {
+ CamelCipherCertInfo *cinfo = link->data;
- uri = camel_url_new_with_base (emf->base, location);
- cid = camel_url_to_string (uri, 0);
- camel_url_free (uri);
- } else {
- cid = g_strdup (location);
+ if ((cinfo->name != NULL && *cinfo->name != '\0') ||
+ (cinfo->email != NULL && *cinfo->email != '\0')) {
+ g_queue_push_tail (&valid, cinfo);
}
}
- em_format_add_puri (
- emf, sizeof (EMFormatPURI), cid,
- part, efh_write_text_html);
- d(printf("adding iframe, location %s\n", cid));
- content = g_strdup_printf (
- "<iframe src=\"%s\" frameborder=0 scrolling=no>"
- "could not get %s</iframe>\n</div>\n", cid, cid);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
- g_free (cid);
-}
-
-/* This is a lot of code for something useless ... */
-static void
-efh_message_external (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelContentType *type;
- const gchar *access_type;
- gchar *url = NULL, *desc = NULL;
- gchar *content;
-
- if (!part) {
- camel_stream_write_string (
- stream, _("Unknown external-body part."),
- cancellable, NULL);
- return;
- }
-
- /* needs to be cleaner */
- type = camel_mime_part_get_content_type (part);
- access_type = camel_content_type_param (type, "access-type");
- if (!access_type) {
- camel_stream_write_string (
- stream, _("Malformed external-body part."),
- cancellable, NULL);
- return;
- }
-
- if (!g_ascii_strcasecmp(access_type, "ftp") ||
- !g_ascii_strcasecmp(access_type, "anon-ftp")) {
- const gchar *name, *site, *dir, *mode;
- gchar *path;
- gchar ftype[16];
-
- name = camel_content_type_param (type, "name");
- site = camel_content_type_param (type, "site");
- dir = camel_content_type_param (type, "directory");
- mode = camel_content_type_param (type, "mode");
- if (name == NULL || site == NULL)
- goto fail;
-
- /* Generate the path. */
- if (dir)
- path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name);
- else
- path = g_strdup_printf("/%s", *name=='/'?name+1:name);
-
- if (mode && *mode)
- sprintf(ftype, ";type=%c", *mode);
- else
- ftype[0] = 0;
-
- url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype);
- g_free (path);
- desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url);
- } else if (!g_ascii_strcasecmp (access_type, "local-file")) {
- const gchar *name, *site;
-
- name = camel_content_type_param (type, "name");
- site = camel_content_type_param (type, "site");
- if (name == NULL)
- goto fail;
-
- url = g_filename_to_uri (name, NULL, NULL);
- if (site)
- desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site);
- else
- desc = g_strdup_printf(_("Pointer to local file (%s)"), name);
- } else if (!g_ascii_strcasecmp (access_type, "URL")) {
- const gchar *urlparam;
- gchar *s, *d;
-
- /* RFC 2017 */
-
- urlparam = camel_content_type_param (type, "url");
- if (urlparam == NULL)
- goto fail;
-
- /* For obscure MIMEy reasons, the URL may be split into words */
- url = g_strdup (urlparam);
- s = d = url;
- while (*s) {
- /* FIXME: use camel_isspace */
- if (!isspace ((guchar) * s))
- *d++ = *s;
- s++;
- }
- *d = 0;
- desc = g_strdup_printf (_("Pointer to remote data (%s)"), url);
- } else
- goto fail;
-
- content = g_strdup_printf ("<a href=\"%s\">%s</a>", url, desc);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-
- g_free (url);
- g_free (desc);
-
- return;
-
-fail:
- content = g_strdup_printf (
- _("Pointer to unknown external data (\"%s\" type)"),
- access_type);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-}
-
-static void
-efh_message_deliverystatus (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatHTML *efh = EM_FORMAT_HTML (emf);
- CamelStream *filtered_stream;
- CamelMimeFilter *html_filter;
- guint32 rgb = 0x737373;
- gchar *content;
-
- /* Yuck, this is copied from efh_text_plain */
- content = g_strdup_printf (
- "<div style=\"border: solid #%06x 1px; "
- "background-color: #%06x; padding: 10px; "
- "color: #%06x;\">\n",
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_FRAME]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_CONTENT]),
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_TEXT]));
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-
- filtered_stream = camel_stream_filter_new (stream);
- html_filter = camel_mime_filter_tohtml_new (efh->text_html_flags, rgb);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filtered_stream), html_filter);
- g_object_unref (html_filter);
-
- camel_stream_write_string (stream, "<tt>\n" EFH_MESSAGE_START, cancellable, NULL);
- em_format_format_text (
- emf, filtered_stream,
- (CamelDataWrapper *) part, cancellable);
- camel_stream_flush (filtered_stream, cancellable, NULL);
- camel_stream_write_string (stream, "</tt>\n", cancellable, NULL);
-
- camel_stream_write_string (stream, "</div>", cancellable, NULL);
-}
-
-static void
-emfh_write_related (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- em_format_format_content (emf, stream, puri->part, cancellable);
-
- camel_stream_close (stream, cancellable, NULL);
-}
-
-static void
-emfh_multipart_related_check (struct _EMFormatHTMLJob *job,
- GCancellable *cancellable)
-{
- EMFormat *format;
- GList *link;
- gchar *oldpartid;
-
- if (g_cancellable_is_cancelled (cancellable))
+ if (g_queue_is_empty (&valid))
return;
- format = EM_FORMAT (job->format);
+ g_string_append (output_buffer, " (");
- d(printf(" running multipart/related check task\n"));
- oldpartid = g_strdup (format->part_id->str);
+ while (!g_queue_is_empty (&valid)) {
+ CamelCipherCertInfo *cinfo;
- link = g_queue_peek_head_link (job->puri_level->data);
+ cinfo = g_queue_pop_head (&valid);
- if (!link) {
- g_string_printf (format->part_id, "%s", oldpartid);
- g_free (oldpartid);
- return;
- }
+ if (cinfo->name != NULL && *cinfo->name != '\0') {
+ g_string_append (output_buffer, cinfo->name);
- while (link != NULL) {
- EMFormatPURI *puri = link->data;
-
- if (puri->use_count == 0) {
- d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count));
- if (puri->func == emfh_write_related) {
- g_string_printf (format->part_id, "%s", puri->part_id);
- /* FIXME Not passing a GCancellable here. */
- em_format_part (
- format, CAMEL_STREAM (job->stream),
- puri->part, NULL);
+ if (cinfo->email != NULL && *cinfo->email != '\0') {
+ g_string_append (output_buffer, " <");
+ g_string_append (output_buffer, cinfo->email);
+ g_string_append (output_buffer, ">");
}
- /* else it was probably added by a previous format this loop */
- }
-
- link = g_list_next (link);
- }
-
- g_string_printf (format->part_id, "%s", oldpartid);
- g_free (oldpartid);
-}
-
-/* RFC 2387 */
-static void
-efh_multipart_related (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- CamelMultipart *mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part);
- CamelMimePart *body_part, *display_part = NULL;
- CamelContentType *content_type;
- const gchar *start;
- gint i, nparts, partidlen, displayid = 0;
- struct _EMFormatHTMLJob *job;
-
- if (!CAMEL_IS_MULTIPART (mp)) {
- em_format_format_source (emf, stream, part, cancellable);
- return;
- }
-
- nparts = camel_multipart_get_number (mp);
- content_type = camel_mime_part_get_content_type (part);
- start = camel_content_type_param (content_type, "start");
- if (start && strlen (start) > 2) {
- gint len;
- const gchar *cid;
- /* strip <>'s */
- len = strlen (start) - 2;
- start++;
-
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- cid = camel_mime_part_get_content_id (body_part);
-
- if (cid && !strncmp (cid, start, len) && strlen (cid) == len) {
- display_part = body_part;
- displayid = i;
- break;
- }
+ } else if (cinfo->email != NULL && *cinfo->email != '\0') {
+ g_string_append (output_buffer, cinfo->email);
}
- } else {
- display_part = camel_multipart_get_part (mp, 0);
- }
- if (display_part == NULL) {
- em_format_part_as (
- emf, stream, part,
- "multipart/mixed", cancellable);
- return;
- }
-
- em_format_push_level (emf);
-
- partidlen = emf->part_id->len;
-
- /* queue up the parts for possible inclusion */
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- if (body_part != display_part) {
- g_string_append_printf(emf->part_id, "related.%d", i);
- em_format_add_puri (emf, sizeof (EMFormatPURI), NULL, body_part, emfh_write_related);
- g_string_truncate (emf->part_id, partidlen);
- d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid));
- }
+ if (!g_queue_is_empty (&valid))
+ g_string_append (output_buffer, ", ");
}
- g_string_append_printf(emf->part_id, "related.%d", displayid);
- em_format_part (emf, stream, display_part, cancellable);
- g_string_truncate (emf->part_id, partidlen);
- camel_stream_flush (stream, cancellable, NULL);
-
- /* queue a job to check for un-referenced parts to add as attachments */
- job = em_format_html_job_new (
- EM_FORMAT_HTML (emf), emfh_multipart_related_check, NULL);
- job->stream = stream;
- g_object_ref (stream);
- em_format_html_job_queue ((EMFormatHTML *) emf, job);
-
- em_format_pull_level (emf);
-}
-
-static void
-efh_write_image (EMFormat *emf,
- CamelStream *stream,
- EMFormatPURI *puri,
- GCancellable *cancellable)
-{
- CamelDataWrapper *dw = camel_medium_get_content ((CamelMedium *) puri->part);
-
- d(printf("writing image '%s'\n", puri->cid));
- camel_data_wrapper_decode_to_stream_sync (
- dw, stream, cancellable, NULL);
- camel_stream_close (stream, cancellable, NULL);
-}
-
-static void
-efh_image (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- EMFormatPURI *puri;
- gchar *content;
-
- puri = em_format_add_puri (
- emf, sizeof (EMFormatPURI), NULL, part, efh_write_image);
-
- content = g_strdup_printf (
- "<img hspace=10 vspace=10 src=\"%s\">", puri->cid);
- camel_stream_write_string (stream, content, cancellable, NULL);
- g_free (content);
-}
-
-/* Notes:
- *
- * image/tiff is omitted because it's a multi-page image format, but
- * gdk-pixbuf unconditionally renders the first page only, and doesn't
- * even indicate through meta-data whether multiple pages are present
- * (see bug 335959). Therefore, make no attempt to render TIFF images
- * inline and defer to an application that can handle multi-page TIFF
- * files properly like Evince or Gimp. Once the referenced bug is
- * fixed we can reevaluate this policy.
- */
-static EMFormatHandler type_builtin_table[] = {
- { (gchar *) "image/gif", efh_image },
- { (gchar *) "image/jpeg", efh_image },
- { (gchar *) "image/png", efh_image },
- { (gchar *) "image/x-png", efh_image },
- { (gchar *) "image/x-bmp", efh_image },
- { (gchar *) "image/bmp", efh_image },
- { (gchar *) "image/svg", efh_image },
- { (gchar *) "image/x-cmu-raster", efh_image },
- { (gchar *) "image/x-ico", efh_image },
- { (gchar *) "image/x-portable-anymap", efh_image },
- { (gchar *) "image/x-portable-bitmap", efh_image },
- { (gchar *) "image/x-portable-graymap", efh_image },
- { (gchar *) "image/x-portable-pixmap", efh_image },
- { (gchar *) "image/x-xpixmap", efh_image },
- { (gchar *) "text/enriched", efh_text_enriched },
- { (gchar *) "text/plain", efh_text_plain },
- { (gchar *) "text/html", efh_text_html },
- { (gchar *) "text/richtext", efh_text_enriched },
- { (gchar *) "text/*", efh_text_plain },
- { (gchar *) "message/external-body", efh_message_external },
- { (gchar *) "message/delivery-status", efh_message_deliverystatus },
- { (gchar *) "multipart/related", efh_multipart_related },
-
- /* This is where one adds those busted, non-registered types,
- * that some idiot mailer writers out there decide to pull out
- * of their proverbials at random. */
-
- { (gchar *) "image/jpg", efh_image },
- { (gchar *) "image/pjpeg", efh_image },
-
- /* special internal types */
-
- { (gchar *) "x-evolution/message/rfc822", efh_format_message }
-};
-
-static void
-efh_builtin_init (EMFormatHTMLClass *efhc)
-{
- EMFormatClass *efc;
- gint ii;
-
- efc = (EMFormatClass *) efhc;
-
- for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
- em_format_class_add_handler (
- efc, &type_builtin_table[ii]);
+ g_string_append_c (output_buffer, ')');
}
-/* ********************************************************************** */
-
static void
efh_format_text_header (EMFormatHTML *emfh,
GString *buffer,
@@ -2608,37 +2188,34 @@ efh_format_text_header (EMFormatHTML *emfh,
html = value;
is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
- if (emfh->simple_headers) {
- fmt = "<b>%s</b>: %s<br>";
+
+ if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) {
+ if (flags & EM_FORMAT_HEADER_BOLD) {
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td><b>%s:</b> %s</td></tr>";
+ } else {
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td>%s: %s</td></tr>";
+ }
+ } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) {
+ if (is_rtl)
+ fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
} else {
- if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) {
- if (flags & EM_FORMAT_HEADER_BOLD) {
- fmt = "<tr><td><b>%s:</b> %s</td></tr>";
- } else {
- fmt = "<tr><td>%s: %s</td></tr>";
- }
- } else if (flags & EM_FORMAT_HTML_HEADER_NODEC) {
+ if (flags & EM_FORMAT_HEADER_BOLD) {
if (is_rtl)
- fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+ fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
else
- fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
} else {
-
- if (flags & EM_FORMAT_HEADER_BOLD) {
- if (is_rtl)
- fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
- else
- fmt = "<tr><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
- } else {
- if (is_rtl)
- fmt = "<tr><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
- else
- fmt = "<tr><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
- }
+ if (is_rtl)
+ fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
}
}
- g_string_append_printf (buffer, fmt, label, html);
+ g_string_append_printf (buffer, fmt,
+ (flags & EM_FORMAT_HTML_HEADER_HIDDEN ? "none" : "table-row"), label, html);
g_free (mhtml);
}
@@ -2653,22 +2230,15 @@ static gchar *
efh_format_address (EMFormatHTML *efh,
GString *out,
struct _camel_header_address *a,
- gchar *field)
+ gchar *field,
+ gboolean no_links)
{
guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
gchar *name, *mailto, *addr;
gint i = 0;
- gboolean wrap = FALSE;
gchar *str = NULL;
gint limit = mail_config_get_address_count ();
- if (field ) {
- if ((!strcmp (field, _("To")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_TO))
- || (!strcmp (field, _("Cc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_CC))
- || (!strcmp (field, _("Bcc")) && !(efh->header_wrap_flags & EM_FORMAT_HTML_HEADER_BCC)))
- wrap = TRUE;
- }
-
while (a) {
if (a->name)
name = camel_text_to_html (a->name, flags, 0);
@@ -2700,7 +2270,10 @@ efh_format_address (EMFormatHTML *efh,
mailto = camel_url_encode (a->v.addr, "?=&()");
}
addr = camel_text_to_html (a->v.addr, flags, 0);
- g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr);
+ if (no_links)
+ g_string_append_printf (out, "%s", addr);
+ else
+ g_string_append_printf (out, "<a href=\"mailto:%s\">%s</a>", mailto, addr);
g_free (mailto);
g_free (addr);
@@ -2709,7 +2282,7 @@ efh_format_address (EMFormatHTML *efh,
break;
case CAMEL_HEADER_ADDRESS_GROUP:
g_string_append_printf (out, "%s: ", name);
- efh_format_address (efh, out, a->v.members, field);
+ efh_format_address (efh, out, a->v.members, field, no_links);
g_string_append_printf (out, ";");
break;
default:
@@ -2725,48 +2298,51 @@ efh_format_address (EMFormatHTML *efh,
g_string_append (out, ", ");
/* Let us add a '...' if we have more addresses */
- if (limit > 0 && wrap && a && (i > (limit - 1))) {
- gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
-
- if (!strcmp (field, _("To"))) {
- g_string_append (out, "<a href=\"##TO##\">...</a>");
- str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/plus.png\"></a> ", evolution_imagesdir);
- }
- else if (!strcmp (field, _("Cc"))) {
- g_string_append (out, "<a href=\"##CC##\">...</a>");
- str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/plus.png\"></a> ", evolution_imagesdir);
+ if (limit > 0 && (i == limit - 1)) {
+ const gchar *id = NULL;
+
+ if (strcmp (field, _("To")) == 0) {
+ id = "to";
+ } else if (strcmp (field, _("Cc")) == 0) {
+ id = "cc";
+ } else if (strcmp (field, _("Bcc")) == 0) {
+ id = "bcc";
}
- else if (!strcmp (field, _("Bcc"))) {
- g_string_append (out, "<a href=\"##BCC##\">...</a>");
- str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/plus.png\"></a> ", evolution_imagesdir);
- }
-
- g_free (evolution_imagesdir);
- if (str)
- return str;
+ if (id) {
+ g_string_append_printf (out,
+ "<span id=\"__evo-moreaddr-%s\" "
+ "style=\"display: none;\">", id);
+ str = g_strdup_printf (
+ "<img src=\"evo-file://%s/plus.png\" "
+ "id=\"__evo-moreaddr-img-%s\" class=\"navigable\">",
+ EVOLUTION_IMAGESDIR, id);
+ }
}
-
}
- if (limit > 0 && i > (limit)) {
- gchar *evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+ if (str) {
+ const gchar *id = NULL;
- if (!strcmp (field, _("To"))) {
- str = g_strdup_printf ("<a href=\"##TO##\"><img src=\"%s/minus.png\"></a> ", evolution_imagesdir);
- }
- else if (!strcmp (field, _("Cc"))) {
- str = g_strdup_printf ("<a href=\"##CC##\"><img src=\"%s/minus.png\"></a> ", evolution_imagesdir);
- }
- else if (!strcmp (field, _("Bcc"))) {
- str = g_strdup_printf ("<a href=\"##BCC##\"><img src=\"%s/minus.png\"></a> ", evolution_imagesdir);
+ if (strcmp (field, _("To")) == 0) {
+ id = "to";
+ } else if (strcmp (field, _("Cc")) == 0) {
+ id = "cc";
+ } else if (strcmp (field, _("Bcc")) == 0) {
+ id = "bcc";
}
- g_free (evolution_imagesdir);
+ if (id) {
+ g_string_append_printf (out,
+ "</span>"
+ "<span class=\"navigable\" "
+ "id=\"__evo-moreaddr-ellipsis-%s\" "
+ "style=\"display: inline;\">...</span>",
+ id);
+ }
}
return str;
-
}
static void
@@ -2793,15 +2369,15 @@ canon_header_name (gchar *name)
}
}
-static void
-efh_format_header (EMFormat *emf,
- GString *buffer,
- CamelMedium *part,
- struct _camel_header_raw *header,
- guint32 flags,
- const gchar *charset)
+void
+em_format_html_format_header (EMFormat *emf,
+ GString *buffer,
+ CamelMedium *part,
+ struct _camel_header_raw *header,
+ guint32 flags,
+ const gchar *charset)
{
- EMFormatHTML *efh = (EMFormatHTML *) emf;
+ EMFormatHTML *efh = EM_FORMAT_HTML (emf);
gchar *name, *buf, *value = NULL;
const gchar *label, *txt;
gboolean addrspec = FALSE;
@@ -2825,9 +2401,11 @@ efh_format_header (EMFormat *emf,
struct _camel_header_address *addrs;
GString *html;
gchar *img;
+ const gchar *charset = em_format_get_charset (emf) ?
+ em_format_get_charset (emf) : em_format_get_default_charset (emf);
buf = camel_header_unfold (header->value);
- if (!(addrs = camel_header_address_decode (buf, emf->charset ? emf->charset : emf->default_charset))) {
+ if (!(addrs = camel_header_address_decode (buf, charset))) {
g_free (buf);
return;
}
@@ -2835,7 +2413,8 @@ efh_format_header (EMFormat *emf,
g_free (buf);
html = g_string_new("");
- img = efh_format_address (efh, html, addrs, (gchar *) label);
+ img = efh_format_address (efh, html, addrs, (gchar *) label,
+ (flags & EM_FORMAT_HTML_HEADER_NOLINKS));
if (img) {
str_field = g_strdup_printf ("%s%s:", img, label);
@@ -2921,7 +2500,11 @@ efh_format_header (EMFormat *emf,
html = g_string_new("");
scan = ng;
while (scan) {
- g_string_append_printf(html, "<a href=\"news:%s\">%s</a>", scan->newsgroup, scan->newsgroup);
+ if (flags & EM_FORMAT_HTML_HEADER_NOLINKS)
+ g_string_append_printf (html, "%s", scan->newsgroup);
+ else
+ g_string_append_printf(html, "<a href=\"news:%s\">%s</a>",
+ scan->newsgroup, scan->newsgroup);
scan = scan->next;
if (scan)
g_string_append_printf(html, ", ");
@@ -2949,100 +2532,129 @@ efh_format_header (EMFormat *emf,
}
static void
-efh_format_headers (EMFormatHTML *efh,
- GString *buffer,
- CamelMedium *part,
- GCancellable *cancellable)
+efh_format_short_headers (EMFormatHTML *efh,
+ GString *buffer,
+ CamelMedium *part,
+ gboolean visible,
+ GCancellable *cancellable)
{
- EMFormat *emf = (EMFormat *) efh;
+ EMFormat *emf = EM_FORMAT (efh);
const gchar *charset;
CamelContentType *ct;
- struct _camel_header_raw *header;
- gboolean have_icon = FALSE;
- const gchar *photo_name = NULL;
- CamelInternetAddress *cia = NULL;
- gboolean face_decoded = FALSE, contact_has_photo = FALSE;
- guchar *face_header_value = NULL;
- gsize face_header_len = 0;
- gchar *header_sender = NULL, *header_from = NULL, *name;
- gboolean mail_from_delegate = FALSE;
const gchar *hdr_charset;
gchar *evolution_imagesdir;
+ gchar *subject = NULL;
+ struct _camel_header_address *addrs = NULL;
+ struct _camel_header_raw *header;
+ GString *from;
+ gboolean is_rtl;
- if (!part)
+ if (cancellable && g_cancellable_is_cancelled (cancellable))
return;
ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
charset = camel_content_type_param (ct, "charset");
charset = camel_iconv_charset_name (charset);
+ hdr_charset = em_format_get_charset (emf) ?
+ em_format_get_charset (emf) : em_format_get_default_charset (emf);
- if (!efh->simple_headers)
- g_string_append_printf (
- buffer, "<font color=\"#%06x\">\n"
- "<table cellpadding=\"0\" width=\"100%%\">",
- e_color_to_value (
- &efh->priv->colors[
- EM_FORMAT_HTML_COLOR_HEADER]));
-
- hdr_charset = emf->charset ? emf->charset : emf->default_charset;
evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+ from = g_string_new ("");
- /* If the header is collapsed, display just subject and sender in one row and leave */
- if (efh->priv->headers_state == EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED && efh->priv->headers_collapsable) {
- gchar *subject = NULL;
- struct _camel_header_address *addrs = NULL;
- GString *from = g_string_new ("");
+ g_string_append_printf (buffer,
+ "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" "
+ "id=\"__evo-short-headers\" style=\"display: %s\">",
+ visible ? "block" : "none");
- header = ((CamelMimePart *) part)->headers;
- while (header) {
- if (!g_ascii_strcasecmp (header->name, "From")) {
- GString *tmp;
- if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) {
- header = header->next;
- continue;
- }
- tmp = g_string_new ("");
- efh_format_address (efh, tmp, addrs, header->name);
-
- if (tmp->len)
- g_string_printf (from, _("From: %s"), tmp->str);
- g_string_free (tmp, TRUE);
- } else if (!g_ascii_strcasecmp (header->name, "Subject")) {
- gchar *buf = NULL;
- buf = camel_header_unfold (header->value);
- g_free (subject);
- subject = camel_header_decode_string (buf, hdr_charset);
- g_free (buf);
+ header = ((CamelMimePart *) part)->headers;
+ while (header) {
+ if (!g_ascii_strcasecmp (header->name, "From")) {
+ GString *tmp;
+ if (!(addrs = camel_header_address_decode (header->value, hdr_charset))) {
+ header = header->next;
+ continue;
}
- header = header->next;
+ tmp = g_string_new ("");
+ efh_format_address (efh, tmp, addrs, header->name, FALSE);
+
+ if (tmp->len)
+ g_string_printf (from, _("From: %s"), tmp->str);
+ g_string_free (tmp, TRUE);
+
+ } else if (!g_ascii_strcasecmp (header->name, "Subject")) {
+ gchar *buf = NULL;
+ subject = camel_header_unfold (header->value);
+ buf = camel_header_decode_string (subject, hdr_charset);
+ g_free (subject);
+ subject = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRESERVE_8BIT, 0);
+ g_free (buf);
}
+ header = header->next;
+ }
+ is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
+ if (is_rtl) {
g_string_append_printf (
buffer,
- "<tr>"
- "<td width=\"20\" valign=\"top\">"
- "<a href=\"##HEADERS##\">"
- "<img src=\"%s/plus.png\">"
- "</a></td>"
- "<td><strong>%s</strong> %s%s%s</td>"
- "</tr>",
- evolution_imagesdir,
- subject ? subject : _("(no subject)"),
- from->len ? "(" : "",
- from->str,
- from->len ? ")" : "");
-
- g_free (subject);
- if (addrs)
- camel_header_address_list_clear (&addrs);
- g_string_free (from, TRUE);
+ "<tr><td width=\"100%%\" align=\"right\">%s%s%s <strong>%s</strong></td></tr>",
+ from->len ? "(" : "", from->str, from->len ? ")" : "",
+ subject ? subject : _("(no subject)"));
+ } else {
+ g_string_append_printf (
+ buffer,
+ "<tr><td><strong>%s</strong> %s%s%s</td></tr>",
+ subject ? subject : _("(no subject)"),
+ from->len ? "(" : "", from->str, from->len ? ")" : "");
+ }
+
+ g_string_append (buffer, "</table>");
+
+ g_free (subject);
+ if (addrs)
+ camel_header_address_list_clear (&addrs);
- g_string_append (buffer, "</table>");
+ g_string_free (from, TRUE);
+ g_free (evolution_imagesdir);
+}
- g_free (evolution_imagesdir);
+static void
+efh_format_full_headers (EMFormatHTML *efh,
+ GString *buffer,
+ CamelMedium *part,
+ gboolean all_headers,
+ gboolean visible,
+ GCancellable *cancellable)
+{
+ EMFormat *emf = EM_FORMAT (efh);
+ const gchar *charset;
+ CamelContentType *ct;
+ struct _camel_header_raw *header;
+ gboolean have_icon = FALSE;
+ const gchar *photo_name = NULL;
+ CamelInternetAddress *cia = NULL;
+ gboolean face_decoded = FALSE, contact_has_photo = FALSE;
+ guchar *face_header_value = NULL;
+ gsize face_header_len = 0;
+ gchar *header_sender = NULL, *header_from = NULL, *name;
+ gboolean mail_from_delegate = FALSE;
+ const gchar *hdr_charset;
+ gchar *evolution_imagesdir;
+ if (cancellable && g_cancellable_is_cancelled (cancellable))
return;
- }
+
+ ct = camel_mime_part_get_content_type ((CamelMimePart *) part);
+ charset = camel_content_type_param (ct, "charset");
+ charset = camel_iconv_charset_name (charset);
+ hdr_charset = em_format_get_charset (emf) ?
+ em_format_get_charset (emf) : em_format_get_default_charset (emf);
+
+ evolution_imagesdir = g_filename_to_uri (EVOLUTION_IMAGESDIR, NULL, NULL);
+
+ g_string_append_printf (buffer,
+ "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" "
+ "id=\"__evo-full-headers\" style=\"display: %s\" width=\"100%%\">",
+ visible ? "block" : "none");
header = ((CamelMimePart *) part)->headers;
while (header) {
@@ -3054,7 +2666,7 @@ efh_format_headers (EMFormatHTML *efh,
break;
html = g_string_new("");
- name = efh_format_address (efh, html, addrs, header->name);
+ name = efh_format_address (efh, html, addrs, header->name, FALSE);
header_sender = html->str;
camel_header_address_list_clear (&addrs);
@@ -3069,7 +2681,7 @@ efh_format_headers (EMFormatHTML *efh,
break;
html = g_string_new("");
- name = efh_format_address (efh, html, addrs, header->name);
+ name = efh_format_address (efh, html, addrs, header->name, FALSE);
header_from = html->str;
camel_header_address_list_clear (&addrs);
@@ -3113,50 +2725,15 @@ efh_format_headers (EMFormatHTML *efh,
g_free (header_sender);
g_free (header_from);
- if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) {
- if (efh->priv->headers_collapsable)
- g_string_append_printf (
- buffer,
- "<tr>"
- "<td valign=\"top\" width=\"20\">"
- "<a href=\"##HEADERS##\">"
- "<img src=\"%s/minus.png\">"
- "</a></td>"
- "<td><table width=\"100%%\" border=0 "
- "cellpadding=\"0\">\n",
- evolution_imagesdir);
- else
- g_string_append (
- buffer,
- "<tr><td>"
- "<table width=\"100%%\" border=0 "
- "cellpadding=\"0\">\n");
-
- } else {
- if (efh->priv->headers_collapsable)
- g_string_append_printf (
- buffer,
- "<tr>"
- "<td valign=\"top\" width=\"20\">"
- "<a href=\"##HEADERS##\">"
- "<img src=\"%s/minus.png\">"
- "</a></td>"
- "<td><table border=0 cellpadding=\"0\">\n",
- evolution_imagesdir);
- else
- g_string_append (
- buffer,
- "<tr><td>"
- "<table border=0 cellpadding=\"0\">\n");
- }
+ g_string_append (buffer, "<tr><td><table border=0 cellpadding=\"0\">\n");
g_free (evolution_imagesdir);
/* dump selected headers */
- if (emf->mode == EM_FORMAT_MODE_ALLHEADERS) {
+ if (all_headers) {
header = ((CamelMimePart *) part)->headers;
while (header) {
- efh_format_header (
+ em_format_html_format_header (
emf, buffer, part, header,
EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset);
header = header->next;
@@ -3205,7 +2782,7 @@ efh_format_headers (EMFormatHTML *efh,
xmailer.value = use_header->value;
mailer_shown = TRUE;
- efh_format_header (
+ em_format_html_format_header (
emf, buffer, part,
&xmailer, h->flags, charset);
if (strstr(use_header->value, "Evolution"))
@@ -3226,7 +2803,7 @@ efh_format_headers (EMFormatHTML *efh,
face_decoded = TRUE;
/* Showing an encoded "Face" header makes little sense */
} else if (!g_ascii_strcasecmp (header->name, h->name) && !face) {
- efh_format_header (
+ em_format_html_format_header (
emf, buffer, part,
header, h->flags, charset);
}
@@ -3238,214 +2815,162 @@ efh_format_headers (EMFormatHTML *efh,
}
}
- if (!efh->simple_headers) {
- g_string_append (buffer, "</table></td>");
-
- if (photo_name) {
- gchar *classid;
- CamelMimePart *photopart;
- gboolean only_local_photo;
-
- cia = camel_internet_address_new ();
- camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name);
- only_local_photo = em_format_html_get_only_local_photos (efh);
- photopart = em_utils_contact_photo (cia, only_local_photo);
-
- if (photopart) {
- contact_has_photo = TRUE;
- classid = g_strdup_printf (
- "icon:///em-format-html/%s/photo/header",
- emf->part_id->str);
- g_string_append_printf (
- buffer,
- "<td align=\"right\" valign=\"top\">"
- "<img width=64 src=\"%s\"></td>",
- classid);
- em_format_add_puri (emf, sizeof (EMFormatPURI), classid,
- photopart, efh_write_image);
- g_object_unref (photopart);
-
- g_free (classid);
- }
- g_object_unref (cia);
- }
+ g_string_append (buffer, "</table></td>");
- if (!contact_has_photo && face_decoded) {
- gchar *classid;
- CamelMimePart *part;
-
- part = camel_mime_part_new ();
- camel_mime_part_set_content (
- (CamelMimePart *) part,
- (const gchar *) face_header_value,
- face_header_len, "image/png");
- classid = g_strdup_printf (
- "icon:///em-format-html/face/photo/header");
- g_string_append_printf (
- buffer,
- "<td align=\"right\" valign=\"top\">"
- "<img width=48 src=\"%s\"></td>",
- classid);
- em_format_add_puri (
- emf, sizeof (EMFormatPURI),
- classid, part, efh_write_image);
- g_object_unref (part);
- }
+ if (photo_name) {
+ const gchar *classid;
+ CamelMimePart *photopart;
+ gboolean only_local_photo;
- if (have_icon && efh->show_icon) {
- GtkIconInfo *icon_info;
- gchar *classid;
- CamelMimePart *iconpart = NULL;
+ cia = camel_internet_address_new ();
+ camel_address_decode ((CamelAddress *) cia, (const gchar *) photo_name);
+ only_local_photo = em_format_html_get_only_local_photos (efh);
+ photopart = em_utils_contact_photo (cia, only_local_photo);
- classid = g_strdup_printf (
- "icon:///em-format-html/%s/icon/header",
- emf->part_id->str);
+ if (photopart) {
+ EMFormatPURI *puri;
+ contact_has_photo = TRUE;
+ classid = "icon:///em-format-html/headers/photo";
g_string_append_printf (
buffer,
"<td align=\"right\" valign=\"top\">"
- "<img width=16 height=16 src=\"%s\"></td>",
+ "<img width=64 src=\"%s\"></td>",
classid);
-
- icon_info = gtk_icon_theme_lookup_icon (
- gtk_icon_theme_get_default (),
- "evolution", 16, GTK_ICON_LOOKUP_NO_SVG);
- if (icon_info != NULL) {
- iconpart = em_format_html_file_part (
- (EMFormatHTML *) emf, "image/png",
- gtk_icon_info_get_filename (icon_info),
- cancellable);
- gtk_icon_info_free (icon_info);
- }
-
- if (iconpart) {
- em_format_add_puri (
- emf, sizeof (EMFormatPURI),
- classid, iconpart, efh_write_image);
- g_object_unref (iconpart);
- }
- g_free (classid);
+ puri = em_format_puri_new (
+ emf, sizeof (EMFormatPURI), photopart, classid);
+ puri->write_func = efh_write_image;
+ em_format_add_puri (emf, puri);
+ g_object_unref (photopart);
}
-
- g_string_append (buffer, "</tr></table>\n</font>\n");
+ g_object_unref (cia);
}
-}
-
-static void
-efh_format_message (EMFormat *emf,
- CamelStream *stream,
- CamelMimePart *part,
- const EMFormatHandler *info,
- GCancellable *cancellable,
- gboolean is_fallback)
-{
- const EMFormatHandler *handle;
- GString *buffer;
-
- /* TODO: make this validity stuff a method */
- EMFormatHTML *efh = (EMFormatHTML *) emf;
- CamelCipherValidity *save = emf->valid, *save_parent = emf->valid_parent;
-
- emf->valid = NULL;
- emf->valid_parent = NULL;
-
- buffer = g_string_sized_new (1024);
- if (emf->message != (CamelMimeMessage *) part)
- g_string_append (buffer, "<blockquote>\n");
-
- if (!efh->hide_headers)
- efh_format_headers (
- efh, buffer, CAMEL_MEDIUM (part), cancellable);
-
- camel_stream_write (
- stream, buffer->str, buffer->len, cancellable, NULL);
-
- g_string_free (buffer, TRUE);
+ if (!contact_has_photo && face_decoded) {
+ const gchar *classid;
+ CamelMimePart *part;
+ EMFormatPURI *puri;
+
+ part = camel_mime_part_new ();
+ camel_mime_part_set_content (
+ (CamelMimePart *) part,
+ (const gchar *) face_header_value,
+ face_header_len, "image/png");
+ classid = "icon:///em-format-html/headers/face/photo";
+ g_string_append_printf (
+ buffer,
+ "<td align=\"right\" valign=\"top\">"
+ "<img width=48 src=\"%s\"></td>",
+ classid);
- handle = em_format_find_handler(emf, "x-evolution/message/post-header");
- if (handle)
- handle->handler (
- emf, stream, part, handle, cancellable, FALSE);
+ puri = em_format_puri_new (
+ emf, sizeof (EMFormatPURI), part, classid);
+ puri->write_func = efh_write_image;
+ em_format_add_puri (emf, puri);
- camel_stream_write_string (
- stream, EM_FORMAT_HTML_VPAD, cancellable, NULL);
- em_format_part (emf, stream, part, cancellable);
+ g_object_unref (part);
+ g_free (face_header_value);
+ }
- if (emf->message != (CamelMimeMessage *) part)
- camel_stream_write_string (
- stream, "</blockquote>\n", cancellable, NULL);
+ if (have_icon && efh->show_icon) {
+ GtkIconInfo *icon_info;
+ const gchar *classid;
+ CamelMimePart *iconpart = NULL;
+ EMFormatPURI *puri;
- camel_cipher_validity_free (emf->valid);
+ classid = "icon:///em-format-html/header/icon";
+ g_string_append_printf (
+ buffer,
+ "<td align=\"right\" valign=\"top\">"
+ "<img width=16 height=16 src=\"%s\"></td>",
+ classid);
+ icon_info = gtk_icon_theme_lookup_icon (
+ gtk_icon_theme_get_default (),
+ "evolution", 16, GTK_ICON_LOOKUP_NO_SVG);
+ if (icon_info != NULL) {
+ iconpart = em_format_html_file_part (
+ (EMFormatHTML *) emf, "image/png",
+ gtk_icon_info_get_filename (icon_info),
+ cancellable);
+ gtk_icon_info_free (icon_info);
+ }
+ if (iconpart) {
+ puri = em_format_puri_new (
+ emf, sizeof (EMFormatPURI), iconpart, classid);
+ puri->write_func = efh_write_image;
+ em_format_add_puri (emf, puri);
+ g_object_unref (iconpart);
+ }
+ }
- emf->valid = save;
- emf->valid_parent = save_parent;
+ g_string_append (buffer, "</tr></table>");
}
-void
-em_format_html_format_cert_infos (GQueue *cert_infos,
- GString *output_buffer)
+gboolean
+em_format_html_can_load_images (EMFormatHTML *efh)
{
- GQueue valid = G_QUEUE_INIT;
- GList *head, *link;
+ g_return_val_if_fail (EM_IS_FORMAT_HTML (efh), FALSE);
- g_return_if_fail (cert_infos != NULL);
- g_return_if_fail (output_buffer != NULL);
+ return ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) ||
+ ((efh->priv->image_loading_policy == E_MAIL_IMAGE_LOADING_POLICY_SOMETIMES) &&
+ efh->priv->can_load_images));
+}
- head = g_queue_peek_head_link (cert_infos);
+void
+em_format_html_animation_extract_frame (const GByteArray *anim,
+ gchar **frame,
+ gsize *len)
+{
+ GdkPixbufLoader *loader;
+ GdkPixbufAnimation *animation;
+ GdkPixbuf *frame_buf;
+
+ /* GIF89a (GIF image signature) */
+ const gchar GIF_HEADER[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
+ const gint GIF_HEADER_LEN = sizeof (GIF_HEADER);
+
+ /* NETSCAPE2.0 (extension describing animated GIF, starts on 0x310) */
+ const gchar GIF_APPEXT[] = { 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41,
+ 0x50, 0x45, 0x32, 0x2E, 0x30 };
+ const gint GIF_APPEXT_LEN = sizeof (GIF_APPEXT);
+
+ /* Check if the image is an animated GIF. We don't care about any
+ * other animated formats (APNG or MNG) as WebKit does not support them
+ * and displays only the first frame. */
+ if ((anim->len < 0x331)
+ || (memcmp (anim->data, GIF_HEADER, GIF_HEADER_LEN) != 0)
+ || (memcmp (&anim->data[0x310], GIF_APPEXT, GIF_APPEXT_LEN) != 0)) {
+
+ *frame = g_memdup (anim->data, anim->len);
+ *len = anim->len;
+ return;
+ }
- /* Make sure we have a valid CamelCipherCertInfo before
- * appending anything to the output buffer, so we don't
- * end up with "()". */
- for (link = head; link != NULL; link = g_list_next (link)) {
- CamelCipherCertInfo *cinfo = link->data;
+ loader = gdk_pixbuf_loader_new ();
+ gdk_pixbuf_loader_write (loader, (guchar *) anim->data, anim->len, NULL);
+ gdk_pixbuf_loader_close (loader, NULL);
+ animation = gdk_pixbuf_loader_get_animation (loader);
+ if (!animation) {
- if ((cinfo->name != NULL && *cinfo->name != '\0') ||
- (cinfo->email != NULL && *cinfo->email != '\0'))
- g_queue_push_tail (&valid, cinfo);
+ *frame = g_memdup (anim->data, anim->len);
+ *len = anim->len;
+ g_object_unref (loader);
+ return;
}
- if (g_queue_is_empty (&valid))
+ /* Extract first frame */
+ frame_buf = gdk_pixbuf_animation_get_static_image (animation);
+ if (!frame_buf) {
+ *frame = g_memdup (anim->data, anim->len);
+ *len = anim->len;
+ g_object_unref (loader);
+ g_object_unref (animation);
return;
-
- g_string_append (output_buffer, " (");
-
- while (!g_queue_is_empty (&valid)) {
- CamelCipherCertInfo *cinfo;
-
- cinfo = g_queue_pop_head (&valid);
-
- if (cinfo->name != NULL && *cinfo->name != '\0') {
- g_string_append (output_buffer, cinfo->name);
-
- if (cinfo->email != NULL && *cinfo->email != '\0') {
- g_string_append (output_buffer, " &lt;");
- g_string_append (output_buffer, cinfo->email);
- g_string_append (output_buffer, "&gt;");
- }
-
- } else if (cinfo->email != NULL && *cinfo->email != '\0') {
- g_string_append (output_buffer, cinfo->email);
- }
-
- if (!g_queue_is_empty (&valid))
- g_string_append (output_buffer, ", ");
}
- g_string_append_c (output_buffer, ')');
-}
+ /* Unforunatelly, GdkPixbuf cannot save to GIF, but WebKit does not
+ * have any trouble displaying PNG image despite the part having
+ * image/gif mime-type */
+ gdk_pixbuf_save_to_buffer (frame_buf, frame, len, "png", NULL, NULL);
-/* unref returned pointer with g_object_unref(), if not NULL */
-CamelStream *
-em_format_html_get_cached_image (EMFormatHTML *efh,
- const gchar *image_uri)
-{
- g_return_val_if_fail (efh != NULL, NULL);
- g_return_val_if_fail (image_uri != NULL, NULL);
-
- if (!emfh_http_cache)
- return NULL;
-
- return camel_data_cache_get (
- emfh_http_cache, EMFH_HTTP_CACHE_PATH, image_uri, NULL);
+ g_object_unref (loader);
}
-
diff --git a/mail/em-format-html.h b/mail/em-format-html.h
index bc6a171255..9749c37f31 100644
--- a/mail/em-format-html.h
+++ b/mail/em-format-html.h
@@ -30,7 +30,6 @@
#include <em-format/em-format.h>
#include <misc/e-web-view.h>
-#include <gtkhtml/gtkhtml-embedded.h>
#include <libemail-engine/e-mail-enums.h>
/* Standard GObject macros */
@@ -57,6 +56,7 @@ G_BEGIN_DECLS
typedef struct _EMFormatHTML EMFormatHTML;
typedef struct _EMFormatHTMLClass EMFormatHTMLClass;
typedef struct _EMFormatHTMLPrivate EMFormatHTMLPrivate;
+typedef struct _EMFormatWidgetPURI EMFormatWidgetPURI;
enum _em_format_html_header_flags {
EM_FORMAT_HTML_HEADER_TO = 1 << 0,
@@ -65,16 +65,6 @@ enum _em_format_html_header_flags {
};
typedef enum {
- EM_FORMAT_HTML_STATE_NONE = 0,
- EM_FORMAT_HTML_STATE_RENDERING
-} EMFormatHTMLState;
-
-typedef enum {
- EM_FORMAT_HTML_HEADERS_STATE_EXPANDED = 0, /* Default value */
- EM_FORMAT_HTML_HEADERS_STATE_COLLAPSED
-} EMFormatHTMLHeadersState;
-
-typedef enum {
EM_FORMAT_HTML_COLOR_BODY, /* header area background */
EM_FORMAT_HTML_COLOR_CITATION, /* citation font color */
EM_FORMAT_HTML_COLOR_CONTENT, /* message area background */
@@ -84,94 +74,13 @@ typedef enum {
EM_FORMAT_HTML_NUM_COLOR_TYPES
} EMFormatHTMLColorType;
-/* A HTMLJob will be executed in another thread, in sequence.
- * It's job is to write to its stream, close it if successful,
- * then exit. */
-
-typedef struct _EMFormatHTMLJob EMFormatHTMLJob;
-
-typedef void (*EMFormatHTMLJobCallback) (EMFormatHTMLJob *job,
- GCancellable *cancellable);
-
-/**
- * struct _EMFormatHTMLJob - A formatting job.
- *
- * @format: Set by allocation function.
- * @stream: Free for use by caller.
- * @puri_level: Set by allocation function.
- * @base: Set by allocation function, used to save state.
- * @callback: This callback will always be invoked, only once, even if the user
- * cancelled the display. So the callback should free any extra data
- * it allocated every time it is called.
- * @u: Union data, free for caller to use.
- *
- * This object is used to queue a long-running-task which cannot be
- * processed in the primary thread. When its turn comes, the job will
- * be de-queued and the @callback invoked to perform its processing,
- * restoring various state to match the original state. This is used
- * for image loading and other internal tasks.
- *
- * This object is struct-subclassable. Only em_format_html_job_new()
- * may be used to allocate these.
- **/
-struct _EMFormatHTMLJob {
- EMFormatHTML *format;
- CamelStream *stream;
-
- /* We need to track the state of the visibility tree at
- * the point this uri was generated */
- GNode *puri_level;
- CamelURL *base;
-
- EMFormatHTMLJobCallback callback;
- union {
- gchar *uri;
- CamelMedium *msg;
- EMFormatPURI *puri;
- GNode *puri_level;
- gpointer data;
- } u;
-};
-
-/* Pending object (classid: url) */
-typedef struct _EMFormatHTMLPObject EMFormatHTMLPObject;
-
-typedef gboolean
- (*EMFormatHTMLPObjectFunc) (EMFormatHTML *md,
- GtkHTMLEmbedded *eb,
- EMFormatHTMLPObject *pobject);
-
-/**
- * struct _EMFormatHTMLPObject - Pending object.
- *
- * @free: Invoked when the object is no longer needed.
- * @format: The parent formatter.
- * @classid: The assigned class id as passed to add_pobject().
- * @func: Callback function.
- * @part: The part as passed to add_pobject().
- *
- * This structure is used to track OBJECT tags which have been
- * inserted into the HTML stream. When GtkHTML requests them the
- * @func will be invoked to create the embedded widget.
- *
- * This object is struct-subclassable. Only
- * em_format_html_add_pobject() may be used to allocate these.
- **/
-struct _EMFormatHTMLPObject {
- void (*free)(EMFormatHTMLPObject *);
- EMFormatHTML *format;
-
- gchar *classid;
-
- EMFormatHTMLPObjectFunc func;
- CamelMimePart *part;
-};
-
#define EM_FORMAT_HTML_HEADER_NOCOLUMNS (EM_FORMAT_HEADER_LAST)
/* header already in html format */
#define EM_FORMAT_HTML_HEADER_HTML (EM_FORMAT_HEADER_LAST<<1)
#define EM_FORMAT_HTML_HEADER_NODEC (EM_FORMAT_HEADER_LAST<<2)
+#define EM_FORMAT_HTML_HEADER_NOLINKS (EM_FORMAT_HEADER_LAST<<3)
+#define EM_FORMAT_HTML_HEADER_HIDDEN (EM_FORMAT_HEADER_LAST<<4)
#define EM_FORMAT_HTML_HEADER_LAST (EM_FORMAT_HEADER_LAST<<8)
@@ -197,14 +106,13 @@ struct _EMFormatHTMLPObject {
* @load_http:2:
* @load_http_now:1:
* @mark_citations:1:
- * @simple_headers:1:
* @hide_headers:1:
* @show_icon:1:
*
* Most of these fields are private or read-only.
*
* The base HTML formatter object. This object drives HTML generation
- * into a GtkHTML parser. It also handles text to HTML conversion,
+ * into a WebKit parser. It also handles text to HTML conversion,
* multipart/related objects and inline images.
**/
struct _EMFormatHTML {
@@ -216,12 +124,9 @@ struct _EMFormatHTML {
GSList *headers;
guint32 text_html_flags; /* default flags for text to html conversion */
- guint simple_headers:1; /* simple header format, no box/table */
guint hide_headers:1; /* no headers at all */
guint show_icon:1; /* show an icon when the sender used Evo */
guint32 header_wrap_flags;
-
- EMFormatHTMLState state; /* actual state of the object */
};
struct _EMFormatHTMLClass {
@@ -231,8 +136,6 @@ struct _EMFormatHTMLClass {
};
GType em_format_html_get_type (void);
-EWebView * em_format_html_get_web_view (EMFormatHTML *efh);
-void em_format_html_load_images (EMFormatHTML *efh);
void em_format_html_get_color (EMFormatHTML *efh,
EMFormatHTMLColorType type,
GdkColor *color);
@@ -260,65 +163,61 @@ gboolean em_format_html_get_show_sender_photo
void em_format_html_set_show_sender_photo
(EMFormatHTML *efh,
gboolean show_sender_photo);
-
-/* retrieves a pseudo-part icon wrapper for a file */
-CamelMimePart * em_format_html_file_part (EMFormatHTML *efh,
- const gchar *mime_type,
- const gchar *filename,
- GCancellable *cancellable);
-
-/* for implementers */
-EMFormatHTMLPObject *
- em_format_html_add_pobject (EMFormatHTML *efh,
- gsize size,
- const gchar *classid,
- CamelMimePart *part,
- EMFormatHTMLPObjectFunc func);
-EMFormatHTMLPObject *
- em_format_html_find_pobject (EMFormatHTML *efh,
- const gchar *classid);
-EMFormatHTMLPObject *
- em_format_html_find_pobject_func
- (EMFormatHTML *efh,
- CamelMimePart *part,
- EMFormatHTMLPObjectFunc func);
-void em_format_html_remove_pobject (EMFormatHTML *efh,
- EMFormatHTMLPObject *pobject);
-void em_format_html_clear_pobject (EMFormatHTML *efh);
-EMFormatHTMLJob *
- em_format_html_job_new (EMFormatHTML *efh,
- EMFormatHTMLJobCallback callback,
- gpointer data);
-void em_format_html_job_queue (EMFormatHTML *efh,
- EMFormatHTMLJob *job);
+gboolean em_format_html_get_animate_images
+ (EMFormatHTML *efh);
+void em_format_html_set_animate_images
+ (EMFormatHTML *efh,
+ gboolean animate_images);
void em_format_html_clone_sync (CamelFolder *folder,
const gchar *message_uid,
CamelMimeMessage *message,
EMFormatHTML *efh,
EMFormat *source);
-
gboolean em_format_html_get_show_real_date
(EMFormatHTML *efh);
void em_format_html_set_show_real_date
(EMFormatHTML *efh,
gboolean show_real_date);
-EMFormatHTMLHeadersState
- em_format_html_get_headers_state
- (EMFormatHTML *efh);
-void em_format_html_set_headers_state
- (EMFormatHTML *efh,
- EMFormatHTMLHeadersState state);
-gboolean em_format_html_get_headers_collapsable
- (EMFormatHTML *efh);
-void em_format_html_set_headers_collapsable
- (EMFormatHTML *efh,
- gboolean collapsable);
+
+/* retrieves a pseudo-part icon wrapper for a file */
+CamelMimePart * em_format_html_file_part (EMFormatHTML *efh,
+ const gchar *mime_type,
+ const gchar *filename,
+ GCancellable *cancellable);
+
void em_format_html_format_cert_infos
(GQueue *cert_infos,
GString *output_buffer);
-CamelStream * em_format_html_get_cached_image (EMFormatHTML *efh,
- const gchar *image_uri);
+void em_format_html_format_message (EMFormatHTML *efh,
+ CamelStream *stream,
+ GCancellable *cancellable);
+
+void em_format_html_format_message_part
+ (EMFormatHTML *efh,
+ const gchar *part_id,
+ CamelStream *stream,
+ GCancellable *cancellable);
+
+void em_format_html_format_headers (EMFormatHTML *efh,
+ CamelStream *stream,
+ CamelMedium *part,
+ gboolean all_headers,
+ GCancellable *cancellable);
+void em_format_html_format_header (EMFormat *emf,
+ GString *buffer,
+ CamelMedium *part,
+ struct _camel_header_raw *header,
+ guint32 flags,
+ const gchar *charset);
+
+gboolean em_format_html_can_load_images (EMFormatHTML *efh);
+
+void em_format_html_animation_extract_frame
+ (const GByteArray *anim,
+ gchar **frame,
+ gsize *len);
+
G_END_DECLS
#endif /* EM_FORMAT_HTML_H */
diff --git a/mail/em-html-stream.c b/mail/em-html-stream.c
deleted file mode 100644
index a633946541..0000000000
--- a/mail/em-html-stream.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *
- * 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:
- * Jeffrey Stedfast <fejj@ximian.com>
- * Michael Zucchi <notzed@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <gtk/gtk.h>
-#include <glib/gi18n-lib.h>
-#include "em-html-stream.h"
-
-#define d(x)
-
-G_DEFINE_TYPE (EMHTMLStream, em_html_stream, EM_TYPE_SYNC_STREAM)
-
-static void
-html_stream_cleanup (EMHTMLStream *emhs)
-{
- if (emhs->sync.cancel && emhs->html_stream)
- gtk_html_stream_close (
- emhs->html_stream, GTK_HTML_STREAM_ERROR);
-
- emhs->html_stream = NULL;
- emhs->sync.cancel = TRUE;
- g_signal_handler_disconnect (emhs->html, emhs->destroy_id);
- g_object_unref (emhs->html);
- emhs->html = NULL;
-}
-
-static void
-html_stream_gtkhtml_destroy (GtkHTML *html,
- EMHTMLStream *emhs)
-{
- emhs->sync.cancel = TRUE;
- html_stream_cleanup (emhs);
-}
-
-static void
-html_stream_dispose (GObject *object)
-{
- EMHTMLStream *emhs = EM_HTML_STREAM (object);
-
- if (emhs->html_stream) {
- /* set 'in finalize' flag */
- camel_stream_close (CAMEL_STREAM (emhs), NULL, NULL);
- }
-}
-
-static gssize
-html_stream_sync_write (CamelStream *stream,
- const gchar *buffer,
- gsize n,
- GError **error)
-{
- EMHTMLStream *emhs = EM_HTML_STREAM (stream);
-
- if (emhs->html == NULL) {
- g_set_error (
- error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("No HTML stream available"));
- return -1;
- }
-
- if (emhs->html_stream == NULL)
- emhs->html_stream = gtk_html_begin_full (
- emhs->html, NULL, NULL, emhs->flags);
-
- gtk_html_stream_write (emhs->html_stream, buffer, n);
-
- return (gssize) n;
-}
-
-static gint
-html_stream_sync_flush (CamelStream *stream,
- GError **error)
-{
- EMHTMLStream *emhs = (EMHTMLStream *) stream;
-
- if (emhs->html_stream == NULL) {
- g_set_error (
- error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("No HTML stream available"));
- return -1;
- }
-
- gtk_html_flush (emhs->html);
-
- return 0;
-}
-
-static gint
-html_stream_sync_close (CamelStream *stream,
- GError **error)
-{
- EMHTMLStream *emhs = (EMHTMLStream *) stream;
-
- if (emhs->html_stream == NULL) {
- g_set_error (
- error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("No HTML stream available"));
- return -1;
- }
-
- gtk_html_stream_close (emhs->html_stream, GTK_HTML_STREAM_OK);
- html_stream_cleanup (emhs);
-
- return 0;
-}
-
-static void
-em_html_stream_class_init (EMHTMLStreamClass *class)
-{
- GObjectClass *object_class;
- EMSyncStreamClass *sync_stream_class;
-
- object_class = G_OBJECT_CLASS (class);
- object_class->dispose = html_stream_dispose;
-
- sync_stream_class = EM_SYNC_STREAM_CLASS (class);
- sync_stream_class->sync_write = html_stream_sync_write;
- sync_stream_class->sync_flush = html_stream_sync_flush;
- sync_stream_class->sync_close = html_stream_sync_close;
-}
-
-static void
-em_html_stream_init (EMHTMLStream *emhs)
-{
-}
-
-/* TODO: Could pass NULL for html_stream, and do a gtk_html_begin
- * on first data -> less flashing */
-CamelStream *
-em_html_stream_new (GtkHTML *html,
- GtkHTMLStream *html_stream)
-{
- EMHTMLStream *new;
-
- g_return_val_if_fail (GTK_IS_HTML (html), NULL);
-
- new = g_object_new (EM_TYPE_HTML_STREAM, NULL);
- new->html_stream = html_stream;
- new->html = g_object_ref (html);
- new->flags = 0;
- new->destroy_id = g_signal_connect (
- html, "destroy",
- G_CALLBACK (html_stream_gtkhtml_destroy), new);
-
- em_sync_stream_set_buffer_size (&new->sync, 8192);
-
- return CAMEL_STREAM (new);
-}
-
-void
-em_html_stream_set_flags (EMHTMLStream *emhs,
- GtkHTMLBeginFlags flags)
-{
- g_return_if_fail (EM_IS_HTML_STREAM (emhs));
-
- emhs->flags = flags;
-}
diff --git a/mail/em-html-stream.h b/mail/em-html-stream.h
deleted file mode 100644
index 24d32f76d8..0000000000
--- a/mail/em-html-stream.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- *
- * 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:
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef EM_HTML_STREAM_H
-#define EM_HTML_STREAM_H
-
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-stream.h>
-#include <mail/em-sync-stream.h>
-
-/* Standard GObject macros */
-#define EM_TYPE_HTML_STREAM \
- (em_html_stream_get_type ())
-#define EM_HTML_STREAM(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), EM_TYPE_HTML_STREAM, EMHTMLStream))
-#define EM_HTML_STREAM_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), EM_TYPE_HTML_STREAM, EMHTMLStreamClass))
-#define EM_IS_HTML_STREAM(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), EM_TYPE_HTML_STREAM))
-#define EM_IS_HTML_STREAM_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), EM_TYPE_HTML_STREAM))
-#define EM_HTML_STREAM_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), EM_TYPE_HTML_STREAM, EMHTMLStreamClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EMHTMLStream EMHTMLStream;
-typedef struct _EMHTMLStreamClass EMHTMLStreamClass;
-
-struct _EMHTMLStream {
- EMSyncStream sync;
-
- guint destroy_id;
- GtkHTML *html;
- GtkHTMLStream *html_stream;
- GtkHTMLBeginFlags flags;
-};
-
-struct _EMHTMLStreamClass {
- EMSyncStreamClass parent_class;
-
-};
-
-GType em_html_stream_get_type (void);
-CamelStream * em_html_stream_new (GtkHTML *html,
- GtkHTMLStream *html_stream);
-void em_html_stream_set_flags (EMHTMLStream *emhs,
- GtkHTMLBeginFlags flags);
-
-G_END_DECLS
-
-#endif /* EM_HTML_STREAM_H */
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 4d74b80a13..a9b57125a2 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -72,9 +72,11 @@
#include "e-mail-tag-editor.h"
#include "em-composer-utils.h"
-#include "em-format-quote.h"
+#include "em-format-html-display.h"
#include "em-format-html-print.h"
#include "em-utils.h"
+#include "e-mail-printer.h"
+#include "em-format/em-format-quote.h"
/* XXX This is a dirty hack on a dirty hack. We really need
* to rework or get rid of the functions that use this. */
@@ -380,7 +382,7 @@ em_utils_flag_for_followup (EMailReader *reader,
EMailBackend *backend;
EShellSettings *shell_settings;
EShellBackend *shell_backend;
- EMFormatHTML *formatter;
+ EMailDisplay *display;
GtkWidget *editor;
GtkWindow *window;
CamelTag *tags;
@@ -470,8 +472,8 @@ em_utils_flag_for_followup (EMailReader *reader,
camel_folder_thaw (folder);
camel_tag_list_free (&tags);
- formatter = e_mail_reader_get_formatter (reader);
- em_format_queue_redraw (EM_FORMAT (formatter));
+ display = e_mail_reader_get_mail_display (reader);
+ e_mail_display_reload (display);
exit:
/* XXX We shouldn't be freeing this. */
@@ -616,26 +618,44 @@ em_utils_write_messages_to_stream (CamelFolder *folder,
return res;
}
+static void
+do_print_msg_to_file (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+
+ EMFormatHTML *efh = EM_FORMAT_HTML (source);
+ gchar *filename = user_data;
+
+ EMailPrinter *printer;
+
+ printer = e_mail_printer_new (efh);
+ e_mail_printer_set_export_filename (printer, filename);
+ g_signal_connect_swapped (printer, "done",
+ G_CALLBACK (g_object_unref), printer);
+
+ e_mail_printer_print (printer, TRUE, NULL);
+
+ g_object_unref (efh);
+}
+
static gboolean
em_utils_print_messages_to_file (CamelFolder *folder,
const gchar *uid,
const gchar *filename)
{
- EMFormatHTMLPrint *efhp;
+ EMFormatHTMLDisplay *efhd;
CamelMimeMessage *message;
message = camel_folder_get_message_sync (folder, uid, NULL, NULL);
if (message == NULL)
return FALSE;
- efhp = em_format_html_print_new (NULL, GTK_PRINT_OPERATION_ACTION_EXPORT);
- efhp->export_filename = g_strdup (filename);
- efhp->async = FALSE;
+ efhd = em_format_html_display_new ();
+ ((EMFormat *) efhd)->message_uid = g_strdup (uid);
- em_format_html_print_message (efhp, message, folder, uid);
-
- g_object_unref (efhp);
- g_object_unref (message);
+ em_format_parse_async ((EMFormat *) efhd, message, folder, NULL,
+ (GAsyncReadyCallback) do_print_msg_to_file, g_strdup (filename));
return TRUE;
}
@@ -1173,7 +1193,7 @@ em_utils_message_to_html (CamelMimeMessage *message,
camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (mem), buf);
emfq = em_format_quote_new (credits, mem, flags);
- ((EMFormat *) emfq)->composer = TRUE;
+ em_format_set_composer ((EMFormat *) emfq, TRUE);
if (!source) {
GSettings *settings;
@@ -1189,10 +1209,30 @@ em_utils_message_to_html (CamelMimeMessage *message,
}
/* FIXME Not passing a GCancellable here. */
- em_format_format_clone (
- EM_FORMAT (emfq), NULL, NULL, message, source, NULL);
- if (validity_found)
- *validity_found = ((EMFormat *)emfq)->validity_found;
+ em_format_parse (EM_FORMAT (emfq), message, NULL, NULL);
+
+ if (validity_found) {
+ GList *iter;
+ EMFormat *emf = (EMFormat *) emfq;
+
+ if (validity_found)
+ *validity_found = 0;
+
+ /* Return all found validities */
+ for (iter = emf->mail_part_list; iter; iter = iter->next) {
+
+ EMFormatPURI *puri = iter->data;
+ if (!puri)
+ continue;
+
+ if (*validity_found && puri->validity_type)
+ *validity_found |= puri->validity_type;
+ }
+
+ }
+
+ em_format_quote_write (emfq, mem, NULL);
+
g_object_unref (emfq);
if (append && *append)
diff --git a/mail/mail.error.xml b/mail/mail.error.xml
index 2a516d9bfa..b5a714ff18 100644
--- a/mail/mail.error.xml
+++ b/mail/mail.error.xml
@@ -520,5 +520,10 @@ An mbox account will be created to preserve the old mbox folders. You can delete
<_secondary xml:space="preserve">The attachment named {0} is a hidden file and may contain sensitive data. Please review it before sending.</_secondary>
</error>
+ <error id="printing-failed" type="error">
+ <_primary>Printing failed.</_primary>
+ <_secondary>The printer replied &quot;{0}&quot;.</_secondary>
+ </error>
+
</error-list>
diff --git a/modules/Makefile.am b/modules/Makefile.am
index ee3cdfdb1a..671215b952 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -35,6 +35,7 @@ SUBDIRS = \
plugin-manager \
spamassassin \
startup-wizard \
+ web-inspector \
$(MONO_DIR) \
$(PYTHON_DIR) \
$(ONLINE_ACCOUNTS_DIR) \
diff --git a/modules/addressbook/e-book-shell-content.c b/modules/addressbook/e-book-shell-content.c
index 0183512f5b..f3457e8999 100644
--- a/modules/addressbook/e-book-shell-content.c
+++ b/modules/addressbook/e-book-shell-content.c
@@ -286,20 +286,11 @@ book_shell_content_constructed (GObject *object)
EAB_CONTACT_DISPLAY (widget),
EAB_CONTACT_DISPLAY_RENDER_NORMAL);
- eab_contact_display_set_orientation (
- EAB_CONTACT_DISPLAY (widget),
- priv->orientation);
-
eab_contact_display_set_show_maps (
EAB_CONTACT_DISPLAY (widget),
priv->preview_show_maps);
g_object_bind_property (
- object, "orientation",
- widget, "orientation",
- G_BINDING_SYNC_CREATE);
-
- g_object_bind_property (
object, "preview-show-maps",
widget, "show-maps",
G_BINDING_SYNC_CREATE);
diff --git a/modules/mail/e-mail-config-format-html.c b/modules/mail/e-mail-config-format-html.c
index 31ca88b744..dad5f9a518 100644
--- a/modules/mail/e-mail-config-format-html.c
+++ b/modules/mail/e-mail-config-format-html.c
@@ -76,6 +76,11 @@ mail_config_format_html_constructed (GObject *object)
extensible, "show-real-date",
G_BINDING_SYNC_CREATE);
+ g_object_bind_property (
+ shell_settings, "mail-show-animated-images",
+ extensible, "animate-images",
+ G_BINDING_SYNC_CREATE);
+
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (parent_class)->constructed (object);
}
diff --git a/modules/mail/e-mail-config-web-view.c b/modules/mail/e-mail-config-web-view.c
index 80f3f65c0f..813256efd1 100644
--- a/modules/mail/e-mail-config-web-view.c
+++ b/modules/mail/e-mail-config-web-view.c
@@ -151,12 +151,6 @@ mail_config_web_view_realize (GtkWidget *widget,
{
g_object_bind_property (
extension->shell_settings,
- "mail-show-animated-images",
- widget, "animate",
- G_BINDING_SYNC_CREATE);
-
- g_object_bind_property (
- extension->shell_settings,
"composer-inline-spelling",
widget, "inline-spelling",
G_BINDING_SYNC_CREATE);
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index 25902f34df..a30c0240de 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -835,37 +835,70 @@ e_mail_labels_get_filter_options (void)
return g_slist_reverse (list);
}
+static void
+message_parsed_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EMFormatHTML *formatter = EM_FORMAT_HTML (source_object);
+ GObject *preview = user_data;
+ EMailDisplay *display;
+
+ display = g_object_get_data (preview, "mbox-imp-display");
+ e_mail_display_set_formatter (display, formatter);
+ e_mail_display_load (display, EM_FORMAT (formatter)->uri_base);
+}
+
/* utility functions for mbox importer */
static void
mbox_create_preview_cb (GObject *preview,
GtkWidget **preview_widget)
{
- EMFormatHTMLDisplay *format;
- EWebView *web_view;
+ EMailDisplay *display;
g_return_if_fail (preview != NULL);
g_return_if_fail (preview_widget != NULL);
- format = em_format_html_display_new ();
- g_object_set_data_full (
- preview, "mbox-imp-formatter", format, g_object_unref);
- web_view = em_format_html_get_web_view (EM_FORMAT_HTML (format));
+ display = g_object_new (E_TYPE_MAIL_DISPLAY, NULL);
+ g_object_set_data_full (preview, "mbox-imp-display",
+ g_object_ref (display), g_object_unref);
- *preview_widget = GTK_WIDGET (web_view);
+ *preview_widget = GTK_WIDGET (display);
}
static void
mbox_fill_preview_cb (GObject *preview,
CamelMimeMessage *msg)
{
- EMFormatHTMLDisplay *format;
+ EMailDisplay *display;
+ EMFormat *formatter;
+ GHashTable *formatters;
+ SoupSession *session;
+ gchar *mail_uri;
g_return_if_fail (preview != NULL);
g_return_if_fail (msg != NULL);
- format = g_object_get_data (preview, "mbox-imp-formatter");
- g_return_if_fail (format != NULL);
+ display = g_object_get_data (preview, "mbox-imp-display");
+ g_return_if_fail (display != NULL);
+
+ session = webkit_get_default_session ();
+ formatters = g_object_get_data (G_OBJECT (session), "formatters");
+ if (!formatters) {
+ formatters = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, NULL);
+ g_object_set_data (G_OBJECT (session), "formatters", formatters);
+ }
+
+ mail_uri = em_format_build_mail_uri (NULL, msg->message_id, NULL, NULL);
+
+ formatter = EM_FORMAT (em_format_html_display_new ());
+ formatter->message_uid = g_strdup (msg->message_id);
+ formatter->uri_base = g_strdup (mail_uri);
+
+ /* Don't free the mail_uri!! */
+ g_hash_table_insert (formatters, mail_uri, formatter);
- /* FIXME Not passing a GCancellable here. */
- em_format_format (EM_FORMAT (format), NULL, NULL, msg, NULL);
+ em_format_parse_async (formatter, msg, NULL, NULL,
+ message_parsed_cb, preview);
}
diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c
index 5bb60e3784..1bf6d4e29a 100644
--- a/modules/mail/e-mail-shell-content.c
+++ b/modules/mail/e-mail-shell-content.c
@@ -307,8 +307,8 @@ mail_shell_content_get_backend (EMailReader *reader)
return e_mail_reader_get_backend (reader);
}
-static EMFormatHTML *
-mail_shell_content_get_formatter (EMailReader *reader)
+static EMailDisplay *
+mail_shell_content_get_mail_display (EMailReader *reader)
{
EMailShellContent *mail_shell_content;
@@ -317,8 +317,7 @@ mail_shell_content_get_formatter (EMailReader *reader)
/* Forward this to our internal EMailView, which
* also implements the EMailReader interface. */
reader = E_MAIL_READER (mail_shell_content->priv->mail_view);
-
- return e_mail_reader_get_formatter (reader);
+ return e_mail_reader_get_mail_display (reader);
}
static gboolean
@@ -464,7 +463,7 @@ e_mail_shell_content_reader_init (EMailReaderInterface *interface)
{
interface->get_action_group = mail_shell_content_get_action_group;
interface->get_backend = mail_shell_content_get_backend;
- interface->get_formatter = mail_shell_content_get_formatter;
+ interface->get_mail_display = mail_shell_content_get_mail_display;
interface->get_hide_deleted = mail_shell_content_get_hide_deleted;
interface->get_message_list = mail_shell_content_get_message_list;
interface->get_popup_menu = mail_shell_content_get_popup_menu;
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index b39d22d13d..5487920c74 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -891,14 +891,16 @@ action_mail_smart_backward_cb (GtkAction *action,
EMailShellContent *mail_shell_content;
EMailShellSidebar *mail_shell_sidebar;
EMFolderTree *folder_tree;
- EMFormatHTML *formatter;
EMailReader *reader;
EMailView *mail_view;
GtkWidget *message_list;
GtkToggleAction *toggle_action;
- EWebView *web_view;
+ GtkWidget *window;
+ GtkAdjustment *adj;
+ EMailDisplay *display;
gboolean caret_mode;
gboolean magic_spacebar;
+ gdouble value;
/* This implements the so-called "Magic Backspace". */
@@ -914,7 +916,7 @@ action_mail_smart_backward_cb (GtkAction *action,
folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
reader = E_MAIL_READER (mail_view);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
message_list = e_mail_reader_get_message_list (reader);
magic_spacebar = e_shell_settings_get_boolean (
@@ -923,32 +925,43 @@ action_mail_smart_backward_cb (GtkAction *action,
toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE));
caret_mode = gtk_toggle_action_get_active (toggle_action);
- web_view = em_format_html_get_web_view (formatter);
-
- if (e_web_view_scroll_backward (web_view))
+ window = gtk_widget_get_parent (GTK_WIDGET (display));
+ if (!GTK_IS_SCROLLED_WINDOW (window))
return;
- if (caret_mode || !magic_spacebar)
- return;
+ adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (window));
+ value = gtk_adjustment_get_value (adj);
+ if (value == 0) {
- /* XXX Are two separate calls really necessary? */
+ if (caret_mode || !magic_spacebar)
+ return;
- if (message_list_select (
- MESSAGE_LIST (message_list),
- MESSAGE_LIST_SELECT_PREVIOUS,
- 0, CAMEL_MESSAGE_SEEN))
- return;
+ /* XXX Are two separate calls really necessary? */
- if (message_list_select (
- MESSAGE_LIST (message_list),
- MESSAGE_LIST_SELECT_PREVIOUS |
- MESSAGE_LIST_SELECT_WRAP, 0,
- CAMEL_MESSAGE_SEEN))
- return;
+ if (message_list_select (
+ MESSAGE_LIST (message_list),
+ MESSAGE_LIST_SELECT_PREVIOUS,
+ 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ if (message_list_select (
+ MESSAGE_LIST (message_list),
+ MESSAGE_LIST_SELECT_PREVIOUS |
+ MESSAGE_LIST_SELECT_WRAP,
+ 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ em_folder_tree_select_next_path (folder_tree, TRUE);
- em_folder_tree_select_prev_path (folder_tree, TRUE);
+ gtk_widget_grab_focus (message_list);
- gtk_widget_grab_focus (message_list);
+ } else {
+
+ gtk_adjustment_set_value (adj,
+ value - gtk_adjustment_get_page_increment (adj));
+
+ return;
+ }
}
static void
@@ -962,14 +975,17 @@ action_mail_smart_forward_cb (GtkAction *action,
EMailShellContent *mail_shell_content;
EMailShellSidebar *mail_shell_sidebar;
EMFolderTree *folder_tree;
- EMFormatHTML *formatter;
EMailReader *reader;
EMailView *mail_view;
GtkWidget *message_list;
+ GtkWidget *window;
+ GtkAdjustment *adj;
GtkToggleAction *toggle_action;
- EWebView *web_view;
+ EMailDisplay *display;
gboolean caret_mode;
gboolean magic_spacebar;
+ gdouble value;
+ gdouble upper;
/* This implements the so-called "Magic Spacebar". */
@@ -985,7 +1001,7 @@ action_mail_smart_forward_cb (GtkAction *action,
folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar);
reader = E_MAIL_READER (mail_view);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
message_list = e_mail_reader_get_message_list (reader);
magic_spacebar = e_shell_settings_get_boolean (
@@ -994,32 +1010,44 @@ action_mail_smart_forward_cb (GtkAction *action,
toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE));
caret_mode = gtk_toggle_action_get_active (toggle_action);
- web_view = em_format_html_get_web_view (formatter);
-
- if (e_web_view_scroll_forward (web_view))
+ window = gtk_widget_get_parent (GTK_WIDGET (display));
+ if (!GTK_IS_SCROLLED_WINDOW (window))
return;
- if (caret_mode || !magic_spacebar)
- return;
+ adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (window));
+ value = gtk_adjustment_get_value (adj);
+ upper = gtk_adjustment_get_upper (adj);
+ if (value + gtk_adjustment_get_page_size (adj) >= upper) {
- /* XXX Are two separate calls really necessary? */
+ if (caret_mode || !magic_spacebar)
+ return;
- if (message_list_select (
- MESSAGE_LIST (message_list),
- MESSAGE_LIST_SELECT_NEXT,
- 0, CAMEL_MESSAGE_SEEN))
- return;
+ /* XXX Are two separate calls really necessary? */
- if (message_list_select (
- MESSAGE_LIST (message_list),
- MESSAGE_LIST_SELECT_NEXT |
- MESSAGE_LIST_SELECT_WRAP,
- 0, CAMEL_MESSAGE_SEEN))
- return;
+ if (message_list_select (
+ MESSAGE_LIST (message_list),
+ MESSAGE_LIST_SELECT_NEXT,
+ 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ if (message_list_select (
+ MESSAGE_LIST (message_list),
+ MESSAGE_LIST_SELECT_NEXT |
+ MESSAGE_LIST_SELECT_WRAP,
+ 0, CAMEL_MESSAGE_SEEN))
+ return;
+
+ em_folder_tree_select_next_path (folder_tree, TRUE);
- em_folder_tree_select_next_path (folder_tree, TRUE);
+ gtk_widget_grab_focus (message_list);
- gtk_widget_grab_focus (message_list);
+ } else {
+
+ gtk_adjustment_set_value (adj,
+ value + gtk_adjustment_get_page_increment (adj));
+
+ return;
+ }
}
static void
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index 8838f1a1ae..9a58f9d2b7 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -331,11 +331,10 @@ mail_shell_view_popup_event_cb (EMailShellView *mail_shell_view,
const gchar *uri)
{
EMailShellContent *mail_shell_content;
- EMFormatHTML *formatter;
+ EMailDisplay *display;
EShellView *shell_view;
EMailReader *reader;
EMailView *mail_view;
- EWebView *web_view;
GtkMenu *menu;
if (uri != NULL)
@@ -345,10 +344,9 @@ mail_shell_view_popup_event_cb (EMailShellView *mail_shell_view,
mail_view = e_mail_shell_content_get_mail_view (mail_shell_content);
reader = E_MAIL_READER (mail_view);
- formatter = e_mail_reader_get_formatter (reader);
- web_view = em_format_html_get_web_view (formatter);
+ display = e_mail_reader_get_mail_display (reader);
- if (e_web_view_get_cursor_image (web_view) != NULL)
+ if (e_web_view_get_cursor_image (E_WEB_VIEW (display)) != NULL)
return FALSE;
menu = e_mail_reader_get_popup_menu (reader);
@@ -368,76 +366,19 @@ mail_shell_view_popup_event_cb (EMailShellView *mail_shell_view,
}
static void
-mail_shell_view_scroll_cb (EMailShellView *mail_shell_view,
- GtkOrientation orientation,
- GtkScrollType scroll_type,
- gfloat position,
- GtkHTML *html)
-{
- EShell *shell;
- EShellView *shell_view;
- EShellWindow *shell_window;
- EShellSettings *shell_settings;
- EMailShellContent *mail_shell_content;
- EMailReader *reader;
- EMailView *mail_view;
- EWebView *web_view;
- GtkWidget *message_list;
- gboolean magic_spacebar;
-
- web_view = E_WEB_VIEW (html);
-
- if (html->binding_handled || e_web_view_get_caret_mode (web_view))
- return;
-
- if (orientation != GTK_ORIENTATION_VERTICAL)
- return;
-
- shell_view = E_SHELL_VIEW (mail_shell_view);
- shell_window = e_shell_view_get_shell_window (shell_view);
- shell = e_shell_window_get_shell (shell_window);
- shell_settings = e_shell_get_shell_settings (shell);
-
- magic_spacebar = e_shell_settings_get_boolean (
- shell_settings, "mail-magic-spacebar");
-
- if (!magic_spacebar)
- return;
-
- mail_shell_content = mail_shell_view->priv->mail_shell_content;
- mail_view = e_mail_shell_content_get_mail_view (mail_shell_content);
-
- reader = E_MAIL_READER (mail_view);
- message_list = e_mail_reader_get_message_list (reader);
-
- if (scroll_type == GTK_SCROLL_PAGE_FORWARD)
- message_list_select (
- MESSAGE_LIST (message_list),
- MESSAGE_LIST_SELECT_NEXT,
- 0, CAMEL_MESSAGE_SEEN);
- else
- message_list_select (
- MESSAGE_LIST (message_list),
- MESSAGE_LIST_SELECT_PREVIOUS,
- 0, CAMEL_MESSAGE_SEEN);
-}
-
-static void
mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
EMailReader *reader)
{
GtkWidget *message_list;
- EMFormatHTML *formatter;
- EWebView *web_view;
+ EMailDisplay *display;
EShellView *shell_view;
EShellTaskbar *shell_taskbar;
shell_view = E_SHELL_VIEW (mail_shell_view);
shell_taskbar = e_shell_view_get_shell_taskbar (shell_view);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
message_list = e_mail_reader_get_message_list (reader);
- web_view = em_format_html_get_web_view (formatter);
e_shell_view_update_actions (E_SHELL_VIEW (mail_shell_view));
e_mail_shell_view_update_sidebar (mail_shell_view);
@@ -464,23 +405,17 @@ mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view,
mail_shell_view, G_CONNECT_SWAPPED);
g_signal_connect_object (
- web_view, "key-press-event",
+ display, "key-press-event",
G_CALLBACK (mail_shell_view_key_press_event_cb),
mail_shell_view, G_CONNECT_SWAPPED);
g_signal_connect_object (
- web_view, "popup-event",
+ display, "popup-event",
G_CALLBACK (mail_shell_view_popup_event_cb),
mail_shell_view, G_CONNECT_SWAPPED);
g_signal_connect_object (
- web_view, "scroll",
- G_CALLBACK (mail_shell_view_scroll_cb),
- mail_shell_view,
- G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-
- g_signal_connect_object (
- web_view, "status-message",
+ display, "status-message",
G_CALLBACK (e_shell_taskbar_set_message),
shell_taskbar, G_CONNECT_SWAPPED);
}
@@ -634,7 +569,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
EShellTaskbar *shell_taskbar;
EShellWindow *shell_window;
EShellSearchbar *searchbar;
- EMFormatHTML *formatter;
EMFolderTree *folder_tree;
EActionComboBox *combo_box;
ERuleContext *context;
@@ -647,7 +581,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
EMailSession *session;
EMailReader *reader;
EMailView *mail_view;
- EWebView *web_view;
+ EMailDisplay *display;
const gchar *source;
guint merge_id;
gint ii = 0;
@@ -691,7 +625,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
reader = E_MAIL_READER (shell_content);
- formatter = e_mail_reader_get_formatter (reader);
+ display = e_mail_reader_get_mail_display (reader);
message_list = e_mail_reader_get_message_list (reader);
em_folder_tree_set_selectable_widget (folder_tree, message_list);
@@ -710,8 +644,6 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
G_CALLBACK (mail_shell_view_search_filter_changed_cb),
mail_shell_view, G_CONNECT_SWAPPED);
- web_view = em_format_html_get_web_view (formatter);
-
g_signal_connect_object (
folder_tree, "folder-selected",
G_CALLBACK (mail_shell_view_folder_tree_selected_cb),
@@ -784,23 +716,17 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
mail_shell_view, G_CONNECT_SWAPPED);
g_signal_connect_object (
- web_view, "key-press-event",
+ display, "key-press-event",
G_CALLBACK (mail_shell_view_key_press_event_cb),
mail_shell_view, G_CONNECT_SWAPPED);
g_signal_connect_object (
- web_view, "popup-event",
+ display, "popup-event",
G_CALLBACK (mail_shell_view_popup_event_cb),
mail_shell_view, G_CONNECT_SWAPPED);
g_signal_connect_object (
- web_view, "scroll",
- G_CALLBACK (mail_shell_view_scroll_cb),
- mail_shell_view,
- G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-
- g_signal_connect_object (
- web_view, "status-message",
+ display, "status-message",
G_CALLBACK (e_shell_taskbar_set_message),
shell_taskbar, G_CONNECT_SWAPPED);
diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c
index 3f5e371296..d81656f997 100644
--- a/modules/mail/em-mailer-prefs.c
+++ b/modules/mail/em-mailer-prefs.c
@@ -529,10 +529,10 @@ toggle_button_init (EMMailerPrefs *prefs,
const gchar *key,
GCallback toggled)
{
- gboolean bool;
+ gboolean v_bool;
- bool = g_settings_get_boolean (prefs->settings, key);
- gtk_toggle_button_set_active (toggle, not ? !bool : bool);
+ v_bool = g_settings_get_boolean (prefs->settings, key);
+ gtk_toggle_button_set_active (toggle, not ? !v_bool : v_bool);
if (toggled) {
g_object_set_data ((GObject *) toggle, "key", (gpointer) key);
diff --git a/modules/web-inspector/Makefile.am b/modules/web-inspector/Makefile.am
new file mode 100644
index 0000000000..f8a1fc9340
--- /dev/null
+++ b/modules/web-inspector/Makefile.am
@@ -0,0 +1,23 @@
+module_LTLIBRARIES = libevolution-module-web-inspector.la
+
+libevolution_module_web_inspector_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/widgets \
+ -DG_LOG_DOMAIN=\"evolution-web-inspector\" \
+ $(EVOLUTION_DATA_SERVER_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS)
+
+libevolution_module_web_inspector_la_SOURCES = \
+ evolution-web-inspector.c
+
+libevolution_module_web_inspector_la_LIBADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/widgets/misc/libemiscwidgets.la \
+ $(EVOLUTION_DATA_SERVER_LIBS) \
+ $(GNOME_PLATFORM_LIBS)
+
+libevolution_module_web_inspector_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/web-inspector/evolution-web-inspector.c b/modules/web-inspector/evolution-web-inspector.c
new file mode 100644
index 0000000000..27a21e0693
--- /dev/null
+++ b/modules/web-inspector/evolution-web-inspector.c
@@ -0,0 +1,185 @@
+/*
+ * evolution-web-inspector.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <misc/e-web-view.h>
+#include <libebackend/e-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEB_INSPECTOR \
+ (e_web_inspector_get_type ())
+#define E_WEB_INSPECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_WEB_INSPECTOR, EWebInspector))
+
+typedef struct _EWebInspector EWebInspector;
+typedef struct _EWebInspectorClass EWebInspectorClass;
+
+struct _EWebInspector {
+ EExtension parent;
+};
+
+struct _EWebInspectorClass {
+ EExtensionClass parent_class;
+};
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <placeholder name='inspect-menu' >"
+" <menuitem action='inspect'/>"
+" </placeholder>"
+" </popup>"
+"</ui>";
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_web_inspector_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (EWebInspector, e_web_inspector, E_TYPE_EXTENSION)
+
+static EWebView *
+web_inspector_get_web_view (EWebInspector *extension)
+{
+ EExtensible *extensible;
+
+ extensible = e_extension_get_extensible (E_EXTENSION (extension));
+
+ return E_WEB_VIEW (extensible);
+}
+
+static void
+web_inspector_action_inspect_cb (GtkAction *action,
+ EWebInspector *extension)
+{
+ WebKitWebInspector *inspector;
+ EWebView *web_view;
+
+ web_view = web_inspector_get_web_view (extension);
+ inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (web_view));
+
+ webkit_web_inspector_show (inspector);
+}
+
+static GtkActionEntry inspect_entries[] = {
+
+ { "inspect",
+ NULL,
+ N_("_Inspect..."),
+ NULL,
+ N_("Inspect the HTML content (debugging feature)"),
+ G_CALLBACK (web_inspector_action_inspect_cb) }
+};
+
+static WebKitWebView *
+web_inspector_inspect_web_view_cb (WebKitWebInspector *inspector,
+ EWebInspector *extension)
+{
+ GtkWidget *web_view;
+ GtkWidget *window;
+ const gchar *title;
+
+ title = _("Evolution Web Inspector");
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), title);
+ gtk_widget_set_size_request (window, 600, 400);
+ gtk_widget_show (window);
+
+ web_view = webkit_web_view_new ();
+ gtk_container_add (GTK_CONTAINER (window), web_view);
+ gtk_widget_show (web_view);
+
+ return WEBKIT_WEB_VIEW (web_view);
+}
+
+static void
+web_inspector_constructed (GObject *object)
+{
+ EWebInspector *extension;
+ WebKitWebSettings *settings;
+ WebKitWebInspector *inspector;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ EWebView *web_view;
+ GError *error = NULL;
+
+ extension = E_WEB_INSPECTOR (object);
+ web_view = web_inspector_get_web_view (extension);
+
+ ui_manager = e_web_view_get_ui_manager (web_view);
+ action_group = e_web_view_get_action_group (web_view, "standard");
+
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view));
+ g_object_set (settings, "enable-developer-extras", TRUE, NULL);
+
+ inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (web_view));
+
+ g_signal_connect (
+ inspector, "inspect-web-view",
+ G_CALLBACK (web_inspector_inspect_web_view_cb), extension);
+
+ gtk_action_group_add_actions (
+ action_group, inspect_entries,
+ G_N_ELEMENTS (inspect_entries), extension);
+
+ /* Because we are loading from a hard-coded string, there is
+ * no chance of I/O errors. Failure here implies a malformed
+ * UI definition. Full stop. */
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+ if (error != NULL)
+ g_error ("%s", error->message);
+}
+
+static void
+e_web_inspector_class_init (EWebInspectorClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = web_inspector_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_WEB_VIEW;
+}
+
+static void
+e_web_inspector_class_finalize (EWebInspectorClass *class)
+{
+}
+
+static void
+e_web_inspector_init (EWebInspector *extension)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_web_inspector_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}