1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-multipart.c : Abstract class for a multipart */
6 * Bertrand Guiheneuf <bertrand@helixcode.com>
8 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
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.
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
30 #include <string.h> /* strlen() */
31 #include <time.h> /* for time */
32 #include <unistd.h> /* for getpid */
34 #include "camel-mime-part.h"
35 #include "camel-multipart.h"
36 #include "camel-stream-mem.h"
40 G_DEFINE_TYPE (CamelMultipart, camel_multipart, CAMEL_TYPE_DATA_WRAPPER)
43 multipart_dispose (GObject *object)
45 CamelMultipart *multipart = CAMEL_MULTIPART (object);
47 g_list_foreach (multipart->parts, (GFunc) g_object_unref, NULL);
48 g_list_free (multipart->parts);
49 multipart->parts = NULL;
51 /* Chain up to parent's dispose() method. */
52 G_OBJECT_CLASS (camel_multipart_parent_class)->dispose (object);
56 multipart_finalize (GObject *object)
58 CamelMultipart *multipart = CAMEL_MULTIPART (object);
60 g_free (multipart->preface);
61 g_free (multipart->postface);
63 /* Chain up to parent's finalize() method. */
64 G_OBJECT_CLASS (camel_multipart_parent_class)->finalize (object);
68 multipart_is_offline (CamelDataWrapper *data_wrapper)
70 CamelMultipart *multipart = CAMEL_MULTIPART (data_wrapper);
72 CamelDataWrapper *part;
74 if (CAMEL_DATA_WRAPPER_CLASS (camel_multipart_parent_class)->is_offline (data_wrapper))
76 for (node = multipart->parts; node; node = node->next) {
78 if (camel_data_wrapper_is_offline (part))
85 /* this is MIME specific, doesn't belong here really */
87 multipart_write_to_stream_sync (CamelDataWrapper *data_wrapper,
89 GCancellable *cancellable,
92 CamelMultipart *multipart = CAMEL_MULTIPART (data_wrapper);
93 const gchar *boundary;
99 /* get the bundary text */
100 boundary = camel_multipart_get_boundary (multipart);
102 /* we cannot write a multipart without a boundary string */
103 g_return_val_if_fail (boundary, -1);
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 ...."
110 if (multipart->preface) {
111 count = camel_stream_write_string (
112 stream, multipart->preface, cancellable, error);
119 * Now, write all the parts, separated by the boundary
122 node = multipart->parts;
124 content = g_strdup_printf ("\n--%s\n", boundary);
125 count = camel_stream_write_string (
126 stream, content, cancellable, error);
132 count = camel_data_wrapper_write_to_stream_sync (
133 CAMEL_DATA_WRAPPER (node->data),
134 stream, cancellable, error);
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);
150 /* and finally the postface */
151 if (multipart->postface) {
152 count = camel_stream_write_string (
153 stream, multipart->postface, cancellable, error);
163 multipart_add_part (CamelMultipart *multipart,
166 multipart->parts = g_list_append (
167 multipart->parts, g_object_ref (part));
171 multipart_add_part_at (CamelMultipart *multipart,
175 multipart->parts = g_list_insert (
176 multipart->parts, g_object_ref (part), index);
180 multipart_remove_part (CamelMultipart *multipart,
183 /* Make sure we don't unref a part we don't have. */
184 if (g_list_find (multipart->parts, part) == NULL)
187 multipart->parts = g_list_remove (multipart->parts, part);
188 g_object_unref (part);
191 static CamelMimePart *
192 multipart_remove_part_at (CamelMultipart *multipart,
195 CamelMimePart *removed_part;
198 if (!(multipart->parts))
201 link = g_list_nth (multipart->parts, index);
204 "CamelMultipart::remove_part_at: "
205 "part to remove is NULL\n");
208 removed_part = CAMEL_MIME_PART (link->data);
210 multipart->parts = g_list_remove_link (multipart->parts, link);
212 g_object_unref (link->data);
213 g_list_free_1 (link);
218 static CamelMimePart *
219 multipart_get_part (CamelMultipart *multipart,
224 if (!(multipart->parts))
227 part = g_list_nth (multipart->parts, index);
229 return CAMEL_MIME_PART (part->data);
235 multipart_get_number (CamelMultipart *multipart)
237 return g_list_length (multipart->parts);
241 multipart_set_boundary (CamelMultipart *multipart,
242 const gchar *boundary)
244 CamelDataWrapper *cdw = CAMEL_DATA_WRAPPER (multipart);
245 gchar *bgen, bbuf[27], *p;
250 g_return_if_fail (cdw->mime_type != NULL);
252 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
253 digest = g_alloca (length);
258 /* Generate a fairly random boundary string. */
259 bgen = g_strdup_printf (
261 (gpointer) multipart,
263 (gulong) time (NULL));
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);
274 p += g_base64_encode_step (
275 (guchar *) digest, length, FALSE, p, &state, &save);
281 camel_content_type_set_param (cdw->mime_type, "boundary", boundary);
285 multipart_get_boundary (CamelMultipart *multipart)
287 CamelDataWrapper *cdw = CAMEL_DATA_WRAPPER (multipart);
289 g_return_val_if_fail (cdw->mime_type != NULL, NULL);
290 return camel_content_type_param (cdw->mime_type, "boundary");
294 multipart_construct_from_parser (CamelMultipart *multipart,
298 CamelContentType *content_type;
299 CamelMimePart *bodypart;
303 g_assert (camel_mime_parser_state (mp) == CAMEL_MIME_PARSER_STATE_MULTIPART);
305 /* FIXME: we should use a came-mime-mutlipart, not jsut a camel-multipart, but who cares */
306 d (printf ("Creating multi-part\n"));
308 content_type = camel_mime_parser_content_type (mp);
309 camel_multipart_set_boundary (
311 camel_content_type_param (content_type, "boundary"));
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);
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));
326 err = camel_mime_parser_errno (mp);
335 camel_multipart_class_init (CamelMultipartClass *class)
337 GObjectClass *object_class;
338 CamelDataWrapperClass *data_wrapper_class;
340 object_class = G_OBJECT_CLASS (class);
341 object_class->dispose = multipart_dispose;
342 object_class->finalize = multipart_finalize;
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;
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;
361 camel_multipart_init (CamelMultipart *multipart)
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;
371 * camel_multipart_new:
373 * Create a new #CamelMultipart object.
375 * Returns: a new #CamelMultipart object
378 camel_multipart_new (void)
380 CamelMultipart *multipart;
382 multipart = g_object_new (CAMEL_TYPE_MULTIPART, NULL);
383 multipart->preface = NULL;
384 multipart->postface = NULL;
390 * camel_multipart_add_part:
391 * @multipart: a #CamelMultipart object
392 * @part: a #CamelMimePart to add
394 * Appends the part to the multipart object.
397 camel_multipart_add_part (CamelMultipart *multipart,
400 CamelMultipartClass *class;
402 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
403 g_return_if_fail (CAMEL_IS_MIME_PART (part));
405 class = CAMEL_MULTIPART_GET_CLASS (multipart);
406 g_return_if_fail (class->add_part != NULL);
408 class->add_part (multipart, part);
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
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().
422 camel_multipart_add_part_at (CamelMultipart *multipart,
426 CamelMultipartClass *class;
428 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
429 g_return_if_fail (CAMEL_IS_MIME_PART (part));
431 class = CAMEL_MULTIPART_GET_CLASS (multipart);
432 g_return_if_fail (class->add_part_at != NULL);
434 class->add_part_at (multipart, part, index);
438 * camel_multipart_remove_part:
439 * @multipart: a #CamelMultipart object
440 * @part: a #CamelMimePart to remove
442 * Removes @part from @multipart.
445 camel_multipart_remove_part (CamelMultipart *multipart,
448 CamelMultipartClass *class;
450 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
451 g_return_if_fail (CAMEL_IS_MIME_PART (part));
453 class = CAMEL_MULTIPART_GET_CLASS (multipart);
454 g_return_if_fail (class->remove_part != NULL);
456 class->remove_part (multipart, part);
460 * camel_multipart_remove_part_at:
461 * @multipart: a #CamelMultipart object
462 * @index: a zero-based index indicating the part to remove
464 * Remove the indicated part from the multipart object.
466 * Returns: the removed part. Note that it is g_object_unref()'ed
467 * before being returned, which may cause it to be destroyed.
470 camel_multipart_remove_part_at (CamelMultipart *multipart,
473 CamelMultipartClass *class;
475 g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), NULL);
477 class = CAMEL_MULTIPART_GET_CLASS (multipart);
478 g_return_val_if_fail (class->remove_part_at != NULL, NULL);
480 return class->remove_part_at (multipart, index);
484 * camel_multipart_get_part:
485 * @multipart: a #CamelMultipart object
486 * @index: a zero-based index indicating the part to get
488 * Returns: the indicated subpart, or %NULL
491 camel_multipart_get_part (CamelMultipart *multipart,
494 CamelMultipartClass *class;
496 g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), NULL);
498 class = CAMEL_MULTIPART_GET_CLASS (multipart);
499 g_return_val_if_fail (class->get_part != NULL, NULL);
501 return class->get_part (multipart, index);
505 * camel_multipart_get_number:
506 * @multipart: a #CamelMultipart object
508 * Returns: the number of subparts in @multipart
511 camel_multipart_get_number (CamelMultipart *multipart)
513 CamelMultipartClass *class;
515 g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), 0);
517 class = CAMEL_MULTIPART_GET_CLASS (multipart);
518 g_return_val_if_fail (class->get_number != NULL, 0);
520 return class->get_number (multipart);
524 * camel_multipart_set_boundary:
525 * @multipart: a #CamelMultipart object
526 * @boundary: the message boundary, or %NULL
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
534 camel_multipart_set_boundary (CamelMultipart *multipart,
535 const gchar *boundary)
537 CamelMultipartClass *class;
539 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
541 class = CAMEL_MULTIPART_GET_CLASS (multipart);
542 g_return_if_fail (class->set_boundary != NULL);
544 class->set_boundary (multipart, boundary);
548 * camel_multipart_get_boundary:
549 * @multipart: a #CamelMultipart object
551 * Returns: the boundary
554 camel_multipart_get_boundary (CamelMultipart *multipart)
556 CamelMultipartClass *class;
558 g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), NULL);
560 class = CAMEL_MULTIPART_GET_CLASS (multipart);
561 g_return_val_if_fail (class->get_boundary != NULL, NULL);
563 return class->get_boundary (multipart);
567 * camel_multipart_set_preface:
568 * @multipart: a #CamelMultipart object
569 * @preface: the multipart preface
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.
576 camel_multipart_set_preface (CamelMultipart *multipart,
577 const gchar *preface)
579 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
581 if (multipart->preface == preface)
584 g_free (multipart->preface);
585 multipart->preface = g_strdup (preface);
589 * camel_multipart_set_postface:
590 * @multipart: a #CamelMultipart object
591 * @postface: multipat postface
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
597 * Generally postface texts should not be sent with multipart messages.
600 camel_multipart_set_postface (CamelMultipart *multipart,
601 const gchar *postface)
603 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
605 if (multipart->postface == postface)
608 g_free (multipart->postface);
609 multipart->postface = g_strdup (postface);
613 * camel_multipart_construct_from_parser:
614 * @multipart: a #CamelMultipart object
615 * @parser: a #CamelMimeParser object
617 * Construct a multipart from a parser.
619 * Returns: %0 on success or %-1 on fail
622 camel_multipart_construct_from_parser (CamelMultipart *multipart,
625 CamelMultipartClass *class;
627 g_return_val_if_fail (CAMEL_IS_MULTIPART (multipart), -1);
628 g_return_val_if_fail (CAMEL_IS_MIME_PARSER (mp), -1);
630 class = CAMEL_MULTIPART_GET_CLASS (multipart);
631 g_return_val_if_fail (class->construct_from_parser != NULL, -1);
633 return class->construct_from_parser (multipart, mp);