Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-multipart.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  *
5  * Author :
6  *  Bertrand Guiheneuf <bertrand@helixcode.com>
7  *
8  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <string.h> /* strlen() */
31 #include <time.h>   /* for time */
32 #include <unistd.h> /* for getpid */
33
34 #include "camel-mime-part.h"
35 #include "camel-multipart.h"
36 #include "camel-stream-mem.h"
37
38 #define d(x)
39
40 G_DEFINE_TYPE (CamelMultipart, camel_multipart, CAMEL_TYPE_DATA_WRAPPER)
41
42 static void
43 multipart_dispose (GObject *object)
44 {
45         CamelMultipart *multipart = CAMEL_MULTIPART (object);
46
47         g_list_foreach (multipart->parts, (GFunc) g_object_unref, NULL);
48         g_list_free (multipart->parts);
49         multipart->parts = NULL;
50
51         /* Chain up to parent's dispose() method. */
52         G_OBJECT_CLASS (camel_multipart_parent_class)->dispose (object);
53 }
54
55 static void
56 multipart_finalize (GObject *object)
57 {
58         CamelMultipart *multipart = CAMEL_MULTIPART (object);
59
60         g_free (multipart->preface);
61         g_free (multipart->postface);
62
63         /* Chain up to parent's finalize() method. */
64         G_OBJECT_CLASS (camel_multipart_parent_class)->finalize (object);
65 }
66
67 static gboolean
68 multipart_is_offline (CamelDataWrapper *data_wrapper)
69 {
70         CamelMultipart *multipart = CAMEL_MULTIPART (data_wrapper);
71         GList *node;
72         CamelDataWrapper *part;
73
74         if (CAMEL_DATA_WRAPPER_CLASS (camel_multipart_parent_class)->is_offline (data_wrapper))
75                 return TRUE;
76         for (node = multipart->parts; node; node = node->next) {
77                 part = node->data;
78                 if (camel_data_wrapper_is_offline (part))
79                         return TRUE;
80         }
81
82         return FALSE;
83 }
84
85 /* this is MIME specific, doesn't belong here really */
86 static gssize
87 multipart_write_to_stream_sync (CamelDataWrapper *data_wrapper,
88                                 CamelStream *stream,
89                                 GCancellable *cancellable,
90                                 GError **error)
91 {
92         CamelMultipart *multipart = CAMEL_MULTIPART (data_wrapper);
93         const gchar *boundary;
94         GList *node;
95         gchar *content;
96         gssize total = 0;
97         gssize count;
98
99         /* get the bundary text */
100         boundary = camel_multipart_get_boundary (multipart);
101
102         /* we cannot write a multipart without a boundary string */
103         g_return_val_if_fail (boundary, -1);
104
105         /*
106          * write the preface text (usually something like
107          *   "This is a mime message, if you see this, then
108          *    your mail client probably doesn't support ...."
109          */
110         if (multipart->preface) {
111                 count = camel_stream_write_string (
112                         stream, multipart->preface, cancellable, error);
113                 if (count == -1)
114                         return -1;
115                 total += count;
116         }
117
118         /*
119          * Now, write all the parts, separated by the boundary
120          * delimiter
121          */
122         node = multipart->parts;
123         while (node) {
124                 content = g_strdup_printf ("\n--%s\n", boundary);
125                 count = camel_stream_write_string (
126                         stream, content, cancellable, error);
127                 g_free (content);
128                 if (count == -1)
129                         return -1;
130                 total += count;
131
132                 count = camel_data_wrapper_write_to_stream_sync (
133                         CAMEL_DATA_WRAPPER (node->data),
134                         stream, cancellable, error);
135                 if (count == -1)
136                         return -1;
137                 total += count;
138                 node = node->next;
139         }
140
141         /* write the terminating boudary delimiter */
142         content = g_strdup_printf ("\n--%s--\n", boundary);
143         count = camel_stream_write_string (
144                 stream, content, cancellable, error);
145         g_free (content);
146         if (count == -1)
147                 return -1;
148         total += count;
149
150         /* and finally the postface */
151         if (multipart->postface) {
152                 count = camel_stream_write_string (
153                         stream, multipart->postface, cancellable, error);
154                 if (count == -1)
155                         return -1;
156                 total += count;
157         }
158
159         return total;
160 }
161
162 static void
163 multipart_add_part (CamelMultipart *multipart,
164                     CamelMimePart *part)
165 {
166         multipart->parts = g_list_append (
167                 multipart->parts, g_object_ref (part));
168 }
169
170 static void
171 multipart_add_part_at (CamelMultipart *multipart,
172                        CamelMimePart *part,
173                        guint index)
174 {
175         multipart->parts = g_list_insert (
176                 multipart->parts, g_object_ref (part), index);
177 }
178
179 static void
180 multipart_remove_part (CamelMultipart *multipart,
181                        CamelMimePart *part)
182 {
183         /* Make sure we don't unref a part we don't have. */
184         if (g_list_find (multipart->parts, part) == NULL)
185                 return;
186
187         multipart->parts = g_list_remove (multipart->parts, part);
188         g_object_unref (part);
189 }
190
191 static CamelMimePart *
192 multipart_remove_part_at (CamelMultipart *multipart,
193                           guint index)
194 {
195         CamelMimePart *removed_part;
196         GList *link;
197
198         if (!(multipart->parts))
199                 return NULL;
200
201         link = g_list_nth (multipart->parts, index);
202         if (link == NULL) {
203                 g_warning (
204                         "CamelMultipart::remove_part_at: "
205                         "part to remove is NULL\n");
206                 return NULL;
207         }
208         removed_part = CAMEL_MIME_PART (link->data);
209
210         multipart->parts = g_list_remove_link (multipart->parts, link);
211         if (link->data)
212                 g_object_unref (link->data);
213         g_list_free_1 (link);
214
215         return removed_part;
216 }
217
218 static CamelMimePart *
219 multipart_get_part (CamelMultipart *multipart,
220                     guint index)
221 {
222         GList *part;
223
224         if (!(multipart->parts))
225                 return NULL;
226
227         part = g_list_nth (multipart->parts, index);
228         if (part)
229                 return CAMEL_MIME_PART (part->data);
230         else
231                 return NULL;
232 }
233
234 static guint
235 multipart_get_number (CamelMultipart *multipart)
236 {
237         return g_list_length (multipart->parts);
238 }
239
240 static void
241 multipart_set_boundary (CamelMultipart *multipart,
242                         const gchar *boundary)
243 {
244         CamelDataWrapper *cdw = CAMEL_DATA_WRAPPER (multipart);
245         gchar *bgen, bbuf[27], *p;
246         guint8 *digest;
247         gsize length;
248         gint state, save;
249
250         g_return_if_fail (cdw->mime_type != NULL);
251
252         length = g_checksum_type_get_length (G_CHECKSUM_MD5);
253         digest = g_alloca (length);
254
255         if (!boundary) {
256                 GChecksum *checksum;
257
258                 /* Generate a fairly random boundary string. */
259                 bgen = g_strdup_printf (
260                         "%p:%lu:%lu",
261                         (gpointer) multipart,
262                         (gulong) getpid (),
263                         (gulong) time (NULL));
264
265                 checksum = g_checksum_new (G_CHECKSUM_MD5);
266                 g_checksum_update (checksum, (guchar *) bgen, -1);
267                 g_checksum_get_digest (checksum, digest, &length);
268                 g_checksum_free (checksum);
269
270                 g_free (bgen);
271                 strcpy (bbuf, "=-");
272                 p = bbuf + 2;
273                 state = save = 0;
274                 p += g_base64_encode_step (
275                         (guchar *) digest, length, FALSE, p, &state, &save);
276                 *p = '\0';
277
278                 boundary = bbuf;
279         }
280
281         camel_content_type_set_param (cdw->mime_type, "boundary", boundary);
282 }
283
284 static const gchar *
285 multipart_get_boundary (CamelMultipart *multipart)
286 {
287         CamelDataWrapper *cdw = CAMEL_DATA_WRAPPER (multipart);
288
289         g_return_val_if_fail (cdw->mime_type != NULL, NULL);
290         return camel_content_type_param (cdw->mime_type, "boundary");
291 }
292
293 static gint
294 multipart_construct_from_parser (CamelMultipart *multipart,
295                                  CamelMimeParser *mp)
296 {
297         gint err;
298         CamelContentType *content_type;
299         CamelMimePart *bodypart;
300         gchar *buf;
301         gsize len;
302
303         g_assert (camel_mime_parser_state (mp) == CAMEL_MIME_PARSER_STATE_MULTIPART);
304
305         /* FIXME: we should use a came-mime-mutlipart, not jsut a camel-multipart, but who cares */
306         d (printf ("Creating multi-part\n"));
307
308         content_type = camel_mime_parser_content_type (mp);
309         camel_multipart_set_boundary (
310                 multipart,
311                 camel_content_type_param (content_type, "boundary"));
312
313         while (camel_mime_parser_step (mp, &buf, &len) != CAMEL_MIME_PARSER_STATE_MULTIPART_END) {
314                 camel_mime_parser_unstep (mp);
315                 bodypart = camel_mime_part_new ();
316                 camel_mime_part_construct_from_parser_sync (
317                         bodypart, mp, NULL, NULL);
318                 camel_multipart_add_part (multipart, bodypart);
319                 g_object_unref (bodypart);
320         }
321
322         /* these are only return valid data in the MULTIPART_END state */
323         camel_multipart_set_preface (multipart, camel_mime_parser_preface (mp));
324         camel_multipart_set_postface (multipart, camel_mime_parser_postface (mp));
325
326         err = camel_mime_parser_errno (mp);
327         if (err != 0) {
328                 errno = err;
329                 return -1;
330         } else
331                 return 0;
332 }
333
334 static void
335 camel_multipart_class_init (CamelMultipartClass *class)
336 {
337         GObjectClass *object_class;
338         CamelDataWrapperClass *data_wrapper_class;
339
340         object_class = G_OBJECT_CLASS (class);
341         object_class->dispose = multipart_dispose;
342         object_class->finalize = multipart_finalize;
343
344         data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
345         data_wrapper_class->is_offline = multipart_is_offline;
346         data_wrapper_class->write_to_stream_sync = multipart_write_to_stream_sync;
347         data_wrapper_class->decode_to_stream_sync = multipart_write_to_stream_sync;
348
349         class->add_part = multipart_add_part;
350         class->add_part_at = multipart_add_part_at;
351         class->remove_part = multipart_remove_part;
352         class->remove_part_at = multipart_remove_part_at;
353         class->get_part = multipart_get_part;
354         class->get_number = multipart_get_number;
355         class->set_boundary = multipart_set_boundary;
356         class->get_boundary = multipart_get_boundary;
357         class->construct_from_parser = multipart_construct_from_parser;
358 }
359
360 static void
361 camel_multipart_init (CamelMultipart *multipart)
362 {
363         camel_data_wrapper_set_mime_type (
364                 CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
365         multipart->parts = NULL;
366         multipart->preface = NULL;
367         multipart->postface = NULL;
368 }
369
370 /**
371  * camel_multipart_new:
372  *
373  * Create a new #CamelMultipart object.
374  *
375  * Returns: a new #CamelMultipart object
376  **/
377 CamelMultipart *
378 camel_multipart_new (void)
379 {
380         CamelMultipart *multipart;
381
382         multipart = g_object_new (CAMEL_TYPE_MULTIPART, NULL);
383         multipart->preface = NULL;
384         multipart->postface = NULL;
385
386         return multipart;
387 }
388
389 /**
390  * camel_multipart_add_part:
391  * @multipart: a #CamelMultipart object
392  * @part: a #CamelMimePart to add
393  *
394  * Appends the part to the multipart object.
395  **/
396 void
397 camel_multipart_add_part (CamelMultipart *multipart,
398                           CamelMimePart *part)
399 {
400         CamelMultipartClass *class;
401
402         g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
403         g_return_if_fail (CAMEL_IS_MIME_PART (part));
404
405         class = CAMEL_MULTIPART_GET_CLASS (multipart);
406         g_return_if_fail (class->add_part != NULL);
407
408         class->add_part (multipart, part);
409 }
410
411 /**
412  * camel_multipart_add_part_at:
413  * @multipart: a #CamelMultipart object
414  * @part: a #CamelMimePart to add
415  * @index: index to add the multipart at
416  *
417  * Adds the part to the multipart object after the @index'th
418  * element. If @index is greater than the number of parts, it is
419  * equivalent to camel_multipart_add_part().
420  **/
421 void
422 camel_multipart_add_part_at (CamelMultipart *multipart,
423                              CamelMimePart *part,
424                              guint index)
425 {
426         CamelMultipartClass *class;
427
428         g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
429         g_return_if_fail (CAMEL_IS_MIME_PART (part));
430
431         class = CAMEL_MULTIPART_GET_CLASS (multipart);
432         g_return_if_fail (class->add_part_at != NULL);
433
434         class->add_part_at (multipart, part, index);
435 }
436
437 /**
438  * camel_multipart_remove_part:
439  * @multipart: a #CamelMultipart object
440  * @part: a #CamelMimePart to remove
441  *
442  * Removes @part from @multipart.
443  **/
444 void
445 camel_multipart_remove_part (CamelMultipart *multipart,
446                              CamelMimePart *part)
447 {
448         CamelMultipartClass *class;
449
450         g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
451         g_return_if_fail (CAMEL_IS_MIME_PART (part));
452
453         class = CAMEL_MULTIPART_GET_CLASS (multipart);
454         g_return_if_fail (class->remove_part != NULL);
455
456         class->remove_part (multipart, part);
457 }
458
459 /**
460  * camel_multipart_remove_part_at:
461  * @multipart: a #CamelMultipart object
462  * @index: a zero-based index indicating the part to remove
463  *
464  * Remove the indicated part from the multipart object.
465  *
466  * Returns: the removed part. Note that it is g_object_unref()'ed
467  * before being returned, which may cause it to be destroyed.
468  **/
469 CamelMimePart *
470 camel_multipart_remove_part_at (CamelMultipart *multipart,
471                                 guint index)
472 {
473         CamelMultipartClass *class;
474
475         g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), NULL);
476
477         class = CAMEL_MULTIPART_GET_CLASS (multipart);
478         g_return_val_if_fail (class->remove_part_at != NULL, NULL);
479
480         return class->remove_part_at (multipart, index);
481 }
482
483 /**
484  * camel_multipart_get_part:
485  * @multipart: a #CamelMultipart object
486  * @index: a zero-based index indicating the part to get
487  *
488  * Returns: the indicated subpart, or %NULL
489  **/
490 CamelMimePart *
491 camel_multipart_get_part (CamelMultipart *multipart,
492                           guint index)
493 {
494         CamelMultipartClass *class;
495
496         g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), NULL);
497
498         class = CAMEL_MULTIPART_GET_CLASS (multipart);
499         g_return_val_if_fail (class->get_part != NULL, NULL);
500
501         return class->get_part (multipart, index);
502 }
503
504 /**
505  * camel_multipart_get_number:
506  * @multipart: a #CamelMultipart object
507  *
508  * Returns: the number of subparts in @multipart
509  **/
510 guint
511 camel_multipart_get_number (CamelMultipart *multipart)
512 {
513         CamelMultipartClass *class;
514
515         g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), 0);
516
517         class = CAMEL_MULTIPART_GET_CLASS (multipart);
518         g_return_val_if_fail (class->get_number != NULL, 0);
519
520         return class->get_number (multipart);
521 }
522
523 /**
524  * camel_multipart_set_boundary:
525  * @multipart: a #CamelMultipart object
526  * @boundary: the message boundary, or %NULL
527  *
528  * Sets the message boundary for @multipart to @boundary. This should
529  * be a string which does not occur anywhere in any of @multipart's
530  * subparts. If @boundary is %NULL, a randomly-generated boundary will
531  * be used.
532  **/
533 void
534 camel_multipart_set_boundary (CamelMultipart *multipart,
535                               const gchar *boundary)
536 {
537         CamelMultipartClass *class;
538
539         g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
540
541         class = CAMEL_MULTIPART_GET_CLASS (multipart);
542         g_return_if_fail (class->set_boundary != NULL);
543
544         class->set_boundary (multipart, boundary);
545 }
546
547 /**
548  * camel_multipart_get_boundary:
549  * @multipart: a #CamelMultipart object
550  *
551  * Returns: the boundary
552  **/
553 const gchar *
554 camel_multipart_get_boundary (CamelMultipart *multipart)
555 {
556         CamelMultipartClass *class;
557
558         g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), NULL);
559
560         class = CAMEL_MULTIPART_GET_CLASS (multipart);
561         g_return_val_if_fail (class->get_boundary != NULL, NULL);
562
563         return class->get_boundary (multipart);
564 }
565
566 /**
567  * camel_multipart_set_preface:
568  * @multipart: a #CamelMultipart object
569  * @preface: the multipart preface
570  *
571  * Set the preface text for this multipart.  Will be written out infront
572  * of the multipart.  This text should only include US-ASCII strings, and
573  * be relatively short, and will be ignored by any MIME mail client.
574  **/
575 void
576 camel_multipart_set_preface (CamelMultipart *multipart,
577                              const gchar *preface)
578 {
579         g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
580
581         if (multipart->preface == preface)
582                 return;
583
584         g_free (multipart->preface);
585         multipart->preface = g_strdup (preface);
586 }
587
588 /**
589  * camel_multipart_set_postface:
590  * @multipart: a #CamelMultipart object
591  * @postface: multipat postface
592  *
593  * Set the postfix text for this multipart.  Will be written out after
594  * the last boundary of the multipart, and ignored by any MIME mail
595  * client.
596  *
597  * Generally postface texts should not be sent with multipart messages.
598  **/
599 void
600 camel_multipart_set_postface (CamelMultipart *multipart,
601                               const gchar *postface)
602 {
603         g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
604
605         if (multipart->postface == postface)
606                 return;
607
608         g_free (multipart->postface);
609         multipart->postface = g_strdup (postface);
610 }
611
612 /**
613  * camel_multipart_construct_from_parser:
614  * @multipart: a #CamelMultipart object
615  * @parser: a #CamelMimeParser object
616  *
617  * Construct a multipart from a parser.
618  *
619  * Returns: %0 on success or %-1 on fail
620  **/
621 gint
622 camel_multipart_construct_from_parser (CamelMultipart *multipart,
623                                        CamelMimeParser *mp)
624 {
625         CamelMultipartClass *class;
626
627         g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), -1);
628         g_return_val_if_fail (CAMEL_IS_MIME_PARSER (mp), -1);
629
630         class = CAMEL_MULTIPART_GET_CLASS (multipart);
631         g_return_val_if_fail (class->construct_from_parser != NULL, -1);
632
633         return class->construct_from_parser (multipart, mp);
634 }