[gmime] Implemented mime part iterators



commit 633101f3dce55f17c174d2ebb4e8b0cd7355fad0
Author: Jeffrey Stedfast <fejj gnome org>
Date:   Sat Aug 13 18:45:51 2011 -0400

    Implemented mime part iterators
    
    2011-08-13  Jeffrey Stedfast  <fejj gnome org>
    
    	* gmime/gmime-part-iter.c: New class for iterating over a tree
    	of GMimeObjects. Fixes bug #652012.

 ChangeLog                         |    5 +
 docs/reference/gmime-docs.sgml    |    2 +
 docs/reference/gmime-sections.txt |   17 +
 gmime/Makefile.am                 |    2 +
 gmime/gmime-part-iter.c           |  612 +++++++++++++++++++++++++++++++++++++
 gmime/gmime-part-iter.h           |   57 ++++
 gmime/gmime.h                     |    1 +
 mono/GMime.metadata               |    3 +
 mono/gmime-api.raw                |   46 +++
 tests/test-parser.c               |   78 +++++-
 tests/test2.eml                   |    1 -
 11 files changed, 819 insertions(+), 5 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 0c0ca11..487d0b5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2011-08-13  Jeffrey Stedfast  <fejj gnome org>
 
+	* gmime/gmime-part-iter.c: New class for iterating over a tree of
+	GMimeObjects. Fixes bug #652012.
+
+2011-08-13  Jeffrey Stedfast  <fejj gnome org>
+
 	* gmime/gmime-multipart.c (g_mime_multipart_replace): New
 	convenience function to replace a mime part at a specified
 	position.
diff --git a/docs/reference/gmime-docs.sgml b/docs/reference/gmime-docs.sgml
index c1d1bb9..c60ee89 100644
--- a/docs/reference/gmime-docs.sgml
+++ b/docs/reference/gmime-docs.sgml
@@ -12,6 +12,7 @@
 <!ENTITY GMimeMultipartEncrypted SYSTEM "xml/gmime-multipart-encrypted.xml">
 <!ENTITY GMimeMultipartSigned SYSTEM "xml/gmime-multipart-signed.xml">
 <!ENTITY GMimePart SYSTEM "xml/gmime-part.xml">
+<!ENTITY GMimePartIter SYSTEM "xml/gmime-part-iter.xml">
 <!ENTITY GMimeMessage SYSTEM "xml/gmime-message.xml">
 <!ENTITY GMimeMessagePart SYSTEM "xml/gmime-message-part.xml">
 <!ENTITY GMimeMessagePartial SYSTEM "xml/gmime-message-partial.xml">
@@ -240,6 +241,7 @@ string utilities, file utilities, a main loop abstraction, and so on.
       &GMimeMultipartSigned;
       &GMimeMessagePart;
       &GMimeMessagePartial;
+      &GMimePartIter;
     </chapter>
 
     <chapter id="Parsers">
diff --git a/docs/reference/gmime-sections.txt b/docs/reference/gmime-sections.txt
index 3b3240f..7e53d8e 100644
--- a/docs/reference/gmime-sections.txt
+++ b/docs/reference/gmime-sections.txt
@@ -747,6 +747,23 @@ GMimePartClass
 </SECTION>
 
 <SECTION>
+<FILE>gmime-part-iter</FILE>
+GMimePartIter
+g_mime_part_iter_new
+g_mime_part_iter_free
+g_mime_part_iter_reset
+g_mime_part_iter_jump_to
+g_mime_part_iter_is_valid
+g_mime_part_iter_next
+g_mime_part_iter_prev
+g_mime_part_iter_get_current
+g_mime_part_iter_get_parent
+g_mime_part_iter_get_path
+g_mime_part_iter_replace
+g_mime_part_iter_remove
+</SECTION>
+
+<SECTION>
 <FILE>gmime-multipart</FILE>
 GMimeMultipart
 g_mime_multipart_new
diff --git a/gmime/Makefile.am b/gmime/Makefile.am
index 048fc1e..bcba7a7 100644
--- a/gmime/Makefile.am
+++ b/gmime/Makefile.am
@@ -56,6 +56,7 @@ libgmime_2_6_la_SOURCES = 		\
 	gmime-parse-utils.c		\
 	gmime-parser.c			\
 	gmime-part.c			\
