1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
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.
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.
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
27 #include <sys/types.h>
35 #include "gmime-multipart.h"
36 #include "gmime-utils.h"
43 * SECTION: gmime-multipart
44 * @title: GMimeMultipart
45 * @short_description: MIME multiparts
48 * A #GMimeMultipart represents all multipart MIME container parts.
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);
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);
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);
75 static GMimeObjectClass *parent_class = NULL;
79 g_mime_multipart_get_type (void)
81 static GType type = 0;
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),
93 (GInstanceInitFunc) g_mime_multipart_init,
96 type = g_type_register_static (GMIME_TYPE_OBJECT, "GMimeMultipart", &info, 0);
104 g_mime_multipart_class_init (GMimeMultipartClass *klass)
106 GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
107 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
109 parent_class = g_type_class_ref (GMIME_TYPE_OBJECT);
111 gobject_class->finalize = g_mime_multipart_finalize;
113 object_class->write_to_stream = multipart_write_to_stream;
114 object_class->encode = multipart_encode;
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;
130 g_mime_multipart_init (GMimeMultipart *multipart, GMimeMultipartClass *klass)
132 multipart->children = g_ptr_array_new ();
133 multipart->preface = NULL;
134 multipart->postface = NULL;
138 g_mime_multipart_finalize (GObject *object)
140 GMimeMultipart *multipart = (GMimeMultipart *) object;
143 g_free (multipart->preface);
144 g_free (multipart->postface);
146 for (i = 0; i < multipart->children->len; i++)
147 g_object_unref (multipart->children->pdata[i]);
149 g_ptr_array_free (multipart->children, TRUE);
151 G_OBJECT_CLASS (parent_class)->finalize (object);
155 multipart_write_to_stream (GMimeObject *object, GMimeStream *stream)
157 GMimeMultipart *multipart = (GMimeMultipart *) object;
158 ssize_t nwritten, total = 0;
159 const char *boundary;
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");
173 /* write the content headers */
174 if ((nwritten = g_mime_header_list_write_to_stream (object->headers, stream)) == -1)
179 /* write the preface */
180 if (multipart->preface) {
181 /* terminate the headers */
182 if (g_mime_stream_write (stream, "\n", 1) == -1)
187 if ((nwritten = g_mime_stream_write_string (stream, multipart->preface)) == -1)
193 for (i = 0; i < multipart->children->len; i++) {
194 part = multipart->children->pdata[i];
196 /* write the boundary */
197 if ((nwritten = g_mime_stream_printf (stream, "\n--%s\n", boundary)) == -1)
202 /* write this part out */
203 if ((nwritten = g_mime_object_write_to_stream (part, stream)) == -1)
209 /* write the end-boundary (but only if a boundary is set) */
211 if ((nwritten = g_mime_stream_printf (stream, "\n--%s--\n", boundary)) == -1)
217 /* write the postface */
218 if (multipart->postface) {
219 if ((nwritten = g_mime_stream_write_string (stream, multipart->postface)) == -1)
229 multipart_encode (GMimeObject *object, GMimeEncodingConstraint constraint)
231 GMimeMultipart *multipart = (GMimeMultipart *) object;
232 GMimeObject *subpart;
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);
243 * g_mime_multipart_new:
245 * Creates a new MIME multipart object with a default content-type of
248 * Returns: an empty MIME multipart object with a default content-type of
252 g_mime_multipart_new (void)
254 GMimeContentType *content_type;
255 GMimeMultipart *multipart;
257 multipart = g_object_newv (GMIME_TYPE_MULTIPART, 0, NULL);
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);
268 * g_mime_multipart_new_with_subtype:
269 * @subtype: content-type subtype
271 * Creates a new MIME multipart object with a content-type of
272 * multipart/@subtype.
274 * Returns: an empty MIME multipart object with a content-type of
275 * multipart/@subtype.
278 g_mime_multipart_new_with_subtype (const char *subtype)
280 GMimeContentType *content_type;
281 GMimeMultipart *multipart;
283 multipart = g_object_newv (GMIME_TYPE_MULTIPART, 0, NULL);
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);
294 * g_mime_multipart_set_preface:
295 * @multipart: a #GMimeMultipart object
298 * Sets the preface on the multipart.
301 g_mime_multipart_set_preface (GMimeMultipart *multipart, const char *preface)
303 g_return_if_fail (GMIME_IS_MULTIPART (multipart));
305 g_free (multipart->preface);
306 multipart->preface = g_strdup (preface);
311 * g_mime_multipart_get_preface:
312 * @multipart: a #GMimeMultipart object
314 * Gets the preface on the multipart.
316 * Returns: a pointer to the preface string on the multipart.
319 g_mime_multipart_get_preface (GMimeMultipart *multipart)
321 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
323 return multipart->preface;
328 * g_mime_multipart_set_postface:
329 * @multipart: a #GMimeMultipart object
330 * @postface: postface
332 * Sets the postface on the multipart.
335 g_mime_multipart_set_postface (GMimeMultipart *multipart, const char *postface)
337 g_return_if_fail (GMIME_IS_MULTIPART (multipart));
339 g_free (multipart->postface);
340 multipart->postface = g_strdup (postface);
345 * g_mime_multipart_get_postface:
346 * @multipart: a #GMimeMultipart object
348 * Gets the postface on the multipart.
350 * Returns: a pointer to the postface string on the multipart.
353 g_mime_multipart_get_postface (GMimeMultipart *multipart)
355 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
357 return multipart->postface;
362 multipart_clear (GMimeMultipart *multipart)
366 for (i = 0; i < multipart->children->len; i++)
367 g_object_unref (multipart->children->pdata[i]);
369 g_ptr_array_set_size (multipart->children, 0);
374 * g_mime_multipart_clear:
375 * @multipart: a #GMimeMultipart object
377 * Removes all subparts from @multipart.
380 g_mime_multipart_clear (GMimeMultipart *multipart)
382 g_return_if_fail (GMIME_IS_MULTIPART (multipart));
384 GMIME_MULTIPART_GET_CLASS (multipart)->clear (multipart);
389 multipart_add (GMimeMultipart *multipart, GMimeObject *part)
391 g_ptr_array_add (multipart->children, part);
397 * g_mime_multipart_add:
398 * @multipart: a #GMimeMultipart object
399 * @part: a #GMimeObject
401 * Adds a mime part to the multipart.
404 g_mime_multipart_add (GMimeMultipart *multipart, GMimeObject *part)
406 g_return_if_fail (GMIME_IS_MULTIPART (multipart));
407 g_return_if_fail (GMIME_IS_OBJECT (part));
409 GMIME_MULTIPART_GET_CLASS (multipart)->add (multipart, part);
414 ptr_array_insert (GPtrArray *array, guint index, gpointer object)
416 unsigned char *dest, *src;
419 if (index < array->len) {
420 /* need to shift some items */
421 g_ptr_array_set_size (array, array->len + 1);
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;
427 g_memmove (dest, src, (sizeof (void *) * n));
428 array->pdata[index] = object;
431 g_ptr_array_add (array, object);
436 multipart_insert (GMimeMultipart *multipart, int index, GMimeObject *part)
438 ptr_array_insert (multipart->children, index, part);
444 * g_mime_multipart_insert:
445 * @multipart: a #GMimeMultipart object
447 * @index: position to insert the mime part
449 * Inserts the specified mime part into the multipart at the position
453 g_mime_multipart_insert (GMimeMultipart *multipart, int index, GMimeObject *part)
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);
459 GMIME_MULTIPART_GET_CLASS (multipart)->insert (multipart, index, part);
464 multipart_remove (GMimeMultipart *multipart, GMimeObject *part)
466 if (!g_ptr_array_remove (multipart->children, part))
469 g_object_unref (part);
476 * g_mime_multipart_remove:
477 * @multipart: a #GMimeMultipart object
480 * Removes the specified mime part from the multipart.
482 * Returns: %TRUE if the part was removed or %FALSE otherwise.
485 g_mime_multipart_remove (GMimeMultipart *multipart, GMimeObject *part)
487 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), FALSE);
488 g_return_val_if_fail (GMIME_IS_OBJECT (part), FALSE);
490 return GMIME_MULTIPART_GET_CLASS (multipart)->remove (multipart, part);
495 multipart_remove_at (GMimeMultipart *multipart, int index)
499 if ((guint) index >= multipart->children->len)
502 part = multipart->children->pdata[index];
504 g_ptr_array_remove_index (multipart->children, index);
511 * g_mime_multipart_remove_at:
512 * @multipart: a #GMimeMultipart object
513 * @index: position of the mime part to remove
515 * Removes the mime part at position @index from the multipart.
517 * Returns: the mime part that was removed or %NULL if the part was
518 * not contained within the multipart.
521 g_mime_multipart_remove_at (GMimeMultipart *multipart, int index)
523 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
524 g_return_val_if_fail (index >= 0, NULL);
526 return GMIME_MULTIPART_GET_CLASS (multipart)->remove_at (multipart, index);
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
536 * Replaces the mime part at position @index within @multipart with
539 * Returns: the mime part that was replaced or %NULL if the part was
540 * not contained within the multipart.
543 g_mime_multipart_replace (GMimeMultipart *multipart, int index, GMimeObject *replacement)
545 GMimeObject *replaced;
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);
551 if ((guint) index >= multipart->children->len)
554 replaced = multipart->children->pdata[index];
555 multipart->children->pdata[index] = replacement;
556 g_object_ref (replacement);
563 multipart_get_part (GMimeMultipart *multipart, int index)
567 if ((guint) index >= multipart->children->len)
570 part = multipart->children->pdata[index];
577 * g_mime_multipart_get_part:
578 * @multipart: a #GMimeMultipart object
579 * @index: position of the mime part
581 * Gets the mime part at position @index within the multipart.
583 * Returns: the mime part at position @index.
586 g_mime_multipart_get_part (GMimeMultipart *multipart, int index)
588 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
589 g_return_val_if_fail (index >= 0, NULL);
591 return GMIME_MULTIPART_GET_CLASS (multipart)->get_part (multipart, index);
596 multipart_contains (GMimeMultipart *multipart, GMimeObject *part)
600 for (i = 0; i < multipart->children->len; i++) {
601 if (part == (GMimeObject *) multipart->children->pdata[i])
610 * g_mime_multipart_contains:
611 * @multipart: a #GMimeMultipart object
614 * Checks if @part is contained within @multipart.
616 * Returns: %TRUE if @part is a subpart of @multipart or %FALSE
620 g_mime_multipart_contains (GMimeMultipart *multipart, GMimeObject *part)
622 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), FALSE);
623 g_return_val_if_fail (GMIME_IS_OBJECT (part), FALSE);
625 return GMIME_MULTIPART_GET_CLASS (multipart)->contains (multipart, part);
630 multipart_index_of (GMimeMultipart *multipart, GMimeObject *part)
634 for (i = 0; i < multipart->children->len; i++) {
635 if (part == (GMimeObject *) multipart->children->pdata[i])
644 * g_mime_multipart_index_of:
645 * @multipart: a #GMimeMultipart object
648 * Gets the index of @part within @multipart.
650 * Returns: the index of @part within @multipart or %-1 if not found.
653 g_mime_multipart_index_of (GMimeMultipart *multipart, GMimeObject *part)
655 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), -1);
656 g_return_val_if_fail (GMIME_IS_OBJECT (part), -1);
658 return GMIME_MULTIPART_GET_CLASS (multipart)->index_of (multipart, part);
663 multipart_get_count (GMimeMultipart *multipart)
665 return multipart->children->len;
670 * g_mime_multipart_get_count:
671 * @multipart: a #GMimeMultipart object
673 * Gets the number of mime parts contained within the multipart.
675 * Returns: the number of mime parts contained within the multipart.
678 g_mime_multipart_get_count (GMimeMultipart *multipart)
680 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), -1);
682 return GMIME_MULTIPART_GET_CLASS (multipart)->get_count (multipart);
687 read_random_pool (unsigned char *buffer, size_t bytes)
694 if ((fd = open ("/dev/urandom", O_RDONLY)) == -1) {
695 if ((fd = open ("/dev/random", O_RDONLY)) == -1)
701 n = read (fd, (char *) buffer + nread, bytes - nread);
702 } while (n == -1 && errno == EINTR);
704 if (n == -1 || n == 0)
708 } while (nread < bytes);
714 srand ((unsigned int) time (NULL));
716 for (i = 0; i < bytes; i++)
717 buffer[i] = (unsigned char) (rand () % 256);
722 multipart_set_boundary (GMimeMultipart *multipart, const char *boundary)
727 /* Generate a fairly random boundary string. */
728 unsigned char digest[16], *p;
732 read_random_pool (digest, 16);
735 p = (unsigned char *) bbuf + 2;
736 p += g_mime_encoding_base64_encode_step (digest, 16, p, &state, &save);
742 g_mime_object_set_content_type_parameter (GMIME_OBJECT (multipart), "boundary", boundary);
747 * g_mime_multipart_set_boundary:
748 * @multipart: a #GMimeMultipart object
749 * @boundary: boundary or %NULL to autogenerate one
751 * Sets @boundary as the boundary on the multipart. If @boundary is
752 * %NULL, then a boundary will be auto-generated for you.
755 g_mime_multipart_set_boundary (GMimeMultipart *multipart, const char *boundary)
757 g_return_if_fail (GMIME_IS_MULTIPART (multipart));
759 GMIME_MULTIPART_GET_CLASS (multipart)->set_boundary (multipart, boundary);
764 multipart_get_boundary (GMimeMultipart *multipart)
766 GMimeObject *object = (GMimeObject *) multipart;
767 const char *boundary;
769 if ((boundary = g_mime_object_get_content_type_parameter (object, "boundary")))
772 multipart_set_boundary (multipart, NULL);
774 return g_mime_object_get_content_type_parameter (object, "boundary");
779 * g_mime_multipart_get_boundary:
780 * @multipart: a #GMimeMultipart object
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
786 * Returns: the boundary on the multipart.
789 g_mime_multipart_get_boundary (GMimeMultipart *multipart)
791 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
793 return GMIME_MULTIPART_GET_CLASS (multipart)->get_boundary (multipart);
798 multipart_foreach (GMimeMultipart *multipart, GMimeObjectForeachFunc callback, gpointer user_data)
803 for (i = 0; i < multipart->children->len; i++) {
804 part = (GMimeObject *) multipart->children->pdata[i];
805 callback ((GMimeObject *) multipart, part, user_data);
807 if (GMIME_IS_MULTIPART (part))
808 multipart_foreach ((GMimeMultipart *) part, callback, user_data);
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
819 * Recursively calls @callback on each of @multipart's subparts.
822 g_mime_multipart_foreach (GMimeMultipart *multipart, GMimeObjectForeachFunc callback, gpointer user_data)
824 g_return_if_fail (GMIME_IS_MULTIPART (multipart));
825 g_return_if_fail (callback != NULL);
827 multipart_foreach (multipart, callback, user_data);
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
836 * Gets the mime part with the content-id @content_id from the
837 * multipart @multipart.
839 * Returns: the #GMimeObject whose content-id matches the search string,
840 * or %NULL if a match cannot be found.
843 g_mime_multipart_get_subpart_from_content_id (GMimeMultipart *multipart, const char *content_id)
845 GMimeObject *object = (GMimeObject *) multipart;
846 GMimeObject *subpart, *part;
847 GMimeMultipart *mpart;
850 g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
851 g_return_val_if_fail (content_id != NULL, NULL);
853 if (object->content_id && !strcmp (object->content_id, content_id))
856 for (i = 0; i < multipart->children->len; i++) {
857 subpart = multipart->children->pdata[i];
859 if (subpart->content_id && !strcmp (subpart->content_id, content_id))
862 if (GMIME_IS_MULTIPART (subpart)) {
863 mpart = (GMimeMultipart *) subpart;
864 if ((part = g_mime_multipart_get_subpart_from_content_id (mpart, content_id)))