/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* camel-multipart.c : Abstract class for a multipart
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This library 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.
*
* This library 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 this library. If not, see .
*
* Authors: Michael Zucchi
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "camel-mime-filter-canon.h"
#include "camel-mime-filter-crlf.h"
#include "camel-mime-message.h"
#include "camel-mime-parser.h"
#include "camel-mime-part.h"
#include "camel-multipart-signed.h"
#include "camel-object.h"
#include "camel-stream-filter.h"
#include "camel-stream-mem.h"
#define CAMEL_MULTIPART_SIGNED_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_MULTIPART_SIGNED, CamelMultipartSignedPrivate))
struct _CamelMultipartSignedPrivate {
/* These are the client visible parts,
* decoded forms of our data wrapper content. */
CamelMimePart *content;
CamelMimePart *signature;
/* The raw content which must go over the wire, if we have
* generated it.
*
* XXX Perhaps this should just set data_wrapper->stream and
* update start1/end1 accordingly, as it is done for other
* parts, or visa versa? */
CamelStream *contentraw;
/* Offset pointers of start of boundary in content object. */
goffset start1, end1;
goffset start2, end2;
};
G_DEFINE_TYPE (
CamelMultipartSigned,
camel_multipart_signed,
CAMEL_TYPE_MULTIPART)
static CamelStream *
multipart_signed_clip_stream (CamelMultipartSigned *mps,
goffset start,
goffset end)
{
CamelDataWrapper *data_wrapper;
GByteArray *src;
GByteArray *dst;
g_return_val_if_fail (start != -1, NULL);
g_return_val_if_fail (end != -1, NULL);
g_return_val_if_fail (end >= start, NULL);
data_wrapper = CAMEL_DATA_WRAPPER (mps);
src = camel_data_wrapper_get_byte_array (data_wrapper);
dst = g_byte_array_new ();
if (start >= 0 && end < src->len) {
const guint8 *data = &src->data[start];
g_byte_array_append (dst, data, end - start);
}
/* Stream takes ownership of the byte array. */
return camel_stream_mem_new_with_byte_array (dst);
}
static gint
multipart_signed_skip_content (CamelMimeParser *cmp)
{
gchar *buf;
gsize len;
gint state;
switch (camel_mime_parser_state (cmp)) {
case CAMEL_MIME_PARSER_STATE_HEADER:
/* body part */
while (camel_mime_parser_step (cmp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
/* NOOP */ ;
break;
case CAMEL_MIME_PARSER_STATE_MESSAGE:
/* message body part */
state = camel_mime_parser_step (cmp, &buf, &len);
if (state != CAMEL_MIME_PARSER_STATE_EOF &&
state != CAMEL_MIME_PARSER_STATE_FROM_END) {
multipart_signed_skip_content (cmp);
} else {
camel_mime_parser_unstep (cmp);
break;
}
/* clean up followon state if any, see camel-mime-message.c */
state = camel_mime_parser_step (cmp, &buf, &len);
switch (state) {
case CAMEL_MIME_PARSER_STATE_EOF:
case CAMEL_MIME_PARSER_STATE_FROM_END: /* these doesn't belong to us */
camel_mime_parser_unstep (cmp);
case CAMEL_MIME_PARSER_STATE_MESSAGE_END:
break;
default:
g_error ("Bad parser state: Expecting MESSAGE_END or EOF or EOM, got: %u", camel_mime_parser_state (cmp));
camel_mime_parser_unstep (cmp);
return -1;
}
break;
case CAMEL_MIME_PARSER_STATE_MULTIPART:
/* embedded multipart */
while (camel_mime_parser_step (cmp, &buf, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END)
multipart_signed_skip_content (cmp);
break;
default:
g_warning ("Invalid state encountered???: %u", camel_mime_parser_state (cmp));
}
return 0;
}
static gint
multipart_signed_parse_content (CamelMultipartSigned *mps)
{
CamelMimeParser *cmp;
CamelMultipart *mp = (CamelMultipart *) mps;
CamelDataWrapper *data_wrapper;
GByteArray *byte_array;
CamelStream *stream;
const gchar *boundary;
gchar *buf;
gsize len;
gint state;
boundary = camel_multipart_get_boundary (mp);
g_return_val_if_fail (boundary != NULL, -1);
data_wrapper = CAMEL_DATA_WRAPPER (mps);
byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
stream = camel_stream_mem_new ();
/* Do not give the stream ownership of the byte array. */
camel_stream_mem_set_byte_array (
CAMEL_STREAM_MEM (stream), byte_array);
/* This is all seriously complex.
* This is so we can parse all cases properly, without altering the content.
* All we are doing is finding part offsets. */
cmp = camel_mime_parser_new ();
camel_mime_parser_init_with_stream (cmp, stream, NULL);
camel_mime_parser_push_state (cmp, CAMEL_MIME_PARSER_STATE_MULTIPART, boundary);
mps->priv->start1 = -1;
mps->priv->end1 = -1;
mps->priv->start2 = -1;
mps->priv->end2 = -1;
while ((state = camel_mime_parser_step (cmp, &buf, &len)) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
if (mps->priv->start1 == -1) {
mps->priv->start1 = camel_mime_parser_tell_start_headers (cmp);
} else if (mps->priv->start2 == -1) {
mps->priv->start2 = camel_mime_parser_tell_start_headers (cmp);
mps->priv->end1 = camel_mime_parser_tell_start_boundary (cmp);
if (mps->priv->end1 > mps->priv->start1 && byte_array->data[mps->priv->end1 - 1] == '\n')
mps->priv->end1--;
if (mps->priv->end1 > mps->priv->start1 && byte_array->data[mps->priv->end1 - 1] == '\r')
mps->priv->end1--;
} else {
g_warning ("multipart/signed has more than 2 parts, remaining parts ignored");
state = CAMEL_MIME_PARSER_STATE_MULTIPART_END;
break;
}
if (multipart_signed_skip_content (cmp) == -1)
break;
}
if (state == CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
mps->priv->end2 = camel_mime_parser_tell_start_boundary (cmp);
camel_multipart_set_preface (mp, camel_mime_parser_preface (cmp));
camel_multipart_set_postface (mp, camel_mime_parser_postface (cmp));
}
g_object_unref (cmp);
g_object_unref (stream);
if (mps->priv->end2 == -1 || mps->priv->start2 == -1) {
if (mps->priv->end1 == -1)
mps->priv->start1 = -1;
return -1;
}
return 0;
}
static void
multipart_signed_dispose (GObject *object)
{
CamelMultipartSignedPrivate *priv;
priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (object);
g_clear_object (&priv->content);
g_clear_object (&priv->signature);
g_clear_object (&priv->contentraw);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (camel_multipart_signed_parent_class)->dispose (object);
}
static gssize
multipart_signed_write_to_stream_sync (CamelDataWrapper *data_wrapper,
CamelStream *stream,
GCancellable *cancellable,
GError **error)
{
CamelMultipartSignedPrivate *priv;
CamelMultipart *mp = (CamelMultipart *) data_wrapper;
GByteArray *byte_array;
const gchar *boundary;
const gchar *preface;
const gchar *postface;
gssize total = 0;
gssize count;
gchar *content;
priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (data_wrapper);
byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
/* 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 (byte_array->len > 0) {
return camel_stream_write (
stream, (gchar *) byte_array->data,
byte_array->len, cancellable, error);
}
/* 3 */
if (priv->contentraw == NULL) {
g_set_error (
error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
_("No content available"));
return -1;
}
/* 3 */
if (priv->signature == NULL) {
g_set_error (
error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
_("No signature available"));
return -1;
}
boundary = camel_multipart_get_boundary (mp);
preface = camel_multipart_get_preface (mp);
postface = camel_multipart_get_postface (mp);
/* 2 */
if (preface != NULL) {
count = camel_stream_write_string (
stream, preface, cancellable, error);
if (count == -1)
return -1;
total += count;
}
/* first boundary */
content = g_strdup_printf ("\n--%s\n", boundary);
count = camel_stream_write_string (
stream, content, cancellable, error);
g_free (content);
if (count == -1)
return -1;
total += count;
/* output content part */
/* XXX Both CamelGpgContext and CamelSMIMEContext set this
* to a memory stream, so assume it's always seekable. */
g_seekable_seek (
G_SEEKABLE (priv->contentraw), 0, G_SEEK_SET, NULL, NULL);
count = camel_stream_write_to_stream (
priv->contentraw, stream, cancellable, error);
if (count == -1)
return -1;
total += count;
/* boundary */
content = g_strdup_printf ("\n--%s\n", boundary);
count = camel_stream_write_string (
stream, content, cancellable, error);
g_free (content);
if (count == -1)
return -1;
total += count;
/* signature */
count = camel_data_wrapper_write_to_stream_sync (
CAMEL_DATA_WRAPPER (priv->signature),
stream, cancellable, error);
if (count == -1)
return -1;
total += count;
/* write the terminating boudary delimiter */
content = g_strdup_printf ("\n--%s--\n", boundary);
count = camel_stream_write_string (
stream, content, cancellable, error);
g_free (content);
if (count == -1)
return -1;
total += count;
/* and finally the postface */
if (postface != NULL) {
count = camel_stream_write_string (
stream, postface, cancellable, error);
if (count == -1)
return -1;
total += count;
}
return total;
}
static gboolean
multipart_signed_construct_from_stream_sync (CamelDataWrapper *data_wrapper,
CamelStream *stream,
GCancellable *cancellable,
GError **error)
{
CamelMultipartSignedPrivate *priv;
CamelDataWrapperClass *parent_class;
gboolean success;
priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (data_wrapper);
/* Chain up to parent's construct_from_stream_sync() method. */
parent_class = CAMEL_DATA_WRAPPER_CLASS (
camel_multipart_signed_parent_class);
success = parent_class->construct_from_stream_sync (
data_wrapper, stream, cancellable, error);
if (success) {
priv->start1 = -1;
g_clear_object (&priv->content);
g_clear_object (&priv->signature);
g_clear_object (&priv->contentraw);
}
return success;
}
static gssize
multipart_signed_write_to_output_stream_sync (CamelDataWrapper *data_wrapper,
GOutputStream *output_stream,
GCancellable *cancellable,
GError **error)
{
CamelMultipartSignedPrivate *priv;
CamelMultipart *mp = (CamelMultipart *) data_wrapper;
GByteArray *byte_array;
const gchar *boundary;
const gchar *preface;
const gchar *postface;
gsize bytes_written;
gssize total = 0;
gssize result;
gchar *content;
gboolean success;
priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (data_wrapper);
byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
/* 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 (byte_array->len > 0) {
success = g_output_stream_write_all (
output_stream,
byte_array->data, byte_array->len,
&bytes_written, cancellable, error);
return success ? (gssize) bytes_written : -1;
}
/* 3 */
if (priv->contentraw == NULL) {
g_set_error (
error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
_("No content available"));
return -1;
}
/* 3 */
if (priv->signature == NULL) {
g_set_error (
error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
_("No signature available"));
return -1;
}
boundary = camel_multipart_get_boundary (mp);
preface = camel_multipart_get_preface (mp);
postface = camel_multipart_get_postface (mp);
/* 2 */
if (preface != NULL) {
success = g_output_stream_write_all (
output_stream,
preface, strlen (preface),
&bytes_written, cancellable, error);
if (!success)
return -1;
total += (gssize) bytes_written;
}
/* first boundary */
content = g_strdup_printf ("\n--%s\n", boundary);
success = g_output_stream_write_all (
output_stream,
content, strlen (content),
&bytes_written, cancellable, error);
g_free (content);
if (!success)
return -1;
total += (gssize) bytes_written;
/* output content part */
/* XXX Both CamelGpgContext and CamelSMIMEContext set this
* to a memory stream, so we'll assume it to be so and
* grab its GByteArray. Would be better to store the
* raw message content as a reference-counted GBytes
* rather than a stream. */
g_return_val_if_fail (CAMEL_IS_STREAM_MEM (priv->contentraw), -1);
byte_array = camel_stream_mem_get_byte_array (
CAMEL_STREAM_MEM (priv->contentraw));
success = g_output_stream_write_all (
output_stream,
byte_array->data, byte_array->len,
&bytes_written, cancellable, error);
if (!success)
return -1;
total += (gssize) bytes_written;
/* boundary */
content = g_strdup_printf ("\n--%s\n", boundary);
success = g_output_stream_write_all (
output_stream,
content, strlen (content),
&bytes_written, cancellable, error);
g_free (content);
if (!success)
return -1;
total += (gssize) bytes_written;
/* signature */
result = camel_data_wrapper_write_to_output_stream_sync (
CAMEL_DATA_WRAPPER (priv->signature),
output_stream, cancellable, error);
if (result == -1)
return -1;
total += result;
/* write the terminating boudary delimiter */
content = g_strdup_printf ("\n--%s--\n", boundary);
success = g_output_stream_write_all (
output_stream,
content, strlen (content),
&bytes_written, cancellable, error);
g_free (content);
if (!success)
return -1;
total += (gssize) bytes_written;
/* and finally the postface */
if (postface != NULL) {
success = g_output_stream_write_all (
output_stream,
postface, strlen (postface),
&bytes_written, cancellable, error);
if (!success)
return -1;
total += (gssize) bytes_written;
}
return total;
}
static gboolean
multipart_signed_construct_from_input_stream_sync (CamelDataWrapper *data_wrapper,
GInputStream *input_stream,
GCancellable *cancellable,
GError **error)
{ CamelMultipartSignedPrivate *priv;
gboolean success;
priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (data_wrapper);
/* Chain up to parent's construct_from_input_stream_sync() method. */
success = CAMEL_DATA_WRAPPER_CLASS (
camel_multipart_signed_parent_class)->
construct_from_input_stream_sync (
data_wrapper, input_stream, cancellable, error);
if (success) {
priv->start1 = -1;
g_clear_object (&priv->content);
g_clear_object (&priv->signature);
g_clear_object (&priv->contentraw);
}
return success;
}
static void
multipart_signed_add_part (CamelMultipart *multipart,
CamelMimePart *part)
{
g_warning ("Cannot add parts to a signed part using add_part");
}
static CamelMimePart *
multipart_signed_get_part (CamelMultipart *multipart,
guint index)
{
CamelMultipartSigned *mps;
CamelDataWrapper *data_wrapper;
CamelStream *stream;
GByteArray *byte_array;
mps = CAMEL_MULTIPART_SIGNED (multipart);
data_wrapper = CAMEL_DATA_WRAPPER (multipart);
byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
switch (index) {
case CAMEL_MULTIPART_SIGNED_CONTENT:
if (mps->priv->content != NULL)
return mps->priv->content;
if (mps->priv->contentraw != NULL) {
g_return_val_if_fail (
G_IS_SEEKABLE (mps->priv->contentraw), NULL);
stream = g_object_ref (mps->priv->contentraw);
} else if (mps->priv->start1 == -1
&& multipart_signed_parse_content (mps) == -1
&& byte_array->len == 0) {
g_warning ("Trying to get content on an invalid multipart/signed");
return NULL;
} else if (byte_array->len == 0) {
return NULL;
} else if (mps->priv->start1 == -1) {
stream = camel_stream_mem_new ();
camel_stream_mem_set_byte_array (
CAMEL_STREAM_MEM (stream), byte_array);
} else {
stream = multipart_signed_clip_stream (
mps, mps->priv->start1, mps->priv->end1);
}
g_seekable_seek (
G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
mps->priv->content = camel_mime_part_new ();
camel_data_wrapper_construct_from_stream_sync (
CAMEL_DATA_WRAPPER (mps->priv->content),
stream, NULL, NULL);
g_object_unref (stream);
return mps->priv->content;
case CAMEL_MULTIPART_SIGNED_SIGNATURE:
if (mps->priv->signature != NULL)
return mps->priv->signature;
if (mps->priv->start1 == -1
&& multipart_signed_parse_content (mps) == -1) {
g_warning ("Trying to get signature on invalid multipart/signed");
return NULL;
} else if (byte_array->len == 0) {
return NULL;
}
stream = multipart_signed_clip_stream (
mps, mps->priv->start2, mps->priv->end2);
g_seekable_seek (
G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
mps->priv->signature = camel_mime_part_new ();
camel_data_wrapper_construct_from_stream_sync (
CAMEL_DATA_WRAPPER (mps->priv->signature),
stream, NULL, NULL);
g_object_unref (stream);
return mps->priv->signature;
default:
g_warning ("trying to get object out of bounds for multipart");
}
return NULL;
}
static guint
multipart_signed_get_number (CamelMultipart *multipart)
{
CamelMultipartSigned *mps = (CamelMultipartSigned *) multipart;
CamelDataWrapper *data_wrapper;
GByteArray *byte_array;
data_wrapper = CAMEL_DATA_WRAPPER (multipart);
byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
/* check what we have, so we return something reasonable */
if ((mps->priv->content || mps->priv->contentraw) && mps->priv->signature)
return 2;
if (mps->priv->start1 == -1 && multipart_signed_parse_content (mps) == -1) {
if (byte_array->len == 0)
return 0;
else
return 1;
} else {
return 2;
}
}
static gint
multipart_signed_construct_from_parser (CamelMultipart *multipart,
CamelMimeParser *mp)
{
CamelMultipartSignedPrivate *priv;
gint err;
CamelContentType *content_type;
CamelDataWrapper *data_wrapper;
GByteArray *byte_array;
gchar *buf;
gsize len;
priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (multipart);
/* 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_return_val_if_fail (camel_mime_parser_state (mp) == CAMEL_MIME_PARSER_STATE_HEADER, -1);
/* All we do is copy it to a memstream */
content_type = camel_mime_parser_content_type (mp);
camel_multipart_set_boundary (multipart, camel_content_type_param (content_type, "boundary"));
data_wrapper = CAMEL_DATA_WRAPPER (multipart);
byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
/* Wipe any previous contents from the byte array. */
g_byte_array_set_size (byte_array, 0);
while (camel_mime_parser_step (mp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
g_byte_array_append (byte_array, (guint8 *) buf, len);
priv->start1 = -1;
g_clear_object (&priv->content);
g_clear_object (&priv->signature);
g_clear_object (&priv->contentraw);
err = camel_mime_parser_errno (mp);
if (err != 0) {
errno = err;
return -1;
} else
return 0;
}
static void
camel_multipart_signed_class_init (CamelMultipartSignedClass *class)
{
GObjectClass *object_class;
CamelDataWrapperClass *data_wrapper_class;
CamelMultipartClass *multipart_class;
g_type_class_add_private (class, sizeof (CamelMultipartSignedPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->dispose = multipart_signed_dispose;
data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
data_wrapper_class->write_to_stream_sync = multipart_signed_write_to_stream_sync;
data_wrapper_class->decode_to_stream_sync = multipart_signed_write_to_stream_sync;
data_wrapper_class->construct_from_stream_sync = multipart_signed_construct_from_stream_sync;
data_wrapper_class->write_to_output_stream_sync = multipart_signed_write_to_output_stream_sync;
data_wrapper_class->decode_to_output_stream_sync = multipart_signed_write_to_output_stream_sync;
data_wrapper_class->construct_from_input_stream_sync = multipart_signed_construct_from_input_stream_sync;
multipart_class = CAMEL_MULTIPART_CLASS (class);
multipart_class->add_part = multipart_signed_add_part;
multipart_class->get_part = multipart_signed_get_part;
multipart_class->get_number = multipart_signed_get_number;
multipart_class->construct_from_parser = multipart_signed_construct_from_parser;
}
static void
camel_multipart_signed_init (CamelMultipartSigned *multipart)
{
multipart->priv = CAMEL_MULTIPART_SIGNED_GET_PRIVATE (multipart);
camel_data_wrapper_set_mime_type (
CAMEL_DATA_WRAPPER (multipart), "multipart/signed");
multipart->priv->start1 = -1;
}
/**
* 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 #CAMEL_MIME_PARSER_STATE_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).
*
* Returns: a new #CamelMultipartSigned object
**/
CamelMultipartSigned *
camel_multipart_signed_new (void)
{
return g_object_new (CAMEL_TYPE_MULTIPART_SIGNED, NULL);
}
/**
* camel_multipart_signed_get_content_stream:
* @mps: a #CamlMultipartSigned object
* @error: return location for a #GError, or %NULL
*
* Get the raw signed content stream of the multipart/signed MIME part
* suitable for use with verification of the signature.
*
* Returns: the signed content stream
**/
CamelStream *
camel_multipart_signed_get_content_stream (CamelMultipartSigned *mps,
GError **error)
{
CamelStream *constream;
/* we need to be able to verify stuff we just signed as well as stuff we loaded from a stream/parser */
if (mps->priv->contentraw) {
constream = g_object_ref (mps->priv->contentraw);
} else {
CamelStream *base_stream;
CamelStream *filter_stream;
CamelMimeFilter *canon_filter;
if (mps->priv->start1 == -1 && multipart_signed_parse_content (mps) == -1) {
g_set_error (
error, CAMEL_ERROR,
CAMEL_ERROR_GENERIC,
_("parse error"));
return NULL;
}
/* first, prepare our parts */
base_stream = multipart_signed_clip_stream (
mps, mps->priv->start1, mps->priv->end1);
filter_stream = camel_stream_filter_new (base_stream);
/* Note: see rfc2015 or rfc3156, section 5 */
canon_filter = camel_mime_filter_canon_new (
CAMEL_MIME_FILTER_CANON_CRLF);
camel_stream_filter_add (
CAMEL_STREAM_FILTER (filter_stream), canon_filter);
g_object_unref (canon_filter);
/* We want to return a pure memory stream,
* so apply the CRLF filter ahead of time. */
constream = camel_stream_mem_new ();
camel_stream_write_to_stream (
filter_stream, constream, NULL, NULL);
g_object_unref (filter_stream);
g_object_unref (base_stream);
/* rewind to the beginning of a stream */
g_seekable_seek (G_SEEKABLE (constream), 0, G_SEEK_SET, NULL, NULL);
}
return constream;
}
/**
* camel_multipart_signed_set_content_stream:
* @mps: a #CamelMultipartSigned
* @content_stream: a #CamelStream
*
* Explicits sets the raw signed content stream of the multipart/signed
* MIME part.
*
* Since: 3.12
**/
void
camel_multipart_signed_set_content_stream (CamelMultipartSigned *mps,
CamelStream *content_stream)
{
g_return_if_fail (CAMEL_IS_MULTIPART_SIGNED (mps));
g_return_if_fail (CAMEL_IS_STREAM (content_stream));
g_clear_object (&mps->priv->contentraw);
mps->priv->contentraw = g_object_ref (content_stream);
}
/**
* camel_multipart_signed_set_signature:
* @mps: a #CamelMultipartSigned
* @signature: a #CamelMimePart
*
* Explicitly sets the signature part of @mps.
*
* Since: 3.12
**/
void
camel_multipart_signed_set_signature (CamelMultipartSigned *mps,
CamelMimePart *signature)
{
g_return_if_fail (CAMEL_IS_MULTIPART_SIGNED (mps));
g_return_if_fail (CAMEL_IS_MIME_PART (signature));
g_clear_object (&mps->priv->signature);
mps->priv->signature = g_object_ref (signature);
}