+	gmime-part-iter.c		\
 	gmime-signature.c		\
 	gmime-stream.c			\
 	gmime-stream-buffer.c		\
@@ -113,6 +114,7 @@ gmimeinclude_HEADERS = 			\
 	gmime-param.h			\
 	gmime-parser.h			\
 	gmime-part.h			\
+	gmime-part-iter.h		\
 	gmime-signature.h		\
 	gmime-stream.h			\
 	gmime-stream-buffer.h		\
diff --git a/gmime/gmime-part-iter.c b/gmime/gmime-part-iter.c
new file mode 100644
index 0000000..cfea7cd
--- /dev/null
+++ b/gmime/gmime-part-iter.c
@@ -0,0 +1,612 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*  GMime
+ *  Copyright (C) 2000-2011 Jeffrey Stedfast
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gmime-part-iter.h"
+#include "gmime-message-part.h"
+#include "gmime-multipart.h"
+#include "gmime-message.h"
+#include "gmime-part.h"
+
+/**
+ * SECTION: gmime-part-iter
+ * @title: GMimePartIter
+ * @short_description: MIME part iterators
+ * @see_also: #GMimeObject
+ *
+ * #GMimePartIter is an iterator for traversing a #GMimeObject tree.
+ **/
+
+
+typedef struct _GMimeObjectStack GMimeObjectStack;
+
+struct _GMimeObjectStack {
+	GMimeObjectStack *parent;
+	GMimeObject *object;
+};
+
+struct _GMimePartIter {
+	GMimeObjectStack *parent;
+	GMimeObject *toplevel;
+	GMimeObject *current;
+	GArray *path;
+	int index;
+};
+
+static void
+g_mime_part_iter_push (GMimePartIter *iter, GMimeObject *object, int index)
+{
+	GMimeObjectStack *node;
+	
+	if (index != -1)
+		g_array_append_val (iter->path, index);
+	
+	node = g_slice_new (GMimeObjectStack);
+	node->parent = iter->parent;
+	node->object = object;
+	iter->parent = node;
+}
+
+static void
+g_mime_part_iter_pop (GMimePartIter *iter)
+{
+	GMimeObjectStack *node;
+	GMimeObject *object;
+	
+	iter->index = g_array_index (iter->path, int, iter->path->len - 1);
+	g_array_set_size (iter->path, iter->path->len - 1);
+	
+	iter->current = iter->parent->object;
+	
+	node = iter->parent;
+	iter->parent = node->parent;
+	g_slice_free (GMimeObjectStack, node);
+}
+
+
+/**
+ * g_mime_part_iter_new:
+ * @toplevel: a #GMimeObject to use as the toplevel
+ *
+ * Creates a new #GMimePartIter for iterating over @toplevel's subparts.
+ *
+ * Returns: a newly allocated #GMimePartIter which should be freed
+ * using g_mime_part_iter_free() when finished with it.
+ **/
+GMimePartIter *
+g_mime_part_iter_new (GMimeObject *toplevel)
+{
+	GMimePartIter *iter;
+	
+	g_return_val_if_fail (GMIME_IS_OBJECT (toplevel), NULL);
+	
+	iter = g_slice_new (GMimePartIter);
+	iter->path = g_array_new (FALSE, FALSE, sizeof (int));
+	iter->toplevel = toplevel;
+	g_object_ref (toplevel);
+	iter->parent = NULL;
+	
+	g_mime_part_iter_reset (iter);
+	
+	return iter;
+}
+
+
+/**
+ * g_mime_part_iter_free:
+ * @iter: a #GMimePartIter
+ *
+ * Frees the memory allocated by g_mime_part_iter_new().
+ **/
+void
+g_mime_part_iter_free (GMimePartIter *iter)
+{
+	if (iter == NULL)
+		return;
+	
+	g_object_unref (iter->toplevel);
+	g_array_free (iter->path, TRUE);
+	g_slice_free_chain (GMimeObjectStack, iter->parent, parent);
+	g_slice_free (GMimePartIter, iter);
+}
+
+
+/**
+ * g_mime_part_iter_reset:
+ * @iter: a #GMimePartIter
+ *
+ * Resets the state of @iter to its initial state.
+ **/
+void
+g_mime_part_iter_reset (GMimePartIter *iter)
+{
+	g_return_if_fail (iter != NULL);
+	
+	if (GMIME_IS_MESSAGE (iter->toplevel))
+		iter->current = g_mime_message_get_mime_part ((GMimeMessage *) iter->toplevel);
+	else
+		iter->current = iter->toplevel;
+	
+	g_slice_free_chain (GMimeObjectStack, iter->parent, parent);
+	g_array_set_size (iter->path, 0);
+	iter->parent = NULL;
+	iter->index = 0;
+}
+
+
+/**
+ * g_mime_part_iter_jump_to:
+ * @iter: a #GMimePartIter
+ * @path: a string representing the path to jump to
+ *
+ * Updates the state of @iter to point to the #GMimeObject specified
+ * by @path.
+ *
+ * Returns: %TRUE if the #GMimeObject specified by @path exists or
+ * %FALSE otherwise.
+ **/
+gboolean
+g_mime_part_iter_jump_to (GMimePartIter *iter, const char *path)
+{
+	GMimeMessagePart *message_part;
+	GMimeMultipart *multipart;
+	GMimeMessage *message;
+	GMimeObject *current;
+	const char *inptr;
+	long int index;
+	char *dot;
+	
+	g_return_val_if_fail (iter != NULL, FALSE);
+	
+	g_mime_part_iter_reset (iter);
+	
+	if (!path || !path[0])
+		return TRUE;
+	
+	current = iter->toplevel;
+	iter->current = NULL;
+	inptr = path;
+	
+	while (*inptr) {
+		/* Note: path components are 1-based instead of 0-based */
+		if ((index = strtol (inptr, &dot, 10)) <= 0 || errno == ERANGE ||
+		    index > G_MAXINT || !(*dot == '.' || *dot == '\0'))
+			return FALSE;
+		
+		/* normalize to a 0-based index */
+		index--;
+		
+		if (GMIME_IS_MESSAGE_PART (current)) {
+			if (index != 0)
+				return FALSE;
+			
+			message_part = (GMimeMessagePart *) current;
+			if (!(message = g_mime_message_part_get_message (message_part)))
+				return FALSE;
+			
+			g_mime_part_iter_push (iter, current, iter->index);
+			
+			if (!(current = g_mime_message_get_mime_part (message)))
+				return FALSE;
+			
+			iter->index = 0;
+		} else if (GMIME_IS_MULTIPART (current)) {
+			multipart = (GMimeMultipart *) current;
+			if (index >= g_mime_multipart_get_count (multipart))
+				return FALSE;
+			
+			g_mime_part_iter_push (iter, current, iter->index);
+			
+			current = g_mime_multipart_get_part (multipart, index);
+			iter->index = index;
+		} else if (GMIME_IS_MESSAGE (current)) {
+			/* Note: it should only be possible to get here on the first iteration */
+			if (index != 0)
+				return FALSE;
+			
+			if (!(current = g_mime_message_get_mime_part ((GMimeMessage *) current)))
+				return FALSE;
+			
+			iter->index = 0;
+		} else {
+			return FALSE;
+		}
+		
+		if (*dot != '.')
+			break;
+		
+		inptr = dot + 1;
+	}
+	
+	iter->current = current;
+	iter->index = index;
+	
+	return current != NULL;
+}
+
+
+/**
+ * g_mime_part_iter_is_valid:
+ * @iter: a #GMimePartIter
+ *
+ * Checks that the current state of @iter is valid.
+ *
+ * Returns: %TRUE if @iter is valid or %FALSE otherwise.
+ **/
+gboolean
+g_mime_part_iter_is_valid (GMimePartIter *iter)
+{
+	GMimeMessagePart *message_part;
+	GMimeMultipart *multipart;
+	GMimeMessage *message;
+	GMimeObject *current;
+	int i, index;
+	
+	g_return_val_if_fail (iter != NULL, FALSE);
+	
+	/* quick check */
+	if (iter->current == NULL)
+		return FALSE;
+
+#ifdef EXPENSIVE_ITER_VALIDATION
+	/* find our root part */
+	if (GMIME_IS_MESSAGE (iter->toplevel)) {
+		message = (GMimeMessage *) iter->toplevel;
+		current = g_mime_message_get_mime_part (message);
+	} else {
+		current = iter->toplevel;
+	}
+	
+	for (i = 0; i < iter->path->len; i++) {
+		index = g_array_index (iter->path, int, i);
+		if (GMIME_IS_MESSAGE_PART (current)) {
+			if (index != 0)
+				return FALSE;
+			
+			message_part = (GMimeMessagePart *) current;
+			message = g_mime_message_part_get_message (message_part);
+			current = g_mime_message_get_mime_part (message);
+		} else if (GMIME_IS_MULTIPART (current)) {
+			multipart = (GMimeMultipart *) current;
+			if (index >= g_mime_multipart_get_count (multipart))
+				return FALSE;
+			
+			current = g_mime_multipart_get_part (multipart, index);
+		} else {
+			return FALSE;
+		}
+	}
+	
+	index = iter->index;
+	if (GMIME_IS_MESSAGE_PART (current)) {
+		if (index != 0)
+			return FALSE;
+		
+		message_part = (GMimeMessagePart *) current;
+		message = g_mime_message_part_get_message (message_part);
+		current = g_mime_message_get_mime_part (message);
+	} else if (GMIME_IS_MULTIPART (current)) {
+		multipart = (GMimeMultipart *) current;
+		if (index >= g_mime_multipart_get_count (multipart))
+			return FALSE;
+		
+		current = g_mime_multipart_get_part (multipart, index);
+	} else if (index != 0) {
+		return FALSE;
+	}
+	
+	if (current != iter->current)
+		return FALSE;
+#endif /* EXPENSIVE_ITER_VALIDATION */
+	
+	return TRUE;
+}
+
+
+/**
+ * g_mime_part_iter_next:
+ * @iter: a #GMimePartIter
+ *
+ * Advances to the next part in the MIME structure used to initialize
+ * @iter.
+ *
+ * Returns: %TRUE if successful or %FALSE otherwise.
+ **/
+gboolean
+g_mime_part_iter_next (GMimePartIter *iter)
+{
+	GMimeMessagePart *message_part;
+	GMimeMultipart *multipart;
+	GMimeObject *mime_part;
+	GMimeMessage *message;
+	
+	if (!g_mime_part_iter_is_valid (iter))
+		return FALSE;
+	
+	if (GMIME_IS_MESSAGE_PART (iter->current)) {
+		/* descend into our children */
+		message_part = (GMimeMessagePart *) iter->current;
+		message = g_mime_message_part_get_message (message_part);
+		mime_part = message ? g_mime_message_get_mime_part (message) : NULL;
+		if (mime_part != NULL) {
+			g_mime_part_iter_push (iter, iter->current, iter->index);
+			iter->current = mime_part;
+			iter->index = 0;
+			return TRUE;
+		}
+	} else if (GMIME_IS_MULTIPART (iter->current)) {
+		/* descend into our children */
+		multipart = (GMimeMultipart *) iter->current;
+		if (g_mime_multipart_get_count (multipart) > 0) {
+			g_mime_part_iter_push (iter, iter->current, iter->index);
+			iter->current = g_mime_multipart_get_part (multipart, 0);
+			iter->index = 0;
+			return TRUE;
+		}
+	}
+	
+	/* find the next sibling */
+	while (iter->parent) {
+		if (GMIME_IS_MULTIPART (iter->parent->object)) {
+			/* iterate to the next part in the multipart */
+			multipart = (GMimeMultipart *) iter->parent->object;
+			iter->index++;
+			
+			if (g_mime_multipart_get_count (multipart) > iter->index) {
+				iter->current = g_mime_multipart_get_part (multipart, iter->index);
+				return TRUE;
+			}
+		}
+		
+		g_mime_part_iter_pop (iter);
+	}
+	
+	iter->current = NULL;
+	iter->index = -1;
+	
+	return FALSE;
+}
+
+
+/**
+ * g_mime_part_iter_prev:
+ * @iter: a #GMimePartIter
+ *
+ * Rewinds to the previous part in the MIME structure used to
+ * initialize @iter.
+ *
+ * Returns: %TRUE if successful or %FALSE otherwise.
+ **/
+gboolean
+g_mime_part_iter_prev (GMimePartIter *iter)
+{
+	GMimeMessagePart *message_part;
+	GMimeMultipart *multipart;
+	
+	if (!g_mime_part_iter_is_valid (iter))
+		return FALSE;
+	
+	if (iter->parent == NULL) {
+		iter->current = NULL;
+		iter->index = -1;
+		return FALSE;
+	}
+	
+	if (GMIME_IS_MULTIPART (iter->parent->object)) {
+		/* revert backward to the previous part in the multipart */
+		multipart = (GMimeMultipart *) iter->parent->object;
+		iter->index--;
+		
+		if (iter->index >= 0) {
+			iter->current = g_mime_multipart_get_part (multipart, iter->index);
+			return TRUE;
+		}
+	}
+	
+	g_mime_part_iter_pop (iter);
+	
+	return TRUE;
+}
+
+
+/**
+ * g_mime_part_iter_get_current:
+ * @iter: a #GMimePartIter
+ *
+ * Gets the #GMimeObject at the current #GMimePartIter position.
+ *
+ * Returns: the current #GMimeObject or %NULL if the state of @iter is
+ * invalid.
+ **/
+GMimeObject *
+g_mime_part_iter_get_current (GMimePartIter *iter)
+{
+	if (!g_mime_part_iter_is_valid (iter))
+		return NULL;
+	
+	return iter->current;
+}
+
+
+/**
+ * g_mime_part_iter_get_parent:
+ * @iter: a #GMimePartIter
+ *
+ * Gets the parent of the #GMimeObject at the current #GMimePartIter
+ * position.
+ *
+ * Returns: the parent #GMimeObject or %NULL if the state of @iter is
+ * invalid.
+ **/
+GMimeObject *
+g_mime_part_iter_get_parent (GMimePartIter *iter)
+{
+	if (!g_mime_part_iter_is_valid (iter))
+		return NULL;
+	
+	return iter->parent ? iter->parent->object : iter->toplevel;
+}
+
+
+/**
+ * g_mime_part_iter_get_path:
+ * @iter: a #GMimePartIter
+ *
+ * Gets the path of the current #GMimeObject in the MIME structure
+ * used to initialize @iter.
+ *
+ * Returns: a newly allocated string representation of the path to the
+ * #GMimeObject at the current #GMimePartIter position.
+ **/
+char *
+g_mime_part_iter_get_path (GMimePartIter *iter)
+{
+	GString *path;
+	int i, v;
+	
+	if (!g_mime_part_iter_is_valid (iter))
+		return NULL;
+	
+	/* Note: path components are 1-based instead of 0-based */
+	
+	path = g_string_new ("");
+	for (i = 0; i < iter->path->len; i++) {
+		v = g_array_index (iter->path, int, i);
+		g_string_append_printf (path, "%d.", v + 1);
+	}
+	
+	g_string_append_printf (path, "%d", iter->index + 1);
+	
+	return g_string_free (path, FALSE);
+}
+
+
+/**
+ * g_mime_part_iter_replace:
+ * @iter: a #GMimePartIter
+ * @replacement: a #GMimeObject
+ *
+ * Replaces the #GMimeObject at the current position with @replacement.
+ *
+ * Returns: %TRUE if the part at the current position was replaced or
+ * %FALSE otherwise.
+ **/
+gboolean
+g_mime_part_iter_replace (GMimePartIter *iter, GMimeObject *replacement)
+{
+	GMimeMessagePart *message_part;
+	GMimeMessage *message;
+	GMimeObject *current;
+	GMimeObject *parent;
+	int index;
+	
+	g_return_val_if_fail (GMIME_IS_OBJECT (replacement), FALSE);
+	
+	if (!g_mime_part_iter_is_valid (iter))
+		return FALSE;
+	
+	if (iter->current == iter->toplevel) {
+		g_object_unref (iter->toplevel);
+		iter->toplevel = replacement;
+		g_object_ref (replacement);
+		return TRUE;
+	}
+	
+	parent = iter->parent ? iter->parent->object : iter->toplevel;
+	index = iter->index;
+	
+	/* now we can safely replace the previously referenced part in its parent */
+	if (GMIME_IS_MESSAGE_PART (parent)) {
+		/* depending on what we've been given as a
+		 * replacement, we might replace the message in the
+		 * message/rfc822 part or we might end up replacing
+		 * the toplevel mime part of said message. */
+		message_part = (GMimeMessagePart *) parent;
+		message = g_mime_message_part_get_message (message_part);
+		if (GMIME_IS_MESSAGE (replacement))
+			g_mime_message_part_set_message (message_part, (GMimeMessage *) replacement);
+		else
+			g_mime_message_set_mime_part (message, replacement);
+	} else if (GMIME_IS_MULTIPART (parent)) {
+		current = g_mime_multipart_replace ((GMimeMultipart *) parent, index, replacement);
+		g_object_unref (current);
+	} else if (GMIME_IS_MESSAGE (parent)) {
+		g_mime_message_set_mime_part ((GMimeMessage *) parent, replacement);
+	} else {
+		g_assert_not_reached ();
+	}
+	
+	iter->current = replacement;
+	
+	return TRUE;
+}
+
+
+/**
+ * g_mime_part_iter_remove:
+ * @iter: a #GMimePartIter
+ *
+ * Removes the #GMimeObject at the current position from its parent.
+ *
+ * Returns: %TRUE if the part at the current position was removed or
+ * %FALSE otherwise.
+ **/
+gboolean
+g_mime_part_iter_remove (GMimePartIter *iter)
+{
+	GMimeObject *current;
+	GMimeObject *parent;
+	int index;
+	
+	if (!g_mime_part_iter_is_valid (iter))
+		return FALSE;
+	
+	if (iter->current == iter->toplevel)
+		return FALSE;
+	
+	parent = iter->parent ? iter->parent->object : iter->toplevel;
+	current = iter->current;
+	index = iter->index;
+	
+	/* iterate to the next part so we have something valid to refer to */
+	g_mime_part_iter_next (iter);
+	
+	/* now we can safely remove the previously referenced part from its parent */
+	if (GMIME_IS_MESSAGE_PART (parent)) {
+		g_mime_message_part_set_message ((GMimeMessagePart *) parent, NULL);
+	} else if (GMIME_IS_MULTIPART (parent)) {
+		g_mime_multipart_remove_at ((GMimeMultipart *) parent, index);
+		g_object_unref (current);
+	} else if (GMIME_IS_MESSAGE (parent)) {
+		g_mime_message_set_mime_part ((GMimeMessage *) parent, NULL);
+	} else {
+		g_assert_not_reached ();
+	}
+	
+	return TRUE;
+}
diff --git a/gmime/gmime-part-iter.h b/gmime/gmime-part-iter.h
new file mode 100644
index 0000000..548ad29
--- /dev/null
+++ b/gmime/gmime-part-iter.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*  GMime
+ *  Copyright (C) 2000-2011 Jeffrey Stedfast
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+
+#ifndef __GMIME_PART_ITER_H__
+#define __GMIME_PART_ITER_H__
+
+#include <gmime/gmime-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GMimePartIter:
+ *
+ * A MIME part iterator.
+ **/
+typedef struct _GMimePartIter GMimePartIter;
+
+GMimePartIter *g_mime_part_iter_new (GMimeObject *toplevel);
+void g_mime_part_iter_free (GMimePartIter *iter);
+
+void g_mime_part_iter_reset (GMimePartIter *iter);
+
+gboolean g_mime_part_iter_jump_to (GMimePartIter *iter, const char *path);
+
+gboolean g_mime_part_iter_is_valid (GMimePartIter *iter);
+
+gboolean g_mime_part_iter_next (GMimePartIter *iter);
+gboolean g_mime_part_iter_prev (GMimePartIter *iter);
+
+GMimeObject *g_mime_part_iter_get_current (GMimePartIter *iter);
+GMimeObject *g_mime_part_iter_get_parent (GMimePartIter *iter);
+char *g_mime_part_iter_get_path (GMimePartIter *iter);
+
+gboolean g_mime_part_iter_replace (GMimePartIter *iter, GMimeObject *replacement);
+gboolean g_mime_part_iter_remove (GMimePartIter *iter);
+
+G_END_DECLS
+
+#endif /* __GMIME_PART_ITER_H__ */
diff --git a/gmime/gmime.h b/gmime/gmime.h
index 1925040..84aefc6 100644
--- a/gmime/gmime.h
+++ b/gmime/gmime.h
@@ -34,6 +34,7 @@
 #include <gmime/gmime-data-wrapper.h>
 #include <gmime/gmime-object.h>
 #include <gmime/gmime-part.h>
