Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-part-iter.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 <string.h>
28 #include <errno.h>
29
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"
35
36 /**
37  * SECTION: gmime-part-iter
38  * @title: GMimePartIter
39  * @short_description: MIME part iterators
40  * @see_also: #GMimeObject
41  *
42  * #GMimePartIter is an iterator for traversing a #GMimeObject tree in
43  * Depth-First order.
44  **/
45
46
47 typedef struct _GMimeObjectStack GMimeObjectStack;
48
49 struct _GMimeObjectStack {
50         GMimeObjectStack *parent;
51         GMimeObject *object;
52         gboolean indexed;
53 };
54
55 struct _GMimePartIter {
56         GMimeObjectStack *parent;
57         GMimeObject *toplevel;
58         GMimeObject *current;
59         GArray *path;
60         int index;
61 };
62
63 static void
64 g_mime_part_iter_push (GMimePartIter *iter, GMimeObject *object, int index)
65 {
66         GMimeObjectStack *node;
67         
68         if (index != -1)
69                 g_array_append_val (iter->path, index);
70         
71         node = g_slice_new (GMimeObjectStack);
72         node->indexed = index != -1;
73         node->parent = iter->parent;
74         node->object = object;
75         iter->parent = node;
76 }
77
78 static gboolean
79 g_mime_part_iter_pop (GMimePartIter *iter)
80 {
81         GMimeObjectStack *node;
82         
83         if (!iter->parent || !iter->parent->parent)
84                 return FALSE;
85         
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);
89         }
90         
91         iter->current = iter->parent->object;
92         
93         node = iter->parent;
94         iter->parent = node->parent;
95         g_slice_free (GMimeObjectStack, node);
96         
97         return TRUE;
98 }
99
100
101 /**
102  * g_mime_part_iter_new:
103  * @toplevel: a #GMimeObject to use as the toplevel
104  *
105  * Creates a new #GMimePartIter for iterating over @toplevel's subparts.
106  *
107  * Returns: a newly allocated #GMimePartIter which should be freed
108  * using g_mime_part_iter_free() when finished with it.
109  **/
110 GMimePartIter *
111 g_mime_part_iter_new (GMimeObject *toplevel)
112 {
113         GMimePartIter *iter;
114         
115         g_return_val_if_fail (GMIME_IS_OBJECT (toplevel), NULL);
116         
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);
121         iter->parent = NULL;
122         
123         g_mime_part_iter_reset (iter);
124         
125         return iter;
126 }
127
128
129 /**
130  * g_mime_part_iter_free:
131  * @iter: a #GMimePartIter
132  *
133  * Frees the memory allocated by g_mime_part_iter_new().
134  **/
135 void
136 g_mime_part_iter_free (GMimePartIter *iter)
137 {
138         if (iter == NULL)
139                 return;
140         
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);
145 }
146
147
148 /**
149  * g_mime_part_iter_reset:
150  * @iter: a #GMimePartIter
151  *
152  * Resets the state of @iter to its initial state.
153  **/
154 void
155 g_mime_part_iter_reset (GMimePartIter *iter)
156 {
157         g_return_if_fail (iter != NULL);
158         
159         if (GMIME_IS_MESSAGE (iter->toplevel))
160                 iter->current = g_mime_message_get_mime_part ((GMimeMessage *) iter->toplevel);
161         else
162                 iter->current = iter->toplevel;
163         
164         g_slice_free_chain (GMimeObjectStack, iter->parent, parent);
165         g_array_set_size (iter->path, 0);
166         iter->parent = NULL;
167         iter->index = -1;
168         
169         /* set our initial 'current' part to our first child */
170         g_mime_part_iter_next (iter);
171 }
172
173
174 /**
175  * g_mime_part_iter_jump_to:
176  * @iter: a #GMimePartIter
177  * @path: a string representing the path to jump to
178  *
179  * Updates the state of @iter to point to the #GMimeObject specified
180  * by @path.
181  *
182  * Returns: %TRUE if the #GMimeObject specified by @path exists or
183  * %FALSE otherwise.
184  **/
185 gboolean
186 g_mime_part_iter_jump_to (GMimePartIter *iter, const char *path)
187 {
188         GMimeMessagePart *message_part;
189         GMimeMultipart *multipart;
190         GMimeMessage *message;
191         GMimeObject *current;
192         GMimeObject *parent;
193         const char *inptr;
194         int index;
195         char *dot;
196         
197         g_return_val_if_fail (iter != NULL, FALSE);
198         
199         g_mime_part_iter_reset (iter);
200         
201         if (!path || !path[0])
202                 return TRUE;
203         
204         parent = iter->parent->object;
205         iter->current = NULL;
206         current = NULL;
207         inptr = path;
208         index = -1;
209         
210         while (*inptr) {
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'))
214                         return FALSE;
215                 
216                 /* normalize to a 0-based index */
217                 index--;
218                 
219                 if (GMIME_IS_MESSAGE_PART (parent)) {
220                         message_part = (GMimeMessagePart *) parent;
221                         if (!(message = g_mime_message_part_get_message (message_part)))
222                                 return FALSE;
223                         
224                         if (!(parent = g_mime_message_get_mime_part (message)))
225                                 return FALSE;
226                         
227                         if (!GMIME_IS_MULTIPART (parent))
228                                 return FALSE;
229                         
230                         goto multipart;
231                 } else if (GMIME_IS_MULTIPART (parent)) {
232                 multipart:
233                         multipart = (GMimeMultipart *) parent;
234                         if (index >= g_mime_multipart_get_count (multipart))
235                                 return FALSE;
236                         
237                         current = g_mime_multipart_get_part (multipart, index);
238                         iter->index = index;
239                 } else if (GMIME_IS_MESSAGE (parent)) {
240                         message = (GMimeMessage *) parent;
241                         if (!(current = g_mime_message_get_mime_part (message)))
242                                 return FALSE;
243                         
244                         iter->index = -1;
245                 } else {
246                         return FALSE;
247                 }
248                 
249                 if (*dot == '\0')
250                         break;
251                 
252                 g_mime_part_iter_push (iter, current, iter->index);
253                 parent = current;
254                 current = NULL;
255                 index = -1;
256                 
257                 if (*dot != '.')
258                         break;
259                 
260                 inptr = dot + 1;
261         }
262         
263         iter->current = current;
264         iter->index = index;
265         
266         return current != NULL;
267 }
268
269
270 /**
271  * g_mime_part_iter_is_valid:
272  * @iter: a #GMimePartIter
273  *
274  * Checks that the current state of @iter is valid.
275  *
276  * Returns: %TRUE if @iter is valid or %FALSE otherwise.
277  **/
278 gboolean
279 g_mime_part_iter_is_valid (GMimePartIter *iter)
280 {
281         g_return_val_if_fail (iter != NULL, FALSE);
282         
283         return iter->current != NULL;
284 }
285
286
287 /**
288  * g_mime_part_iter_next:
289  * @iter: a #GMimePartIter
290  *
291  * Advances to the next part in the MIME structure used to initialize
292  * @iter.
293  *
294  * Returns: %TRUE if successful or %FALSE otherwise.
295  **/
296 gboolean
297 g_mime_part_iter_next (GMimePartIter *iter)
298 {
299         GMimeMessagePart *message_part;
300         GMimeMultipart *multipart;
301         GMimeObject *mime_part;
302         GMimeMessage *message;
303         
304         if (!g_mime_part_iter_is_valid (iter))
305                 return FALSE;
306         
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;
315                         
316                         if (GMIME_IS_MULTIPART (mime_part)) {
317                                 iter->index = -1;
318                                 goto multipart;
319                         }
320                         
321                         iter->index = 0;
322                         
323                         return TRUE;
324                 }
325         } else if (GMIME_IS_MULTIPART (iter->current)) {
326                 /* descend into our children */
327         multipart:
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);
332                         iter->index = 0;
333                         return TRUE;
334                 }
335         }
336         
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;
342                         iter->index++;
343                         
344                         if (g_mime_multipart_get_count (multipart) > iter->index) {
345                                 iter->current = g_mime_multipart_get_part (multipart, iter->index);
346                                 return TRUE;
347                         }
348                 }
349                 
350                 if (!g_mime_part_iter_pop (iter))
351                         break;
352         }
353         
354         iter->current = NULL;
355         iter->index = -1;
356         
357         return FALSE;
358 }
359
360
361 /**
362  * g_mime_part_iter_prev:
363  * @iter: a #GMimePartIter
364  *
365  * Rewinds to the previous part in the MIME structure used to
366  * initialize @iter.
367  *
368  * Returns: %TRUE if successful or %FALSE otherwise.
369  **/
370 gboolean
371 g_mime_part_iter_prev (GMimePartIter *iter)
372 {
373         GMimeMultipart *multipart;
374         
375         if (!g_mime_part_iter_is_valid (iter))
376                 return FALSE;
377         
378         if (iter->parent == NULL) {
379                 iter->current = NULL;
380                 iter->index = -1;
381                 return FALSE;
382         }
383         
384         if (GMIME_IS_MULTIPART (iter->parent->object)) {
385                 /* revert backward to the previous part in the multipart */
386                 multipart = (GMimeMultipart *) iter->parent->object;
387                 iter->index--;
388                 
389                 if (iter->index >= 0) {
390                         iter->current = g_mime_multipart_get_part (multipart, iter->index);
391                         return TRUE;
392                 }
393         }
394         
395         return g_mime_part_iter_pop (iter);
396 }
397
398
399 /**
400  * g_mime_part_iter_get_toplevel:
401  * @iter: a #GMimePartIter
402  *
403  * Gets the toplevel #GMimeObject used to initialize @iter.
404  *
405  * Returns: the toplevel #GMimeObject.
406  **/
407 GMimeObject *
408 g_mime_part_iter_get_toplevel (GMimePartIter *iter)
409 {
410         g_return_val_if_fail (iter != NULL, NULL);
411         
412         return iter->toplevel;
413 }
414
415
416 /**
417  * g_mime_part_iter_get_current:
418  * @iter: a #GMimePartIter
419  *
420  * Gets the #GMimeObject at the current #GMimePartIter position.
421  *
422  * Returns: the current #GMimeObject or %NULL if the state of @iter is
423  * invalid.
424  **/
425 GMimeObject *
426 g_mime_part_iter_get_current (GMimePartIter *iter)
427 {
428         g_return_val_if_fail (iter != NULL, NULL);
429         
430         return iter->current;
431 }
432
433
434 /**
435  * g_mime_part_iter_get_parent:
436  * @iter: a #GMimePartIter
437  *
438  * Gets the parent of the #GMimeObject at the current #GMimePartIter
439  * position.
440  *
441  * Returns: the parent #GMimeObject or %NULL if the state of @iter is
442  * invalid.
443  **/
444 GMimeObject *
445 g_mime_part_iter_get_parent (GMimePartIter *iter)
446 {
447         g_return_val_if_fail (iter != NULL, NULL);
448         
449         if (!g_mime_part_iter_is_valid (iter))
450                 return NULL;
451         
452         return iter->parent->object;
453 }
454
455
456 /**
457  * g_mime_part_iter_get_path:
458  * @iter: a #GMimePartIter
459  *
460  * Gets the path of the current #GMimeObject in the MIME structure
461  * used to initialize @iter.
462  *
463  * Returns: a newly allocated string representation of the path to the
464  * #GMimeObject at the current #GMimePartIter position.
465  **/
466 char *
467 g_mime_part_iter_get_path (GMimePartIter *iter)
468 {
469         GString *path;
470         int i, v;
471         
472         if (!g_mime_part_iter_is_valid (iter))
473                 return NULL;
474         
475         /* Note: path components are 1-based instead of 0-based */
476         
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);
481         }
482         
483         g_string_append_printf (path, "%d", iter->index + 1);
484         
485         return g_string_free (path, FALSE);
486 }
487
488
489 /**
490  * g_mime_part_iter_replace:
491  * @iter: a #GMimePartIter
492  * @replacement: a #GMimeObject
493  *
494  * Replaces the #GMimeObject at the current position with @replacement.
495  *
496  * Returns: %TRUE if the part at the current position was replaced or
497  * %FALSE otherwise.
498  **/
499 gboolean
500 g_mime_part_iter_replace (GMimePartIter *iter, GMimeObject *replacement)
501 {
502         GMimeMessagePart *message_part;
503         GMimeMessage *message;
504         GMimeObject *current;
505         GMimeObject *parent;
506         int index;
507         
508         g_return_val_if_fail (GMIME_IS_OBJECT (replacement), FALSE);
509         
510         if (!g_mime_part_iter_is_valid (iter))
511                 return FALSE;
512         
513         if (iter->current == iter->toplevel) {
514                 g_object_unref (iter->toplevel);
515                 iter->toplevel = replacement;
516                 g_object_ref (replacement);
517                 return TRUE;
518         }
519         
520         parent = iter->parent ? iter->parent->object : iter->toplevel;
521         index = iter->index;
522         
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);
533                 else
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);
540         } else {
541                 g_assert_not_reached ();
542         }
543         
544         iter->current = replacement;
545         
546         return TRUE;
547 }
548
549
550 /**
551  * g_mime_part_iter_remove:
552  * @iter: a #GMimePartIter
553  *
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).
557  *
558  * Returns: %TRUE if the part at the current position was removed or
559  * %FALSE otherwise.
560  **/
561 gboolean
562 g_mime_part_iter_remove (GMimePartIter *iter)
563 {
564         GMimeObject *current;
565         GMimeObject *parent;
566         int index;
567         
568         if (!g_mime_part_iter_is_valid (iter))
569                 return FALSE;
570         
571         if (iter->current == iter->toplevel)
572                 return FALSE;
573         
574         parent = iter->parent ? iter->parent->object : iter->toplevel;
575         current = iter->current;
576         index = iter->index;
577         
578         /* iterate to the next part so we have something valid to refer to */
579         g_mime_part_iter_next (iter);
580         
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);
589         } else {
590                 g_assert_not_reached ();
591         }
592         
593         return TRUE;
594 }