Fix FSF address (Tobias Mueller, #470445)
[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 2002 Ximian, Inc. (www.ximian.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 #ifdef HAVE_ALLOCA_H
34 #include <alloca.h>
35 #endif
36
37 #include <glib.h>
38 #include <glib/gi18n-lib.h>
39
40 #include <libedataserver/md5-utils.h>
41
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"
53
54 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))
55                #include <stdio.h>;*/
56
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);
63
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);
68
69 static CamelMultipartClass *parent_class = NULL;
70
71 /* Returns the class for a CamelMultipartSigned */
72 #define CMP_CLASS(so) CAMEL_MULTIPART_SIGNED_CLASS (CAMEL_OBJECT_GET_CLASS(so))
73
74 /* Returns the class for a CamelDataWrapper */
75 #define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
76
77 static void
78 camel_multipart_signed_class_init (CamelMultipartSignedClass *camel_multipart_signed_class)
79 {
80         CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS(camel_multipart_signed_class);
81         CamelMultipartClass *mpclass = (CamelMultipartClass *)camel_multipart_signed_class;
82
83         parent_class = (CamelMultipartClass *)camel_multipart_get_type();
84
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;
90
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;
98
99 /*
100         mpclass->get_boundary = signed_get_boundary;
101         mpclass->set_boundary = signed_set_boundary;
102 */
103 }
104
105 static void
106 camel_multipart_signed_init (gpointer object, gpointer klass)
107 {
108         CamelMultipartSigned *multipart = (CamelMultipartSigned *)object;
109
110         camel_data_wrapper_set_mime_type(CAMEL_DATA_WRAPPER(multipart), "multipart/signed");
111         multipart->start1 = -1;
112 }
113
114 static void
115 camel_multipart_signed_finalize (CamelObject *object)
116 {
117         CamelMultipartSigned *mps = (CamelMultipartSigned *)object;
118
119         g_free(mps->protocol);
120         g_free(mps->micalg);
121         if (mps->signature)
122                 camel_object_unref((CamelObject *)mps->signature);
123         if (mps->content)
124                 camel_object_unref((CamelObject *)mps->content);
125         if (mps->contentraw)
126                 camel_object_unref((CamelObject *)mps->contentraw);
127 }
128
129 CamelType
130 camel_multipart_signed_get_type (void)
131 {
132         static CamelType camel_multipart_signed_type = CAMEL_INVALID_TYPE;
133
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,
139                                                                    NULL,
140                                                                    (CamelObjectInitFunc) camel_multipart_signed_init,
141                                                                    (CamelObjectFinalizeFunc) camel_multipart_signed_finalize);
142         }
143
144         return camel_multipart_signed_type;
145 }
146
147 /**
148  * camel_multipart_signed_new:
149  *
150  * Create a new #CamelMultipartSigned object.
151  *
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.
157  *
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.
162  *
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.
167  *
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).
172  *
173  * Returns a new #CamelMultipartSigned object
174  **/
175 CamelMultipartSigned *
176 camel_multipart_signed_new (void)
177 {
178         CamelMultipartSigned *multipart;
179
180         multipart = (CamelMultipartSigned *)camel_object_new(CAMEL_MULTIPART_SIGNED_TYPE);
181
182         return multipart;
183 }
184
185 static int
186 skip_content(CamelMimeParser *cmp)
187 {
188         char *buf;
189         size_t len;
190         int state;
191
192         switch (camel_mime_parser_state(cmp)) {
193         case CAMEL_MIME_PARSER_STATE_HEADER:
194                 /* body part */
195                 while (camel_mime_parser_step(cmp, &buf, &len) != CAMEL_MIME_PARSER_STATE_BODY_END)
196                         /* NOOP */ ;
197                 break;
198         case CAMEL_MIME_PARSER_STATE_MESSAGE:
199                 /* message body part */
200                 (void)camel_mime_parser_step(cmp, &buf, &len);
201                 skip_content(cmp);
202
203                 /* clean up followon state if any, see camel-mime-message.c */
204                 state = camel_mime_parser_step(cmp, &buf, &len);
205                 switch (state) {
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:
210                         break;
211                 default:
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);
214                         return -1;
215                 }
216                 break;
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)
220                         skip_content(cmp);
221                 break;
222         default:
223                 g_warning("Invalid state encountered???: %u", camel_mime_parser_state (cmp));
224         }
225
226         return 0;
227 }
228
229 static int
230 parse_content(CamelMultipartSigned *mps)
231 {
232         CamelMimeParser *cmp;
233         CamelMultipart *mp = (CamelMultipart *)mps;
234         CamelStreamMem *mem;
235         const char *boundary;
236         char *buf;
237         size_t len;
238         int state;
239
240         boundary = camel_multipart_get_boundary(mp);
241         if (boundary == NULL) {
242                 g_warning("Trying to get multipart/signed content without setting boundary first");
243                 return -1;
244         }
245
246         mem = (CamelStreamMem *)((CamelDataWrapper *)mps)->stream;
247         if (mem == NULL) {
248                 g_warning("Trying to parse multipart/signed without constructing first");
249                 return -1;
250         }
251
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. */
255
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);
260
261         mps->start1 = -1;
262         mps->end1 = -1;
263         mps->start2 = -1;
264         mps->end2 = -1;
265
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')
273                                 mps->end1--;
274                         if (mps->end1 > mps->start1 && mem->buffer->data[mps->end1-1] == '\r')
275                                 mps->end1--;
276                 } else {
277                         g_warning("multipart/signed has more than 2 parts, remaining parts ignored");
278                         break;
279                 }
280
281                 if (skip_content(cmp) == -1)
282                         break;
283         }
284
285         if (state == CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
286                 mps->end2 = camel_mime_parser_tell_start_boundary(cmp);
287                 
288                 camel_multipart_set_preface(mp, camel_mime_parser_preface(cmp));
289                 camel_multipart_set_postface(mp, camel_mime_parser_postface(cmp));
290         }
291
292         camel_object_unref(cmp);
293
294         if (mps->end2 == -1 || mps->start2 == -1) {
295                 return -1;
296         }
297
298         return 0;
299 }
300
301 /* we snoop the mime type to get boundary and hash info */
302 static void
303 set_mime_type_field(CamelDataWrapper *data_wrapper, CamelContentType *mime_type)
304 {
305         CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
306
307         ((CamelDataWrapperClass *)parent_class)->set_mime_type_field(data_wrapper, mime_type);
308         if (mime_type) {
309                 const char *micalg, *protocol;
310
311                 protocol = camel_content_type_param(mime_type, "protocol");
312                 g_free(mps->protocol);
313                 mps->protocol = g_strdup(protocol);
314
315                 micalg = camel_content_type_param(mime_type, "micalg");
316                 g_free(mps->micalg);
317                 mps->micalg = g_strdup(micalg);
318         }
319 }
320
321 static void
322 signed_add_part(CamelMultipart *multipart, CamelMimePart *part)
323 {
324         g_warning("Cannot add parts to a signed part using add_part");
325 }
326
327 static void
328 signed_add_part_at(CamelMultipart *multipart, CamelMimePart *part, guint index)
329 {
330         g_warning("Cannot add parts to a signed part using add_part_at");
331 }
332
333 static void
334 signed_remove_part(CamelMultipart *multipart, CamelMimePart *part)
335 {
336         g_warning("Cannot remove parts from a signed part using remove_part");
337 }
338
339 static CamelMimePart *
340 signed_remove_part_at (CamelMultipart *multipart, guint index)
341 {
342         g_warning("Cannot remove parts from a signed part using remove_part");
343         return NULL;
344 }
345
346 static CamelMimePart *
347 signed_get_part(CamelMultipart *multipart, guint index)
348 {
349         CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
350         CamelDataWrapper *dw = (CamelDataWrapper *)multipart;
351         CamelStream *stream;
352
353         switch (index) {
354         case CAMEL_MULTIPART_SIGNED_CONTENT:
355                 if (mps->content)
356                         return mps->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");
364                         return NULL;
365                 } else if (dw->stream == NULL) {
366                         return NULL;
367                 } else if (mps->start1 == -1) {
368                         stream = dw->stream;
369                         camel_object_ref(stream);
370                 } else {
371                         stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start1, mps->end1);
372                 }
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);
377                 return mps->content;
378         case CAMEL_MULTIPART_SIGNED_SIGNATURE:
379                 if (mps->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");
384                         return NULL;
385                 } else if (dw->stream == NULL) {
386                         return NULL;
387                 }
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;
394         default:
395                 g_warning("trying to get object out of bounds for multipart");
396         }
397
398         return NULL;
399 }
400
401 static guint
402 signed_get_number(CamelMultipart *multipart)
403 {
404         CamelDataWrapper *dw = (CamelDataWrapper *)multipart;
405         CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
406
407         /* check what we have, so we return something reasonable */
408
409         if ((mps->content || mps->contentraw) && mps->signature)
410                 return 2;
411
412         if (mps->start1 == -1 && parse_content(mps) == -1) {
413                 if (dw->stream == NULL)
414                         return 0;
415                 else
416                         return 1;
417         } else {
418                 return 2;
419         }
420 }
421
422 static void
423 set_stream(CamelMultipartSigned *mps, CamelStream *mem)
424 {
425         CamelDataWrapper *dw = (CamelDataWrapper *)mps;
426
427         if (dw->stream)
428                 camel_object_unref((CamelObject *)dw->stream);
429         dw->stream = (CamelStream *)mem;
430
431         mps->start1 = -1;
432         if (mps->content) {
433                 camel_object_unref((CamelObject *)mps->content);
434                 mps->content = NULL;
435         }
436         if (mps->contentraw) {
437                 camel_object_unref((CamelObject *)mps->contentraw);
438                 mps->contentraw = NULL;
439         }
440         if (mps->signature) {
441                 camel_object_unref((CamelObject *)mps->signature);
442                 mps->signature = NULL;
443         }
444 }
445
446 static int
447 construct_from_stream(CamelDataWrapper *data_wrapper, CamelStream *stream)
448 {
449         CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
450         CamelStream *mem = camel_stream_mem_new();
451
452         if (camel_stream_write_to_stream(stream, mem) == -1)
453                 return -1;
454
455         set_stream(mps, mem);
456
457         return 0;
458 }
459
460 static int
461 signed_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp)
462 {
463         int err;
464         CamelContentType *content_type;
465         CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart;
466         char *buf;
467         size_t len;
468         CamelStream *mem;
469
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);
473
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"));
477
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);
481
482         set_stream(mps, mem);
483
484         err = camel_mime_parser_errno(mp);
485         if (err != 0) {
486                 errno = err;
487                 return -1;
488         } else
489                 return 0;
490 }
491
492 static ssize_t
493 write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
494 {
495         CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper;
496         CamelMultipart *mp = (CamelMultipart *)mps;
497         const char *boundary;
498         ssize_t total = 0;
499         ssize_t count;
500         
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
504            3. invalid
505         */
506
507         /* 1 */
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);
512         }
513
514         /* 3 */
515         if (mps->signature == NULL || mps->contentraw == NULL)
516                 return -1;
517
518         /* 2 */
519         boundary = camel_multipart_get_boundary(mp);
520         if (mp->preface) {
521                 count = camel_stream_write_string(stream, mp->preface);
522                 if (count == -1)
523                         return -1;
524                 total += count;
525         }
526
527         /* first boundary */
528         count = camel_stream_printf(stream, "\n--%s\n", boundary);
529         if (count == -1)
530                 return -1;
531         total += count;
532
533         /* output content part */
534         camel_stream_reset(mps->contentraw);
535         count = camel_stream_write_to_stream(mps->contentraw, stream);
536         if (count == -1)
537                 return -1;
538         total += count;
539         
540         /* boundary */
541         count = camel_stream_printf(stream, "\n--%s\n", boundary);
542         if (count == -1)
543                 return -1;
544         total += count;
545
546         /* signature */
547         count = camel_data_wrapper_write_to_stream((CamelDataWrapper *)mps->signature, stream);
548         if (count == -1)
549                 return -1;
550         total += count;
551
552         /* write the terminating boudary delimiter */
553         count = camel_stream_printf(stream, "\n--%s--\n", boundary);
554         if (count == -1)
555                 return -1;
556         total += count;
557
558         /* and finally the postface */
559         if (mp->postface) {
560                 count = camel_stream_write_string(stream, mp->postface);
561                 if (count == -1)
562                         return -1;
563                 total += count;
564         }
565
566         return total;   
567 }
568
569
570 /**
571  * camel_multipart_signed_get_content_stream:
572  * @mps: a #CamlMultipartSigned object
573  * @ex: a #CamelException
574  *
575  * Get the raw signed content stream of the multipart/signed MIME part
576  * suitable for use with verification of the signature.
577  *
578  * Returns the signed content stream
579  **/
580 CamelStream *
581 camel_multipart_signed_get_content_stream(CamelMultipartSigned *mps, CamelException *ex)
582 {
583         CamelStream *constream;
584
585         /* we need to be able to verify stuff we just signed as well as stuff we loaded from a stream/parser */
586
587         if (mps->contentraw) {
588                 constream = mps->contentraw;
589                 camel_object_ref((CamelObject *)constream);
590         } else {
591                 CamelStream *sub;
592                 CamelMimeFilter *canon_filter;
593
594                 if (mps->start1 == -1 && parse_content(mps) == -1) {
595                         camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("parse error"));
596                         return NULL;
597                 }
598
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);
603                 
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);
608         }
609
610         return constream;
611 }