+#include <gmime/gmime-part-iter.h>
 #include <gmime/gmime-multipart.h>
 #include <gmime/gmime-multipart-encrypted.h>
 #include <gmime/gmime-multipart-signed.h>
diff --git a/mono/GMime.metadata b/mono/GMime.metadata
index 2543fa4..8d355cf 100644
--- a/mono/GMime.metadata
+++ b/mono/GMime.metadata
@@ -302,6 +302,9 @@
   <!--<remove-node path="/api/namespace/class[ name='Decode']"/>-->
 
   <!-- Part -->
+  
+  <!-- PartIter -->
+  <remove-node path="/api/namespace/struct[ name='PartIter']"/>
 
   <!-- References -->
   <attr path="/api/namespace/struct[ name='References']" name="opaque">true</attr>
diff --git a/mono/gmime-api.raw b/mono/gmime-api.raw
index e4a3865..053e24c 100644
--- a/mono/gmime-api.raw
+++ b/mono/gmime-api.raw
@@ -2925,6 +2925,52 @@
         </parameters>
       </method>
     </struct>
+    <struct name="PartIter" cname="GMimePartIter" opaque="true">
+      <method name="Free" cname="g_mime_part_iter_free">
+        <return-type type="void" />
+      </method>
+      <method name="GetCurrent" cname="g_mime_part_iter_get_current">
+        <return-type type="GMimeObject*" />
+      </method>
+      <method name="GetParent" cname="g_mime_part_iter_get_parent">
+        <return-type type="GMimeObject*" />
+      </method>
+      <method name="GetPath" cname="g_mime_part_iter_get_path">
+        <return-type type="char*" />
+      </method>
+      <method name="IsValid" cname="g_mime_part_iter_is_valid">
+        <return-type type="gboolean" />
+      </method>
+      <method name="JumpTo" cname="g_mime_part_iter_jump_to">
+        <return-type type="gboolean" />
+        <parameters>
+          <parameter type="const-char*" name="path" />
+        </parameters>
+      </method>
+      <constructor cname="g_mime_part_iter_new">
+        <parameters>
+          <parameter type="GMimeObject*" name="toplevel" />
+        </parameters>
+      </constructor>
+      <method name="Next" cname="g_mime_part_iter_next">
+        <return-type type="gboolean" />
+      </method>
+      <method name="Prev" cname="g_mime_part_iter_prev">
+        <return-type type="gboolean" />
+      </method>
+      <method name="Remove" cname="g_mime_part_iter_remove">
+        <return-type type="gboolean" />
+      </method>
+      <method name="Replace" cname="g_mime_part_iter_replace">
+        <return-type type="gboolean" />
+        <parameters>
+          <parameter type="GMimeObject*" name="replacement" />
+        </parameters>
+      </method>
+      <method name="Reset" cname="g_mime_part_iter_reset">
+        <return-type type="void" />
+      </method>
+    </struct>
     <struct name="References" cname="GMimeReferences">
       <field name="Next" cname="next" type="GMimeReferences*" />
       <field name="Msgid" cname="msgid" type="char*" />
