[gmime] Implemented mime part iterators
- From: Jeffrey Stedfast <fejj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gmime] Implemented mime part iterators
- Date: Sat, 13 Aug 2011 22:46:51 +0000 (UTC)
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]