1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 * camel-multipart.c : Abstract class for a multipart
4 * Authors: Michael Zucchi <notzed@ximian.com>
6 * Copyright 2002 Ximian, Inc. (www.ximian.com)
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.
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.
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
38 #include <glib/gi18n-lib.h>
40 #include <libedataserver/md5-utils.h>
42 #include "camel-exception.h"
43 #include "camel-mime-filter-canon.h"
44 #include "camel-mime-filter-crlf.h"
45 #include "camel-mime-message.h"
46 #include "camel-mime-parser.h"
47 #include "camel-mime-part.h"
48 #include "camel-mime-part.h"
49 #include "camel-multipart-signed.h"
50 #include "camel-seekable-substream.h"
51 #include "camel-stream-filter.h"
52 #include "camel-stream-mem.h"
54 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))
57 static void signed_add_part (CamelMultipart *multipart, CamelMimePart *part);
58 static void signed_add_part_at (CamelMultipart *multipart, CamelMimePart *part, guint index);
59 static void signed_remove_part (CamelMultipart *multipart, CamelMimePart *part);
60 static CamelMimePart *signed_remove_part_at (CamelMultipart *multipart, guint index);
61 static CamelMimePart *signed_get_part (CamelMultipart *multipart, guint index);
62 static guint signed_get_number (CamelMultipart *multipart);
64 static ssize_t write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
65 static void set_mime_type_field (CamelDataWrapper *data_wrapper, CamelContentType *mime_type);
66 static int construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
67 static int signed_construct_from_parser (CamelMultipart *multipart, struct _CamelMimeParser *mp);
69 static CamelMultipartClass *parent_class = NULL;
71 /* Returns the class for a CamelMultipartSigned */
72 #define CMP_CLASS(so) CAMEL_MULTIPART_SIGNED_CLASS (CAMEL_OBJECT_GET_CLASS(so))
74 /* Returns the class for a CamelDataWrapper */
75 #define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
78 camel_multipart_signed_class_init (CamelMultipartSignedClass *camel_multipart_signed_class)
80 CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS(camel_multipart_signed_class);
81 CamelMultipartClass *mpclass = (CamelMultipartClass *)camel_multipart_signed_class;
83 parent_class = (CamelMultipartClass *)camel_multipart_get_type();
85 /* virtual method overload */
86 camel_data_wrapper_class->construct_from_stream = construct_from_stream;
87 camel_data_wrapper_class->write_to_stream = write_to_stream;
88 camel_data_wrapper_class->decode_to_stream = write_to_stream;
89 camel_data_wrapper_class->set_mime_type_field = set_mime_type_field;
91 mpclass->add_part = signed_add_part;
92 mpclass->add_part_at = signed_add_part_at;
93 mpclass->remove_part = signed_remove_part;
94 mpclass->remove_part_at = signed_remove_part_at;
95 mpclass->get_part = signed_get_part;
96 mpclass->get_number = signed_get_number;
97 mpclass->construct_from_parser = signed_construct_from_parser;
100 mpclass->get_boundary = signed_get_boundary;
101 mpclass->set_boundary = signed_set_boundary;
106 camel_multipart_signed_init (gpointer object, gpointer klass)
108 CamelMultipartSigned *multipart = (CamelMultipartSigned *)object;
110 camel_data_wrapper_set_mime_type(CAMEL_DATA_WRAPPER(multipart), "multipart/signed");
111 multipart->start1 = -1;
115 camel_multipart_signed_finalize (CamelObject *object)
117 CamelMultipartSigned *mps = (CamelMultipartSigned *)object;
119 g_free(mps->protocol);
122 camel_object_unref((CamelObject *)mps->signature);
124 camel_object_unref((CamelObject *)mps->content);
126 camel_object_unref((CamelObject *)mps->contentraw);
130 camel_multipart_signed_get_type (void)
132 static CamelType camel_multipart_signed_type = CAMEL_INVALID_TYPE;
134 if (camel_multipart_signed_type == CAMEL_INVALID_TYPE) {
135 camel_multipart_signed_type = camel_type_register (camel_multipart_get_type (), "CamelMultipartSigned",
136 sizeof (CamelMultipartSigned),
137 sizeof (CamelMultipartSignedClass),
138 (CamelObjectClassInitFunc) camel_multipart_signed_class_init,
140 (CamelObjectInitFunc) camel_multipart_signed_init,
141 (CamelObjectFinalizeFunc) camel_multipart_signed_finalize);
144 return camel_multipart_signed_type;
148 * camel_multipart_signed_new:
150 * Create a new #CamelMultipartSigned object.
152 * A MultipartSigned should be used to store and create parts of
153 * type "multipart/signed". This is because multipart/signed is
154 * entirely broken-by-design (tm) and uses completely
155 * different semantics to other mutlipart types. It must be treated
156 * as opaque data by any transport. See rfc 3156 for details.
158 * There are 3 ways to create the part:
159 * Use construct_from_stream. If this is used, then you must
160 * set the mime_type appropriately to match the data uses, so
161 * that the multiple parts my be extracted.
163 * Use construct_from_parser. The parser MUST be in the #CAMEL_MIME_PARSER_STATE_HEADER
164 * state, and the current content_type MUST be "multipart/signed" with
165 * the appropriate boundary and it SHOULD include the appropriate protocol
166 * and hash specifiers.
168 * Use sign_part. A signature part will automatically be created
169 * and the whole part may be written using write_to_stream to
170 * create a 'transport-safe' version (as safe as can be expected with
171 * such a broken specification).
173 * Returns a new #CamelMultipartSigned object
175 CamelMultipartSigned *
176 camel_multipart_signed_new (void)
178 CamelMultipartSigned *multipart;
180 multipart = (CamelMultipartSigned *)camel_object_new(CAMEL_MULTIPART_SIGNED_TYPE);
186 skip_content(CamelMimeParser *cmp)
192 switch (camel_mime_parser_state(cmp)) {
193 case CAMEL_MIME_PARSER_STATE_HEADER:
195 while (camel_mime_parser_step(cmp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
198 case CAMEL_MIME_PARSER_STATE_MESSAGE:
199 /* message body part */
200 (void)camel_mime_parser_step(cmp, &buf, &len);
203 /* clean up followon state if any, see camel-mime-message.c */
204 state = camel_mime_parser_step(cmp, &buf, &len);
206 case CAMEL_MIME_PARSER_STATE_EOF:
207 case CAMEL_MIME_PARSER_STATE_FROM_END: /* these doesn't belong to us */
208 camel_mime_parser_unstep(cmp);
209 case CAMEL_MIME_PARSER_STATE_MESSAGE_END:
212 g_error ("Bad parser state: Expecting MESSAGE_END or EOF or EOM, got: %u", camel_mime_parser_state (cmp));
213 camel_mime_parser_unstep(cmp);
217 case CAMEL_MIME_PARSER_STATE_MULTIPART:
218 /* embedded multipart */
219 while ((state = camel_mime_parser_step(cmp, &buf, &len)) != CAMEL_MIME_PARSER_STATE_MULTIPART_END)
223 g_warning("Invalid state encountered???: %u", camel_mime_parser_state (cmp));
230 parse_content(CamelMultipartSigned *mps)
232 CamelMimeParser *cmp;
233 CamelMultipart *mp = (CamelMultipart *)mps;
235 const char *boundary;
240 boundary = camel_multipart_get_boundary(mp);
241 if (boundary == NULL) {
242 g_warning("Trying to get multipart/signed content without setting boundary first");
246 mem = (CamelStreamMem *)((CamelDataWrapper *)mps)->stream;
248 g_warning("Trying to parse multipart/signed without constructing first");
252 /* This is all seriously complex.
253 This is so we can parse all cases properly, without altering the content.
254 All we are doing is finding part offsets. */
256 camel_stream_reset((CamelStream *)mem);
257 cmp = camel_mime_parser_new();
258 camel_mime_parser_init_with_stream(cmp, (CamelStream *)mem);
259 camel_mime_parser_push_state(cmp, CAMEL_MIME_PARSER_STATE_MULTIPART, boundary);
266 while ((state = camel_mime_parser_step(cmp, &buf, &len)) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
267 if (mps->start1 == -1) {
268 mps->start1 = camel_mime_parser_tell_start_headers(cmp);
269 } else if (mps->start2 == -1) {
270 mps->start2 = camel_mime_parser_tell_start_headers(cmp);
271 mps->end1 = camel_mime_parser_tell_start_boundary(cmp);
272 if (mps->end1 > mps->start1 && mem->buffer->data[mps->end1-1] == '\n')
274 if (mps->end1 > mps->start1 && mem->buffer->data[mps->end1-1] == '\r')
277 g_warning("multipart/signed has more than 2 parts, remaining parts ignored");
281 if (skip_content(cmp) == -1)
285 if (state == CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
286 mps->end2 = camel_mime_parser_tell_start_boundary(cmp);
288 camel_multipart_set_preface(mp, camel_mime_parser_preface(cmp));
289 camel_multipart_set_postface(mp, camel_mime_parser_postface(cmp));
292 camel_object_unref(cmp);
294 if (mps->end2 == -1 || mps->start2 == -1) {
301 /* we snoop the mime type to get boundary and hash info */
303 set_mime_type_field(CamelDataWrapper *data_wrapper, CamelContentType *mime_type)
305 CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
307 ((CamelDataWrapperClass *)parent_class)->set_mime_type_field(data_wrapper, mime_type);
309 const char *micalg, *protocol;
311 protocol = camel_content_type_param(mime_type, "protocol");
312 g_free(mps->protocol);
313 mps->protocol = g_strdup(protocol);
315 micalg = camel_content_type_param(mime_type, "micalg");
317 mps->micalg = g_strdup(micalg);
322 signed_add_part(CamelMultipart *multipart, CamelMimePart *part)
324 g_warning("Cannot add parts to a signed part using add_part");
328 signed_add_part_at(CamelMultipart *multipart, CamelMimePart *part, guint index)
330 g_warning("Cannot add parts to a signed part using add_part_at");
334 signed_remove_part(CamelMultipart *multipart, CamelMimePart *part)
336 g_warning("Cannot remove parts from a signed part using remove_part");
339 static CamelMimePart *
340 signed_remove_part_at (CamelMultipart *multipart, guint index)
342 g_warning("Cannot remove parts from a signed part using remove_part");
346 static CamelMimePart *
347 signed_get_part(CamelMultipart *multipart, guint index)
349 CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
350 CamelDataWrapper *dw = (CamelDataWrapper *)multipart;
354 case CAMEL_MULTIPART_SIGNED_CONTENT:
357 if (mps->contentraw) {
358 stream = mps->contentraw;
359 camel_object_ref((CamelObject *)stream);
360 } else if (mps->start1 == -1
361 && parse_content(mps) == -1
362 && (stream = ((CamelDataWrapper *)mps)->stream) == NULL) {
363 g_warning("Trying to get content on an invalid multipart/signed");
365 } else if (dw->stream == NULL) {
367 } else if (mps->start1 == -1) {
369 camel_object_ref(stream);
371 stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start1, mps->end1);
373 camel_stream_reset(stream);
374 mps->content = camel_mime_part_new();
375 camel_data_wrapper_construct_from_stream((CamelDataWrapper *)mps->content, stream);
376 camel_object_unref(stream);
378 case CAMEL_MULTIPART_SIGNED_SIGNATURE:
380 return mps->signature;
381 if (mps->start1 == -1
382 && parse_content(mps) == -1) {
383 g_warning("Trying to get signature on invalid multipart/signed");
385 } else if (dw->stream == NULL) {
388 stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start2, mps->end2);
389 camel_stream_reset(stream);
390 mps->signature = camel_mime_part_new();
391 camel_data_wrapper_construct_from_stream((CamelDataWrapper *)mps->signature, stream);
392 camel_object_unref((CamelObject *)stream);
393 return mps->signature;
395 g_warning("trying to get object out of bounds for multipart");
402 signed_get_number(CamelMultipart *multipart)
404 CamelDataWrapper *dw = (CamelDataWrapper *)multipart;
405 CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
407 /* check what we have, so we return something reasonable */
409 if ((mps->content || mps->contentraw) && mps->signature)
412 if (mps->start1 == -1 && parse_content(mps) == -1) {
413 if (dw->stream == NULL)
423 set_stream(CamelMultipartSigned *mps, CamelStream *mem)
425 CamelDataWrapper *dw = (CamelDataWrapper *)mps;
428 camel_object_unref((CamelObject *)dw->stream);
429 dw->stream = (CamelStream *)mem;
433 camel_object_unref((CamelObject *)mps->content);
436 if (mps->contentraw) {
437 camel_object_unref((CamelObject *)mps->contentraw);
438 mps->contentraw = NULL;
440 if (mps->signature) {
441 camel_object_unref((CamelObject *)mps->signature);
442 mps->signature = NULL;
447 construct_from_stream(CamelDataWrapper *data_wrapper, CamelStream *stream)
449 CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
450 CamelStream *mem = camel_stream_mem_new();
452 if (camel_stream_write_to_stream(stream, mem) == -1)
455 set_stream(mps, mem);
461 signed_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp)
464 CamelContentType *content_type;
465 CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
470 /* we *must not* be in multipart state, otherwise the mime parser will
471 parse the headers which is a no no @#$@# stupid multipart/signed spec */
472 g_assert(camel_mime_parser_state(mp) == CAMEL_MIME_PARSER_STATE_HEADER);
474 /* All we do is copy it to a memstream */
475 content_type = camel_mime_parser_content_type(mp);
476 camel_multipart_set_boundary(multipart, camel_content_type_param(content_type, "boundary"));
478 mem = camel_stream_mem_new();
479 while (camel_mime_parser_step(mp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
480 camel_stream_write(mem, buf, len);
482 set_stream(mps, mem);
484 err = camel_mime_parser_errno(mp);
493 write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
495 CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
496 CamelMultipart *mp = (CamelMultipart *)mps;
497 const char *boundary;
501 /* we have 3 basic cases:
502 1. constructed, we write out the data wrapper stream we got
503 2. signed content, we create and write out a new stream
508 /* FIXME: locking? */
509 if (data_wrapper->stream) {
510 camel_stream_reset(data_wrapper->stream);
511 return camel_stream_write_to_stream(data_wrapper->stream, stream);
515 if (mps->signature == NULL || mps->contentraw == NULL)
519 boundary = camel_multipart_get_boundary(mp);
521 count = camel_stream_write_string(stream, mp->preface);
528 count = camel_stream_printf(stream, "\n--%s\n", boundary);
533 /* output content part */
534 camel_stream_reset(mps->contentraw);
535 count = camel_stream_write_to_stream(mps->contentraw, stream);
541 count = camel_stream_printf(stream, "\n--%s\n", boundary);
547 count = camel_data_wrapper_write_to_stream((CamelDataWrapper *)mps->signature, stream);
552 /* write the terminating boudary delimiter */
553 count = camel_stream_printf(stream, "\n--%s--\n", boundary);
558 /* and finally the postface */
560 count = camel_stream_write_string(stream, mp->postface);
571 * camel_multipart_signed_get_content_stream:
572 * @mps: a #CamlMultipartSigned object
573 * @ex: a #CamelException
575 * Get the raw signed content stream of the multipart/signed MIME part
576 * suitable for use with verification of the signature.
578 * Returns the signed content stream
581 camel_multipart_signed_get_content_stream(CamelMultipartSigned *mps, CamelException *ex)
583 CamelStream *constream;
585 /* we need to be able to verify stuff we just signed as well as stuff we loaded from a stream/parser */
587 if (mps->contentraw) {
588 constream = mps->contentraw;
589 camel_object_ref((CamelObject *)constream);
592 CamelMimeFilter *canon_filter;
594 if (mps->start1 == -1 && parse_content(mps) == -1) {
595 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("parse error"));
599 /* first, prepare our parts */
600 sub = camel_seekable_substream_new((CamelSeekableStream *)((CamelDataWrapper *)mps)->stream, mps->start1, mps->end1);
601 constream = (CamelStream *)camel_stream_filter_new_with_stream(sub);
602 camel_object_unref((CamelObject *)sub);
604 /* Note: see rfc2015 or rfc3156, section 5 */
605 canon_filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF);
606 camel_stream_filter_add((CamelStreamFilter *)constream, (CamelMimeFilter *)canon_filter);
607 camel_object_unref((CamelObject *)canon_filter);