diff --git a/tests/test-parser.c b/tests/test-parser.c
index 94f4a8c..23f9a3c 100644
--- a/tests/test-parser.c
+++ b/tests/test-parser.c
@@ -37,11 +37,12 @@
 #include "zentimer.h"
 #endif
 
-/*#define TEST_RAW_HEADER*/
+//#define TEST_RAW_HEADER
 #define TEST_PRESERVE_HEADERS
 #define PRINT_MIME_STRUCT
-/*#define TEST_WRITE_TO_STREAM*/
+//#define TEST_WRITE_TO_STREAM
 
+#ifdef PRINT_MIME_STRUCT
 static void
 print_depth (int depth)
 {
@@ -90,6 +91,74 @@ print_mime_struct (GMimeObject *part, int depth)
 }
 
 static void
+print_mime_struct_iter (GMimeMessage *message)
+{
+	const GMimeContentType *type;
+	GMimePartIter *iter;
+	GMimeObject *part;
+	gboolean has_md5;
+	char *path;
+	
+	iter = g_mime_part_iter_new ((GMimeObject *) message);
+	
+	do {
+		part = g_mime_part_iter_get_current (iter);
+		type = g_mime_object_get_content_type (part);
+		path = g_mime_part_iter_get_path (iter);
+		
+		if (GMIME_IS_PART (part))
+			has_md5 = g_mime_object_get_header (part, "Content-Md5") != NULL;
+		else
+			has_md5 = FALSE;
+		
+		fprintf (stdout, "%s\tContent-Type: %s/%s%s", path,
+			 type->type, type->subtype, has_md5 ? "; md5sum=" : "\n");
+		
+		if (has_md5) {
+			/* validate the Md5 sum */
+			if (g_mime_part_verify_content_md5 ((GMimePart *) part))
+				fprintf (stdout, "GOOD\n");
+			else
+				fprintf (stdout, "BAD\n");
+		}
+		
+		g_free (path);
+	} while (g_mime_part_iter_next (iter));
+
+#if 0
+	fprintf (stdout, "Jumping to 1.1.2\n");
+	if (g_mime_part_iter_jump_to (iter, "1.1.2")) {
+		part = g_mime_part_iter_get_current (iter);
+		type = g_mime_object_get_content_type (part);
+		path = g_mime_part_iter_get_path (iter);
+		
+		if (GMIME_IS_PART (part))
+			has_md5 = g_mime_object_get_header (part, "Content-Md5") != NULL;
+		else
+			has_md5 = FALSE;
+		
+		fprintf (stdout, "%s\tContent-Type: %s/%s%s", path,
+			 type->type, type->subtype, has_md5 ? "; md5sum=" : "\n");
+		
+		if (has_md5) {
+			/* validate the Md5 sum */
+			if (g_mime_part_verify_content_md5 ((GMimePart *) part))
+				fprintf (stdout, "GOOD\n");
+			else
+				fprintf (stdout, "BAD\n");
+		}
+		
+		g_free (path);
+	} else {
+		fprintf (stdout, "Failed to jump to 1.1.2\n");
+	}
+#endif
+	
+	g_mime_part_iter_free (iter);
+}
+#endif /* PRINT_MIME_STRUCT */
+
+static void
 test_parser (GMimeStream *stream)
 {
 	GMimeParser *parser;
@@ -119,7 +188,7 @@ test_parser (GMimeStream *stream)
 	{
 		char *raw;
 		
-		raw = g_mime_message_get_headers (message);
+		raw = g_mime_object_get_headers ((GMimeObject *) message);
 		fprintf (stdout, "\nTesting raw headers...\n\n%s\n", raw);
 		g_free (raw);
 	}
@@ -152,7 +221,8 @@ test_parser (GMimeStream *stream)
 	
 #ifdef PRINT_MIME_STRUCT
 	/* print mime structure */
-	print_mime_struct (message->mime_part, 0);
+	//print_mime_struct (message->mime_part, 0);
+	print_mime_struct_iter (message);
 #endif
 	
 	g_object_unref (message);
diff --git a/tests/test2.eml b/tests/test2.eml
index cd3569e..ffc4872 100644
--- a/tests/test2.eml
+++ b/tests/test2.eml
@@ -1,4 +1,3 @@
-From Muhri
 Return-Path: <touring_fan mindspring com>
 Received: from largo (root one [192.168.0.1])
 	by two.muhri.net (8.9.3/8.9.3) with ESMTP id WAA07964



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]