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