aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-multipart-signed.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-multipart-signed.c')
-rw-r--r--camel/camel-multipart-signed.c720
1 files changed, 720 insertions, 0 deletions
diff --git a/camel/camel-multipart-signed.c b/camel/camel-multipart-signed.c
new file mode 100644
index 0000000000..95e0e848da
--- /dev/null
+++ b/camel/camel-multipart-signed.c
@@ -0,0 +1,720 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * camel-multipart.c : Abstract class for a multipart
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <errno.h>
+
+#include "camel-mime-part.h"
+#include "camel-mime-message.h"
+#include "camel-mime-parser.h"
+#include "camel-stream-mem.h"
+#include "camel-multipart-signed.h"
+#include "camel-mime-part.h"
+#include "camel-exception.h"
+#include "md5-utils.h"
+
+#include "camel-stream-filter.h"
+#include "camel-seekable-substream.h"
+#include "camel-mime-filter-chomp.h"
+#include "camel-mime-filter-crlf.h"
+#include "camel-mime-filter-canon.h"
+
+#define d(x)
+
+static void signed_add_part(CamelMultipart *multipart, CamelMimePart *part);
+static void signed_add_part_at(CamelMultipart *multipart, CamelMimePart *part, guint index);
+static void signed_remove_part(CamelMultipart *multipart, CamelMimePart *part);
+static CamelMimePart *signed_remove_part_at (CamelMultipart *multipart, guint index);
+static CamelMimePart *signed_get_part(CamelMultipart *multipart, guint index);
+static guint signed_get_number(CamelMultipart *multipart);
+
+static int write_to_stream(CamelDataWrapper *data_wrapper, CamelStream *stream);
+static void set_mime_type_field(CamelDataWrapper *data_wrapper, CamelContentType *mime_type);
+static int construct_from_stream(CamelDataWrapper *data_wrapper, CamelStream *stream);
+static int signed_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp);
+
+static CamelMultipartClass *parent_class = NULL;
+
+/* Returns the class for a CamelMultipartSigned */
+#define CMP_CLASS(so) CAMEL_MULTIPART_SIGNED_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+/* Returns the class for a CamelDataWrapper */
+#define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
+
+static void
+camel_multipart_signed_class_init (CamelMultipartSignedClass *camel_multipart_signed_class)
+{
+ CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS(camel_multipart_signed_class);
+ CamelMultipartClass *mpclass = (CamelMultipartClass *)camel_multipart_signed_class;
+
+ parent_class = (CamelMultipartClass *)camel_multipart_get_type();
+
+ /* virtual method overload */
+ camel_data_wrapper_class->construct_from_stream = construct_from_stream;
+ camel_data_wrapper_class->write_to_stream = write_to_stream;
+ camel_data_wrapper_class->set_mime_type_field = set_mime_type_field;
+
+ mpclass->add_part = signed_add_part;
+ mpclass->add_part_at = signed_add_part_at;
+ mpclass->remove_part = signed_remove_part;
+ mpclass->remove_part_at = signed_remove_part_at;
+ mpclass->get_part = signed_get_part;
+ mpclass->get_number = signed_get_number;
+ mpclass->construct_from_parser = signed_construct_from_parser;
+
+/*
+ mpclass->get_boundary = signed_get_boundary;
+ mpclass->set_boundary = signed_set_boundary;
+*/
+}
+
+static void
+camel_multipart_signed_init (gpointer object, gpointer klass)
+{
+ CamelMultipartSigned *multipart = (CamelMultipartSigned *)object;
+
+ camel_data_wrapper_set_mime_type(CAMEL_DATA_WRAPPER(multipart), "multipart/signed");
+ multipart->start1 = -1;
+}
+
+static void
+camel_multipart_signed_finalize (CamelObject *object)
+{
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)object;
+
+ g_free(mps->protocol);
+ g_free(mps->micalg);
+ if (mps->signature)
+ camel_object_unref((CamelObject *)mps->signature);
+ if (mps->content)
+ camel_object_unref((CamelObject *)mps->content);
+ if (mps->contentraw)
+ camel_object_unref((CamelObject *)mps->contentraw);
+}
+
+CamelType
+camel_multipart_signed_get_type (void)
+{
+ static CamelType camel_multipart_signed_type = CAMEL_INVALID_TYPE;
+
+ if (camel_multipart_signed_type == CAMEL_INVALID_TYPE) {
+ camel_multipart_signed_type = camel_type_register (camel_multipart_get_type (), "CamelMultipartSigned",
+ sizeof (CamelMultipartSigned),
+ sizeof (CamelMultipartSignedClass),
+ (CamelObjectClassInitFunc) camel_multipart_signed_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_multipart_signed_init,
+ (CamelObjectFinalizeFunc) camel_multipart_signed_finalize);
+ }
+
+ return camel_multipart_signed_type;
+}
+
+/**
+ * camel_multipart_signed_new:
+ *
+ * Create a new CamelMultipartSigned object.
+ *
+ * A MultipartSigned should be used to store and create parts of
+ * type "multipart/signed". This is because multipart/signed is
+ * entirely broken-by-design (tm) and uses completely
+ * different semantics to other mutlipart types. It must be treated
+ * as opaque data by any transport. See rfc 3156 for details.
+ *
+ * There are 3 ways to create the part:
+ * Use construct_from_stream. If this is used, then you must
+ * set the mime_type appropriately to match the data uses, so
+ * that the multiple parts my be extracted.
+ *
+ * Use construct_from_parser. The parser MUST be in the HSCAN_HEADER
+ * state, and the current content_type MUST be "multipart/signed" with
+ * the appropriate boundary and it SHOULD include the appropriate protocol
+ * and hash specifiers.
+ *
+ * Use sign_part. A signature part will automatically be created
+ * and the whole part may be written using write_to_stream to
+ * create a 'transport-safe' version (as safe as can be expected with
+ * such a broken specification).
+ *
+ * Return value: a new CamelMultipartSigned
+ **/
+CamelMultipartSigned *
+camel_multipart_signed_new (void)
+{
+ CamelMultipartSigned *multipart;
+
+ multipart = (CamelMultipartSigned *)camel_object_new(CAMEL_MULTIPART_SIGNED_TYPE);
+
+ return multipart;
+}
+
+/* yeah yuck.
+ Well, we could probably use the normal mime parser, but then it would change our
+ headers.
+ This is good enough ... till its not! */
+static int
+parse_content(CamelMultipartSigned *mps)
+{
+ CamelMultipart *mp = (CamelMultipart *)mps;
+ char *start, *end, *start2, *end2, *last;
+ CamelStreamMem *mem;
+ char *bound;
+ const char *boundary;
+
+ boundary = camel_multipart_get_boundary(mp);
+ if (boundary == NULL) {
+ g_warning("Trying to get multipart/signed content without setting boundary first");
+ return -1;
+ }
+
+ /* turn it into a string, and 'fix' it up */
+ /* this is extremely dodgey but should work! */
+ mem = (CamelStreamMem *)((CamelDataWrapper *)mps)->stream;
+ if (mem == NULL) {
+ g_warning("Trying to parse multipart/signed without constructing first");
+ return -1;
+ }
+
+ camel_stream_write((CamelStream *)mem, "", 1);
+ g_byte_array_set_size(mem->buffer, mem->buffer->len-1);
+ last = mem->buffer->data + mem->buffer->len;
+
+ bound = alloca(strlen(boundary)+5);
+ sprintf(bound, "--%s", boundary);
+
+ start = strstr(mem->buffer->data, bound);
+ if (start == NULL) {
+ printf("construct from stream, cannot find first boundary\n");
+ return -1;
+ }
+
+ if (start > (char *)mem->buffer->data) {
+ char *tmp = g_strndup(mem->buffer->data, start-(char *)mem->buffer->data-1);
+ camel_multipart_set_preface(mp, tmp);
+ g_free(tmp);
+ }
+
+ start += strlen(bound)+1;
+ if (start >= last)
+ return -1;
+ end = strstr(start, bound);
+ if (end == NULL) {
+ printf("construct from stream, cannot find second boundary\n");
+ return -1;
+ }
+
+ start2 = end + strlen(bound)+1;
+ if (start2 >= last)
+ return -1;
+ sprintf(bound, "--%s--", boundary);
+ end2 = strstr(start2, bound);
+ if (end2 == NULL) {
+ printf("construct from stream, cannot find last boundary\n");
+ return -1;
+ }
+
+ if (end2+strlen(bound)+1 < last)
+ camel_multipart_set_postface(mp, end2+strlen(bound)+1);
+
+ mps->start1 = start-(char *)mem->buffer->data;
+ mps->end1 = end-(char *)mem->buffer->data-1;
+ mps->start2 = start2-(char *)mem->buffer->data;
+ mps->end2 = end2-(char *)mem->buffer->data-1;
+
+ return 0;
+}
+
+/* we snoop the mime type to get boundary and hash info */
+static void
+set_mime_type_field(CamelDataWrapper *data_wrapper, CamelContentType *mime_type)
+{
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
+
+ ((CamelDataWrapperClass *)parent_class)->set_mime_type_field(data_wrapper, mime_type);
+ if (mime_type) {
+ const char *micalg, *protocol;
+
+ protocol = header_content_type_param(mime_type, "protocol");
+ g_free(mps->protocol);
+ mps->protocol = g_strdup(protocol);
+
+ micalg = header_content_type_param(mime_type, "micalg");
+ g_free(mps->micalg);
+ mps->micalg = g_strdup(micalg);
+ }
+}
+
+static void
+signed_add_part(CamelMultipart *multipart, CamelMimePart *part)
+{
+ g_warning("Cannot add parts to a signed part using add_part");
+}
+
+static void
+signed_add_part_at(CamelMultipart *multipart, CamelMimePart *part, guint index)
+{
+ g_warning("Cannot add parts to a signed part using add_part_at");
+}
+
+static void
+signed_remove_part(CamelMultipart *multipart, CamelMimePart *part)
+{
+ g_warning("Cannot remove parts from a signed part using remove_part");
+}
+
+static CamelMimePart *
+signed_remove_part_at (CamelMultipart *multipart, guint index)
+{
+ g_warning("Cannot remove parts from a signed part using remove_part");
+ return NULL;
+}
+
+static CamelMimePart *
+signed_get_part(CamelMultipart *multipart, guint index)
+{
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
+ CamelDataWrapper *dw = (CamelDataWrapper *)multipart;
+ CamelStream *stream;
+
+ switch (index) {
+ case CAMEL_MULTIPART_SIGNED_CONTENT:
+ if (mps->content)
+ return mps->content;
+ if (mps->contentraw) {
+ stream = mps->contentraw;
+ camel_object_ref((CamelObject *)stream);
+ } else if (mps->start1 == -1
+ && parse_content(mps) == -1
+ && (stream = ((CamelDataWrapper *)mps)->stream) == NULL) {
+ g_warning("Trying to get content on an invalid multipart/signed");
+ return NULL;
+ } else if (dw->stream == NULL) {
+ return NULL;
+ } else {
+ stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start1, mps->end1);
+ }
+ camel_stream_reset(stream);
+ mps->content = camel_mime_part_new();
+ camel_data_wrapper_construct_from_stream((CamelDataWrapper *)mps->content, stream);
+ camel_object_unref((CamelObject *)stream);
+ return mps->content;
+ case CAMEL_MULTIPART_SIGNED_SIGNATURE:
+ if (mps->signature)
+ return mps->signature;
+ if (mps->start1 == -1
+ && parse_content(mps) == -1) {
+ g_warning("Trying to get signature on invalid multipart/signed");
+ return NULL;
+ } else if (dw->stream == NULL) {
+ return NULL;
+ }
+ stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start2, mps->end2);
+ camel_stream_reset(stream);
+ mps->signature = camel_mime_part_new();
+ camel_data_wrapper_construct_from_stream((CamelDataWrapper *)mps->signature, stream);
+ camel_object_unref((CamelObject *)stream);
+ return mps->signature;
+ default:
+ g_warning("trying to get object out of bounds for multipart");
+ }
+
+ return NULL;
+}
+
+static guint
+signed_get_number(CamelMultipart *multipart)
+{
+ CamelDataWrapper *dw = (CamelDataWrapper *)multipart;
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
+
+ /* check what we have, so we return something reasonable */
+
+ if ((mps->content || mps->contentraw) && mps->signature)
+ return 2;
+
+ if (mps->start1 == -1 && parse_content(mps) == -1) {
+ if (dw->stream == NULL)
+ return 0;
+ else
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static void
+set_stream(CamelMultipartSigned *mps, CamelStream *mem)
+{
+ CamelDataWrapper *dw = (CamelDataWrapper *)mps;
+
+ if (dw->stream)
+ camel_object_unref((CamelObject *)dw->stream);
+ dw->stream = (CamelStream *)mem;
+
+ mps->start1 = -1;
+ if (mps->content) {
+ camel_object_unref((CamelObject *)mps->content);
+ mps->content = NULL;
+ }
+ if (mps->contentraw) {
+ camel_object_unref((CamelObject *)mps->contentraw);
+ mps->contentraw = NULL;
+ }
+ if (mps->signature) {
+ camel_object_unref((CamelObject *)mps->signature);
+ mps->signature = NULL;
+ }
+}
+
+static int
+construct_from_stream(CamelDataWrapper *data_wrapper, CamelStream *stream)
+{
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
+ CamelStream *mem = camel_stream_mem_new();
+
+ if (camel_stream_write_to_stream(stream, mem) == -1)
+ return -1;
+
+ set_stream(mps, mem);
+
+ return 0;
+}
+
+static int
+signed_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp)
+{
+ int err;
+ struct _header_content_type *content_type;
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
+ char *buf;
+ unsigned int len;
+ CamelStream *mem;
+
+ /* we *must not* be in multipart state, otherwise the mime parser will
+ parse the headers which is a no no @#$@# stupid multipart/signed spec */
+ g_assert(camel_mime_parser_state(mp) == HSCAN_HEADER);
+
+ /* All we do is copy it to a memstream */
+ content_type = camel_mime_parser_content_type(mp);
+ camel_multipart_set_boundary(multipart, header_content_type_param(content_type, "boundary"));
+
+ mem = camel_stream_mem_new();
+ while (camel_mime_parser_step(mp, &buf, &len) != HSCAN_BODY_END)
+ camel_stream_write(mem, buf, len);
+
+ set_stream(mps, mem);
+
+ err = camel_mime_parser_errno(mp);
+ if (err != 0) {
+ errno = err;
+ return -1;
+ } else
+ return 0;
+}
+
+static int
+write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
+{
+ CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
+ CamelMultipart *mp = (CamelMultipart *)mps;
+ const char *boundary;
+ int count, total=0;
+
+ /* we have 3 basic cases:
+ 1. constructed, we write out the data wrapper stream we got
+ 2. signed content, we create and write out a new stream
+ 3. invalid
+ */
+
+ /* 1 */
+ /* FIXME: locking? */
+ if (data_wrapper->stream) {
+ camel_stream_reset(data_wrapper->stream);
+ return camel_stream_write_to_stream(data_wrapper->stream, stream);
+ }
+
+ /* 3 */
+ if (mps->signature == NULL || mps->contentraw == NULL)
+ return -1;
+
+ /* 2 */
+ boundary = camel_multipart_get_boundary(mp);
+ if (mp->preface) {
+ count = camel_stream_write_string(stream, mp->preface);
+ if (count == -1)
+ return -1;
+ total += count;
+ }
+
+ /* first boundary */
+ count = camel_stream_printf(stream, "\n--%s\n", boundary);
+ if (count == -1)
+ return -1;
+ total += count;
+
+ /* output content part */
+ camel_stream_reset(mps->contentraw);
+ count = camel_stream_write_to_stream(mps->contentraw, stream);
+ if (count == -1)
+ return -1;
+ total += count;
+
+ /* boundary */
+ count = camel_stream_printf(stream, "\n--%s\n", boundary);
+ if (count == -1)
+ return -1;
+ total += count;
+
+ /* signature */
+ count = camel_data_wrapper_write_to_stream((CamelDataWrapper *)mps->signature, stream);
+ if (count == -1)
+ return -1;
+ total += count;
+
+ /* write the terminating boudary delimiter */
+ count = camel_stream_printf(stream, "\n--%s--\n", boundary);
+ if (count == -1)
+ return -1;
+ total += count;
+
+ /* and finally the postface */
+ if (mp->postface) {
+ count = camel_stream_write_string(stream, mp->postface);
+ if (count == -1)
+ return -1;
+ total += count;
+ }
+
+ return total;
+}
+
+/* See rfc3156, section 2 and others */
+/* We do this simply: Anything not base64 must be qp
+ This is so that we can safely translate any occurance of "From "
+ into the quoted-printable escaped version safely. */
+static void
+prepare_sign(CamelMimePart *mime_part)
+{
+ CamelDataWrapper *wrapper;
+ CamelMimePartEncodingType encoding;
+ int parts, i;
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+ if (!wrapper)
+ return;
+
+ if (CAMEL_IS_MULTIPART (wrapper)) {
+ parts = camel_multipart_get_number((CamelMultipart *)wrapper);
+ for (i = 0; i < parts; i++)
+ prepare_sign(camel_multipart_get_part((CamelMultipart *)wrapper, i));
+ } else if (CAMEL_IS_MIME_MESSAGE (wrapper)) {
+ prepare_sign((CamelMimePart *)wrapper);
+ } else {
+ encoding = camel_mime_part_get_encoding(mime_part);
+
+ if (encoding != CAMEL_MIME_PART_ENCODING_BASE64
+ && encoding != CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE) {
+ camel_mime_part_set_encoding(mime_part, CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE);
+ }
+ }
+}
+
+/**
+ * camel_multipart_signed_sign:
+ * @mps:
+ * @context: The CipherContext to use for signing.
+ * @content: CamelMimePart content you wish to sign/transport.
+ * @userid: The id of the signing key to use.
+ * @hash: The algorithm to use.
+ * @ex:
+ *
+ * Sign the part @content, and attach it as the first part
+ * (CAMEL_MULTIPART_SIGNED_CONTENT) of the multipart @mps. A
+ * signature object will be created and setup as the second part
+ * (CAMEL_MULTIPART_SIGNED_SIGNATURE) of the object. Once a part has
+ * been successfully signed the mutlipart is ready for transmission.
+ *
+ * This method should be used to create multipart/signed objects
+ * which are properly canoncalised before signing, etc.
+ *
+ * Return value: -1 on error, setting @ex appropriately. On error
+ * neither the content or signature parts will be setup.
+ **/
+int
+camel_multipart_signed_sign(CamelMultipartSigned *mps, CamelCipherContext *context, CamelMimePart *content, const char *userid, CamelCipherHash hash, CamelException *ex)
+{
+ CamelMimeFilter *canon_filter;
+ CamelStream *sigstream, *mem;
+ CamelStreamFilter *filter;
+ CamelContentType *mime_type;
+ CamelMimePart *signature;
+ CamelDataWrapper *dw;
+ char *type;
+
+ /* this needs to be set */
+ g_return_val_if_fail(context->protocol != NULL, -1);
+
+ prepare_sign(content);
+
+ mem = camel_stream_mem_new();
+ filter = camel_stream_filter_new_with_stream(mem);
+
+ canon_filter = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM);
+ camel_stream_filter_add(filter, (CamelMimeFilter *)canon_filter);
+ camel_object_unref((CamelObject *)canon_filter);
+
+ camel_data_wrapper_write_to_stream((CamelDataWrapper *)content, (CamelStream *)filter);
+ camel_stream_flush((CamelStream *)filter);
+ camel_object_unref((CamelObject *)filter);
+ camel_stream_reset(mem);
+
+#if 0
+ printf("-- Signing:\n");
+ fwrite(((CamelStreamMem *)mem)->buffer->data, ((CamelStreamMem *)mem)->buffer->len, 1, stdout);
+ printf("-- end\n");
+#endif
+
+ sigstream = camel_stream_mem_new();
+
+ if (camel_cipher_sign(context, userid, hash, mem, sigstream, ex) == -1) {
+ camel_object_unref((CamelObject *)mem);
+ camel_object_unref((CamelObject *)sigstream);
+ return -1;
+ }
+
+ /* create the signature wrapper object */
+ signature = camel_mime_part_new();
+ dw = camel_data_wrapper_new();
+ type = alloca(strlen(context->protocol) + 32);
+ sprintf(type, "%s; name=signature.asc", context->protocol);
+ camel_data_wrapper_set_mime_type(dw, type);
+ camel_stream_reset(sigstream);
+ camel_data_wrapper_construct_from_stream(dw, sigstream);
+ camel_object_unref((CamelObject *)sigstream);
+ camel_medium_set_content_object((CamelMedium *)signature, dw);
+ camel_object_unref((CamelObject *)dw);
+ camel_mime_part_set_description(signature, _("This is a digitally signed message part"));
+
+ /* setup our mime type and boundary */
+ mime_type = header_content_type_new("multipart", "signed");
+ header_content_type_set_param(mime_type, "micalg", camel_cipher_hash_to_id(context, hash));
+ header_content_type_set_param(mime_type, "protocol", context->protocol);
+ camel_data_wrapper_set_mime_type_field(CAMEL_DATA_WRAPPER (mps), mime_type);
+ header_content_type_unref(mime_type);
+ camel_multipart_set_boundary((CamelMultipart *)mps, NULL);
+
+ /* just keep the whole raw content. We dont *really* need to do this because
+ we know how we just proccessed it, but, well, better to be safe than sorry */
+ mps->signature = signature;
+ mps->contentraw = mem;
+ camel_stream_reset(mem);
+
+ /* clear the data-wrapper stream - tells write_to_stream to use the right object */
+ if (((CamelDataWrapper *)mps)->stream) {
+ camel_object_unref((CamelObject *) ((CamelDataWrapper *)mps)->stream);
+ ((CamelDataWrapper *)mps)->stream = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * camel_multipart_signed_verify:
+ * @mps:
+ * @context:
+ * @ex:
+ *
+ * Verify a signed object. This may be used to verify newly signed
+ * objects as well as those created from external streams or parsers.
+ *
+ * Return value: A validity value, or NULL on error, setting @ex
+ * appropriately.
+ **/
+CamelCipherValidity *
+camel_multipart_signed_verify(CamelMultipartSigned *mps, CamelCipherContext *context, CamelException *ex)
+{
+ CamelCipherValidity *valid;
+ CamelMimePart *sigpart;
+ CamelStream *sigstream, *constream;
+
+ /* we need to be able to verify stuff we just signed as well as stuff we loaded from a stream/parser */
+
+ if (mps->contentraw) {
+ constream = mps->contentraw;
+ camel_object_ref((CamelObject *)constream);
+ } else {
+ CamelStream *sub;
+ CamelMimeFilter *canon_filter;
+
+ if (mps->start1 == -1 && parse_content(mps) == -1) {
+ camel_exception_setv(ex, 1, _("parse error"));
+ return NULL;
+ }
+
+ /* first, prepare our parts */
+ sub = camel_seekable_substream_new((CamelSeekableStream *)((CamelDataWrapper *)mps)->stream, mps->start1, mps->end1);
+ constream = (CamelStream *)camel_stream_filter_new_with_stream(sub);
+ camel_object_unref((CamelObject *)sub);
+
+ /* Note: see rfc2015 or rfc3156, section 5 */
+ canon_filter = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF);
+ camel_stream_filter_add((CamelStreamFilter *)constream, (CamelMimeFilter *)canon_filter);
+ camel_object_unref((CamelObject *)canon_filter);
+ }
+
+ /* we do this as a normal mime part so we can have it handle transfer encoding etc */
+ sigstream = camel_stream_mem_new();
+ sigpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_SIGNATURE);
+ camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, sigstream);
+ camel_stream_reset(sigstream);
+
+ /* do the magic, the caller must supply the right context for this kind of object */
+ valid = camel_cipher_verify(context, camel_cipher_id_to_hash(context, mps->micalg), constream, sigstream, ex);
+
+#if 0
+ {
+ CamelStream *sout = camel_stream_fs_new_with_fd(dup(0));
+
+ camel_stream_printf(sout, "-- Verifying:\n");
+ camel_stream_reset(constream);
+ camel_stream_write_to_stream(constream, sout);
+ camel_stream_printf(sout, "-- end\n");
+ camel_object_unref((CamelObject *)sout);
+ }
+#endif
+
+ camel_object_unref((CamelObject *)constream);
+ camel_object_unref((CamelObject *)sigstream);
+
+ return valid;
+}
+
+