aboutsummaryrefslogtreecommitdiffstats
path: root/em-format
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2010-12-13 23:30:21 +0800
committerMilan Crha <mcrha@redhat.com>2010-12-13 23:30:21 +0800
commita8bde44b69e42fdb5640930e28c5005b897053df (patch)
treefea363167793260e40ae0ddff89dce6aef9f1a93 /em-format
parentda223fce69fe73305c0ab03abd4f092a72686556 (diff)
downloadgsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.tar
gsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.tar.gz
gsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.tar.bz2
gsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.tar.lz
gsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.tar.xz
gsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.tar.zst
gsoc2013-evolution-a8bde44b69e42fdb5640930e28c5005b897053df.zip
Bug #436914 - Reply to inline GPG quotes raw GPG message
Diffstat (limited to 'em-format')
-rw-r--r--em-format/Makefile.am2
-rw-r--r--em-format/em-format-quote.c60
-rw-r--r--em-format/em-inline-filter.c444
-rw-r--r--em-format/em-inline-filter.h80
4 files changed, 586 insertions, 0 deletions
diff --git a/em-format/Makefile.am b/em-format/Makefile.am
index 212b45f074..df052fe915 100644
--- a/em-format/Makefile.am
+++ b/em-format/Makefile.am
@@ -5,6 +5,7 @@ privsolib_LTLIBRARIES = libemformat.la
emformatinclude_HEADERS = \
em-format.h \
em-format-quote.h \
+ em-inline-filter.h \
em-stripsig-filter.h
libemformat_la_CPPFLAGS = \
@@ -18,6 +19,7 @@ libemformat_la_SOURCES = \
$(emformatinclude_HEADERS) \
em-format.c \
em-format-quote.c \
+ em-inline-filter.c \
em-stripsig-filter.c
libemformat_la_LDFLAGS = $(NO_UNDEFINED)
diff --git a/em-format/em-format-quote.c b/em-format/em-format-quote.c
index 6d6ed6a4ab..bdb632b252 100644
--- a/em-format/em-format-quote.c
+++ b/em-format/em-format-quote.c
@@ -30,6 +30,7 @@
#include <glib/gi18n.h>
#include <gconf/gconf-client.h>
+#include "em-inline-filter.h"
#include "em-stripsig-filter.h"
#include "em-format-quote.h"
@@ -529,6 +530,52 @@ emfq_format_message (EMFormat *emf,
cancellable, NULL);
}
+/* Decodes inline encoded parts of 'part'. The returned pointer, if not NULL, should
+ be unreffed with g_object_unref().
+*/
+static CamelMimePart *
+decode_inline_parts (CamelMimePart *part, GCancellable *cancellable)
+{
+ CamelMultipart *mp;
+ CamelStream *null;
+ CamelStream *filtered_stream;
+ EMInlineFilter *inline_filter;
+
+ g_return_val_if_fail (part != NULL, NULL);
+
+ null = camel_stream_null_new ();
+ filtered_stream = camel_stream_filter_new (null);
+ g_object_unref (null);
+
+ inline_filter = em_inline_filter_new (camel_mime_part_get_encoding (part), camel_mime_part_get_content_type (part));
+ camel_stream_filter_add (
+ CAMEL_STREAM_FILTER (filtered_stream),
+ CAMEL_MIME_FILTER (inline_filter));
+ camel_data_wrapper_decode_to_stream_sync (
+ camel_medium_get_content (CAMEL_MEDIUM (part)), (CamelStream *)filtered_stream, cancellable, NULL);
+ camel_stream_close ((CamelStream *)filtered_stream, cancellable, NULL);
+ g_object_unref (filtered_stream);
+
+ if (!em_inline_filter_found_any (inline_filter)) {
+ g_object_unref (inline_filter);
+ return NULL;
+ }
+
+ mp = em_inline_filter_get_multipart (inline_filter);
+
+ g_object_unref (inline_filter);
+
+ if (mp) {
+ part = camel_mime_part_new ();
+ camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp));
+ g_object_unref (mp);
+ } else {
+ g_object_ref (part);
+ }
+
+ return part;
+}
+
static void
emfq_text_plain (EMFormat *emf,
CamelStream *stream,
@@ -541,6 +588,7 @@ emfq_text_plain (EMFormat *emf,
CamelStream *filtered_stream;
CamelMimeFilter *html_filter;
CamelMimeFilter *sig_strip;
+ CamelMimePart *mp;
CamelContentType *type;
const gchar *format;
guint32 rgb = 0x737373, flags;
@@ -548,6 +596,18 @@ emfq_text_plain (EMFormat *emf,
if (!part)
return;
+ mp = decode_inline_parts (part, cancellable);
+ if (mp) {
+ if (CAMEL_IS_MULTIPART (camel_medium_get_content (CAMEL_MEDIUM (mp)))) {
+ em_format_part (emf, stream, mp, cancellable);
+ g_object_unref (mp);
+
+ return;
+ }
+
+ g_object_unref (mp);
+ }
+
flags = emfq->text_html_flags;
/* Check for RFC 2646 flowed text. */
diff --git a/em-format/em-inline-filter.c b/em-format/em-inline-filter.c
new file mode 100644
index 0000000000..23e5a6f6ed
--- /dev/null
+++ b/em-format/em-inline-filter.c
@@ -0,0 +1,444 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "em-inline-filter.h"
+
+#include "em-format/em-format.h"
+
+#define d(x)
+
+G_DEFINE_TYPE (EMInlineFilter, em_inline_filter, CAMEL_TYPE_MIME_FILTER)
+
+enum {
+ EMIF_PLAIN,
+ EMIF_BINHEX,
+ EMIF_POSTSCRIPT,
+ EMIF_PGPSIGNED,
+ EMIF_PGPENCRYPTED
+};
+
+static const struct {
+ const gchar *type;
+ const gchar *subtype;
+ CamelTransferEncoding encoding;
+ guint plain:1;
+} emif_types[] = {
+ { "text", "plain", CAMEL_TRANSFER_ENCODING_DEFAULT, 1, },
+ { "application", "mac-binhex40", CAMEL_TRANSFER_ENCODING_7BIT, 0, },
+ { "application", "postscript", CAMEL_TRANSFER_ENCODING_7BIT, 0, },
+ { "application", "x-inlinepgp-signed", CAMEL_TRANSFER_ENCODING_DEFAULT, 0, },
+ { "application", "x-inlinepgp-encrypted", CAMEL_TRANSFER_ENCODING_DEFAULT, 0, },
+};
+
+static CamelMimePart *
+construct_part_from_stream (CamelStream *mem, const GByteArray *data)
+{
+ CamelMimePart *part = NULL;
+ CamelMimeParser *parser;
+
+ g_return_val_if_fail (mem != NULL, NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+
+ if (data->len <= 13 || g_ascii_strncasecmp ((const gchar *) data->data, "Content-Type:", 13) != 0)
+ return NULL;
+
+ parser = camel_mime_parser_new ();
+ camel_mime_parser_scan_from (parser, FALSE);
+ camel_mime_parser_scan_pre_from (parser, FALSE);
+
+ if (camel_mime_parser_init_with_stream (parser, mem, NULL) != -1) {
+ part = camel_mime_part_new ();
+ if (!camel_mime_part_construct_from_parser_sync (part, parser, NULL, NULL)) {
+ g_object_unref (part);
+ part = NULL;
+ }
+ }
+
+ g_object_unref (parser);
+
+ return part;
+}
+
+static void
+inline_filter_add_part (EMInlineFilter *emif, const gchar *data, gint len)
+{
+ CamelTransferEncoding encoding;
+ CamelContentType *content_type;
+ CamelDataWrapper *dw;
+ const gchar *mimetype;
+ CamelMimePart *part;
+ CamelStream *mem;
+ gchar *type;
+
+ if (emif->state == EMIF_PLAIN || emif->state == EMIF_PGPSIGNED || emif->state == EMIF_PGPENCRYPTED)
+ encoding = emif->base_encoding;
+ else
+ encoding = emif_types[emif->state].encoding;
+
+ g_byte_array_append (emif->data, (guchar *)data, len);
+ /* check the part will actually have content */
+ if (emif->data->len <= 0) {
+ return;
+ }
+
+ mem = camel_stream_mem_new_with_byte_array (emif->data);
+ part = construct_part_from_stream (mem, emif->data);
+ if (part) {
+ g_object_unref (mem);
+ emif->data = g_byte_array_new ();
+ g_free (emif->filename);
+ emif->filename = NULL;
+
+ emif->parts = g_slist_append (emif->parts, part);
+ emif->found_any = TRUE;
+
+ return;
+ }
+
+ emif->data = g_byte_array_new ();
+ camel_stream_reset (mem, NULL);
+
+ dw = camel_data_wrapper_new ();
+ if (encoding == emif->base_encoding && (encoding == CAMEL_TRANSFER_ENCODING_BASE64 || encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)) {
+ CamelMimeFilter *enc_filter = camel_mime_filter_basic_new (encoding == CAMEL_TRANSFER_ENCODING_BASE64 ? CAMEL_MIME_FILTER_BASIC_BASE64_ENC : CAMEL_MIME_FILTER_BASIC_QP_ENC);
+ CamelStream *filter_stream;
+
+ filter_stream = camel_stream_filter_new (mem);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), enc_filter);
+
+ /* properly encode content */
+ camel_data_wrapper_construct_from_stream_sync (
+ dw, filter_stream, NULL, NULL);
+
+ g_object_unref (enc_filter);
+ g_object_unref (filter_stream);
+ } else {
+ camel_data_wrapper_construct_from_stream_sync (
+ dw, mem, NULL, NULL);
+ }
+ g_object_unref (mem);
+
+ if (emif_types[emif->state].plain && emif->base_type) {
+ /* create a copy */
+ type = camel_content_type_format (emif->base_type);
+ content_type = camel_content_type_decode (type);
+ g_free (type);
+ } else {
+ /* we want to preserve all params */
+ type = camel_content_type_format (emif->base_type);
+ content_type = camel_content_type_decode (type);
+ g_free (type);
+
+ g_free (content_type->type);
+ g_free (content_type->subtype);
+ content_type->type = g_strdup (emif_types[emif->state].type);
+ content_type->subtype = g_strdup (emif_types[emif->state].subtype);
+ }
+
+ camel_data_wrapper_set_mime_type_field (dw, content_type);
+ camel_content_type_unref (content_type);
+ dw->encoding = encoding;
+
+ part = camel_mime_part_new ();
+ camel_medium_set_content ((CamelMedium *)part, dw);
+ camel_mime_part_set_encoding (part, encoding);
+ g_object_unref (dw);
+
+ if (emif->filename)
+ camel_mime_part_set_filename (part, emif->filename);
+
+ /* pre-snoop the mime type of unknown objects, and poke and hack it into place */
+ if (camel_content_type_is(dw->mime_type, "application", "octet-stream")
+ && (mimetype = em_format_snoop_type (part))
+ && strcmp(mimetype, "application/octet-stream") != 0) {
+ camel_data_wrapper_set_mime_type (dw, mimetype);
+ camel_mime_part_set_content_type (part, mimetype);
+ if (emif->filename)
+ camel_mime_part_set_filename (part, emif->filename);
+ }
+
+ g_free (emif->filename);
+ emif->filename = NULL;
+
+ emif->parts = g_slist_append (emif->parts, part);
+}
+
+static gint
+inline_filter_scan (CamelMimeFilter *f, gchar *in, gsize len, gint final)
+{
+ EMInlineFilter *emif = (EMInlineFilter *)f;
+ gchar *inptr = in, *inend = in+len;
+ gchar *data_start = in;
+ gchar *start = in;
+
+ while (inptr < inend) {
+ gint rest_len;
+
+ start = inptr;
+
+ while (inptr < inend && *inptr != '\n')
+ inptr++;
+
+ if (inptr == inend && start == inptr) {
+ if (!final) {
+ camel_mime_filter_backup (f, start, inend-start);
+ inend = start;
+ }
+ break;
+ }
+
+ rest_len = inend - start;
+ if (inptr < inend)
+ *inptr++ = 0;
+
+ #define restore_inptr() G_STMT_START { if (inptr < inend) inptr[-1] = '\n'; } G_STMT_END
+
+ switch (emif->state) {
+ case EMIF_PLAIN:
+ if (rest_len >= 45 && strncmp (start, "(This file must be converted with BinHex 4.0)", 45) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_BINHEX;
+ } else if (rest_len >= 11 && strncmp (start, "%!PS-Adobe-", 11) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_POSTSCRIPT;
+ } else if (rest_len >= 34 && strncmp (start, "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_PGPSIGNED;
+ } else if (rest_len >= 27 && strncmp (start, "-----BEGIN PGP MESSAGE-----", 27) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_PGPENCRYPTED;
+ }
+
+ break;
+ case EMIF_BINHEX:
+ if (inptr > (start+1) && inptr[-2] == ':') {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ emif->found_any = TRUE;
+ }
+ break;
+ case EMIF_POSTSCRIPT:
+ if (rest_len >= 5 && strncmp (start, "%%EOF", 5) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ emif->found_any = TRUE;
+ }
+ break;
+ case EMIF_PGPSIGNED:
+ if (rest_len >= 27 && strncmp (start, "-----END PGP SIGNATURE-----", 27) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ emif->found_any = TRUE;
+ }
+ break;
+ case EMIF_PGPENCRYPTED:
+ if (rest_len >= 25 && strncmp (start, "-----END PGP MESSAGE-----", 25) == 0) {
+ restore_inptr ();
+ inline_filter_add_part (emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ emif->found_any = TRUE;
+ }
+ break;
+ }
+
+ restore_inptr ();
+
+ #undef restore_inptr
+ }
+
+ if (final) {
+ /* always stop as plain, especially when not read those tags fully */
+ emif->state = EMIF_PLAIN;
+
+ inline_filter_add_part (emif, data_start, inend - data_start);
+ } else if (start > data_start) {
+ /* backup the last line, in case the tag is divided within buffers */
+ camel_mime_filter_backup (f, start, inend - start);
+ g_byte_array_append (emif->data, (guchar *)data_start, start - data_start);
+ } else {
+ g_byte_array_append (emif->data, (guchar *)data_start, inend - data_start);
+ }
+
+ return 0;
+}
+
+static void
+inline_filter_finalize (GObject *object)
+{
+ EMInlineFilter *emif = EM_INLINE_FILTER (object);
+
+ if (emif->base_type)
+ camel_content_type_unref (emif->base_type);
+
+ camel_mime_filter_reset (CAMEL_MIME_FILTER (object));
+ g_byte_array_free (emif->data, TRUE);
+ g_free (emif->filename);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (em_inline_filter_parent_class)->finalize (object);
+}
+
+static void
+inline_filter_filter (CamelMimeFilter *filter,
+ const gchar *in,
+ gsize len,
+ gsize prespace,
+ gchar **out,
+ gsize *outlen,
+ gsize *outprespace)
+{
+ inline_filter_scan (filter, (gchar *)in, len, FALSE);
+
+ *out = (gchar *)in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+static void
+inline_filter_complete (CamelMimeFilter *filter,
+ const gchar *in,
+ gsize len,
+ gsize prespace,
+ gchar **out,
+ gsize *outlen,
+ gsize *outprespace)
+{
+ inline_filter_scan (filter, (gchar *)in, len, TRUE);
+
+ *out = (gchar *)in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+static void
+inline_filter_reset (CamelMimeFilter *filter)
+{
+ EMInlineFilter *emif = EM_INLINE_FILTER (filter);
+ GSList *l;
+
+ l = emif->parts;
+ while (l) {
+ GSList *n = l->next;
+
+ g_object_unref (l->data);
+ g_slist_free_1 (l);
+
+ l = n;
+ }
+ emif->parts = NULL;
+ g_byte_array_set_size (emif->data, 0);
+ emif->found_any = FALSE;
+}
+
+static void
+em_inline_filter_class_init (EMInlineFilterClass *class)
+{
+ GObjectClass *object_class;
+ CamelMimeFilterClass *mime_filter_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = inline_filter_finalize;
+
+ mime_filter_class = CAMEL_MIME_FILTER_CLASS (class);
+ mime_filter_class->filter = inline_filter_filter;
+ mime_filter_class->complete = inline_filter_complete;
+ mime_filter_class->reset = inline_filter_reset;
+}
+
+static void
+em_inline_filter_init (EMInlineFilter *emif)
+{
+ emif->data = g_byte_array_new ();
+ emif->found_any = FALSE;
+}
+
+/**
+ * em_inline_filter_new:
+ * @base_encoding: The base transfer-encoding of the
+ * raw data being processed.
+ * @base_type: The base content-type of the raw data, should always be
+ * text/plain.
+ *
+ * Create a filter which will scan a (text) stream for
+ * embedded parts. You can then retrieve the contents
+ * as a CamelMultipart object.
+ *
+ * Return value:
+ **/
+EMInlineFilter *
+em_inline_filter_new (CamelTransferEncoding base_encoding, CamelContentType *base_type)
+{
+ EMInlineFilter *emif;
+
+ emif = g_object_new (EM_TYPE_INLINE_FILTER, NULL);
+ emif->base_encoding = base_encoding;
+ if (base_type) {
+ emif->base_type = base_type;
+ camel_content_type_ref (emif->base_type);
+ }
+
+ return emif;
+}
+
+CamelMultipart *
+em_inline_filter_get_multipart (EMInlineFilter *emif)
+{
+ GSList *l = emif->parts;
+ CamelMultipart *mp;
+
+ mp = camel_multipart_new ();
+ while (l) {
+ camel_multipart_add_part (mp, l->data);
+ l = l->next;
+ }
+
+ return mp;
+}
+
+gboolean
+em_inline_filter_found_any (EMInlineFilter *emif)
+{
+ g_return_val_if_fail (emif != NULL, FALSE);
+
+ return emif->found_any;
+}
diff --git a/em-format/em-inline-filter.h b/em-format/em-inline-filter.h
new file mode 100644
index 0000000000..503ec7c66c
--- /dev/null
+++ b/em-format/em-inline-filter.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef EM_INLINE_FILTER_H
+#define EM_INLINE_FILTER_H
+
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define EM_TYPE_INLINE_FILTER \
+ (em_inline_filter_get_type ())
+#define EM_INLINE_FILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), EM_TYPE_INLINE_FILTER, EMInlineFilter))
+#define EM_INLINE_FILTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), EM_TYPE_INLINE_FILTER, EMInlineFilterClass))
+#define EM_IS_INLINE_FILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), EM_TYPE_INLINE_FILTER))
+#define EM_IS_INLINE_FILTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), EM_TYPE_INLINE_FILTER))
+#define EM_INLINE_FILTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), EM_TYPE_INLINE_FILTER, EMInlineFilterClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMInlineFilter EMInlineFilter;
+typedef struct _EMInlineFilterClass EMInlineFilterClass;
+
+struct _EMInlineFilter {
+ CamelMimeFilter filter;
+
+ gint state;
+
+ CamelTransferEncoding base_encoding;
+ CamelContentType *base_type;
+
+ GByteArray *data;
+ gchar *filename;
+ GSList *parts;
+
+ gboolean found_any;
+};
+
+struct _EMInlineFilterClass {
+ CamelMimeFilterClass filter_class;
+};
+
+GType em_inline_filter_get_type (void);
+EMInlineFilter *em_inline_filter_new (CamelTransferEncoding base_encoding,
+ CamelContentType *type);
+CamelMultipart *em_inline_filter_get_multipart (EMInlineFilter *emif);
+gboolean em_inline_filter_found_any (EMInlineFilter *emif);
+
+G_END_DECLS
+
+#endif /* EM_INLINE_FILTER_H */