Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-multipart.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <time.h>
34
35 #include "gmime-multipart.h"
36 #include "gmime-utils.h"
37
38
39 #define d(x)
40
41
42 /**
43  * SECTION: gmime-multipart
44  * @title: GMimeMultipart
45  * @short_description: MIME multiparts
46  * @see_also:
47  *
48  * A #GMimeMultipart represents all multipart MIME container parts.
49  **/
50
51
52 /* GObject class methods */
53 static void g_mime_multipart_class_init (GMimeMultipartClass *klass);
54 static void g_mime_multipart_init (GMimeMultipart *multipart, GMimeMultipartClass *klass);
55 static void g_mime_multipart_finalize (GObject *object);
56
57 /* GMimeObject class methods */
58 static ssize_t multipart_write_to_stream (GMimeObject *object, GMimeStream *stream);
59 static void multipart_encode (GMimeObject *object, GMimeEncodingConstraint constraint);
60
61 /* GMimeMultipart class methods */
62 static void multipart_clear (GMimeMultipart *multipart);
63 static void multipart_add (GMimeMultipart *multipart, GMimeObject *part);
64 static void multipart_insert (GMimeMultipart *multipart, int index, GMimeObject *part);
65 static gboolean multipart_remove (GMimeMultipart *multipart, GMimeObject *part);
66 static GMimeObject *multipart_remove_at (GMimeMultipart *multipart, int index);
67 static GMimeObject *multipart_get_part (GMimeMultipart *multipart, int index);
68 static gboolean multipart_contains (GMimeMultipart *multipart, GMimeObject *part);
69 static int multipart_index_of (GMimeMultipart *multipart, GMimeObject *part);
70 static int multipart_get_count (GMimeMultipart *multipart);
71 static void multipart_set_boundary (GMimeMultipart *multipart, const char *boundary);
72 static const char *multipart_get_boundary (GMimeMultipart *multipart);
73
74
75 static GMimeObjectClass *parent_class = NULL;
76
77
78 GType
79 g_mime_multipart_get_type (void)
80 {
81         static GType type = 0;
82         
83         if (!type) {
84                 static const GTypeInfo info = {
85                         sizeof (GMimeMultipartClass),
86                         NULL, /* base_class_init */
87                         NULL, /* base_class_finalize */
88                         (GClassInitFunc) g_mime_multipart_class_init,
89                         NULL, /* class_finalize */
90                         NULL, /* class_data */
91                         sizeof (GMimeMultipart),
92                         0,    /* n_preallocs */
93                         (GInstanceInitFunc) g_mime_multipart_init,
94                 };
95                 
96                 type = g_type_register_static (GMIME_TYPE_OBJECT, "GMimeMultipart", &info, 0);
97         }
98         
99         return type;
100 }
101
102
103 static void
104 g_mime_multipart_class_init (GMimeMultipartClass *klass)
105 {
106         GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
107         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
108         
109         parent_class = g_type_class_ref (GMIME_TYPE_OBJECT);
110         
111         gobject_class->finalize = g_mime_multipart_finalize;
112         
113         object_class->write_to_stream = multipart_write_to_stream;
114         object_class->encode = multipart_encode;
115         
116         klass->add = multipart_add;
117         klass->clear = multipart_clear;
118         klass->insert = multipart_insert;
119         klass->remove = multipart_remove;
120         klass->remove_at = multipart_remove_at;
121         klass->get_part = multipart_get_part;
122         klass->contains = multipart_contains;
123         klass->index_of = multipart_index_of;
124         klass->get_count = multipart_get_count;
125         klass->set_boundary = multipart_set_boundary;
126         klass->get_boundary = multipart_get_boundary;
127 }
128
129 static void
130 g_mime_multipart_init (GMimeMultipart *multipart, GMimeMultipartClass *klass)
131 {
132         multipart->children = g_ptr_array_new ();
133         multipart->preface = NULL;
134         multipart->postface = NULL;
135 }
136
137 static void
138 g_mime_multipart_finalize (GObject *object)
139 {
140         GMimeMultipart *multipart = (GMimeMultipart *) object;
141         guint i;
142         
143         g_free (multipart->preface);
144         g_free (multipart->postface);
145         
146         for (i = 0; i < multipart->children->len; i++)
147                 g_object_unref (multipart->children->pdata[i]);
148         
149         g_ptr_array_free (multipart->children, TRUE);
150         
151         G_OBJECT_CLASS (parent_class)->finalize (object);
152 }
153
154 static ssize_t
155 multipart_write_to_stream (GMimeObject *object, GMimeStream *stream)
156 {
157         GMimeMultipart *multipart = (GMimeMultipart *) object;
158         ssize_t nwritten, total = 0;
159         const char *boundary;
160         GMimeObject *part;
161         guint i;
162         
163         /* make sure a boundary is set unless we are writing out a raw
164          * header (in which case it should already be set... or if
165          * not, then it's a broken multipart and so we don't want to
166          * alter it or we'll completely break the output) */
167         boundary = g_mime_object_get_content_type_parameter (object, "boundary");
168         if (!boundary && !g_mime_header_list_get_stream (object->headers)) {
169                 g_mime_multipart_set_boundary (multipart, NULL);
170                 boundary = g_mime_object_get_content_type_parameter (object, "boundary");
171         }
172         
173         /* write the content headers */
174         if ((nwritten = g_mime_header_list_write_to_stream (object->headers, stream)) == -1)
175                 return -1;
176         
177         total += nwritten;
178         
179         /* write the preface */
180         if (multipart->preface) {
181                 /* terminate the headers */
182                 if (g_mime_stream_write (stream, "\n", 1) == -1)
183                         return -1;
184                 
185                 total++;
186                 
187                 if ((nwritten = g_mime_stream_write_string (stream, multipart->preface)) == -1)
188                         return -1;
189                 
190                 total += nwritten;
191         }
192         
193         for (i = 0; i < multipart->children->len; i++) {
194                 part = multipart->children->pdata[i];
195                 
196                 /* write the boundary */
197                 if ((nwritten = g_mime_stream_printf (stream, "\n--%s\n", boundary)) == -1)
198                         return -1;
199                 
200                 total += nwritten;
201                 
202                 /* write this part out */
203                 if ((nwritten = g_mime_object_write_to_stream (part, stream)) == -1)
204                         return -1;
205                 
206                 total += nwritten;
207         }
208         
209         /* write the end-boundary (but only if a boundary is set) */
210         if (boundary) {
211                 if ((nwritten = g_mime_stream_printf (stream, "\n--%s--\n", boundary)) == -1)
212                         return -1;
213                 
214                 total += nwritten;
215         }
216         
217         /* write the postface */
218         if (multipart->postface) {
219                 if ((nwritten = g_mime_stream_write_string (stream, multipart->postface)) == -1)
220                         return -1;
221                 
222                 total += nwritten;
223         }
224         
225         return total;
226 }
227
228 static void
229 multipart_encode (GMimeObject *object, GMimeEncodingConstraint constraint)
230 {
231         GMimeMultipart *multipart = (GMimeMultipart *) object;
232         GMimeObject *subpart;
233         int i;
234         
235         for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
236                 subpart = g_mime_multipart_get_part (multipart, i);
237                 g_mime_object_encode (subpart, constraint);
238         }
239 }
240
241
242 /**
243  * g_mime_multipart_new:
244  *
245  * Creates a new MIME multipart object with a default content-type of
246  * multipart/mixed.
247  *
248  * Returns: an empty MIME multipart object with a default content-type of
249  * multipart/mixed.
250  **/
251 GMimeMultipart *
252 g_mime_multipart_new (void)
253 {
254         GMimeContentType *content_type;
255         GMimeMultipart *multipart;
256         
257         multipart = g_object_newv (GMIME_TYPE_MULTIPART, 0, NULL);
258         
259         content_type = g_mime_content_type_new ("multipart", "mixed");
260         g_mime_object_set_content_type (GMIME_OBJECT (multipart), content_type);
261         g_object_unref (content_type);
262         
263         return multipart;
264 }
265
266
267 /**
268  * g_mime_multipart_new_with_subtype:
269  * @subtype: content-type subtype
270  *
271  * Creates a new MIME multipart object with a content-type of
272  * multipart/@subtype.
273  *
274  * Returns: an empty MIME multipart object with a content-type of
275  * multipart/@subtype.
276  **/
277 GMimeMultipart *
278 g_mime_multipart_new_with_subtype (const char *subtype)
279 {
280         GMimeContentType *content_type;
281         GMimeMultipart *multipart;
282         
283         multipart = g_object_newv (GMIME_TYPE_MULTIPART, 0, NULL);
284         
285         content_type = g_mime_content_type_new ("multipart", subtype ? subtype : "mixed");
286         g_mime_object_set_content_type (GMIME_OBJECT (multipart), content_type);
287         g_object_unref (content_type);
288         
289         return multipart;
290 }
291
292
293 /**
294  * g_mime_multipart_set_preface:
295  * @multipart: a #GMimeMultipart object
296  * @preface: preface
297  *
298  * Sets the preface on the multipart.
299  **/
300 void
301 g_mime_multipart_set_preface (GMimeMultipart *multipart, const char *preface)
302 {
303         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
304         
305         g_free (multipart->preface);
306         multipart->preface = g_strdup (preface);
307 }
308
309
310 /**
311  * g_mime_multipart_get_preface:
312  * @multipart: a #GMimeMultipart object
313  *
314  * Gets the preface on the multipart.
315  *
316  * Returns: a pointer to the preface string on the multipart.
317  **/
318 const char *
319 g_mime_multipart_get_preface (GMimeMultipart *multipart)
320 {
321         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
322         
323         return multipart->preface;
324 }
325
326
327 /**
328  * g_mime_multipart_set_postface:
329  * @multipart: a #GMimeMultipart object
330  * @postface: postface
331  *
332  * Sets the postface on the multipart.
333  **/
334 void
335 g_mime_multipart_set_postface (GMimeMultipart *multipart, const char *postface)
336 {
337         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
338         
339         g_free (multipart->postface);
340         multipart->postface = g_strdup (postface);
341 }
342
343
344 /**
345  * g_mime_multipart_get_postface:
346  * @multipart: a #GMimeMultipart object
347  *
348  * Gets the postface on the multipart.
349  *
350  * Returns: a pointer to the postface string on the multipart.
351  **/
352 const char *
353 g_mime_multipart_get_postface (GMimeMultipart *multipart)
354 {
355         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
356         
357         return multipart->postface;
358 }
359
360
361 static void
362 multipart_clear (GMimeMultipart *multipart)
363 {
364         guint i;
365         
366         for (i = 0; i < multipart->children->len; i++)
367                 g_object_unref (multipart->children->pdata[i]);
368         
369         g_ptr_array_set_size (multipart->children, 0);
370 }
371
372
373 /**
374  * g_mime_multipart_clear:
375  * @multipart: a #GMimeMultipart object
376  *
377  * Removes all subparts from @multipart.
378  **/
379 void
380 g_mime_multipart_clear (GMimeMultipart *multipart)
381 {
382         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
383         
384         GMIME_MULTIPART_GET_CLASS (multipart)->clear (multipart);
385 }
386
387
388 static void
389 multipart_add (GMimeMultipart *multipart, GMimeObject *part)
390 {
391         g_ptr_array_add (multipart->children, part);
392         g_object_ref (part);
393 }
394
395
396 /**
397  * g_mime_multipart_add:
398  * @multipart: a #GMimeMultipart object
399  * @part: a #GMimeObject
400  *
401  * Adds a mime part to the multipart.
402  **/
403 void
404 g_mime_multipart_add (GMimeMultipart *multipart, GMimeObject *part)
405 {
406         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
407         g_return_if_fail (GMIME_IS_OBJECT (part));
408         
409         GMIME_MULTIPART_GET_CLASS (multipart)->add (multipart, part);
410 }
411
412
413 static void
414 ptr_array_insert (GPtrArray *array, guint index, gpointer object)
415 {
416         unsigned char *dest, *src;
417         guint n;
418         
419         if (index < array->len) {
420                 /* need to shift some items */
421                 g_ptr_array_set_size (array, array->len + 1);
422                 
423                 dest = ((unsigned char *) array->pdata) + (sizeof (void *) * (index + 1));
424                 src = ((unsigned char *) array->pdata) + (sizeof (void *) * index);
425                 n = array->len - index - 1;
426                 
427                 g_memmove (dest, src, (sizeof (void *) * n));
428                 array->pdata[index] = object;
429         } else {
430                 /* the easy case */
431                 g_ptr_array_add (array, object);
432         }
433 }
434
435 static void
436 multipart_insert (GMimeMultipart *multipart, int index, GMimeObject *part)
437 {
438         ptr_array_insert (multipart->children, index, part);
439         g_object_ref (part);
440 }
441
442
443 /**
444  * g_mime_multipart_insert:
445  * @multipart: a #GMimeMultipart object
446  * @part: mime part
447  * @index: position to insert the mime part
448  *
449  * Inserts the specified mime part into the multipart at the position
450  * @index.
451  **/
452 void
453 g_mime_multipart_insert (GMimeMultipart *multipart, int index, GMimeObject *part)
454 {
455         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
456         g_return_if_fail (GMIME_IS_OBJECT (part));
457         g_return_if_fail (index >= 0);
458         
459         GMIME_MULTIPART_GET_CLASS (multipart)->insert (multipart, index, part);
460 }
461
462
463 static gboolean
464 multipart_remove (GMimeMultipart *multipart, GMimeObject *part)
465 {
466         if (!g_ptr_array_remove (multipart->children, part))
467                 return FALSE;
468         
469         g_object_unref (part);
470         
471         return TRUE;
472 }
473
474
475 /**
476  * g_mime_multipart_remove:
477  * @multipart: a #GMimeMultipart object
478  * @part: mime part
479  *
480  * Removes the specified mime part from the multipart.
481  *
482  * Returns: %TRUE if the part was removed or %FALSE otherwise.
483  **/
484 gboolean
485 g_mime_multipart_remove (GMimeMultipart *multipart, GMimeObject *part)
486 {
487         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), FALSE);
488         g_return_val_if_fail (GMIME_IS_OBJECT (part), FALSE);
489         
490         return GMIME_MULTIPART_GET_CLASS (multipart)->remove (multipart, part);
491 }
492
493
494 static GMimeObject *
495 multipart_remove_at (GMimeMultipart *multipart, int index)
496 {
497         GMimeObject *part;
498         
499         if ((guint) index >= multipart->children->len)
500                 return NULL;
501         
502         part = multipart->children->pdata[index];
503         
504         g_ptr_array_remove_index (multipart->children, index);
505         
506         return part;
507 }
508
509
510 /**
511  * g_mime_multipart_remove_at:
512  * @multipart: a #GMimeMultipart object
513  * @index: position of the mime part to remove
514  *
515  * Removes the mime part at position @index from the multipart.
516  *
517  * Returns: the mime part that was removed or %NULL if the part was
518  * not contained within the multipart.
519  **/
520 GMimeObject *
521 g_mime_multipart_remove_at (GMimeMultipart *multipart, int index)
522 {
523         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
524         g_return_val_if_fail (index >= 0, NULL);
525         
526         return GMIME_MULTIPART_GET_CLASS (multipart)->remove_at (multipart, index);
527 }
528
529
530 /**
531  * g_mime_multipart_replace:
532  * @multipart: a #GMimeMultipart object
533  * @index: position of the mime part to replace
534  * @replacement: a #GMimeObject to use as the replacement
535  *
536  * Replaces the mime part at position @index within @multipart with
537  * @replacement.
538  *
539  * Returns: the mime part that was replaced or %NULL if the part was
540  * not contained within the multipart.
541  **/
542 GMimeObject *
543 g_mime_multipart_replace (GMimeMultipart *multipart, int index, GMimeObject *replacement)
544 {
545         GMimeObject *replaced;
546         
547         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
548         g_return_val_if_fail (GMIME_IS_OBJECT (replacement), NULL);
549         g_return_val_if_fail (index >= 0, NULL);
550         
551         if ((guint) index >= multipart->children->len)
552                 return NULL;
553         
554         replaced = multipart->children->pdata[index];
555         multipart->children->pdata[index] = replacement;
556         g_object_ref (replacement);
557         
558         return replaced;
559 }
560
561
562 static GMimeObject *
563 multipart_get_part (GMimeMultipart *multipart, int index)
564 {
565         GMimeObject *part;
566         
567         if ((guint) index >= multipart->children->len)
568                 return NULL;
569         
570         part = multipart->children->pdata[index];
571         
572         return part;
573 }
574
575
576 /**
577  * g_mime_multipart_get_part:
578  * @multipart: a #GMimeMultipart object
579  * @index: position of the mime part
580  *
581  * Gets the mime part at position @index within the multipart.
582  *
583  * Returns: the mime part at position @index.
584  **/
585 GMimeObject *
586 g_mime_multipart_get_part (GMimeMultipart *multipart, int index)
587 {
588         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
589         g_return_val_if_fail (index >= 0, NULL);
590         
591         return GMIME_MULTIPART_GET_CLASS (multipart)->get_part (multipart, index);
592 }
593
594
595 static gboolean
596 multipart_contains (GMimeMultipart *multipart, GMimeObject *part)
597 {
598         guint i;
599         
600         for (i = 0; i < multipart->children->len; i++) {
601                 if (part == (GMimeObject *) multipart->children->pdata[i])
602                         return TRUE;
603         }
604         
605         return FALSE;
606 }
607
608
609 /**
610  * g_mime_multipart_contains:
611  * @multipart: a #GMimeMultipart object
612  * @part: mime part
613  *
614  * Checks if @part is contained within @multipart.
615  *
616  * Returns: %TRUE if @part is a subpart of @multipart or %FALSE
617  * otherwise.
618  **/
619 gboolean
620 g_mime_multipart_contains (GMimeMultipart *multipart, GMimeObject *part)
621 {
622         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), FALSE);
623         g_return_val_if_fail (GMIME_IS_OBJECT (part), FALSE);
624         
625         return GMIME_MULTIPART_GET_CLASS (multipart)->contains (multipart, part);
626 }
627
628
629 static int
630 multipart_index_of (GMimeMultipart *multipart, GMimeObject *part)
631 {
632         guint i;
633         
634         for (i = 0; i < multipart->children->len; i++) {
635                 if (part == (GMimeObject *) multipart->children->pdata[i])
636                         return i;
637         }
638         
639         return -1;
640 }
641
642
643 /**
644  * g_mime_multipart_index_of:
645  * @multipart: a #GMimeMultipart object
646  * @part: mime part
647  *
648  * Gets the index of @part within @multipart.
649  *
650  * Returns: the index of @part within @multipart or %-1 if not found.
651  **/
652 int
653 g_mime_multipart_index_of (GMimeMultipart *multipart, GMimeObject *part)
654 {
655         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), -1);
656         g_return_val_if_fail (GMIME_IS_OBJECT (part), -1);
657         
658         return GMIME_MULTIPART_GET_CLASS (multipart)->index_of (multipart, part);
659 }
660
661
662 static int
663 multipart_get_count (GMimeMultipart *multipart)
664 {
665         return multipart->children->len;
666 }
667
668
669 /**
670  * g_mime_multipart_get_count:
671  * @multipart: a #GMimeMultipart object
672  *
673  * Gets the number of mime parts contained within the multipart.
674  *
675  * Returns: the number of mime parts contained within the multipart.
676  **/
677 int
678 g_mime_multipart_get_count (GMimeMultipart *multipart)
679 {
680         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), -1);
681         
682         return GMIME_MULTIPART_GET_CLASS (multipart)->get_count (multipart);
683 }
684
685
686 static void
687 read_random_pool (unsigned char *buffer, size_t bytes)
688 {
689 #ifdef __unix__
690         size_t nread = 0;
691         ssize_t n;
692         int fd;
693         
694         if ((fd = open ("/dev/urandom", O_RDONLY)) == -1) {
695                 if ((fd = open ("/dev/random", O_RDONLY)) == -1)
696                         return;
697         }
698         
699         do {
700                 do {
701                         n = read (fd, (char *) buffer + nread, bytes - nread);
702                 } while (n == -1 && errno == EINTR);
703                 
704                 if (n == -1 || n == 0)
705                         break;
706                 
707                 nread += n;
708         } while (nread < bytes);
709         
710         close (fd);
711 #else
712         size_t i;
713         
714         srand ((unsigned int) time (NULL));
715         
716         for (i = 0; i < bytes; i++)
717                 buffer[i] = (unsigned char) (rand () % 256);
718 #endif
719 }
720
721 static void
722 multipart_set_boundary (GMimeMultipart *multipart, const char *boundary)
723 {
724         char bbuf[35];
725         
726         if (!boundary) {
727                 /* Generate a fairly random boundary string. */
728                 unsigned char digest[16], *p;
729                 guint32 save = 0;
730                 int state = 0;
731                 
732                 read_random_pool (digest, 16);
733                 
734                 strcpy (bbuf, "=-");
735                 p = (unsigned char *) bbuf + 2;
736                 p += g_mime_encoding_base64_encode_step (digest, 16, p, &state, &save);
737                 *p = '\0';
738                 
739                 boundary = bbuf;
740         }
741         
742         g_mime_object_set_content_type_parameter (GMIME_OBJECT (multipart), "boundary", boundary);
743 }
744
745
746 /**
747  * g_mime_multipart_set_boundary:
748  * @multipart: a #GMimeMultipart object
749  * @boundary: boundary or %NULL to autogenerate one
750  *
751  * Sets @boundary as the boundary on the multipart. If @boundary is
752  * %NULL, then a boundary will be auto-generated for you.
753  **/
754 void
755 g_mime_multipart_set_boundary (GMimeMultipart *multipart, const char *boundary)
756 {
757         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
758         
759         GMIME_MULTIPART_GET_CLASS (multipart)->set_boundary (multipart, boundary);
760 }
761
762
763 static const char *
764 multipart_get_boundary (GMimeMultipart *multipart)
765 {
766         GMimeObject *object = (GMimeObject *) multipart;
767         const char *boundary;
768         
769         if ((boundary = g_mime_object_get_content_type_parameter (object, "boundary")))
770                 return boundary;
771         
772         multipart_set_boundary (multipart, NULL);
773         
774         return g_mime_object_get_content_type_parameter (object, "boundary");
775 }
776
777
778 /**
779  * g_mime_multipart_get_boundary:
780  * @multipart: a #GMimeMultipart object
781  *
782  * Gets the boundary on the multipart. If the internal boundary is
783  * %NULL, then an auto-generated boundary will be set on the multipart
784  * and returned.
785  *
786  * Returns: the boundary on the multipart.
787  **/
788 const char *
789 g_mime_multipart_get_boundary (GMimeMultipart *multipart)
790 {
791         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
792         
793         return GMIME_MULTIPART_GET_CLASS (multipart)->get_boundary (multipart);
794 }
795
796
797 static void
798 multipart_foreach (GMimeMultipart *multipart, GMimeObjectForeachFunc callback, gpointer user_data)
799 {
800         GMimeObject *part;
801         guint i;
802         
803         for (i = 0; i < multipart->children->len; i++) {
804                 part = (GMimeObject *) multipart->children->pdata[i];
805                 callback ((GMimeObject *) multipart, part, user_data);
806                 
807                 if (GMIME_IS_MULTIPART (part))
808                         multipart_foreach ((GMimeMultipart *) part, callback, user_data);
809         }
810 }
811
812
813 /**
814  * g_mime_multipart_foreach: 
815  * @multipart: a #GMimeMultipart
816  * @callback: function to call for each of @multipart's subparts.
817  * @user_data: user-supplied callback data
818  * 
819  * Recursively calls @callback on each of @multipart's subparts.
820  **/
821 void
822 g_mime_multipart_foreach (GMimeMultipart *multipart, GMimeObjectForeachFunc callback, gpointer user_data)
823 {
824         g_return_if_fail (GMIME_IS_MULTIPART (multipart));
825         g_return_if_fail (callback != NULL);
826         
827         multipart_foreach (multipart, callback, user_data);
828 }
829
830
831 /**
832  * g_mime_multipart_get_subpart_from_content_id: 
833  * @multipart: a multipart
834  * @content_id: the content id of the part to look for
835  *
836  * Gets the mime part with the content-id @content_id from the
837  * multipart @multipart.
838  *
839  * Returns: the #GMimeObject whose content-id matches the search string,
840  * or %NULL if a match cannot be found.
841  **/
842 GMimeObject *
843 g_mime_multipart_get_subpart_from_content_id (GMimeMultipart *multipart, const char *content_id)
844 {
845         GMimeObject *object = (GMimeObject *) multipart;
846         GMimeObject *subpart, *part;
847         GMimeMultipart *mpart;
848         guint i;
849         
850         g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
851         g_return_val_if_fail (content_id != NULL, NULL);
852         
853         if (object->content_id && !strcmp (object->content_id, content_id))
854                 return object;
855         
856         for (i = 0; i < multipart->children->len; i++) {
857                 subpart = multipart->children->pdata[i];
858                 
859                 if (subpart->content_id && !strcmp (subpart->content_id, content_id))
860                         return subpart;
861                 
862                 if (GMIME_IS_MULTIPART (subpart)) {
863                         mpart = (GMimeMultipart *) subpart;
864                         if ((part = g_mime_multipart_get_subpart_from_content_id (mpart, content_id)))
865                                 return part;
866                 }
867         }
868         
869         return NULL;
870 }