Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-multipart-signed.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  * camel-multipart.c : Abstract class for a multipart
3  *
4  * Authors: Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20  * USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32
33 #include <glib/gi18n-lib.h>
34
35 #include "camel-mime-filter-canon.h"
36 #include "camel-mime-filter-crlf.h"
37 #include "camel-mime-message.h"
38 #include "camel-mime-parser.h"
39 #include "camel-mime-part.h"
40 #include "camel-multipart-signed.h"
41 #include "camel-stream-filter.h"
42 #include "camel-stream-mem.h"
43
44 #define d(x) /* (printf("%s(%d): ", __FILE__, __LINE__),(x)) */
45
46 G_DEFINE_TYPE (CamelMultipartSigned, camel_multipart_signed, CAMEL_TYPE_MULTIPART)
47
48 static CamelStream *
49 multipart_signed_clip_stream (CamelMultipartSigned *mps,
50                               goffset start,
51                               goffset end)
52 {
53         CamelDataWrapper *data_wrapper;
54         GByteArray *src;
55         GByteArray *dst;
56
57         g_return_val_if_fail (start != -1, NULL);
58         g_return_val_if_fail (end != -1, NULL);
59         g_return_val_if_fail (end >= start, NULL);
60
61         data_wrapper = CAMEL_DATA_WRAPPER (mps);
62
63         src = camel_data_wrapper_get_byte_array (data_wrapper);
64         dst = g_byte_array_new ();
65
66         if (start >= 0 && end < src->len) {
67                 const guint8 *data = &src->data[start];
68                 g_byte_array_append (dst, data, end - start);
69         }
70
71         /* Stream takes ownership of the byte array. */
72         return camel_stream_mem_new_with_byte_array (dst);
73 }
74
75 static gint
76 multipart_signed_skip_content (CamelMimeParser *cmp)
77 {
78         gchar *buf;
79         gsize len;
80         gint state;
81
82         switch (camel_mime_parser_state (cmp)) {
83         case CAMEL_MIME_PARSER_STATE_HEADER:
84                 /* body part */
85                 while (camel_mime_parser_step (cmp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
86                         /* NOOP */ ;
87                 break;
88         case CAMEL_MIME_PARSER_STATE_MESSAGE:
89                 /* message body part */
90                 (void) camel_mime_parser_step (cmp, &buf, &len);
91                 multipart_signed_skip_content (cmp);
92
93                 /* clean up followon state if any, see camel-mime-message.c */
94                 state = camel_mime_parser_step (cmp, &buf, &len);
95                 switch (state) {
96                 case CAMEL_MIME_PARSER_STATE_EOF:
97                 case CAMEL_MIME_PARSER_STATE_FROM_END: /* these doesn't belong to us */
98                         camel_mime_parser_unstep (cmp);
99                 case CAMEL_MIME_PARSER_STATE_MESSAGE_END:
100                         break;
101                 default:
102                         g_error ("Bad parser state: Expecting MESSAGE_END or EOF or EOM, got: %u", camel_mime_parser_state (cmp));
103                         camel_mime_parser_unstep (cmp);
104                         return -1;
105                 }
106                 break;
107         case CAMEL_MIME_PARSER_STATE_MULTIPART:
108                 /* embedded multipart */
109                 while (camel_mime_parser_step (cmp, &buf, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END)
110                         multipart_signed_skip_content (cmp);
111                 break;
112         default:
113                 g_warning ("Invalid state encountered???: %u", camel_mime_parser_state (cmp));
114         }
115
116         return 0;
117 }
118
119 static gint
120 multipart_signed_parse_content (CamelMultipartSigned *mps)
121 {
122         CamelMimeParser *cmp;
123         CamelMultipart *mp = (CamelMultipart *) mps;
124         CamelDataWrapper *data_wrapper;
125         GByteArray *byte_array;
126         CamelStream *stream;
127         const gchar *boundary;
128         gchar *buf;
129         gsize len;
130         gint state;
131
132         boundary = camel_multipart_get_boundary (mp);
133         g_return_val_if_fail (boundary != NULL, -1);
134
135         data_wrapper = CAMEL_DATA_WRAPPER (mps);
136         byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
137
138         stream = camel_stream_mem_new ();
139
140         /* Do not give the stream ownership of the byte array. */
141         camel_stream_mem_set_byte_array (
142                 CAMEL_STREAM_MEM (stream), byte_array);
143
144         /* This is all seriously complex.
145          * This is so we can parse all cases properly, without altering the content.
146          * All we are doing is finding part offsets. */
147
148         cmp = camel_mime_parser_new ();
149         camel_mime_parser_init_with_stream (cmp, stream, NULL);
150         camel_mime_parser_push_state (cmp, CAMEL_MIME_PARSER_STATE_MULTIPART, boundary);
151
152         mps->start1 = -1;
153         mps->end1 = -1;
154         mps->start2 = -1;
155         mps->end2 = -1;
156
157         while ((state = camel_mime_parser_step (cmp, &buf, &len)) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
158                 if (mps->start1 == -1) {
159                         mps->start1 = camel_mime_parser_tell_start_headers (cmp);
160                 } else if (mps->start2 == -1) {
161                         mps->start2 = camel_mime_parser_tell_start_headers (cmp);
162                         mps->end1 = camel_mime_parser_tell_start_boundary (cmp);
163                         if (mps->end1 > mps->start1 && byte_array->data[mps->end1 - 1] == '\n')
164                                 mps->end1--;
165                         if (mps->end1 > mps->start1 && byte_array->data[mps->end1 - 1] == '\r')
166                                 mps->end1--;
167                 } else {
168                         g_warning ("multipart/signed has more than 2 parts, remaining parts ignored");
169                         state = CAMEL_MIME_PARSER_STATE_MULTIPART_END;
170                         break;
171                 }
172
173                 if (multipart_signed_skip_content (cmp) == -1)
174                         break;
175         }
176
177         if (state == CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
178                 mps->end2 = camel_mime_parser_tell_start_boundary (cmp);
179
180                 camel_multipart_set_preface (mp, camel_mime_parser_preface (cmp));
181                 camel_multipart_set_postface (mp, camel_mime_parser_postface (cmp));
182         }
183
184         g_object_unref (cmp);
185         g_object_unref (stream);
186
187         if (mps->end2 == -1 || mps->start2 == -1) {
188                 if (mps->end1 == -1)
189                         mps->start1 = -1;
190
191                 return -1;
192         }
193
194         return 0;
195 }
196
197 static void
198 multipart_signed_dispose (GObject *object)
199 {
200         CamelMultipartSigned *multipart;
201
202         multipart = CAMEL_MULTIPART_SIGNED (object);
203
204         if (multipart->signature != NULL) {
205                 g_object_unref (multipart->signature);
206                 multipart->signature = NULL;
207         }
208
209         if (multipart->content != NULL) {
210                 g_object_unref (multipart->content);
211                 multipart->content = NULL;
212         }
213
214         if (multipart->contentraw != NULL) {
215                 g_object_unref (multipart->contentraw);
216                 multipart->contentraw = NULL;
217         }
218
219         /* Chain up to parent's dispose() method. */
220         G_OBJECT_CLASS (camel_multipart_signed_parent_class)->dispose (object);
221 }
222
223 static void
224 multipart_signed_finalize (GObject *object)
225 {
226         CamelMultipartSigned *multipart;
227
228         multipart = CAMEL_MULTIPART_SIGNED (object);
229
230         g_free (multipart->protocol);
231         g_free (multipart->micalg);
232
233         /* Chain up to parent's finalize() method. */
234         G_OBJECT_CLASS (camel_multipart_signed_parent_class)->finalize (object);
235 }
236
237 static void
238 multipart_signed_set_mime_type_field (CamelDataWrapper *data_wrapper,
239                                       CamelContentType *mime_type)
240 {
241         CamelDataWrapperClass *data_wrapper_class;
242         CamelMultipartSigned *mps = (CamelMultipartSigned *) data_wrapper;
243
244         /* we snoop the mime type to get boundary and hash info */
245
246         /* Chain up to parent's set_mime_type_field() method. */
247         data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (camel_multipart_signed_parent_class);
248         data_wrapper_class->set_mime_type_field (data_wrapper, mime_type);
249
250         if (mime_type) {
251                 const gchar *micalg, *protocol;
252
253                 protocol = camel_content_type_param (mime_type, "protocol");
254                 g_free (mps->protocol);
255                 mps->protocol = g_strdup (protocol);
256
257                 micalg = camel_content_type_param (mime_type, "micalg");
258                 g_free (mps->micalg);
259                 mps->micalg = g_strdup (micalg);
260         }
261 }
262
263 static gssize
264 multipart_signed_write_to_stream_sync (CamelDataWrapper *data_wrapper,
265                                        CamelStream *stream,
266                                        GCancellable *cancellable,
267                                        GError **error)
268 {
269         CamelMultipartSigned *mps = (CamelMultipartSigned *) data_wrapper;
270         CamelMultipart *mp = (CamelMultipart *) mps;
271         GByteArray *byte_array;
272         const gchar *boundary;
273         gssize total = 0;
274         gssize count;
275         gchar *content;
276
277         byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
278
279         /* we have 3 basic cases:
280          * 1. constructed, we write out the data wrapper stream we got
281          * 2. signed content, we create and write out a new stream
282          * 3. invalid
283         */
284
285         /* 1 */
286         /* FIXME: locking? */
287         if (byte_array->len > 0) {
288                 return camel_stream_write (
289                         stream, (gchar *) byte_array->data,
290                         byte_array->len, cancellable, error);
291         }
292
293         /* 3 */
294         if (mps->contentraw == NULL) {
295                 g_set_error (
296                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
297                         _("No content available"));
298                 return -1;
299         }
300
301         /* 3 */
302         if (mps->signature == NULL) {
303                 g_set_error (
304                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
305                         _("No signature available"));
306                 return -1;
307         }
308
309         /* 2 */
310         boundary = camel_multipart_get_boundary (mp);
311         if (mp->preface) {
312                 count = camel_stream_write_string (
313                         stream, mp->preface, cancellable, error);
314                 if (count == -1)
315                         return -1;
316                 total += count;
317         }
318
319         /* first boundary */
320         content = g_strdup_printf ("\n--%s\n", boundary);
321         count = camel_stream_write_string (
322                 stream, content, cancellable, error);
323         g_free (content);
324         if (count == -1)
325                 return -1;
326         total += count;
327
328         /* output content part */
329         /* XXX Both CamelGpgContext and CamelSMIMEContext set this
330          *     to a memory stream, so assume it's always seekable. */
331         g_seekable_seek (
332                 G_SEEKABLE (mps->contentraw), 0, G_SEEK_SET, NULL, NULL);
333         count = camel_stream_write_to_stream (
334                 mps->contentraw, stream, cancellable, error);
335         if (count == -1)
336                 return -1;
337         total += count;
338
339         /* boundary */
340         content = g_strdup_printf ("\n--%s\n", boundary);
341         count = camel_stream_write_string (
342                 stream, content, cancellable, error);
343         g_free (content);
344         if (count == -1)
345                 return -1;
346         total += count;
347
348         /* signature */
349         count = camel_data_wrapper_write_to_stream_sync (
350                 CAMEL_DATA_WRAPPER (mps->signature),
351                 stream, cancellable, error);
352         if (count == -1)
353                 return -1;
354         total += count;
355
356         /* write the terminating boudary delimiter */
357         content = g_strdup_printf ("\n--%s--\n", boundary);
358         count = camel_stream_write_string (
359                 stream, content, cancellable, error);
360         g_free (content);
361         if (count == -1)
362                 return -1;
363         total += count;
364
365         /* and finally the postface */
366         if (mp->postface) {
367                 count = camel_stream_write_string (
368                         stream, mp->postface, cancellable, error);
369                 if (count == -1)
370                         return -1;
371                 total += count;
372         }
373
374         return total;
375 }
376
377 static gboolean
378 multipart_signed_construct_from_stream_sync (CamelDataWrapper *data_wrapper,
379                                              CamelStream *stream,
380                                              GCancellable *cancellable,
381                                              GError **error)
382 {
383         CamelDataWrapperClass *parent_class;
384         CamelMultipartSigned *mps;
385         gboolean success;
386
387         mps = CAMEL_MULTIPART_SIGNED (data_wrapper);
388
389         /* Chain up to parent's construct_from_stream_sync() method. */
390         parent_class = CAMEL_DATA_WRAPPER_CLASS (
391                 camel_multipart_signed_parent_class);
392         success = parent_class->construct_from_stream_sync (
393                 data_wrapper, stream, cancellable, error);
394
395         if (success) {
396                 mps->start1 = -1;
397                 if (mps->content != NULL) {
398                         g_object_unref (mps->content);
399                         mps->content = NULL;
400                 }
401                 if (mps->contentraw != NULL) {
402                         g_object_unref (mps->contentraw);
403                         mps->contentraw = NULL;
404                 }
405                 if (mps->signature != NULL) {
406                         g_object_unref (mps->signature);
407                         mps->signature = NULL;
408                 }
409         }
410
411         return success;
412 }
413
414 static void
415 multipart_signed_add_part (CamelMultipart *multipart,
416                            CamelMimePart *part)
417 {
418         g_warning ("Cannot add parts to a signed part using add_part");
419 }
420
421 static void
422 multipart_signed_add_part_at (CamelMultipart *multipart,
423                               CamelMimePart *part,
424                               guint index)
425 {
426         g_warning ("Cannot add parts to a signed part using add_part_at");
427 }
428
429 static void
430 multipart_signed_remove_part (CamelMultipart *multipart,
431                               CamelMimePart *part)
432 {
433         g_warning ("Cannot remove parts from a signed part using remove_part");
434 }
435
436 static CamelMimePart *
437 multipart_signed_remove_part_at (CamelMultipart *multipart,
438                                  guint index)
439 {
440         g_warning ("Cannot remove parts from a signed part using remove_part");
441
442         return NULL;
443 }
444
445 static CamelMimePart *
446 multipart_signed_get_part (CamelMultipart *multipart,
447                            guint index)
448 {
449         CamelMultipartSigned *mps = (CamelMultipartSigned *) multipart;
450         CamelDataWrapper *data_wrapper;
451         CamelStream *stream;
452         GByteArray *byte_array;
453
454         data_wrapper = CAMEL_DATA_WRAPPER (multipart);
455         byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
456
457         switch (index) {
458         case CAMEL_MULTIPART_SIGNED_CONTENT:
459                 if (mps->content)
460                         return mps->content;
461                 if (mps->contentraw) {
462                         g_return_val_if_fail (
463                                 G_IS_SEEKABLE (mps->contentraw), NULL);
464                         stream = g_object_ref (mps->contentraw);
465                 } else if (mps->start1 == -1
466                            && multipart_signed_parse_content (mps) == -1
467                            && byte_array->len == 0) {
468                         g_warning ("Trying to get content on an invalid multipart/signed");
469                         return NULL;
470                 } else if (byte_array->len == 0) {
471                         return NULL;
472                 } else if (mps->start1 == -1) {
473                         stream = camel_stream_mem_new ();
474                         camel_stream_mem_set_byte_array (
475                                 CAMEL_STREAM_MEM (stream), byte_array);
476                 } else {
477                         stream = multipart_signed_clip_stream (
478                                 mps, mps->start1, mps->end1);
479                 }
480                 g_seekable_seek (
481                         G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
482                 mps->content = camel_mime_part_new ();
483                 camel_data_wrapper_construct_from_stream_sync (
484                         CAMEL_DATA_WRAPPER (mps->content), stream, NULL, NULL);
485                 g_object_unref (stream);
486                 return mps->content;
487         case CAMEL_MULTIPART_SIGNED_SIGNATURE:
488                 if (mps->signature)
489                         return mps->signature;
490                 if (mps->start1 == -1
491                     && multipart_signed_parse_content (mps) == -1) {
492                         g_warning ("Trying to get signature on invalid multipart/signed");
493                         return NULL;
494                 } else if (byte_array->len == 0) {
495                         return NULL;
496                 }
497                 stream = multipart_signed_clip_stream (
498                         mps, mps->start2, mps->end2);
499                 g_seekable_seek (
500                         G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
501                 mps->signature = camel_mime_part_new ();
502                 camel_data_wrapper_construct_from_stream_sync (
503                         CAMEL_DATA_WRAPPER (mps->signature),
504                         stream, NULL, NULL);
505                 g_object_unref (stream);
506                 return mps->signature;
507         default:
508                 g_warning ("trying to get object out of bounds for multipart");
509         }
510
511         return NULL;
512 }
513
514 static guint
515 multipart_signed_get_number (CamelMultipart *multipart)
516 {
517         CamelMultipartSigned *mps = (CamelMultipartSigned *) multipart;
518         CamelDataWrapper *data_wrapper;
519         GByteArray *byte_array;
520
521         data_wrapper = CAMEL_DATA_WRAPPER (multipart);
522         byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
523
524         /* check what we have, so we return something reasonable */
525
526         if ((mps->content || mps->contentraw) && mps->signature)
527                 return 2;
528
529         if (mps->start1 == -1 && multipart_signed_parse_content (mps) == -1) {
530                 if (byte_array->len == 0)
531                         return 0;
532                 else
533                         return 1;
534         } else {
535                 return 2;
536         }
537 }
538
539 static gint
540 multipart_signed_construct_from_parser (CamelMultipart *multipart,
541                                         CamelMimeParser *mp)
542 {
543         gint err;
544         CamelContentType *content_type;
545         CamelMultipartSigned *mps = (CamelMultipartSigned *) multipart;
546         CamelDataWrapper *data_wrapper;
547         GByteArray *byte_array;
548         gchar *buf;
549         gsize len;
550
551         /* we *must not* be in multipart state, otherwise the mime parser will
552          * parse the headers which is a no no @#$@# stupid multipart/signed spec */
553         g_assert (camel_mime_parser_state (mp) == CAMEL_MIME_PARSER_STATE_HEADER);
554
555         /* All we do is copy it to a memstream */
556         content_type = camel_mime_parser_content_type (mp);
557         camel_multipart_set_boundary (multipart, camel_content_type_param (content_type, "boundary"));
558
559         data_wrapper = CAMEL_DATA_WRAPPER (multipart);
560         byte_array = camel_data_wrapper_get_byte_array (data_wrapper);
561
562         /* Wipe any previous contents from the byte array. */
563         g_byte_array_set_size (byte_array, 0);
564
565         while (camel_mime_parser_step (mp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
566                 g_byte_array_append (byte_array, (guint8 *) buf, len);
567
568         mps->start1 = -1;
569         if (mps->content) {
570                 g_object_unref (mps->content);
571                 mps->content = NULL;
572         }
573         if (mps->contentraw) {
574                 g_object_unref (mps->contentraw);
575                 mps->contentraw = NULL;
576         }
577         if (mps->signature) {
578                 g_object_unref (mps->signature);
579                 mps->signature = NULL;
580         }
581
582         err = camel_mime_parser_errno (mp);
583         if (err != 0) {
584                 errno = err;
585                 return -1;
586         } else
587                 return 0;
588 }
589
590 static void
591 camel_multipart_signed_class_init (CamelMultipartSignedClass *class)
592 {
593         GObjectClass *object_class;
594         CamelDataWrapperClass *data_wrapper_class;
595         CamelMultipartClass *multipart_class;
596
597         object_class = G_OBJECT_CLASS (class);
598         object_class->dispose = multipart_signed_dispose;
599         object_class->finalize = multipart_signed_finalize;
600
601         data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
602         data_wrapper_class->set_mime_type_field = multipart_signed_set_mime_type_field;
603         data_wrapper_class->write_to_stream_sync = multipart_signed_write_to_stream_sync;
604         data_wrapper_class->decode_to_stream_sync = multipart_signed_write_to_stream_sync;
605         data_wrapper_class->construct_from_stream_sync = multipart_signed_construct_from_stream_sync;
606
607         multipart_class = CAMEL_MULTIPART_CLASS (class);
608         multipart_class->add_part = multipart_signed_add_part;
609         multipart_class->add_part_at = multipart_signed_add_part_at;
610         multipart_class->remove_part = multipart_signed_remove_part;
611         multipart_class->remove_part_at = multipart_signed_remove_part_at;
612         multipart_class->get_part = multipart_signed_get_part;
613         multipart_class->get_number = multipart_signed_get_number;
614         multipart_class->construct_from_parser = multipart_signed_construct_from_parser;
615 }
616
617 static void
618 camel_multipart_signed_init (CamelMultipartSigned *multipart)
619 {
620         camel_data_wrapper_set_mime_type (
621                 CAMEL_DATA_WRAPPER (multipart), "multipart/signed");
622
623         multipart->start1 = -1;
624 }
625
626 /**
627  * camel_multipart_signed_new:
628  *
629  * Create a new #CamelMultipartSigned object.
630  *
631  * A MultipartSigned should be used to store and create parts of
632  * type "multipart/signed".  This is because multipart/signed is
633  * entirely broken-by-design (tm) and uses completely
634  * different semantics to other mutlipart types.  It must be treated
635  * as opaque data by any transport.  See rfc 3156 for details.
636  *
637  * There are 3 ways to create the part:
638  * Use construct_from_stream.  If this is used, then you must
639  * set the mime_type appropriately to match the data uses, so
640  * that the multiple parts my be extracted.
641  *
642  * Use construct_from_parser.  The parser MUST be in the #CAMEL_MIME_PARSER_STATE_HEADER
643  * state, and the current content_type MUST be "multipart/signed" with
644  * the appropriate boundary and it SHOULD include the appropriate protocol
645  * and hash specifiers.
646  *
647  * Use sign_part.  A signature part will automatically be created
648  * and the whole part may be written using write_to_stream to
649  * create a 'transport-safe' version (as safe as can be expected with
650  * such a broken specification).
651  *
652  * Returns: a new #CamelMultipartSigned object
653  **/
654 CamelMultipartSigned *
655 camel_multipart_signed_new (void)
656 {
657         return g_object_new (CAMEL_TYPE_MULTIPART_SIGNED, NULL);
658 }
659
660 /**
661  * camel_multipart_signed_get_content_stream:
662  * @mps: a #CamlMultipartSigned object
663  * @error: return location for a #GError, or %NULL
664  *
665  * Get the raw signed content stream of the multipart/signed MIME part
666  * suitable for use with verification of the signature.
667  *
668  * Returns: the signed content stream
669  **/
670 CamelStream *
671 camel_multipart_signed_get_content_stream (CamelMultipartSigned *mps,
672                                            GError **error)
673 {
674         CamelStream *constream;
675
676         /* we need to be able to verify stuff we just signed as well as stuff we loaded from a stream/parser */
677
678         if (mps->contentraw) {
679                 constream = g_object_ref (mps->contentraw);
680         } else {
681                 CamelStream *base_stream;
682                 CamelStream *filter_stream;
683                 CamelMimeFilter *canon_filter;
684
685                 if (mps->start1 == -1 && multipart_signed_parse_content (mps) == -1) {
686                         g_set_error (
687                                 error, CAMEL_ERROR,
688                                 CAMEL_ERROR_GENERIC,
689                                 _("parse error"));
690                         return NULL;
691                 }
692
693                 /* first, prepare our parts */
694
695                 base_stream = multipart_signed_clip_stream (
696                         mps, mps->start1, mps->end1);
697
698                 filter_stream = camel_stream_filter_new (base_stream);
699
700                 /* Note: see rfc2015 or rfc3156, section 5 */
701                 canon_filter = camel_mime_filter_canon_new (
702                         CAMEL_MIME_FILTER_CANON_CRLF);
703                 camel_stream_filter_add (
704                         CAMEL_STREAM_FILTER (filter_stream), canon_filter);
705                 g_object_unref (canon_filter);
706
707                 /* We want to return a pure memory stream,
708                  * so apply the CRLF filter ahead of time. */
709                 constream = camel_stream_mem_new ();
710                 camel_stream_write_to_stream (
711                         filter_stream, constream, NULL, NULL);
712
713                 g_object_unref (filter_stream);
714                 g_object_unref (base_stream);
715
716                 /* rewind to the beginning of a stream */
717                 g_seekable_seek (G_SEEKABLE (constream), 0, G_SEEK_SET, NULL, NULL);
718         }
719
720         return constream;
721 }