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