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
30 #include "gmime-part-iter.h"
31 #include "gmime-message-part.h"
32 #include "gmime-multipart.h"
33 #include "gmime-message.h"
34 #include "gmime-part.h"
37 * SECTION: gmime-part-iter
38 * @title: GMimePartIter
39 * @short_description: MIME part iterators
40 * @see_also: #GMimeObject
42 * #GMimePartIter is an iterator for traversing a #GMimeObject tree in
47 typedef struct _GMimeObjectStack GMimeObjectStack;
49 struct _GMimeObjectStack {
50 GMimeObjectStack *parent;
55 struct _GMimePartIter {
56 GMimeObjectStack *parent;
57 GMimeObject *toplevel;
64 g_mime_part_iter_push (GMimePartIter *iter, GMimeObject *object, int index)
66 GMimeObjectStack *node;
69 g_array_append_val (iter->path, index);
71 node = g_slice_new (GMimeObjectStack);
72 node->indexed = index != -1;
73 node->parent = iter->parent;
74 node->object = object;
79 g_mime_part_iter_pop (GMimePartIter *iter)
81 GMimeObjectStack *node;
83 if (!iter->parent || !iter->parent->parent)
86 if (iter->parent->indexed) {
87 iter->index = g_array_index (iter->path, int, iter->path->len - 1);
88 g_array_set_size (iter->path, iter->path->len - 1);
91 iter->current = iter->parent->object;
94 iter->parent = node->parent;
95 g_slice_free (GMimeObjectStack, node);
102 * g_mime_part_iter_new:
103 * @toplevel: a #GMimeObject to use as the toplevel
105 * Creates a new #GMimePartIter for iterating over @toplevel's subparts.
107 * Returns: a newly allocated #GMimePartIter which should be freed
108 * using g_mime_part_iter_free() when finished with it.
111 g_mime_part_iter_new (GMimeObject *toplevel)
115 g_return_val_if_fail (GMIME_IS_OBJECT (toplevel), NULL);
117 iter = g_slice_new (GMimePartIter);
118 iter->path = g_array_new (FALSE, FALSE, sizeof (int));
119 iter->toplevel = toplevel;
120 g_object_ref (toplevel);
123 g_mime_part_iter_reset (iter);
130 * g_mime_part_iter_free:
131 * @iter: a #GMimePartIter
133 * Frees the memory allocated by g_mime_part_iter_new().
136 g_mime_part_iter_free (GMimePartIter *iter)
141 g_object_unref (iter->toplevel);
142 g_array_free (iter->path, TRUE);
143 g_slice_free_chain (GMimeObjectStack, iter->parent, parent);
144 g_slice_free (GMimePartIter, iter);
149 * g_mime_part_iter_reset:
150 * @iter: a #GMimePartIter
152 * Resets the state of @iter to its initial state.
155 g_mime_part_iter_reset (GMimePartIter *iter)
157 g_return_if_fail (iter != NULL);
159 if (GMIME_IS_MESSAGE (iter->toplevel))
160 iter->current = g_mime_message_get_mime_part ((GMimeMessage *) iter->toplevel);
162 iter->current = iter->toplevel;
164 g_slice_free_chain (GMimeObjectStack, iter->parent, parent);
165 g_array_set_size (iter->path, 0);
169 /* set our initial 'current' part to our first child */
170 g_mime_part_iter_next (iter);
175 * g_mime_part_iter_jump_to:
176 * @iter: a #GMimePartIter
177 * @path: a string representing the path to jump to
179 * Updates the state of @iter to point to the #GMimeObject specified
182 * Returns: %TRUE if the #GMimeObject specified by @path exists or
186 g_mime_part_iter_jump_to (GMimePartIter *iter, const char *path)
188 GMimeMessagePart *message_part;
189 GMimeMultipart *multipart;
190 GMimeMessage *message;
191 GMimeObject *current;
197 g_return_val_if_fail (iter != NULL, FALSE);
199 g_mime_part_iter_reset (iter);
201 if (!path || !path[0])
204 parent = iter->parent->object;
205 iter->current = NULL;
211 /* Note: path components are 1-based instead of 0-based */
212 if ((index = strtol (inptr, &dot, 10)) <= 0 || errno == ERANGE ||
213 index == G_MAXINT || !(*dot == '.' || *dot == '\0'))
216 /* normalize to a 0-based index */
219 if (GMIME_IS_MESSAGE_PART (parent)) {
220 message_part = (GMimeMessagePart *) parent;
221 if (!(message = g_mime_message_part_get_message (message_part)))
224 if (!(parent = g_mime_message_get_mime_part (message)))
227 if (!GMIME_IS_MULTIPART (parent))
231 } else if (GMIME_IS_MULTIPART (parent)) {
233 multipart = (GMimeMultipart *) parent;
234 if (index >= g_mime_multipart_get_count (multipart))
237 current = g_mime_multipart_get_part (multipart, index);
239 } else if (GMIME_IS_MESSAGE (parent)) {
240 message = (GMimeMessage *) parent;
241 if (!(current = g_mime_message_get_mime_part (message)))
252 g_mime_part_iter_push (iter, current, iter->index);
263 iter->current = current;
266 return current != NULL;
271 * g_mime_part_iter_is_valid:
272 * @iter: a #GMimePartIter
274 * Checks that the current state of @iter is valid.
276 * Returns: %TRUE if @iter is valid or %FALSE otherwise.
279 g_mime_part_iter_is_valid (GMimePartIter *iter)
281 g_return_val_if_fail (iter != NULL, FALSE);
283 return iter->current != NULL;
288 * g_mime_part_iter_next:
289 * @iter: a #GMimePartIter
291 * Advances to the next part in the MIME structure used to initialize
294 * Returns: %TRUE if successful or %FALSE otherwise.
297 g_mime_part_iter_next (GMimePartIter *iter)
299 GMimeMessagePart *message_part;
300 GMimeMultipart *multipart;
301 GMimeObject *mime_part;
302 GMimeMessage *message;
304 if (!g_mime_part_iter_is_valid (iter))
307 if (GMIME_IS_MESSAGE_PART (iter->current)) {
308 /* descend into our children */
309 message_part = (GMimeMessagePart *) iter->current;
310 message = g_mime_message_part_get_message (message_part);
311 mime_part = message ? g_mime_message_get_mime_part (message) : NULL;
312 if (mime_part != NULL) {
313 g_mime_part_iter_push (iter, iter->current, iter->index);
314 iter->current = mime_part;
316 if (GMIME_IS_MULTIPART (mime_part)) {
325 } else if (GMIME_IS_MULTIPART (iter->current)) {
326 /* descend into our children */
328 multipart = (GMimeMultipart *) iter->current;
329 if (g_mime_multipart_get_count (multipart) > 0) {
330 g_mime_part_iter_push (iter, iter->current, iter->index);
331 iter->current = g_mime_multipart_get_part (multipart, 0);
337 /* find the next sibling */
338 while (iter->parent) {
339 if (GMIME_IS_MULTIPART (iter->parent->object)) {
340 /* iterate to the next part in the multipart */
341 multipart = (GMimeMultipart *) iter->parent->object;
344 if (g_mime_multipart_get_count (multipart) > iter->index) {
345 iter->current = g_mime_multipart_get_part (multipart, iter->index);
350 if (!g_mime_part_iter_pop (iter))
354 iter->current = NULL;
362 * g_mime_part_iter_prev:
363 * @iter: a #GMimePartIter
365 * Rewinds to the previous part in the MIME structure used to
368 * Returns: %TRUE if successful or %FALSE otherwise.
371 g_mime_part_iter_prev (GMimePartIter *iter)
373 GMimeMultipart *multipart;
375 if (!g_mime_part_iter_is_valid (iter))
378 if (iter->parent == NULL) {
379 iter->current = NULL;
384 if (GMIME_IS_MULTIPART (iter->parent->object)) {
385 /* revert backward to the previous part in the multipart */
386 multipart = (GMimeMultipart *) iter->parent->object;
389 if (iter->index >= 0) {
390 iter->current = g_mime_multipart_get_part (multipart, iter->index);
395 return g_mime_part_iter_pop (iter);
400 * g_mime_part_iter_get_toplevel:
401 * @iter: a #GMimePartIter
403 * Gets the toplevel #GMimeObject used to initialize @iter.
405 * Returns: the toplevel #GMimeObject.
408 g_mime_part_iter_get_toplevel (GMimePartIter *iter)
410 g_return_val_if_fail (iter != NULL, NULL);
412 return iter->toplevel;
417 * g_mime_part_iter_get_current:
418 * @iter: a #GMimePartIter
420 * Gets the #GMimeObject at the current #GMimePartIter position.
422 * Returns: the current #GMimeObject or %NULL if the state of @iter is
426 g_mime_part_iter_get_current (GMimePartIter *iter)
428 g_return_val_if_fail (iter != NULL, NULL);
430 return iter->current;
435 * g_mime_part_iter_get_parent:
436 * @iter: a #GMimePartIter
438 * Gets the parent of the #GMimeObject at the current #GMimePartIter
441 * Returns: the parent #GMimeObject or %NULL if the state of @iter is
445 g_mime_part_iter_get_parent (GMimePartIter *iter)
447 g_return_val_if_fail (iter != NULL, NULL);
449 if (!g_mime_part_iter_is_valid (iter))
452 return iter->parent->object;
457 * g_mime_part_iter_get_path:
458 * @iter: a #GMimePartIter
460 * Gets the path of the current #GMimeObject in the MIME structure
461 * used to initialize @iter.
463 * Returns: a newly allocated string representation of the path to the
464 * #GMimeObject at the current #GMimePartIter position.
467 g_mime_part_iter_get_path (GMimePartIter *iter)
472 if (!g_mime_part_iter_is_valid (iter))
475 /* Note: path components are 1-based instead of 0-based */
477 path = g_string_new ("");
478 for (i = 0; i < iter->path->len; i++) {
479 v = g_array_index (iter->path, int, i);
480 g_string_append_printf (path, "%d.", v + 1);
483 g_string_append_printf (path, "%d", iter->index + 1);
485 return g_string_free (path, FALSE);
490 * g_mime_part_iter_replace:
491 * @iter: a #GMimePartIter
492 * @replacement: a #GMimeObject
494 * Replaces the #GMimeObject at the current position with @replacement.
496 * Returns: %TRUE if the part at the current position was replaced or
500 g_mime_part_iter_replace (GMimePartIter *iter, GMimeObject *replacement)
502 GMimeMessagePart *message_part;
503 GMimeMessage *message;
504 GMimeObject *current;
508 g_return_val_if_fail (GMIME_IS_OBJECT (replacement), FALSE);
510 if (!g_mime_part_iter_is_valid (iter))
513 if (iter->current == iter->toplevel) {
514 g_object_unref (iter->toplevel);
515 iter->toplevel = replacement;
516 g_object_ref (replacement);
520 parent = iter->parent ? iter->parent->object : iter->toplevel;
523 /* now we can safely replace the previously referenced part in its parent */
524 if (GMIME_IS_MESSAGE_PART (parent)) {
525 /* depending on what we've been given as a
526 * replacement, we might replace the message in the
527 * message/rfc822 part or we might end up replacing
528 * the toplevel mime part of said message. */
529 message_part = (GMimeMessagePart *) parent;
530 message = g_mime_message_part_get_message (message_part);
531 if (GMIME_IS_MESSAGE (replacement))
532 g_mime_message_part_set_message (message_part, (GMimeMessage *) replacement);
534 g_mime_message_set_mime_part (message, replacement);
535 } else if (GMIME_IS_MULTIPART (parent)) {
536 current = g_mime_multipart_replace ((GMimeMultipart *) parent, index, replacement);
537 g_object_unref (current);
538 } else if (GMIME_IS_MESSAGE (parent)) {
539 g_mime_message_set_mime_part ((GMimeMessage *) parent, replacement);
541 g_assert_not_reached ();
544 iter->current = replacement;
551 * g_mime_part_iter_remove:
552 * @iter: a #GMimePartIter
554 * Removes the #GMimeObject at the current position from its
555 * parent. If successful, @iter is advanced to the next position
556 * (since the current position will become invalid).
558 * Returns: %TRUE if the part at the current position was removed or
562 g_mime_part_iter_remove (GMimePartIter *iter)
564 GMimeObject *current;
568 if (!g_mime_part_iter_is_valid (iter))
571 if (iter->current == iter->toplevel)
574 parent = iter->parent ? iter->parent->object : iter->toplevel;
575 current = iter->current;
578 /* iterate to the next part so we have something valid to refer to */
579 g_mime_part_iter_next (iter);
581 /* now we can safely remove the previously referenced part from its parent */
582 if (GMIME_IS_MESSAGE_PART (parent)) {
583 g_mime_message_part_set_message ((GMimeMessagePart *) parent, NULL);
584 } else if (GMIME_IS_MULTIPART (parent)) {
585 g_mime_multipart_remove_at ((GMimeMultipart *) parent, index);
586 g_object_unref (current);
587 } else if (GMIME_IS_MESSAGE (parent)) {
588 g_mime_message_set_mime_part ((GMimeMessage *) parent, NULL);
590 g_assert_not_reached ();