aboutsummaryrefslogtreecommitdiffstats
path: root/em-format/em-format.c
diff options
context:
space:
mode:
Diffstat (limited to 'em-format/em-format.c')
-rw-r--r--em-format/em-format.c3840
1 files changed, 1990 insertions, 1850 deletions
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);
}