diff options
Diffstat (limited to 'camel/camel-multipart-signed.c')
-rw-r--r-- | camel/camel-multipart-signed.c | 720 |
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; +} + + |