? README.windows
? autom4te.cache
? iconv-detect.h
? pan-updated-gmime.patch
? pan-updated-gmime.patch.bz2
? pan.iss
? gmime/gssize.sh
? pan/xpm/pan-pixbufs-internal.h
? pan/xpm/pan-pixbufs.h
? tests/filter
? tests/mid
? tests/pstring
? tests/thread
? tests/data/messages/Makefile
? tests/data/messages/Makefile.in
? tests/data/messages/cache/Makefile
? tests/data/messages/cache/Makefile.in
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/pan/ChangeLog,v
retrieving revision 1.2040
diff -u -r1.2040 ChangeLog
--- ChangeLog	2 Apr 2004 02:21:06 -0000	1.2040
+++ ChangeLog	7 Jun 2004 06:10:07 -0000
@@ -1,3 +1,33 @@
+2004-06-07  Jeffrey Stedfast  <fejj@ximian.com>
+
+	This massive patch fixes bug #116269
+
+	* pan/base/util-mime.c (handle_inline_encoded_data): Unref the
+	uudecode filter.
+
+	* pan/base/decode.c (decode_article): Unref the yenc_filter.
+
+	* pan/text.c (insert_part_partfunc): Unref the yenc filter.
+	(find_next_url): Removed.
+	(append_text_buffer_nolock): Rewrote the url scanning code since
+	url_extract() no longer exists in GMime's HTML filter code. This
+	new code also happens to fix bug #98834.
+
+	* pan/message-window.c (populate_message_from_mw): Unref the
+	charset_filter.
+
+	* pan/task-post.c (build_nntp_message): Commented out a function
+	that no longer exists in GMime.
+	(write_header_nofold): #if 0'd out for now. I think this code was
+	to work around something that's since been fixed in GMime?
+
+	* pan/smtp.c (smtp_send_message): Changed InternetAddresses to be
+	const. Also got rid of extra SPACE char in the SMTP RCPT TO
+	command that will break some SMTP servers (experience from
+	Evolution hacking :-)
+
+	* gmime/*: Synced up with GMime CVS with slight modifications.
+
 2004-04-01  Jeffrey Stedfast  <fejj@ximian.com>
 
 	* gmime/gmime-charset.[c,h]: Synced up with GMime CVS to fix bug
@@ -88,7 +118,7 @@
 	(#129432, mjdb)
 
 2004-01-25  Christophe Lambin <chris@rebelbase.com>
-                                                                                
+
 	* pan/base/text-messager.c (wrap_line_at_column): proper handling of
 	UTF8 strings. This broke wrapping text that contained non-ASCII
 	characters.
Index: gmime/Makefile.am
===================================================================
RCS file: /cvs/gnome/pan/gmime/Makefile.am,v
retrieving revision 1.21
diff -u -r1.21 Makefile.am
--- gmime/Makefile.am	12 Dec 2003 13:30:55 -0000	1.21
+++ gmime/Makefile.am	7 Jun 2004 06:10:07 -0000
@@ -11,14 +11,18 @@
 	@CPPFLAGS@
 
 libgmime_a_SOURCES = \
+	cache.c \
 	gmime.c \
 	gmime-charset.c \
+	gmime-common.c \
 	gmime-content-type.c \
 	gmime-data-wrapper.c \
 	gmime-disposition.c \
 	gmime-filter-basic.c \
 	gmime-filter-charset.c \
+	gmime-filter-crlf.c \
 	gmime-filter-html.c \
+	gmime-filter-md5.c \
 	gmime-filter-yenc.c \
 	gmime-filter.c \
 	gmime-header.c \
@@ -37,21 +41,30 @@
 	gmime-stream-file.c \
 	gmime-stream-filter.c \
 	gmime-stream-mem.c \
+	gmime-stream-null.c \
 	gmime-stream.c \
 	gmime-utils.c \
+	gtrie.c \
 	internet-address.c \
+	list.c \
 	md5-utils.c \
-	memchunk.c
+	memchunk.c \
+	url-scanner.c
 
 noinst_HEADERS = \
+	cache.h \
 	gmime-charset.h \
 	gmime-charset-map-private.h \
+	gmime-common.h \
 	gmime-content-type.h \
 	gmime-data-wrapper.h \
 	gmime-disposition.h \
+	gmime-error.h \
 	gmime-filter-basic.h \
 	gmime-filter-charset.h \
+	gmime-filter-crlf.h \
 	gmime-filter-html.h \
+	gmime-filter-md5.h \
 	gmime-filter-yenc.h \
 	gmime-filter.h \
 	gmime-header.h \
@@ -70,11 +83,15 @@
 	gmime-stream-file.h \
 	gmime-stream-filter.h \
 	gmime-stream-mem.h \
+	gmime-stream-null.h \
 	gmime-stream.h \
 	gmime-table-private.h \
 	gmime-type-utils.h \
 	gmime-utils.h \
 	gmime.h \
+	gtrie.h \
 	internet-address.h \
+	list.h \
 	md5-utils.h \
-	memchunk.h
+	memchunk.h \
+	url-scanner.h
Index: gmime/cache.c
===================================================================
RCS file: gmime/cache.c
diff -N gmime/cache.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/cache.c	7 Jun 2004 06:10:07 -0000
@@ -0,0 +1,140 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2003-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "cache.h"
+
+
+Cache *
+cache_new (CacheNodeExpireFunc expire, CacheNodeFreeFunc free_node, unsigned int node_size, unsigned int max_size)
+{
+	Cache *cache;
+	
+	cache = g_new (Cache, 1);
+	list_init (&cache->list);
+	cache->expire = expire;
+	cache->free_node = free_node;
+	cache->node_hash = g_hash_table_new (g_str_hash, g_str_equal);
+	cache->node_chunks = memchunk_new (node_size, max_size, FALSE);
+	cache->max_size = max_size;
+	cache->size = 0;
+	
+	return cache;
+}
+
+
+static void
+cache_node_free (CacheNode *node)
+{
+	Cache *cache;
+	
+	cache = node->cache;
+	
+	cache->free_node (node);
+	g_free (node->key);
+	memchunk_free (cache->node_chunks, node);
+}
+
+
+static void
+cache_node_foreach_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	cache_node_free ((CacheNode *) value);
+}
+
+void
+cache_free (Cache *cache)
+{
+	g_hash_table_foreach (cache->node_hash, cache_node_foreach_cb, NULL);
+	g_hash_table_destroy (cache->node_hash);
+	memchunk_destroy (cache->node_chunks);
+	g_free (cache);
+}
+
+
+void
+cache_expire_unused (Cache *cache)
+{
+	ListNode *node, *prev;
+	
+	node = cache->list.tailpred;
+	while (node->prev && cache->size > cache->max_size) {
+		prev = node->prev;
+		if (cache->expire (cache, (CacheNode *) node)) {
+			g_hash_table_remove (cache->node_hash, ((CacheNode *) node)->key);
+			list_node_unlink (node);
+			cache_node_free ((CacheNode *) node);
+			cache->size--;
+		}
+		node = prev;
+	}
+}
+
+CacheNode *
+cache_node_insert (Cache *cache, const char *key)
+{
+	CacheNode *node;
+	
+	cache->size++;
+	
+	if (cache->size > cache->max_size)
+		cache_expire_unused (cache);
+	
+	node = memchunk_alloc (cache->node_chunks);
+	node->key = g_strdup (key);
+	node->cache = cache;
+	
+	g_hash_table_insert (cache->node_hash, node->key, node);
+	list_prepend_node (&cache->list, (ListNode *) node);
+	
+	return node;
+}
+
+CacheNode *
+cache_node_lookup (Cache *cache, const char *key, gboolean use)
+{
+	CacheNode *node;
+	
+	node = g_hash_table_lookup (cache->node_hash, key);
+	if (node && use) {
+		list_node_unlink ((ListNode *) node);
+		list_prepend_node (&cache->list, (ListNode *) node);
+	}
+	
+	return node;
+}
+
+void
+cache_node_expire (CacheNode *node)
+{
+	Cache *cache;
+	
+	cache = node->cache;
+	g_hash_table_remove (cache->node_hash, node->key);
+	list_node_unlink ((ListNode *) node);
+	cache_node_free (node);
+	cache->size--;
+}
Index: gmime/cache.h
===================================================================
RCS file: gmime/cache.h
diff -N gmime/cache.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/cache.h	7 Jun 2004 06:10:07 -0000
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2003-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __CACHE_H__
+#define __CACHE_H__
+
+#include <glib.h>
+
+#include "list.h"
+#include "memchunk.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+typedef struct _Cache Cache;
+
+typedef struct {
+	ListNode node;
+	Cache *cache;
+	char *key;
+} CacheNode;
+
+typedef gboolean (*CacheNodeExpireFunc) (Cache *cache, CacheNode *node);
+typedef void (*CacheNodeFreeFunc) (CacheNode *node);
+
+struct _Cache {
+	List list;
+	unsigned int size;
+	unsigned int max_size;
+	MemChunk *node_chunks;
+	GHashTable *node_hash;
+	CacheNodeExpireFunc expire;
+	CacheNodeFreeFunc free_node;
+};
+
+
+Cache *cache_new (CacheNodeExpireFunc expire, CacheNodeFreeFunc free_node,
+		  unsigned int bucket_size, unsigned int max_cache_size);
+
+void cache_free (Cache *cache);
+
+CacheNode *cache_node_insert (Cache *cache, const char *key);
+CacheNode *cache_node_lookup (Cache *cache, const char *key, gboolean use);
+
+void cache_expire_unused (Cache *cache);
+void cache_node_expire (CacheNode *node);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CACHE_H__ */
Index: gmime/gmime-charset.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-charset.c,v
retrieving revision 1.15
diff -u -r1.15 gmime-charset.c
--- gmime/gmime-charset.c	2 Apr 2004 02:21:06 -0000	1.15
+++ gmime/gmime-charset.c	7 Jun 2004 06:10:08 -0000
@@ -156,7 +156,7 @@
 /**
  * g_mime_charset_map_shutdown:
  *
- * Frees internal lookup tables created in #g_mime_charset_map_init().
+ * Frees internal lookup tables created in g_mime_charset_map_init().
  **/
 void
 g_mime_charset_map_shutdown (void)
@@ -221,7 +221,7 @@
  * g_mime_charset_map_init:
  *
  * Initializes the locale charset variable for later calls to
- * #g_mime_locale_charset(). Only really needs to be called for non-
+ * g_mime_locale_charset(). Only really needs to be called for non-
  * iso-8859-1 locales.
  **/
 void
@@ -503,7 +503,7 @@
  * Attempts to find a canonical charset name for @charset.
  *
  * Note: Will normally return the same value as
- * #g_mime_charset_iconv_name() unless the system iconv does not use
+ * g_mime_charset_iconv_name() unless the system iconv does not use
  * the canonical ISO charset names (such as using ISO8859-1 rather
  * than the canonical form ISO-8859-1).
  *
@@ -556,7 +556,7 @@
  *
  * Attempts to find an iconv-friendly charset name for @charset.
  *
- * Note: This function is deprecated. Use #g_mime_charset_iconv_name
+ * Note: This function is deprecated. Use g_mime_charset_iconv_name()
  * instead.
  *
  * Returns an iconv-friendly charset name for @charset.
@@ -573,7 +573,7 @@
  *
  * Gets the user's locale charset (or iso-8859-1 by default).
  *
- * Note: This function is deprecated. Use #g_mime_locale_charset
+ * Note: This function is deprecated. Use g_mime_locale_charset()
  * instead.
  *
  * Returns the user's locale charset (or iso-8859-1 by default).
@@ -662,7 +662,7 @@
 
 /**
  * g_mime_charset_step:
- * @charset:
+ * @charset: charset structure
  * @in: input text buffer (must be in UTF-8)
  * @len: input buffer length
  *
Index: gmime/gmime-common.c
===================================================================
RCS file: gmime/gmime-common.c
diff -N gmime/gmime-common.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-common.c	7 Jun 2004 06:10:08 -0000
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2003-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "gmime-common.h"
+
+#ifndef g_tolower
+#define g_tolower(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
+#endif
+
+int
+g_mime_strcase_equal (gconstpointer v, gconstpointer v2)
+{
+	return strcasecmp ((const char *) v, (const char *) v2) == 0;
+}
+
+
+guint
+g_mime_strcase_hash (gconstpointer key)
+{
+	const char *p = key;
+	guint h = 0;
+	
+	while (*p != '\0') {
+		h = (h << 5) - h + g_tolower (*p);
+		p++;
+	}
+	
+	return h;
+}
+
+#ifndef HAVE_STRNCASECMP
+/**
+ * strncasecmp:
+ * @s1: string 1
+ * @s2: string 2
+ * @n:
+ *
+ * Compares the first @n characters of the 2 strings, @s1 and @s2,
+ * ignoring the case of the characters.
+ *
+ * Returns an integer less than, equal to, or greater than zero if @s1
+ * is found, respectively, to be less than, to match, or to be greater
+ * than @s2.
+ **/
+int
+strncasecmp (const char *s1, const char *s2, size_t n)
+{
+	register const char *p1 = s1, *p2 = s2;
+	const char *q1 = s1 + n;
+	
+	for ( ; *p1 && p1 < q1; p1++, p2++)
+		if (g_tolower (*p1) != g_tolower (*p2))
+			return g_tolower (*p1) - g_tolower (*p2);
+	
+	return 0;
+}
+#endif
+
+#ifndef HAVE_STRCASECMP
+/**
+ * strncasecmp:
+ * @s1: string 1
+ * @s2: string 2
+ *
+ * Compares the 2 strings, @s1 and @s2, ignoring the case of the
+ * characters.
+ *
+ * Returns an integer less than, equal to, or greater than zero if @s1
+ * is found, respectively, to be less than, to match, or to be greater
+ * than @s2.
+ **/
+int
+strcasecmp (const char *s1, const char *s2)
+{
+	register const char *p1 = s1, *p2 = s2;
+	
+	for ( ; *p1; p1++, p2++)
+		if (g_tolower (*p1) != g_tolower (*p2))
+			break;
+	
+	return g_tolower (*p1) - g_tolower (*p2);
+}
+#endif
Index: gmime/gmime-common.h
===================================================================
RCS file: gmime/gmime-common.h
diff -N gmime/gmime-common.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-common.h	7 Jun 2004 06:10:08 -0000
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2003-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __GMIME_COMMON_H__
+#define __GMIME_COMMON_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+int g_mime_strcase_equal (gconstpointer v, gconstpointer v2);
+
+guint g_mime_strcase_hash (gconstpointer key);
+
+#ifndef HAVE_STRNCASECMP
+int strncasecmp (const char *s1, const char *s2, size_t n);
+#endif
+
+#ifndef HAVE_STRCASECMP
+int strcasecmp (const char *s1, const char *s2);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GMIME_COMMON_H__ */
Index: gmime/gmime-content-type.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-content-type.c,v
retrieving revision 1.12
diff -u -r1.12 gmime-content-type.c
--- gmime/gmime-content-type.c	30 Dec 2002 16:37:57 -0000	1.12
+++ gmime/gmime-content-type.c	7 Jun 2004 06:10:08 -0000
@@ -1,8 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -32,15 +32,13 @@
 #include "gmime-content-type.h"
 #include "gmime-table-private.h"
 
-#include <glib.h>
-
 #define d(x)
 
 
 static int
 param_equal (gconstpointer v, gconstpointer v2)
 {
-	return g_strcasecmp ((const char *) v, (const char *) v2) == 0;
+	return strcasecmp ((const char *) v, (const char *) v2) == 0;
 }
 
 static guint
@@ -78,9 +76,9 @@
 	} else {
 		if (type && *type) {
 			mime_type->type = g_strdup (type);
-			if (!g_strcasecmp (type, "text")) {
+			if (!strcasecmp (type, "text")) {
 				mime_type->subtype = g_strdup ("plain");
-			} else if (!g_strcasecmp (type, "multipart")) {
+			} else if (!strcasecmp (type, "multipart")) {
 				mime_type->subtype = g_strdup ("mixed");
 			} else {
 				g_free (mime_type->type);
@@ -131,12 +129,15 @@
 	/* get the subtype */
 	if (*inptr == '/') {
 		inptr++;
+		
+		while (is_lwsp (*inptr))
+			inptr++;
+		
 		subtype = (char *) inptr;
 		while (*inptr && is_ttoken (*inptr))
 			inptr++;
 		
 		subtype = g_strndup (subtype, (unsigned) (inptr - subtype));
-		g_strstrip (subtype);
 	} else {
 		subtype = NULL;
 	}
@@ -145,6 +146,9 @@
 	g_free (type);
 	g_free (subtype);
 	
+	while (is_lwsp (*inptr))
+		inptr++;
+	
 	if (*inptr++ == ';' && *inptr) {
 		GMimeParam *p;
 		
@@ -233,12 +237,12 @@
 	g_return_val_if_fail (type != NULL, FALSE);
 	g_return_val_if_fail (subtype != NULL, FALSE);
 	
-	if (!g_strcasecmp (mime_type->type, type)) {
+	if (!strcasecmp (mime_type->type, type)) {
 		if (!strcmp (subtype, "*")) {
 			/* special case */
 			return TRUE;
 		} else {
-			if (!g_strcasecmp (mime_type->subtype, subtype))
+			if (!strcasecmp (mime_type->subtype, subtype))
 				return TRUE;
 			else
 				return FALSE;
Index: gmime/gmime-content-type.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-content-type.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-content-type.h
--- gmime/gmime-content-type.h	30 Dec 2002 16:37:57 -0000	1.4
+++ gmime/gmime-content-type.h	7 Jun 2004 06:10:08 -0000
@@ -1,8 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,17 +20,17 @@
  *
  */
 
+
 #ifndef __GMIME_CONTENT_TYPE_H__
 #define __GMIME_CONTENT_TYPE_H__
 
+#include <glib.h>
+#include <gmime/gmime-param.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <glib/gtypes.h>
-#include <glib/ghash.h>
-#include "gmime-param.h"
 
 struct _GMimeContentType {
 	char *type;
Index: gmime/gmime-data-wrapper.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-data-wrapper.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-data-wrapper.c
--- gmime/gmime-data-wrapper.c	26 Dec 2002 18:50:14 -0000	1.8
+++ gmime/gmime-data-wrapper.c	7 Jun 2004 06:10:08 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
 static void g_mime_data_wrapper_init (GMimeDataWrapper *wrapper, GMimeDataWrapperClass *klass);
 static void g_mime_data_wrapper_finalize (GObject *object);
 
-static gssize write_to_stream (GMimeDataWrapper *wrapper, GMimeStream *stream);
+static ssize_t write_to_stream (GMimeDataWrapper *wrapper, GMimeStream *stream);
 
 
 static GObject *parent_class = NULL;
@@ -89,7 +89,7 @@
 	GMimeDataWrapper *wrapper = (GMimeDataWrapper *) object;
 	
 	if (wrapper->stream)
-		g_mime_stream_unref (wrapper->stream);
+		g_object_unref (wrapper->stream);
 	
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -116,7 +116,7 @@
  *
  * Creates a new GMimeDataWrapper object around @stream.
  *
- * Returns a data wrapper around @stream. Since the wrapper owns it's
+ * Returns a data wrapper around @stream. Since the wrapper owns its
  * own reference on the stream, caller is responsible for unrefing
  * it's own copy.
  **/
@@ -131,7 +131,7 @@
 	wrapper->encoding = encoding;
 	wrapper->stream = stream;
 	if (stream)
-		g_mime_stream_ref (stream);
+		g_object_ref (stream);
 	
 	return wrapper;
 }
@@ -142,8 +142,11 @@
  * @wrapper: data wrapper
  * @stream: stream
  *
- * Replaces the wrapper's internal stream with @stream.
- * Note: caller is responsible for it's own reference on
+ * Replaces the wrapper's internal stream with @stream. Don't forget,
+ * if @stream is not of the same encoding as the old stream, you'll
+ * want to call g_mime_data_wrapper_set_encoding() as well.
+ *
+ * Note: caller is responsible for its own reference on
  * @stream.
  **/
 void
@@ -153,10 +156,10 @@
 	g_return_if_fail (GMIME_IS_STREAM (stream));
 	
 	if (stream)
-		g_mime_stream_ref (stream);
+		g_object_ref (stream);
 	
 	if (wrapper->stream)
-		g_mime_stream_unref (wrapper->stream);
+		g_object_unref (wrapper->stream);
 	
 	wrapper->stream = stream;
 }
@@ -179,7 +182,7 @@
 	if (wrapper->stream == NULL)
 		return NULL;
 	
-	g_mime_stream_ref (wrapper->stream);
+	g_object_ref (wrapper->stream);
 	
 	return wrapper->stream;
 }
@@ -218,12 +221,12 @@
 }
 
 
-static gssize
+static ssize_t
 write_to_stream (GMimeDataWrapper *wrapper, GMimeStream *stream)
 {
 	GMimeStream *filtered_stream;
 	GMimeFilter *filter;
-	gssize written;
+	ssize_t written;
 	
 	g_mime_stream_reset (wrapper->stream);
 	
@@ -232,21 +235,24 @@
 	case GMIME_PART_ENCODING_BASE64:
 		filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_BASE64_DEC);
 		g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
 		break;
 	case GMIME_PART_ENCODING_QUOTEDPRINTABLE:
 		filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_QP_DEC);
 		g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
 		break;
 	case GMIME_PART_ENCODING_UUENCODE:
 		filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_UU_DEC);
 		g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
 		break;
 	default:
 		break;
 	}
 	
 	written = g_mime_stream_write_to_stream (filtered_stream, stream);
-	g_mime_stream_unref (filtered_stream);
+	g_object_unref (filtered_stream);
 	
 	g_mime_stream_reset (wrapper->stream);
 	
@@ -263,7 +269,7 @@
  *
  * Returns the number of bytes written or -1 on failure.
  **/
-gssize
+ssize_t
 g_mime_data_wrapper_write_to_stream (GMimeDataWrapper *wrapper, GMimeStream *stream)
 {
 	g_return_val_if_fail (GMIME_IS_DATA_WRAPPER (wrapper), -1);
Index: gmime/gmime-data-wrapper.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-data-wrapper.h,v
retrieving revision 1.5
diff -u -r1.5 gmime-data-wrapper.h
--- gmime/gmime-data-wrapper.h	30 Dec 2002 16:37:57 -0000	1.5
+++ gmime/gmime-data-wrapper.h	7 Jun 2004 06:10:09 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,19 +24,19 @@
 #ifndef __GMIME_DATA_WRAPPER_H__
 #define __GMIME_DATA_WRAPPER_H__
 
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gmime/gmime-type-utils.h>
+#include <gmime/gmime-content-type.h>
+#include <gmime/gmime-stream.h>
+#include <gmime/gmime-utils.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include <glib-object.h>
-
-#include "gmime-type-utils.h"
-#include "gmime-content-type.h"
-#include "gmime-stream.h"
-#include "gmime-utils.h"
-
 #define GMIME_TYPE_DATA_WRAPPER            (g_mime_data_wrapper_get_type ())
 #define GMIME_DATA_WRAPPER(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_DATA_WRAPPER, GMimeDataWrapper))
 #define GMIME_DATA_WRAPPER_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_DATA_WRAPPER, GMimeDataWrapperClass))
@@ -57,7 +57,7 @@
 struct _GMimeDataWrapperClass {
 	GObjectClass parent_class;
 	
-	gssize (*write_to_stream) (GMimeDataWrapper *wrapper, GMimeStream *stream);
+	ssize_t (* write_to_stream) (GMimeDataWrapper *wrapper, GMimeStream *stream);
 };
 
 
@@ -72,7 +72,7 @@
 void g_mime_data_wrapper_set_encoding (GMimeDataWrapper *wrapper, GMimePartEncodingType encoding);
 GMimePartEncodingType g_mime_data_wrapper_get_encoding (GMimeDataWrapper *wrapper);
 
-gssize g_mime_data_wrapper_write_to_stream (GMimeDataWrapper *wrapper, GMimeStream *stream);
+ssize_t g_mime_data_wrapper_write_to_stream (GMimeDataWrapper *wrapper, GMimeStream *stream);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-disposition.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-disposition.c,v
retrieving revision 1.6
diff -u -r1.6 gmime-disposition.c
--- gmime/gmime-disposition.c	30 Dec 2002 16:37:57 -0000	1.6
+++ gmime/gmime-disposition.c	7 Jun 2004 06:10:09 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -30,14 +30,11 @@
 
 #include "gmime-disposition.h"
 
-#include <glib/gmessages.h>
-#include <glib/gmem.h>
-#include <glib/gstrfuncs.h>
 
 static int
 param_equal (gconstpointer v, gconstpointer v2)
 {
-	return g_strcasecmp ((const char *) v, (const char *) v2) == 0;
+	return strcasecmp ((const char *) v, (const char *) v2) == 0;
 }
 
 static guint
Index: gmime/gmime-disposition.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-disposition.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-disposition.h
--- gmime/gmime-disposition.h	30 Dec 2002 16:37:57 -0000	1.4
+++ gmime/gmime-disposition.h	7 Jun 2004 06:10:09 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,14 +24,13 @@
 #ifndef __GMIME_DISPOSITION_H__
 #define __GMIME_DISPOSITION_H__
 
+#include <glib.h>
+#include <gmime/gmime-param.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <glib/gtypes.h>
-#include <glib/ghash.h>
-#include "gmime-param.h"
 
 
 /**
Index: gmime/gmime-error.h
===================================================================
RCS file: gmime/gmime-error.h
diff -N gmime/gmime-error.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-error.h	7 Jun 2004 06:10:09 -0000
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __GMIME_ERROR_H__
+#define __GMIME_ERROR_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+extern GQuark gmime_error_quark;
+#define GMIME_ERROR_QUARK gmime_error_quark
+
+/**
+ * GMIME_ERROR:
+ *
+ * The GMime error domain GQuark value.
+ **/
+#define GMIME_ERROR GMIME_ERROR_QUARK
+
+/**
+ * GMIME_ERROR_IS_SYSTEM:
+ * @error: integer error value
+ *
+ * Decides if an error is a system error (aka errno value) vs. a GMime
+ * error.
+ *
+ * Meant to be used with GError::code
+ **/
+#define GMIME_ERROR_IS_SYSTEM(error) ((error) > 0)
+
+/* errno is a positive value, so negative values should be safe to use */
+enum {
+	GMIME_ERROR_GENERAL             =  0,
+	GMIME_ERROR_NOT_SUPPORTED       = -1,
+	GMIME_ERROR_PARSE_ERROR         = -2,
+	GMIME_ERROR_PROTOCOL_ERROR      = -3,
+	GMIME_ERROR_BAD_PASSWORD        = -4,
+	GMIME_ERROR_NO_VALID_RECIPIENTS = -5
+};
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GMIME_ERROR_H__ */
Index: gmime/gmime-filter-basic.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-basic.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-filter-basic.c
--- gmime/gmime-filter-basic.c	30 Dec 2002 16:37:57 -0000	1.8
+++ gmime/gmime-filter-basic.c	7 Jun 2004 06:10:09 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
  *
  */
 
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -29,63 +30,77 @@
 #include "gmime-filter-basic.h"
 #include "gmime-utils.h"
 
-#include <glib/gmem.h>
-#include <glib/gmessages.h>
+static void g_mime_filter_basic_class_init (GMimeFilterBasicClass *klass);
+static void g_mime_filter_basic_init (GMimeFilterBasic *filter, GMimeFilterBasicClass *klass);
+static void g_mime_filter_basic_finalize (GObject *object);
 
-static void filter_destroy (GMimeFilter *filter);
 static GMimeFilter *filter_copy (GMimeFilter *filter);
-static void filter_filter (GMimeFilter *filter, char *in, size_t len, 
-			   size_t prespace, char **out, 
-			   size_t *outlen, size_t *outprespace);
-static void filter_complete (GMimeFilter *filter, char *in, size_t len, 
-			     size_t prespace, char **out, 
-			     size_t *outlen, size_t *outprespace);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
 static void filter_reset (GMimeFilter *filter);
 
-static GMimeFilter filter_template = {
-	NULL, NULL, NULL, NULL,
-	0, 0, NULL, 0, 0,
-	filter_destroy,
-	filter_copy,
-	filter_filter,
-	filter_complete,
-	filter_reset,
-};
 
+static GMimeFilterClass *parent_class = NULL;
 
-/**
- * g_mime_filter_basic_new_type:
- * @type: filter type
- *
- * Creates a new filter of type @type.
- *
- * Returns a new basic filter of type @type.
- **/
-GMimeFilter *
-g_mime_filter_basic_new_type (GMimeFilterBasicType type)
+
+GType
+g_mime_filter_basic_get_type (void)
 {
-	GMimeFilterBasic *new;
+	static GType type = 0;
 	
-	new = g_new (GMimeFilterBasic, 1);
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterBasicClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_basic_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilterBasic),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_basic_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterBasic", &info, 0);
+	}
 	
-	new->type = type;
-	new->state = 0;
-	new->save = 0;
+	return type;
+}
+
+
+static void
+g_mime_filter_basic_class_init (GMimeFilterBasicClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
 	
-	g_mime_filter_construct (GMIME_FILTER (new), &filter_template);
+	parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
 	
-	filter_reset (GMIME_FILTER (new));
+	object_class->finalize = g_mime_filter_basic_finalize;
 	
-	return GMIME_FILTER (new);
+	filter_class->copy = filter_copy;
+	filter_class->filter = filter_filter;
+	filter_class->complete = filter_complete;
+	filter_class->reset = filter_reset;
 }
 
+static void
+g_mime_filter_basic_init (GMimeFilterBasic *filter, GMimeFilterBasicClass *klass)
+{
+	filter->type = 0;
+	filter->state = 0;
+	filter->save = 0;
+}
 
 static void
-filter_destroy (GMimeFilter *filter)
+g_mime_filter_basic_finalize (GObject *object)
 {
-	g_free (filter);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
 static GMimeFilter *
 filter_copy (GMimeFilter *filter)
 {
@@ -256,4 +271,27 @@
 		basic->state = 0;
 	}
 	basic->save = 0;
+}
+
+
+/**
+ * g_mime_filter_basic_new_type:
+ * @type: filter type
+ *
+ * Creates a new filter of type @type.
+ *
+ * Returns a new basic filter of type @type.
+ **/
+GMimeFilter *
+g_mime_filter_basic_new_type (GMimeFilterBasicType type)
+{
+	GMimeFilterBasic *new;
+	
+	new = g_object_new (GMIME_TYPE_FILTER_BASIC, NULL, NULL);
+	
+	new->type = type;
+	
+	filter_reset ((GMimeFilter *) new);
+	
+	return (GMimeFilter *) new;
 }
Index: gmime/gmime-filter-basic.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-basic.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-filter-basic.h
--- gmime/gmime-filter-basic.h	15 Mar 2002 20:06:40 -0000	1.4
+++ gmime/gmime-filter-basic.h	7 Jun 2004 06:10:09 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,15 +21,25 @@
  */
 
 
-#ifndef __G_MIME_FILTER_BASIC_H__
-#define __G_MIME_FILTER_BASIC_H__
+#ifndef __GMIME_FILTER_BASIC_H__
+#define __GMIME_FILTER_BASIC_H__
+
+#include <gmime/gmime-filter.h>
 
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include "gmime-filter.h"
+#define GMIME_TYPE_FILTER_BASIC            (g_mime_filter_basic_get_type ())
+#define GMIME_FILTER_BASIC(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER_BASIC, GMimeFilterBasic))
+#define GMIME_FILTER_BASIC_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_BASIC, GMimeFilterBasicClass))
+#define GMIME_IS_FILTER_BASIC(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER_BASIC))
+#define GMIME_IS_FILTER_BASIC_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_BASIC))
+#define GMIME_FILTER_BASIC_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER_BASIC, GMimeFilterBasicClass))
+
+typedef struct _GMimeFilterBasic GMimeFilterBasic;
+typedef struct _GMimeFilterBasicClass GMimeFilterBasicClass;
 
 typedef enum {
 	GMIME_FILTER_BASIC_BASE64_ENC = 1,
@@ -37,18 +47,26 @@
 	GMIME_FILTER_BASIC_QP_ENC,
 	GMIME_FILTER_BASIC_QP_DEC,
 	GMIME_FILTER_BASIC_UU_ENC,
-	GMIME_FILTER_BASIC_UU_DEC,
+	GMIME_FILTER_BASIC_UU_DEC
 } GMimeFilterBasicType;
 
-typedef struct _GMimeFilterBasic {
-	GMimeFilter parent;
+struct _GMimeFilterBasic {
+	GMimeFilter parent_object;
 	
 	GMimeFilterBasicType type;
 	
 	unsigned char uubuf[60];
 	int state;
 	int save;
-} GMimeFilterBasic;
+};
+
+struct _GMimeFilterBasicClass {
+	GMimeFilterClass parent_class;
+	
+};
+
+
+GType g_mime_filter_basic_get_type (void);
 
 GMimeFilter *g_mime_filter_basic_new_type (GMimeFilterBasicType type);
 
Index: gmime/gmime-filter-charset.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-charset.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-filter-charset.c
--- gmime/gmime-filter-charset.c	30 Dec 2002 16:37:57 -0000	1.8
+++ gmime/gmime-filter-charset.c	7 Jun 2004 06:10:10 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -31,72 +31,85 @@
 #include "gmime-charset.h"
 #include "gmime-iconv.h"
 
-#include <glib/gmem.h>
-#include <glib/gstrfuncs.h>
 
-static void filter_destroy (GMimeFilter *filter);
+static void g_mime_filter_charset_class_init (GMimeFilterCharsetClass *klass);
+static void g_mime_filter_charset_init (GMimeFilterCharset *filter, GMimeFilterCharsetClass *klass);
+static void g_mime_filter_charset_finalize (GObject *object);
+
 static GMimeFilter *filter_copy (GMimeFilter *filter);
-static void filter_filter (GMimeFilter *filter, char *in, size_t len, 
-			   size_t prespace, char **out, 
-			   size_t *outlen, size_t *outprespace);
-static void filter_complete (GMimeFilter *filter, char *in, size_t len, 
-			     size_t prespace, char **out, 
-			     size_t *outlen, size_t *outprespace);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
 static void filter_reset (GMimeFilter *filter);
 
-static GMimeFilter filter_template = {
-	NULL, NULL, NULL, NULL,
-	0, 0, NULL, 0, 0,
-	filter_destroy,
-	filter_copy,
-	filter_filter,
-	filter_complete,
-	filter_reset,
-};
 
+static GMimeFilterClass *parent_class = NULL;
 
-/**
- * g_mime_filter_charset_new:
- * @from_charset:
- * @to_charset:
- *
- * Creates a new GMimeFilterCharset filter.
- *
- * Returns a new charset filter.
- **/
-GMimeFilter *
-g_mime_filter_charset_new (const char *from_charset, const char *to_charset)
+
+GType
+g_mime_filter_charset_get_type (void)
 {
-	GMimeFilterCharset *new;
-	iconv_t cd;
+	static GType type = 0;
 	
-	cd = g_mime_iconv_open (to_charset, from_charset);
-	if (cd == (iconv_t) -1)
-		return NULL;
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterCharsetClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_charset_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilterCharset),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_charset_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterCharset", &info, 0);
+	}
 	
-	new = g_new (GMimeFilterCharset, 1);
-	new->from_charset = g_strdup (from_charset);
-	new->to_charset = g_strdup (to_charset);
-	new->cd = cd;
+	return type;
+}
+
+
+static void
+g_mime_filter_charset_class_init (GMimeFilterCharsetClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
+	
+	parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
 	
-	g_mime_filter_construct (GMIME_FILTER (new), &filter_template);
+	object_class->finalize = g_mime_filter_charset_finalize;
 	
-	return GMIME_FILTER (new);
+	filter_class->copy = filter_copy;
+	filter_class->filter = filter_filter;
+	filter_class->complete = filter_complete;
+	filter_class->reset = filter_reset;
 }
 
+static void
+g_mime_filter_charset_init (GMimeFilterCharset *filter, GMimeFilterCharsetClass *klass)
+{
+	filter->from_charset = NULL;
+	filter->to_charset = NULL;
+	filter->cd = (iconv_t) -1;
+}
 
 static void
-filter_destroy (GMimeFilter *filter)
+g_mime_filter_charset_finalize (GObject *object)
 {
-	GMimeFilterCharset *charset = (GMimeFilterCharset *) filter;
+	GMimeFilterCharset *filter = (GMimeFilterCharset *) object;
 	
-	g_free (charset->from_charset);
-	g_free (charset->to_charset);
-	if (charset->cd != (iconv_t) -1)
-		g_mime_iconv_close (charset->cd);
-	g_free (filter);
+	g_free (filter->from_charset);
+	g_free (filter->to_charset);
+	if (filter->cd != (iconv_t) -1)
+		g_mime_iconv_close (filter->cd);
+	
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
 static GMimeFilter *
 filter_copy (GMimeFilter *filter)
 {
@@ -118,46 +131,44 @@
 		goto noop;
 	
 	g_mime_filter_set_size (filter, len * 5 + 16, FALSE);
+	outbuf = filter->outbuf;
+	outleft = filter->outsize;
 	
 	inbuf = in;
 	inleft = len;
 	
 	do {
-		errno = 0;
-		outbuf = filter->outbuf + converted;
-		outleft = filter->outsize - converted;
-		
 		converted = iconv (charset->cd, &inbuf, &inleft, &outbuf, &outleft);
 		if (converted == (size_t) -1) {
-			if (errno != E2BIG && errno != EINVAL)
+			if (errno == E2BIG || errno == EINVAL)
+				break;
+			
+			if (errno == EILSEQ) {
+				/*
+				 * EILSEQ An invalid multibyte sequence has been  encountered
+				 *        in the input.
+				 *
+				 * What we do here is eat the invalid bytes in the sequence and continue
+				 */
+				
+				inbuf++;
+				inleft--;
+			} else {
+				/* unknown error condition */
 				goto noop;
+			}
 		}
-		
-		/*
-		 * E2BIG   There is not sufficient room at *outbuf.
-		 *
-		 * We just need to grow our outbuffer and try again.
-		 */
-		
-		converted = filter->outsize - outleft;
-		if (errno == E2BIG)
-			g_mime_filter_set_size (filter, inleft * 5 + filter->outsize + 16, TRUE);
-		
-	} while (errno == E2BIG && inleft > 0);
-	
-	/*
-	 * EINVAL  An  incomplete  multibyte sequence has been encoun­
-	 *         tered in the input.
-	 *
-	 * This just means that we need to save the remainder
-	 * of the input buffer for the next conversion.
-	 */
+	} while (((int) inleft) > 0);
 	
-	if (inleft > 0)
+	if (((int) inleft) > 0) {
+		/* We've either got an E2BIG or EINVAL. Save the
+                   remainder of the buffer as we'll process this next
+                   time through */
 		g_mime_filter_backup (filter, inbuf, inleft);
+	}
 	
 	*out = filter->outbuf;
-	*outlen = converted;
+	*outlen = outbuf - filter->outbuf;
 	*outprespace = filter->outpre;
 	
 	return;
@@ -182,50 +193,60 @@
 		goto noop;
 	
 	g_mime_filter_set_size (filter, len * 5 + 16, FALSE);
+	outbuf = filter->outbuf;
+	outleft = filter->outsize;
 	
 	inbuf = in;
 	inleft = len;
 	
 	if (inleft > 0) {
 		do {
-			outbuf = filter->outbuf + converted;
-			outleft = filter->outsize - converted;
-			
 			converted = iconv (charset->cd, &inbuf, &inleft, &outbuf, &outleft);
-			if (converted == (size_t) -1) {
-				if (errno != E2BIG && errno != EINVAL)
-					goto noop;
-			}
+			if (converted != (size_t) -1)
+				continue;
 			
-			/*
-			 * E2BIG   There is not sufficient room at *outbuf.
-			 *
-			 * We just need to grow our outbuffer and try again.
-			 */
-			
-			converted = filter->outsize - outleft;
-			if (errno == E2BIG)
+			if (errno == E2BIG) {
+				/*
+				 * E2BIG   There is not sufficient room at *outbuf.
+				 *
+				 * We just need to grow our outbuffer and try again.
+				 */
+				
+				converted = outbuf - filter->outbuf;
 				g_mime_filter_set_size (filter, inleft * 5 + filter->outsize + 16, TRUE);
+				outbuf = filter->outbuf + converted;
+				outleft = filter->outsize - converted;
+			} else if (errno == EILSEQ) {
+				/*
+				 * EILSEQ An invalid multibyte sequence has been  encountered
+				 *        in the input.
+				 *
+				 * What we do here is eat the invalid bytes in the sequence and continue
+				 */
+				
+				inbuf++;
+				inleft--;
+			} else if (errno == EINVAL) {
+				/*
+				 * EINVAL  An  incomplete  multibyte sequence has been encoun­
+				 *         tered in the input.
+				 *
+				 * We assume that this can only happen if we've run out of
+				 * bytes for a multibyte sequence, if not we're in trouble.
+				 */
+				
+				break;
+			} else
+				goto noop;
 			
-		} while (errno == E2BIG);
-	} else {
-		outbuf = filter->outbuf;
-		outleft = filter->outsize;
+		} while (((int) inleft) > 0);
 	}
 	
-	/*
-	 * EINVAL  An  incomplete  multibyte sequence has been encoun­
-	 *         tered in the input.
-	 *
-	 * This just means that we need to save the remainder
-	 * of the input buffer for the next conversion.
-	 */
-	
 	/* flush the iconv conversion */
 	iconv (charset->cd, NULL, NULL, &outbuf, &outleft);
 	
 	*out = filter->outbuf;
-	*outlen = filter->outsize - outleft;
+	*outlen = outbuf - filter->outbuf;
 	*outprespace = filter->outpre;
 	
 	return;
@@ -244,4 +265,32 @@
 	
 	if (charset->cd != (iconv_t) -1)
 		iconv (charset->cd, NULL, NULL, NULL, NULL);
+}
+
+
+/**
+ * g_mime_filter_charset_new:
+ * @from_charset:
+ * @to_charset:
+ *
+ * Creates a new GMimeFilterCharset filter.
+ *
+ * Returns a new charset filter.
+ **/
+GMimeFilter *
+g_mime_filter_charset_new (const char *from_charset, const char *to_charset)
+{
+	GMimeFilterCharset *new;
+	iconv_t cd;
+	
+	cd = g_mime_iconv_open (to_charset, from_charset);
+	if (cd == (iconv_t) -1)
+		return NULL;
+	
+	new = g_object_new (GMIME_TYPE_FILTER_CHARSET, NULL, NULL);
+	new->from_charset = g_strdup (from_charset);
+	new->to_charset = g_strdup (to_charset);
+	new->cd = cd;
+	
+	return (GMimeFilter *) new;
 }
Index: gmime/gmime-filter-charset.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-charset.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-filter-charset.h
--- gmime/gmime-filter-charset.h	14 May 2002 03:24:12 -0000	1.4
+++ gmime/gmime-filter-charset.h	7 Jun 2004 06:10:10 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,22 +24,39 @@
 #ifndef __GMIME_FILTER_CHARSET_H__
 #define __GMIME_FILTER_CHARSET_H__
 
+#include <iconv.h>
+#include <gmime/gmime-filter.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include "gmime-filter.h"
-#include <iconv.h>
+#define GMIME_TYPE_FILTER_CHARSET            (g_mime_filter_charset_get_type ())
+#define GMIME_FILTER_CHARSET(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER_CHARSET, GMimeFilterCharset))
+#define GMIME_FILTER_CHARSET_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_CHARSET, GMimeFilterCharsetClass))
+#define GMIME_IS_FILTER_CHARSET(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER_CHARSET))
+#define GMIME_IS_FILTER_CHARSET_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_CHARSET))
+#define GMIME_FILTER_CHARSET_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER_CHARSET, GMimeFilterCharsetClass))
 
+typedef struct _GMimeFilterCharset GMimeFilterCharset;
+typedef struct _GMimeFilterCharsetClass GMimeFilterCharsetClass;
 
-typedef struct _GMimeFilterCharset {
-	GMimeFilter parent;
+struct _GMimeFilterCharset {
+	GMimeFilter parent_object;
 	
 	char *from_charset;
 	char *to_charset;
 	iconv_t cd;
-} GMimeFilterCharset;
+};
+
+struct _GMimeFilterCharsetClass {
+	GMimeFilterClass parent_class;
+	
+};
+
+
+GType g_mime_filter_charset_get_type (void);
 
 GMimeFilter *g_mime_filter_charset_new (const char *from_charset, const char *to_charset);
 
Index: gmime/gmime-filter-crlf.c
===================================================================
RCS file: gmime/gmime-filter-crlf.c
diff -N gmime/gmime-filter-crlf.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-filter-crlf.c	7 Jun 2004 06:10:10 -0000
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gmime-filter-crlf.h"
+
+static void g_mime_filter_crlf_class_init (GMimeFilterCRLFClass *klass);
+static void g_mime_filter_crlf_init (GMimeFilterCRLF *filter, GMimeFilterCRLFClass *klass);
+static void g_mime_filter_crlf_finalize (GObject *object);
+
+static GMimeFilter *filter_copy (GMimeFilter *filter);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
+static void filter_reset (GMimeFilter *filter);
+
+
+static GMimeFilterClass *parent_class = NULL;
+
+
+GType
+g_mime_filter_crlf_get_type (void)
+{
+	static GType type = 0;
+	
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterCRLFClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_crlf_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilterCRLF),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_crlf_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterCRLF", &info, 0);
+	}
+	
+	return type;
+}
+
+
+static void
+g_mime_filter_crlf_class_init (GMimeFilterCRLFClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
+	
+	parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
+	
+	object_class->finalize = g_mime_filter_crlf_finalize;
+	
+	filter_class->copy = filter_copy;
+	filter_class->filter = filter_filter;
+	filter_class->complete = filter_complete;
+	filter_class->reset = filter_reset;
+}
+
+static void
+g_mime_filter_crlf_init (GMimeFilterCRLF *filter, GMimeFilterCRLFClass *klass)
+{
+	filter->saw_cr = FALSE;
+	filter->saw_lf = FALSE;
+	filter->saw_dot = FALSE;
+}
+
+static void
+g_mime_filter_crlf_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static GMimeFilter *
+filter_copy (GMimeFilter *filter)
+{
+	GMimeFilterCRLF *crlf = (GMimeFilterCRLF *) filter;
+	
+	return g_mime_filter_crlf_new (crlf->direction, crlf->mode);
+}
+
+static void
+filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+	       char **out, size_t *outlen, size_t *outprespace)
+{
+	GMimeFilterCRLF *crlf = (GMimeFilterCRLF *) filter;
+	register const char *inptr;
+	const char *inend;
+	gboolean do_dots;
+	char *outptr;
+	
+	do_dots = crlf->mode == GMIME_FILTER_CRLF_MODE_CRLF_DOTS;
+	
+	inptr = in;
+	inend = in + len;
+	
+	if (crlf->direction == GMIME_FILTER_CRLF_ENCODE) {
+		g_mime_filter_set_size (filter, 3 * len, FALSE);
+		
+		outptr = filter->outbuf;
+		while (inptr < inend) {
+			if (*inptr == '\r') {
+				crlf->saw_cr = TRUE;
+			} else if (*inptr == '\n') {
+				crlf->saw_lf = TRUE;
+				if (!crlf->saw_cr)
+					*outptr++ = '\r';
+				crlf->saw_cr = FALSE;
+			} else {
+				if (do_dots && *inptr == '.' && crlf->saw_lf)
+					*outptr++ = '.';
+				
+				crlf->saw_cr = FALSE;
+				crlf->saw_lf = FALSE;
+			}
+			
+			*outptr++ = *inptr++;
+		}
+	} else {
+		g_mime_filter_set_size (filter, len, FALSE);
+		
+		outptr = filter->outbuf;
+		while (inptr < inend) {
+			if (*inptr == '\r') {
+				crlf->saw_cr = TRUE;
+			} else {
+				if (crlf->saw_cr) {
+					crlf->saw_cr = FALSE;
+					
+					if (*inptr == '\n') {
+						crlf->saw_lf = TRUE;
+						*outptr++ = *inptr++;
+						continue;
+					} else
+						*outptr++ = '\r';
+				}
+				
+				*outptr++ = *inptr;
+			}
+			
+			if (do_dots && *inptr == '.') {
+				if (crlf->saw_lf) {
+					crlf->saw_dot = TRUE;
+					crlf->saw_lf = FALSE;
+					inptr++;
+				} else if (crlf->saw_dot) {
+					crlf->saw_dot = FALSE;
+				}
+			}
+			
+			crlf->saw_lf = FALSE;
+			
+			inptr++;
+		}
+	}
+	
+	*out = filter->outbuf;
+	*outlen = outptr - filter->outbuf;
+	*outprespace = filter->outpre;
+}
+
+static void 
+filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+		 char **out, size_t *outlen, size_t *outprespace)
+{
+	if (in && len)
+		filter_filter (filter, in, len, prespace, out, outlen, outprespace);
+}
+
+static void
+filter_reset (GMimeFilter *filter)
+{
+	GMimeFilterCRLF *crlf = (GMimeFilterCRLF *) filter;
+	
+	crlf->saw_cr = FALSE;
+	crlf->saw_lf = TRUE;
+	crlf->saw_dot = FALSE;
+}
+
+
+/**
+ * g_mime_filter_crlf_new:
+ * @direction: encode direction
+ * @mode: crlf or crlf & dot mode
+ *
+ * Creates a new GMimeFilterCRLF filter.
+ *
+ * Returns a new crlf(/dot) filter.
+ **/
+GMimeFilter *
+g_mime_filter_crlf_new (GMimeFilterCRLFDirection direction, GMimeFilterCRLFMode mode)
+{
+	GMimeFilterCRLF *new;
+	
+	new = g_object_new (GMIME_TYPE_FILTER_CRLF, NULL, NULL);
+	new->direction = direction;
+	new->mode = mode;
+	
+	return (GMimeFilter *) new;
+}
Index: gmime/gmime-filter-crlf.h
===================================================================
RCS file: gmime/gmime-filter-crlf.h
diff -N gmime/gmime-filter-crlf.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-filter-crlf.h	7 Jun 2004 06:10:10 -0000
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __GMIME_FILTER_CRLF_H__
+#define __GMIME_FILTER_CRLF_H__
+
+#include <gmime/gmime-filter.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define GMIME_TYPE_FILTER_CRLF            (g_mime_filter_crlf_get_type ())
+#define GMIME_FILTER_CRLF(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER_CRLF, GMimeFilterCRLF))
+#define GMIME_FILTER_CRLF_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_CRLF, GMimeFilterCRLFClass))
+#define GMIME_IS_FILTER_CRLF(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER_CRLF))
+#define GMIME_IS_FILTER_CRLF_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_CRLF))
+#define GMIME_FILTER_CRLF_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER_CRLF, GMimeFilterCRLFClass))
+
+typedef struct _GMimeFilterCRLF GMimeFilterCRLF;
+typedef struct _GMimeFilterCRLFClass GMimeFilterCRLFClass;
+
+typedef enum {
+	GMIME_FILTER_CRLF_ENCODE,
+	GMIME_FILTER_CRLF_DECODE
+} GMimeFilterCRLFDirection;
+
+typedef enum {
+	GMIME_FILTER_CRLF_MODE_CRLF_DOTS,
+	GMIME_FILTER_CRLF_MODE_CRLF_ONLY
+} GMimeFilterCRLFMode;
+
+struct _GMimeFilterCRLF {
+	GMimeFilter parent_object;
+	
+	GMimeFilterCRLFDirection direction;
+	GMimeFilterCRLFMode mode;
+	gboolean saw_cr;
+	gboolean saw_lf;
+	gboolean saw_dot;
+};
+
+struct _GMimeFilterCRLFClass {
+	GMimeFilterClass parent_class;
+	
+};
+
+
+GType g_mime_filter_crlf_get_type (void);
+
+GMimeFilter *g_mime_filter_crlf_new (GMimeFilterCRLFDirection direction, GMimeFilterCRLFMode mode);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GMIME_FILTER_CRLF_H__ */
Index: gmime/gmime-filter-html.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-html.c,v
retrieving revision 1.9
diff -u -r1.9 gmime-filter-html.c
--- gmime/gmime-filter-html.c	8 Sep 2003 18:58:38 -0000	1.9
+++ gmime/gmime-filter-html.c	7 Jun 2004 06:10:10 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -25,68 +25,113 @@
 #include <config.h>
 #endif
 
-#include "gmime-filter-html.h"
-
 #include <stdio.h>
 #include <string.h>
-#include <ctype.h>
 
-#include <glib/gmem.h>
-#include <glib/gstrfuncs.h>
-#include <glib/gutils.h>
+#include "url-scanner.h"
+#include "gmime-filter-html.h"
+
+#define d(x)
+
+#define CONVERT_WEB_URLS  GMIME_FILTER_HTML_CONVERT_URLS
+#define CONVERT_ADDRSPEC  GMIME_FILTER_HTML_CONVERT_ADDRESSES
+
+static struct {
+	unsigned int mask;
+	urlpattern_t pattern;
+} patterns[] = {
+	{ CONVERT_WEB_URLS, { "file://",   "",        g_url_file_start,     g_url_file_end     } },
+	{ CONVERT_WEB_URLS, { "ftp://",    "",        g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "http://",   "",        g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "https://",  "",        g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "news://",   "",        g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "nntp://",   "",        g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "telnet://", "",        g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "www.",      "http://", g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_WEB_URLS, { "ftp.",      "ftp://",  g_url_web_start,      g_url_web_end      } },
+	{ CONVERT_ADDRSPEC, { "@",         "mailto:", g_url_addrspec_start, g_url_addrspec_end } },
+};
+
+#define NUM_URL_PATTERNS (sizeof (patterns) / sizeof (patterns[0]))
+
+static void g_mime_filter_html_class_init (GMimeFilterHTMLClass *klass);
+static void g_mime_filter_html_init (GMimeFilterHTML *filter, GMimeFilterHTMLClass *klass);
+static void g_mime_filter_html_finalize (GObject *object);
 
-static void filter_destroy (GMimeFilter *filter);
 static GMimeFilter *filter_copy (GMimeFilter *filter);
-static void filter_filter (GMimeFilter *filter, char *in, size_t len, 
-			   size_t prespace, char **out, 
-			   size_t *outlen, size_t *outprespace);
-static void filter_complete (GMimeFilter *filter, char *in, size_t len, 
-			     size_t prespace, char **out, 
-			     size_t *outlen, size_t *outprespace);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
 static void filter_reset (GMimeFilter *filter);
 
-static GMimeFilter filter_template = {
-	NULL, NULL, NULL, NULL,
-	0, 0, NULL, 0, 0,
-	filter_destroy,
-	filter_copy,
-	filter_filter,
-	filter_complete,
-	filter_reset,
-};
 
+static GMimeFilterClass *parent_class = NULL;
 
-/**
- * g_mime_filter_html_new:
- * @flags: html flags
- * @colour: citation colour
- *
- * Creates a new GMimeFilterHTML filter.
- *
- * Returns a new html filter.
- **/
-GMimeFilter *
-g_mime_filter_html_new (guint32 flags, guint32 colour)
+
+GType
+g_mime_filter_html_get_type (void)
 {
-	GMimeFilterHTML *new;
+	static GType type = 0;
 	
-	new = g_new (GMimeFilterHTML, 1);
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterHTMLClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_html_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilterHTML),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_html_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterHTML", &info, 0);
+	}
 	
-	new->flags = flags;
-	new->colour = colour;
+	return type;
+}
+
+
+static void
+g_mime_filter_html_class_init (GMimeFilterHTMLClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
+	
+	parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
 	
-	g_mime_filter_construct (GMIME_FILTER (new), &filter_template);
+	object_class->finalize = g_mime_filter_html_finalize;
 	
-	return GMIME_FILTER (new);
+	filter_class->copy = filter_copy;
+	filter_class->filter = filter_filter;
+	filter_class->complete = filter_complete;
+	filter_class->reset = filter_reset;
 }
 
+static void
+g_mime_filter_html_init (GMimeFilterHTML *filter, GMimeFilterHTMLClass *klass)
+{
+	filter->scanner = g_url_scanner_new ();
+	
+	filter->flags = 0;
+	filter->colour = 0;
+	filter->column = 0;
+	filter->pre_open = FALSE;
+}
 
 static void
-filter_destroy (GMimeFilter *filter)
+g_mime_filter_html_finalize (GObject *object)
 {
-	g_free (filter);
+	GMimeFilterHTML *html = (GMimeFilterHTML *) object;
+	
+	g_url_scanner_free (html->scanner);
+	
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
 static GMimeFilter *
 filter_copy (GMimeFilter *filter)
 {
@@ -112,371 +157,273 @@
 	return filter->outbuf + offset;
 }
 
-/* 1 = non-email-address chars: "()<>@,;:\\\"/[]`'|\n\t "  */
-/* 2 = non-url chars:           "()<>\"[]`'|\n\t "         */
-/* 3 = trailing url garbage:    ",.!?;:>)]}\\`'-_|\n\t "   */
-static unsigned short special_chars[256] = {
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 7, 4, 3, 0, 0, 0, 0, 7, 3, 7, 0, 0, 4, 4, 4, 1,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 3, 0, 7, 4,
-	 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 3, 0, 4,
-	 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 4, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-
-#define IS_NON_ADDR   (1 << 0)
-#define IS_NON_URL    (1 << 1)
-#define IS_GARBAGE    (1 << 2)
-
-#define NON_EMAIL_CHARS         "()<>@,;:\\\"/[]`'|\n\t "
-#define NON_URL_CHARS           "()<>\"[]`'|\n\t "
-#define TRAILING_URL_GARBAGE    ",.!?;:>)}\\`'-_|\n\t "
-
-#define is_addr_char(c) (isprint ((int) c) && !(special_chars[(unsigned char) c] & IS_NON_ADDR))
-#define is_url_char(c)  (isprint ((int) c) && !(special_chars[(unsigned char) c] & IS_NON_URL))
-#define is_trailing_garbage(c) (!isprint ((int) c) || (special_chars[(unsigned char) c] & IS_GARBAGE))
-
-#if 0
-/* this is for building the special_chars table... */
-static void
-table_init (void)
-{
-	char *c;
-	
-	memset (special_chars, 0, sizeof (special_chars));
-	for (c = NON_EMAIL_CHARS; *c; c++)
-		special_chars[(int) *c] |= IS_NON_ADDR;
-	for (c = NON_URL_CHARS; *c; c++)
-		special_chars[(int) *c] |= IS_NON_URL;
-	for (c = TRAILING_URL_GARBAGE; *c; c++)
-		special_chars[(int) *c] |= IS_GARBAGE;
-}
-#endif
-
-char *
-url_extract (const char **in, int inlen, gboolean check, gboolean *backup)
+static int
+citation_depth (const char *in)
 {
-	unsigned char *inptr, *inend, *p;
-	char *url;
-	
-	inptr = (unsigned char *) *in;
-	inend = inptr + inlen;
+	register const char *inptr = in;
+	int depth = 1;
 	
-	while (inptr < inend && is_url_char (*inptr))
-		inptr++;
+	if (*inptr++ != '>')
+		return 0;
 	
-	if ((char *) inptr == *in)
-		return NULL;
-	
-	/* back up if we probably went too far. */
-	while (inptr > (unsigned char *) *in && is_trailing_garbage (*(inptr - 1)))
-		inptr--;
-	
-	if (check) {
-		/* make sure we weren't fooled. */
-		p = memchr (*in, ':', (unsigned) ((char *) inptr - *in));
-		if (!p)
-			return NULL;
-	}
-	
-	if (inptr == inend && backup) {
-		*backup = TRUE;
-		return NULL;
+	/* check that it isn't an escaped From line */
+	if (!strncmp (inptr, "From", 4))
+		return 0;
+	
+	while (*inptr != '\n') {
+		if (*inptr == ' ')
+			inptr++;
+		
+		if (*inptr++ != '>')
+			break;
+		
+		depth++;
 	}
 	
-	url = g_strndup (*in, (unsigned) ((char *) inptr - *in));
-	*in = inptr;
-	
-	return url;
+	return depth;
 }
 
-static char *
-email_address_extract (char **in, char *inend, char *start, char **outptr, gboolean *backup)
+static inline gunichar
+html_utf8_getc (const unsigned char **in, const unsigned char *inend)
 {
-	char *addr, *pre, *end, *dot;
-	
-	/* *in points to the '@'. Look backward for a valid local-part */
-	for (pre = *in; pre - 1 >= start && is_addr_char (*(pre - 1)); pre--);
-	
-	if (pre == *in)
-		return NULL;
+	register const unsigned char *inptr = *in;
+	register unsigned char c, r;
+	register gunichar u, m;
 	
-	/* Now look forward for a valid domain part */
-	for (end = *in + 1, dot = NULL; end < inend && is_addr_char (*end); end++) {
-		if (*end == '.' && !dot)
-			dot = end;
-	}
-	
-	if (end >= inend && backup) {
-		*backup = TRUE;
-		*outptr -= (*in - pre);
-		*in = pre;
-		return NULL;
-	}
+	if (inptr == inend)
+		return 0;
 	
-	if (!dot)
-		return NULL;
-	
-	/* Remove trailing garbage */
-	while (end > *in && is_trailing_garbage (*(end - 1)))
-		end--;
-	if (dot > end)
-		return NULL;
-	
-	addr = g_strndup (pre, (unsigned) (end - pre));
-	*outptr -= (*in - pre);
-	*in = end;
-	
-	return addr;
-}
-
-static gboolean
-is_citation (char *inptr, char *inend, gboolean saw_citation, gboolean *backup)
-{
-	if (*inptr != '>')
-		return FALSE;
-	
-	if (inend - inptr >= 6) {
-		/* make sure this isn't just mbox From-magling... */
-		if (strncmp (inptr, ">From ", 6) != 0)
-			return TRUE;
-	} else if (backup) {
-		/* we don't have enough data to tell, so return */
-		*backup = TRUE;
-		return saw_citation;
-	}
-	
-	/* if the previous line was a citation, then say this one is too */
-	if (saw_citation)
-		return TRUE;
-	
-	/* otherwise it was just an isolated ">From " line */
-	return FALSE;
-}
-
-static gboolean
-is_protocol (char *inptr, char *inend, gboolean *backup)
-{
-	if (inend - inptr >= 8) {
-		if (!g_strncasecmp (inptr, "http://", 7) ||
-		    !g_strncasecmp (inptr, "https://", 8) ||
-		    !g_strncasecmp (inptr, "ftp://", 6) ||
-		    !g_strncasecmp (inptr, "nntp://", 7) ||
-		    !g_strncasecmp (inptr, "mailto:", 7) ||
-		    !g_strncasecmp (inptr, "news:", 5))
-			return TRUE;
-	} else if (backup) {
-		*backup = TRUE;
-		return FALSE;
+	while (inptr < inend) {
+		r = *inptr++;
+	loop:
+		if (r < 0x80) {
+			*in = inptr;
+			return r;
+		} else if (r < 0xf8) { /* valid start char? */
+			u = r;
+			m = 0x7f80;	/* used to mask out the length bits */
+			do {
+				if (inptr >= inend)
+					return 0xffff;
+				
+				c = *inptr++;
+				if ((c & 0xc0) != 0x80) {
+					r = c;
+					goto loop;
+				}
+				
+				u = (u << 6) | (c & 0x3f);
+				r <<= 1;
+				m <<= 5;
+			} while (r & 0x40);
+			
+			*in = inptr;
+			
+			u &= ~m;
+			
+			return u;
+		}
 	}
 	
-	return FALSE;
+	return 0xffff;
 }
 
-static void
-html_convert (GMimeFilter *filter, char *in, size_t inlen, size_t prespace,
-	      char **out, size_t *outlen, size_t *outprespace, gboolean flush)
+static char *
+writeln (GMimeFilter *filter, const unsigned char *in, const unsigned char *inend, char *outptr, char **outend)
 {
 	GMimeFilterHTML *html = (GMimeFilterHTML *) filter;
-	char *inptr, *inend, *outptr, *outend, *start;
-	gboolean backup = FALSE;
-	
-	g_mime_filter_set_size (filter, inlen * 2 + 6, FALSE);
-	
-	inptr = start = in;
-	inend = in + inlen;
-	outptr = filter->outbuf;
-	outend = filter->outbuf + filter->outsize;
-	
-	if (html->flags & GMIME_FILTER_HTML_PRE && !html->pre_open) {
-		outptr = g_stpcpy (outptr, "<pre>");
-		html->pre_open = TRUE;
-	}
+	const unsigned char *inptr = in;
 	
 	while (inptr < inend) {
-		unsigned char u;
-		
-		if (html->flags & GMIME_FILTER_HTML_MARK_CITATION && html->column == 0) {
-			html->saw_citation = is_citation (inptr, inend, html->saw_citation,
-							  flush ? &backup : NULL);
-			if (backup)
-				break;
-			
-			if (html->saw_citation) {
-				if (!html->coloured) {
-					char font[25];
-					
-					g_snprintf (font, 25, "<font color=\"#%06x\">", html->colour);
-					
-					outptr = check_size (filter, outptr, &outend, 25);
-					outptr = g_stpcpy (outptr, font);
-					html->coloured = TRUE;
-				}
-			} else if (html->coloured) {
-				outptr = check_size (filter, outptr, &outend, 10);
-				outptr = g_stpcpy (outptr, "</font>");
-				html->coloured = FALSE;
-			}
-			
-			/* display mbox-mangled ">From " as "From " */
-			if (*inptr == '>' && !html->saw_citation)
-				inptr++;
-		} else if (html->flags & GMIME_FILTER_HTML_CITE && html->column == 0) {
-			outptr = check_size (filter, outptr, &outend, 6);
-			outptr = g_stpcpy (outptr, "&gt; ");
-		}
+		gunichar u;
 		
-		if (html->flags & GMIME_FILTER_HTML_CONVERT_URLS && isalpha ((int) *inptr)) {
-			char *refurl = NULL, *dispurl = NULL;
-			
-			if (is_protocol (inptr, inend, flush ? &backup : NULL)) {
-				dispurl = url_extract ((const char **)&inptr, inend - inptr, TRUE,
-						       flush ? &backup : NULL);
-				if (backup)
-					break;
-				
-				if (dispurl)
-					refurl = g_strdup (dispurl);
-			} else {
-				if (backup)
-					break;
-				
-				if (!g_strncasecmp (inptr, "www.", 4) && ((unsigned char) inptr[4]) < 0x80
-				    && isalnum ((int) inptr[4])) {
-					dispurl = url_extract ((const char **)&inptr, inend - inptr, FALSE,
-							       flush ? &backup : NULL);
-					if (backup)
-						break;
-					
-					if (dispurl)
-						refurl = g_strdup_printf ("http://%s", dispurl);
-				}
-			}
-			
-			if (dispurl) {
-				outptr = check_size (filter, outptr, &outend,
-						     strlen (refurl) +
-						     strlen (dispurl) + 15);
-				outptr += sprintf (outptr, "<a href=\"%s\">%s</a>",
-						   refurl, dispurl);
-				html->column += strlen (dispurl);
-				g_free (refurl);
-				g_free (dispurl);
-			}
-			
-			if (inptr >= inend)
-				break;
-		}
-		
-		if (*inptr == '@' && (html->flags & GMIME_FILTER_HTML_CONVERT_ADDRESSES)) {
-			char *addr, *dispaddr, *outaddr;
-			
-			addr = email_address_extract (&inptr, inend, start, &outptr,
-						      flush ? &backup : NULL);
-			if (backup)
-				break;
-			
-			if (addr) {
-				dispaddr = g_strdup (addr);
-				outaddr = g_strdup_printf ("<a href=\"mailto:%s\">%s</a>",
-							   addr, dispaddr);
-				outptr = check_size (filter, outptr, &outend, strlen (outaddr));
-				outptr = g_stpcpy (outptr, outaddr);
-				html->column += strlen (addr);
-				g_free (addr);
-				g_free (dispaddr);
-				g_free (outaddr);
-			}
-		}
+		outptr = check_size (filter, outptr, outend, 16);
 		
-		outptr = check_size (filter, outptr, &outend, 32);
-		
-		switch ((u = (unsigned char) *inptr++)) {
+		u = html_utf8_getc (&inptr, inend);
+		switch (u) {
+		case 0xffff:
+			g_warning ("Invalid UTF-8 sequence encountered");
+			return outptr;
+			break;
 		case '<':
 			outptr = g_stpcpy (outptr, "&lt;");
 			html->column++;
 			break;
-			
 		case '>':
 			outptr = g_stpcpy (outptr, "&gt;");
 			html->column++;
 			break;
-			
 		case '&':
 			outptr = g_stpcpy (outptr, "&amp;");
 			html->column++;
 			break;
-			
 		case '"':
 			outptr = g_stpcpy (outptr, "&quot;");
 			html->column++;
 			break;
-			
-		case '\n':
-			if (html->flags & GMIME_FILTER_HTML_CONVERT_NL)
-				outptr = g_stpcpy (outptr, "<br>");
-			
-			*outptr++ = '\n';
-			start = inptr;
-			html->column = 0;
-			break;
-			
 		case '\t':
 			if (html->flags & (GMIME_FILTER_HTML_CONVERT_SPACES)) {
 				do {
-					outptr = check_size (filter, outptr, &outend, 7);
+					outptr = check_size (filter, outptr, outend, 7);
 					outptr = g_stpcpy (outptr, "&nbsp;");
 					html->column++;
 				} while (html->column % 8);
 				break;
 			}
 			/* otherwise, FALL THROUGH */
-			
 		case ' ':
 			if (html->flags & GMIME_FILTER_HTML_CONVERT_SPACES) {
-				if (inptr == in || (inptr < inend && (*(inptr + 1) == ' ' ||
-								      *(inptr + 1) == '\t' ||
-								      *(inptr - 1) == '\n'))) {
+				if (inptr == (in + 1) || *inptr == ' ' || *inptr == '\t') {
 					outptr = g_stpcpy (outptr, "&nbsp;");
 					html->column++;
 					break;
 				}
 			}
 			/* otherwise, FALL THROUGH */
-			
 		default:
-			if ((u >= 0x20 && u < 0x80) ||
-			    (u == '\r' || u == '\t')) {
-				/* Default case, just copy. */
-				*outptr++ = (char) u;
+			if (u >= 0x20 && u < 0x80) {
+				*outptr++ = (char) (u & 0xff);
 			} else {
 				if (html->flags & GMIME_FILTER_HTML_ESCAPE_8BIT)
 					*outptr++ = '?';
 				else
-					outptr += g_snprintf (outptr, 9, "&#%d;", (int) u);
+					outptr += sprintf (outptr, "&#%u;", u);
 			}
 			html->column++;
 			break;
 		}
 	}
 	
-	if (inptr < inend)
-		g_mime_filter_backup (filter, inptr, (unsigned) (inend - inptr));
+	return outptr;
+}
+
+static void
+html_convert (GMimeFilter *filter, char *in, size_t inlen, size_t prespace,
+	      char **out, size_t *outlen, size_t *outprespace, gboolean flush)
+{
+	GMimeFilterHTML *html = (GMimeFilterHTML *) filter;
+	register char *inptr, *outptr;
+	char *start, *outend;
+	const char *inend;
+	int depth;
 	
-	if (flush && html->pre_open) {
-		outptr = check_size (filter, outptr, &outend, 10);
-		outptr = g_stpcpy (outptr, "</pre>");
+	g_mime_filter_set_size (filter, inlen * 2 + 6, FALSE);
+	
+	inptr = in;
+	inend = in + inlen;
+	outptr = filter->outbuf;
+	outend = filter->outbuf + filter->outsize;
+	
+	if (html->flags & GMIME_FILTER_HTML_PRE && !html->pre_open) {
+		outptr = g_stpcpy (outptr, "<pre>");
+		html->pre_open = TRUE;
+	}
+	
+	start = inptr;
+	while (inptr < inend && *inptr != '\n')
+		inptr++;
+	
+	while (inptr < inend) {
+		html->column = 0;
+		depth = 0;
+		
+		if (html->flags & GMIME_FILTER_HTML_MARK_CITATION) {
+			if ((depth = citation_depth (start)) > 0) {
+				char font[25];
+				
+				/* FIXME: we could easily support multiple colour depths here */
+				
+				g_snprintf (font, 25, "<font color=\"#%06x\">", html->colour);
+				
+				outptr = check_size (filter, outptr, &outend, 25);
+				outptr = g_stpcpy (outptr, font);
+			} else if (*start == '>') {
+				/* >From line */
+				start++;
+			}
+		} else if (html->flags & GMIME_FILTER_HTML_CITE) {
+			outptr = check_size (filter, outptr, &outend, 6);
+			outptr = g_stpcpy (outptr, "&gt; ");
+			html->column += 2;
+		}
+		
+#define CONVERT_URLS_OR_ADDRESSES (GMIME_FILTER_HTML_CONVERT_URLS | GMIME_FILTER_HTML_CONVERT_ADDRESSES)
+		if (html->flags & CONVERT_URLS_OR_ADDRESSES) {
+			size_t matchlen, buflen, len;
+			urlmatch_t match;
+			
+			len = inptr - start;
+			
+			do {
+				if (g_url_scanner_scan (html->scanner, start, len, &match)) {
+					/* write out anything before the first regex match */
+					outptr = writeln (filter, start, start + match.um_so,
+							  outptr, &outend);
+					
+					start += match.um_so;
+					len -= match.um_so;
+					
+					matchlen = match.um_eo - match.um_so;
+					
+					buflen = 20 + strlen (match.prefix) + matchlen + matchlen;
+					outptr = check_size (filter, outptr, &outend, buflen);
+					
+					/* write out the href tag */
+					outptr = g_stpcpy (outptr, "<a href=\"");
+					outptr = g_stpcpy (outptr, match.prefix);
+					memcpy (outptr, start, matchlen);
+					outptr += matchlen;
+					outptr = g_stpcpy (outptr, "\">");
+					
+					/* now write the matched string */
+					memcpy (outptr, start, matchlen);
+					html->column += matchlen;
+					outptr += matchlen;
+					start += matchlen;
+					len -= matchlen;
+					
+					/* close the href tag */
+					outptr = g_stpcpy (outptr, "</a>");
+				} else {
+					/* nothing matched so write out the remainder of this line buffer */
+					outptr = writeln (filter, start, start + len, outptr, &outend);
+					break;
+				}
+			} while (len > 0);
+		} else {
+			outptr = writeln (filter, start, inptr, outptr, &outend);
+		}
+		
+		if ((html->flags & GMIME_FILTER_HTML_MARK_CITATION) && depth > 0) {
+			outptr = check_size (filter, outptr, &outend, 8);
+			outptr = g_stpcpy (outptr, "</font>");
+		}
+		
+		if (html->flags & GMIME_FILTER_HTML_CONVERT_NL) {
+			outptr = check_size (filter, outptr, &outend, 5);
+			outptr = g_stpcpy (outptr, "<br>");
+		}
+		
+		*outptr++ = '\n';
+		
+		start = ++inptr;
+		while (inptr < inend && *inptr != '\n')
+			inptr++;
+	}
+	
+	if (flush) {
+		/* flush the rest of our input buffer */
+		if (start < inend)
+			outptr = writeln (filter, start, inend, outptr, &outend);
+		
+		if (html->pre_open) {
+			/* close the pre-tag */
+			outptr = check_size (filter, outptr, &outend, 10);
+			outptr = g_stpcpy (outptr, "</pre>");
+		}
+	} else if (start < inend) {
+		/* backup */
+		g_mime_filter_backup (filter, start, (unsigned) (inend - start));
 	}
 	
 	*out = filter->outbuf;
@@ -505,6 +452,33 @@
 	
 	html->column = 0;
 	html->pre_open = FALSE;
-	html->saw_citation = FALSE;
-	html->coloured = FALSE;
+}
+
+
+/**
+ * g_mime_filter_html_new:
+ * @flags: html flags
+ * @colour: citation colour
+ *
+ * Creates a new GMimeFilterHTML filter which can be used to convert a
+ * plain UTF-8 text stream into an html stream.
+ *
+ * Returns a new html filter.
+ **/
+GMimeFilter *
+g_mime_filter_html_new (guint32 flags, guint32 colour)
+{
+	GMimeFilterHTML *new;
+	int i;
+	
+	new = g_object_new (GMIME_TYPE_FILTER_HTML, NULL, NULL);
+	new->flags = flags;
+	new->colour = colour;
+	
+	for (i = 0; i < NUM_URL_PATTERNS; i++) {
+		if (patterns[i].mask & flags)
+			g_url_scanner_add (new->scanner, &patterns[i].pattern);
+	}
+	
+	return (GMimeFilter *) new;
 }
Index: gmime/gmime-filter-html.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-html.h,v
retrieving revision 1.3
diff -u -r1.3 gmime-filter-html.h
--- gmime/gmime-filter-html.h	16 Oct 2002 15:31:43 -0000	1.3
+++ gmime/gmime-filter-html.h	7 Jun 2004 06:10:10 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,21 +21,31 @@
  */
 
 
-#ifndef __G_MIME_FILTER_HTML_H__
-#define __G_MIME_FILTER_HTML_H__
+#ifndef __GMIME_FILTER_HTML_H__
+#define __GMIME_FILTER_HTML_H__
+
+#include <gmime/gmime-filter.h>
 
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include "gmime-filter.h"
+#define GMIME_TYPE_FILTER_HTML            (g_mime_filter_html_get_type ())
+#define GMIME_FILTER_HTML(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER_HTML, GMimeFilterHTML))
+#define GMIME_FILTER_HTML_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_HTML, GMimeFilterHTMLClass))
+#define GMIME_IS_FILTER_HTML(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER_HTML))
+#define GMIME_IS_FILTER_HTML_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_HTML))
+#define GMIME_FILTER_HTML_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER_HTML, GMimeFilterHTMLClass))
+
+typedef struct _GMimeFilterHTML GMimeFilterHTML;
+typedef struct _GMimeFilterHTMLClass GMimeFilterHTMLClass;
 
 
 /**
  * GMIME_FILTER_HTML_PRE:
  *
- * Wrap stream in <pre> tags.
+ * Wrap stream in &lt;pre&gt; tags.
  **/
 #define GMIME_FILTER_HTML_PRE               (1 << 0)
 
@@ -43,7 +53,7 @@
 /**
  * GMIME_FILTER_HTML_CONVERT_NL:
  *
- * Convert new-lines ('\n') into <br> tags.
+ * Convert new-lines ('\n') into &lt;br&gt; tags.
  **/
 #define GMIME_FILTER_HTML_CONVERT_NL        (1 << 1)
 
@@ -60,7 +70,7 @@
 /**
  * GMIME_FILTER_HTML_CONVERT_URLS:
  *
- * Wrap detected URLs in <a href=...> tags.
+ * Wrap detected URLs in &lt;a href=...&gt; tags.
  **/
 #define GMIME_FILTER_HTML_CONVERT_URLS      (1 << 3)
 
@@ -92,26 +102,32 @@
 /**
  * GMIME_FILTER_HTML_CITE:
  *
- * Cites text by prepending "> " to each cited line.
+ * Cites text by prepending "&gt; " to each cited line.
  **/
 #define GMIME_FILTER_HTML_CITE              (1 << 7)
 
 
-typedef struct _GMimeFilterHTML {
-	GMimeFilter parent;
+struct _GMimeFilterHTML {
+	GMimeFilter parent_object;
+	
+	struct _GUrlScanner *scanner;
 	
 	guint32 flags;
 	guint32 colour;
 	
-	guint32 column       : 29;
+	guint32 column       : 31;
 	guint32 pre_open     : 1;
-	guint32 saw_citation : 1;
-	guint32 coloured     : 1;
-} GMimeFilterHTML;
+};
+
+struct _GMimeFilterHTMLClass {
+	GMimeFilterClass parent_class;
+	
+};
 
-GMimeFilter *g_mime_filter_html_new (guint32 flags, guint32 colour);
 
-char * url_extract (const char **in, int inlen, gboolean check, gboolean *backup);
+GType g_mime_filter_html_get_type (void);
+
+GMimeFilter *g_mime_filter_html_new (guint32 flags, guint32 colour);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-filter-md5.c
===================================================================
RCS file: gmime/gmime-filter-md5.c
diff -N gmime/gmime-filter-md5.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-filter-md5.c	7 Jun 2004 06:10:11 -0000
@@ -0,0 +1,173 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "md5-utils.h"
+
+#include "gmime-filter-md5.h"
+
+struct _GMimeFilterMd5Private {
+	MD5Context md5;
+};
+
+static void g_mime_filter_md5_class_init (GMimeFilterMd5Class *klass);
+static void g_mime_filter_md5_init (GMimeFilterMd5 *filter, GMimeFilterMd5Class *klass);
+static void g_mime_filter_md5_finalize (GObject *object);
+
+static GMimeFilter *filter_copy (GMimeFilter *filter);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
+static void filter_reset (GMimeFilter *filter);
+
+
+static GMimeFilterClass *parent_class = NULL;
+
+
+GType
+g_mime_filter_md5_get_type (void)
+{
+	static GType type = 0;
+	
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterMd5Class),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_md5_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilterMd5),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_md5_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterMd5", &info, 0);
+	}
+	
+	return type;
+}
+
+
+static void
+g_mime_filter_md5_class_init (GMimeFilterMd5Class *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
+	
+	parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
+	
+	object_class->finalize = g_mime_filter_md5_finalize;
+	
+	filter_class->copy = filter_copy;
+	filter_class->filter = filter_filter;
+	filter_class->complete = filter_complete;
+	filter_class->reset = filter_reset;
+}
+
+static void
+g_mime_filter_md5_init (GMimeFilterMd5 *filter, GMimeFilterMd5Class *klass)
+{
+	filter->priv = g_new (struct _GMimeFilterMd5Private, 1);
+	md5_init (&filter->priv->md5);
+}
+
+static void
+g_mime_filter_md5_finalize (GObject *object)
+{
+	GMimeFilterMd5 *filter = (GMimeFilterMd5 *) object;
+	
+	g_free (filter->priv);
+	
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static GMimeFilter *
+filter_copy (GMimeFilter *filter)
+{
+	return g_mime_filter_md5_new ();
+}
+
+
+static void
+filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+	       char **out, size_t *outlen, size_t *outprespace)
+{
+	GMimeFilterMd5 *md5 = (GMimeFilterMd5 *) filter;
+	
+	md5_update (&md5->priv->md5, in, len);
+	
+	*out = in;
+	*outlen = len;
+	*outprespace = prespace;
+}
+
+static void
+filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+		 char **out, size_t *outlen, size_t *outprespace)
+{
+	filter_filter (filter, in, len, prespace, out, outlen, outprespace);
+}
+
+static void
+filter_reset (GMimeFilter *filter)
+{
+	GMimeFilterMd5 *md5 = (GMimeFilterMd5 *) filter;
+	
+	md5_init (&md5->priv->md5);
+}
+
+
+/**
+ * g_mime_filter_md5_new:
+ *
+ * Creates a new Md5 filter.
+ *
+ * Returns a new Md5 filter.
+ **/
+GMimeFilter *
+g_mime_filter_md5_new (void)
+{
+	return (GMimeFilter *) g_object_new (GMIME_TYPE_FILTER_MD5, NULL);
+}
+
+
+/**
+ * g_mime_filter_md5_get_digest:
+ * @md5: md5 filter object
+ * @digest: output buffer of at least 16 bytes
+ *
+ * Outputs the md5 digest into @digest.
+ **/
+void
+g_mime_filter_md5_get_digest (GMimeFilterMd5 *md5, unsigned char digest[16])
+{
+	g_return_if_fail (GMIME_IS_FILTER_MD5 (md5));
+	
+	md5_final (&md5->priv->md5, digest);
+}
Index: gmime/gmime-filter-md5.h
===================================================================
RCS file: gmime/gmime-filter-md5.h
diff -N gmime/gmime-filter-md5.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-filter-md5.h	7 Jun 2004 06:10:11 -0000
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __GMIME_FILTER_MD5_H__
+#define __GMIME_FILTER_MD5_H__
+
+#include <gmime/gmime-filter.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define GMIME_TYPE_FILTER_MD5            (g_mime_filter_md5_get_type ())
+#define GMIME_FILTER_MD5(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER_MD5, GMimeFilterMd5))
+#define GMIME_FILTER_MD5_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_MD5, GMimeFilterMd5Class))
+#define GMIME_IS_FILTER_MD5(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER_MD5))
+#define GMIME_IS_FILTER_MD5_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_MD5))
+#define GMIME_FILTER_MD5_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER_MD5, GMimeFilterMd5Class))
+
+typedef struct _GMimeFilterMd5 GMimeFilterMd5;
+typedef struct _GMimeFilterMd5Class GMimeFilterMd5Class;
+
+struct _GMimeFilterMd5 {
+	GMimeFilter parent_object;
+	
+	struct _GMimeFilterMd5Private *priv;
+};
+
+struct _GMimeFilterMd5Class {
+	GMimeFilterClass parent_class;
+	
+};
+
+
+GType g_mime_filter_md5_get_type (void);
+
+GMimeFilter *g_mime_filter_md5_new (void);
+
+void g_mime_filter_md5_get_digest (GMimeFilterMd5 *md5, unsigned char digest[16]);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GMIME_FILTER_MD5_H__ */
Index: gmime/gmime-filter-yenc.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-yenc.c,v
retrieving revision 1.7
diff -u -r1.7 gmime-filter-yenc.c
--- gmime/gmime-filter-yenc.c	30 Dec 2002 16:37:57 -0000	1.7
+++ gmime/gmime-filter-yenc.c	7 Jun 2004 06:10:11 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -28,162 +28,77 @@
 
 #include "gmime-filter-yenc.h"
 
-#include <glib/gmem.h>
-#include <glib/gmessages.h>
+static void g_mime_filter_yenc_class_init (GMimeFilterYencClass *klass);
+static void g_mime_filter_yenc_init (GMimeFilterYenc *filter, GMimeFilterYencClass *klass);
+static void g_mime_filter_yenc_finalize (GObject *object);
 
-static void filter_destroy (GMimeFilter *filter);
 static GMimeFilter *filter_copy (GMimeFilter *filter);
-static void filter_filter (GMimeFilter *filter, char *in, size_t len, 
-			   size_t prespace, char **out, 
-			   size_t *outlen, size_t *outprespace);
-static void filter_complete (GMimeFilter *filter, char *in, size_t len, 
-			     size_t prespace, char **out, 
-			     size_t *outlen, size_t *outprespace);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
 static void filter_reset (GMimeFilter *filter);
 
-static GMimeFilter filter_template = {
-	NULL, NULL, NULL, NULL,
-	0, 0, NULL, 0, 0,
-	filter_destroy,
-	filter_copy,
-	filter_filter,
-	filter_complete,
-	filter_reset,
-};
 
+static GMimeFilterClass *parent_class = NULL;
 
-/**
- * g_mime_filter_yenc_new:
- * @direction: encode direction
- *
- * Creates a new yEnc filter.
- *
- * Returns a new yEnc filter.
- **/
-GMimeFilter *
-g_mime_filter_yenc_new (GMimeFilterYencDirection direction)
+
+GType
+g_mime_filter_yenc_get_type (void)
 {
-	GMimeFilterYenc *new;
-	
-	new = g_new (GMimeFilterYenc, 1);
-	
-	new->direction = direction;
-	new->part = 0;
-	new->pcrc = GMIME_YENCODE_CRC_INIT;
-	new->crc = GMIME_YENCODE_CRC_INIT;
+	static GType type = 0;
 	
-	switch (direction) {
-	case GMIME_FILTER_YENC_DIRECTION_ENCODE:
-		new->state = GMIME_YENCODE_STATE_INIT;
-		break;
-	case GMIME_FILTER_YENC_DIRECTION_DECODE:
-		new->state = GMIME_YDECODE_STATE_INIT;
-		break;
-	default:
-		g_assert_not_reached ();
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterYencClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_yenc_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilterYenc),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_yenc_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterYenc", &info, 0);
 	}
 	
-	g_mime_filter_construct (GMIME_FILTER (new), &filter_template);
-	
-	return GMIME_FILTER (new);
+	return type;
 }
 
 
-/**
- * g_mime_filter_yenc_set_state:
- * @yenc: yEnc filter
- * @state: encode/decode state
- *
- * Sets the current state of the yencoder/ydecoder
- **/
-void
-g_mime_filter_yenc_set_state (GMimeFilterYenc *yenc, int state)
-{
-	g_return_if_fail (yenc != NULL);
-	
-	yenc->state = state;
-}
-
-
-/**
- * g_mime_filter_yenc_set_crc:
- * @yenc: yEnc filter
- * @crc: crc32
- *
- * Sets the current crc32 value on the yEnc filter @yenc to @crc.
- **/
-void
-g_mime_filter_yenc_set_crc (GMimeFilterYenc *yenc, guint32 crc)
-{
-	g_return_if_fail (yenc != NULL);
-	
-	yenc->crc = crc;
-}
-
-
-#if 0
-/* FIXME: once we parse out the yenc part id, we can re-enable this interface */
-/**
- * g_mime_filter_yenc_get_part:
- * @yenc: yEnc filter
- *
- * Gets the part id of the current decoded yEnc stream or -1 on fail.
- *
- * Returns the part id of the current decoded yEnc stream or -1 on
- * fail.
- **/
-int
-g_mime_filter_yenc_get_part (GMimeFilterYenc *yenc)
+static void
+g_mime_filter_yenc_class_init (GMimeFilterYencClass *klass)
 {
-	g_return_val_if_fail (yenc != NULL, -1);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
 	
-	if (yenc->state & GMIME_YDECODE_STATE_PART)
-		return yenc->part;
+	parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
 	
-	return -1;
-}
-#endif
-
-/**
- * g_mime_filter_yenc_get_pcrc:
- * @yenc: yEnc filter
- *
- * Get the computed part crc or (guint32) -1 on fail.
- *
- * Returns the computed part crc or (guint32) -1 on fail.
- **/
-guint32
-g_mime_filter_yenc_get_pcrc (GMimeFilterYenc *yenc)
-{
-	g_return_val_if_fail (yenc != NULL, -1);
+	object_class->finalize = g_mime_filter_yenc_finalize;
 	
-	return GMIME_YENCODE_CRC_FINAL (yenc->pcrc);
+	filter_class->copy = filter_copy;
+	filter_class->filter = filter_filter;
+	filter_class->complete = filter_complete;
+	filter_class->reset = filter_reset;
 }
 
-
-/**
- * g_mime_filter_yenc_get_crc:
- * @yenc: yEnc filter
- *
- * Get the computed crc or (guint32) -1 on fail.
- *
- * Returns the computed crc or (guint32) -1 on fail.
- **/
-guint32
-g_mime_filter_yenc_get_crc (GMimeFilterYenc *yenc)
+static void
+g_mime_filter_yenc_init (GMimeFilterYenc *filter, GMimeFilterYencClass *klass)
 {
-	g_return_val_if_fail (yenc != NULL, -1);
-	
-	return GMIME_YENCODE_CRC_FINAL (yenc->crc);
+	filter->part = 0;
+	filter->pcrc = GMIME_YENCODE_CRC_INIT;
+	filter->crc = GMIME_YENCODE_CRC_INIT;
 }
 
-
 static void
-filter_destroy (GMimeFilter *filter)
+g_mime_filter_yenc_finalize (GObject *object)
 {
-	g_free (filter);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
 static GMimeFilter *
 filter_copy (GMimeFilter *filter)
 {
@@ -241,7 +156,8 @@
 					}
 				
 					/* go to the next line */
-					for ( ; inptr < inend && *inptr != '\n'; inptr++);
+					while (inptr < inend && *inptr != '\n')
+						inptr++;
 					
 					if (inptr < inend)
 						inptr++;
@@ -336,6 +252,126 @@
 	}
 	yenc->pcrc = GMIME_YENCODE_CRC_INIT;
 	yenc->crc = GMIME_YENCODE_CRC_INIT;
+}
+
+
+/**
+ * g_mime_filter_yenc_new:
+ * @direction: encode direction
+ *
+ * Creates a new yEnc filter.
+ *
+ * Returns a new yEnc filter.
+ **/
+GMimeFilter *
+g_mime_filter_yenc_new (GMimeFilterYencDirection direction)
+{
+	GMimeFilterYenc *new;
+	
+	new = g_object_new (GMIME_TYPE_FILTER_YENC, NULL, NULL);
+	new->direction = direction;
+	
+	switch (direction) {
+	case GMIME_FILTER_YENC_DIRECTION_ENCODE:
+		new->state = GMIME_YENCODE_STATE_INIT;
+		break;
+	case GMIME_FILTER_YENC_DIRECTION_DECODE:
+		new->state = GMIME_YDECODE_STATE_INIT;
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	
+	return (GMimeFilter *) new;
+}
+
+
+/**
+ * g_mime_filter_yenc_set_state:
+ * @yenc: yEnc filter
+ * @state: encode/decode state
+ *
+ * Sets the current state of the yencoder/ydecoder
+ **/
+void
+g_mime_filter_yenc_set_state (GMimeFilterYenc *yenc, int state)
+{
+	g_return_if_fail (GMIME_IS_FILTER_YENC (yenc));
+	
+	yenc->state = state;
+}
+
+
+/**
+ * g_mime_filter_yenc_set_crc:
+ * @yenc: yEnc filter
+ * @crc: crc32
+ *
+ * Sets the current crc32 value on the yEnc filter @yenc to @crc.
+ **/
+void
+g_mime_filter_yenc_set_crc (GMimeFilterYenc *yenc, guint32 crc)
+{
+	g_return_if_fail (GMIME_IS_FILTER_YENC (yenc));
+	
+	yenc->crc = crc;
+}
+
+
+#if 0
+/* FIXME: once we parse out the yenc part id, we can re-enable this interface */
+/**
+ * g_mime_filter_yenc_get_part:
+ * @yenc: yEnc filter
+ *
+ * Gets the part id of the current decoded yEnc stream or -1 on fail.
+ *
+ * Returns the part id of the current decoded yEnc stream or -1 on
+ * fail.
+ **/
+int
+g_mime_filter_yenc_get_part (GMimeFilterYenc *yenc)
+{
+	g_return_val_if_fail (GMIME_IS_FILTER_YENC (yenc), -1);
+	
+	if (yenc->state & GMIME_YDECODE_STATE_PART)
+		return yenc->part;
+	
+	return -1;
+}
+#endif
+
+/**
+ * g_mime_filter_yenc_get_pcrc:
+ * @yenc: yEnc filter
+ *
+ * Get the computed part crc or (guint32) -1 on fail.
+ *
+ * Returns the computed part crc or (guint32) -1 on fail.
+ **/
+guint32
+g_mime_filter_yenc_get_pcrc (GMimeFilterYenc *yenc)
+{
+	g_return_val_if_fail (GMIME_IS_FILTER_YENC (yenc), -1);
+	
+	return GMIME_YENCODE_CRC_FINAL (yenc->pcrc);
+}
+
+
+/**
+ * g_mime_filter_yenc_get_crc:
+ * @yenc: yEnc filter
+ *
+ * Get the computed crc or (guint32) -1 on fail.
+ *
+ * Returns the computed crc or (guint32) -1 on fail.
+ **/
+guint32
+g_mime_filter_yenc_get_crc (GMimeFilterYenc *yenc)
+{
+	g_return_val_if_fail (GMIME_IS_FILTER_YENC (yenc), -1);
+	
+	return GMIME_YENCODE_CRC_FINAL (yenc->crc);
 }
 
 
Index: gmime/gmime-filter-yenc.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter-yenc.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-filter-yenc.h
--- gmime/gmime-filter-yenc.h	14 May 2002 03:24:12 -0000	1.4
+++ gmime/gmime-filter-yenc.h	7 Jun 2004 06:10:11 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,19 +21,29 @@
  */
 
 
-#ifndef __G_MIME_FILTER_YENC_H__
-#define __G_MIME_FILTER_YENC_H__
+#ifndef __GMIME_FILTER_YENC_H__
+#define __GMIME_FILTER_YENC_H__
+
+#include <gmime/gmime-filter.h>
 
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include "gmime-filter.h"
+#define GMIME_TYPE_FILTER_YENC            (g_mime_filter_yenc_get_type ())
+#define GMIME_FILTER_YENC(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER_YENC, GMimeFilterYenc))
+#define GMIME_FILTER_YENC_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_YENC, GMimeFilterYencClass))
+#define GMIME_IS_FILTER_YENC(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER_YENC))
+#define GMIME_IS_FILTER_YENC_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_YENC))
+#define GMIME_FILTER_YENC_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER_YENC, GMimeFilterYencClass))
+
+typedef struct _GMimeFilterYenc GMimeFilterYenc;
+typedef struct _GMimeFilterYencClass GMimeFilterYencClass;
 
 typedef enum {
 	GMIME_FILTER_YENC_DIRECTION_ENCODE,
-	GMIME_FILTER_YENC_DIRECTION_DECODE,
+	GMIME_FILTER_YENC_DIRECTION_DECODE
 } GMimeFilterYencDirection;
 
 #define GMIME_YDECODE_STATE_INIT     (0)
@@ -55,8 +65,8 @@
 #define GMIME_YENCODE_CRC_INIT       (~0)
 #define GMIME_YENCODE_CRC_FINAL(crc) (~crc)
 
-typedef struct _GMimeFilterYenc {
-	GMimeFilter parent;
+struct _GMimeFilterYenc {
+	GMimeFilter parent_object;
 	
 	GMimeFilterYencDirection direction;
 	
@@ -65,7 +75,15 @@
 	int state;
 	guint32 pcrc;
 	guint32 crc;
-} GMimeFilterYenc;
+};
+
+struct _GMimeFilterYencClass {
+	GMimeFilterClass parent_class;
+	
+};
+
+
+GType g_mime_filter_yenc_get_type (void);
 
 GMimeFilter *g_mime_filter_yenc_new (GMimeFilterYencDirection direction);
 
Index: gmime/gmime-filter.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter.c,v
retrieving revision 1.6
diff -u -r1.6 gmime-filter.c
--- gmime/gmime-filter.c	30 Dec 2002 16:37:57 -0000	1.6
+++ gmime/gmime-filter.c	7 Jun 2004 06:10:12 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -26,10 +26,9 @@
 #endif
 
 #include <string.h> /* for memcpy */
+
 #include "gmime-filter.h"
 
-#include <glib/gmem.h>
-#include <glib/gmessages.h>
 
 struct _GMimeFilterPrivate {
 	char *inbuf;
@@ -40,21 +39,64 @@
 #define BACK_HEAD (64)
 #define _PRIVATE(o) (((GMimeFilter *)(o))->priv)
 
+static void g_mime_filter_class_init (GMimeFilterClass *klass);
+static void g_mime_filter_init (GMimeFilter *filter, GMimeFilterClass *klass);
+static void g_mime_filter_finalize (GObject *object);
 
+static GMimeFilter *filter_copy (GMimeFilter *filter);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			     char **out, size_t *outlen, size_t *outprespace);
+static void filter_reset (GMimeFilter *filter);
 
-/**
- * g_mime_filter_construct:
- * @filter: filter
- * @filter_template: filter template
- *
- * Initializes a filter object using the virtual methods in @filter_template.
- **/
-void
-g_mime_filter_construct (GMimeFilter *filter, GMimeFilter *filter_template)
+
+static GObjectClass *parent_class = NULL;
+
+
+GType
+g_mime_filter_get_type (void)
+{
+	static GType type = 0;
+	
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeFilterClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_filter_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeFilter),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) g_mime_filter_init,
+		};
+		
+		type = g_type_register_static (G_TYPE_OBJECT, "GMimeFilter", &info, 0);
+	}
+	
+	return type;
+}
+
+
+static void
+g_mime_filter_class_init (GMimeFilterClass *klass)
 {
-	g_return_if_fail (filter != NULL);
-	g_return_if_fail (filter_template != NULL);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	
+	parent_class = g_type_class_ref (G_TYPE_OBJECT);
+	
+	object_class->finalize = g_mime_filter_finalize;
 	
+	klass->copy = filter_copy;
+	klass->filter = filter_filter;
+	klass->complete = filter_complete;
+	klass->reset = filter_reset;
+}
+
+static void
+g_mime_filter_init (GMimeFilter *filter, GMimeFilterClass *klass)
+{
 	filter->priv = g_new0 (struct _GMimeFilterPrivate, 1);
 	filter->outptr = NULL;
 	filter->outreal = NULL;
@@ -64,31 +106,26 @@
 	filter->backbuf = NULL;
 	filter->backsize = 0;
 	filter->backlen = 0;
+}
+
+static void
+g_mime_filter_finalize (GObject *object)
+{
+	GMimeFilter *filter = (GMimeFilter *) object;
 	
-	filter->destroy = filter_template->destroy;
-	filter->copy = filter_template->copy;
-	filter->filter = filter_template->filter;
-	filter->complete = filter_template->complete;
-	filter->reset = filter_template->reset;
+	g_free (filter->priv->inbuf);
+	g_free (filter->priv);
+	g_free (filter->outreal);
+	g_free (filter->backbuf);
+	
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 
-/**
- * g_mime_filter_destroy:
- * @filter: filter
- *
- * Destroys @filter and releases the memory to the system.
- **/
-void
-g_mime_filter_destroy (GMimeFilter *filter)
+static GMimeFilter *
+filter_copy (GMimeFilter *filter)
 {
-	if (filter) {
-		g_free (filter->priv->inbuf);
-		g_free (filter->priv);
-		g_free (filter->outreal);
-		g_free (filter->backbuf);
-		filter->destroy (filter);
-	}
+	return NULL;
 }
 
 
@@ -103,7 +140,9 @@
 GMimeFilter *
 g_mime_filter_copy (GMimeFilter *filter)
 {
-	return filter->copy (filter);
+	g_return_val_if_fail (GMIME_IS_FILTER (filter), NULL);
+	
+	return GMIME_FILTER_GET_CLASS (filter)->copy (filter);
 }
 
 
@@ -114,10 +153,8 @@
 				char *in, size_t len, size_t prespace,
 				char **out, size_t *outlen, size_t *outprespace))
 {
-	/*
-	  here we take a performance hit, if the input buffer doesn't
-	  have the pre-space required.  We make a buffer that does ...
-	*/
+	/* here we take a performance hit, if the input buffer doesn't
+	   have the pre-space required.  We make a buffer that does... */
 	if (prespace < filter->backlen) {
 		struct _GMimeFilterPrivate *p = _PRIVATE (filter);
 		size_t newlen = len + prespace + filter->backlen;
@@ -148,6 +185,14 @@
 }
 
 
+static void
+filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+	       char **out, size_t *outlen, size_t *outprespace)
+{
+	/* no-op */
+}
+
+
 /**
  * g_mime_filter_filter:
  * @filter: filter
@@ -161,13 +206,21 @@
  * Filters the input data and writes it to @out.
  **/
 void
-g_mime_filter_filter (GMimeFilter *filter,
-		      char *in, size_t len, size_t prespace,
+g_mime_filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
 		      char **out, size_t *outlen, size_t *outprespace)
 {
-	g_return_if_fail (filter != NULL);
+	g_return_if_fail (GMIME_IS_FILTER (filter));
 	
-	filter_run (filter, in, len, prespace, out, outlen, outprespace, filter->filter);
+	filter_run (filter, in, len, prespace, out, outlen, outprespace,
+		    GMIME_FILTER_GET_CLASS (filter)->filter);
+}
+
+
+static void
+filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+		 char **out, size_t *outlen, size_t *outprespace)
+{
+	/* no-op */
 }
 
 
@@ -188,9 +241,17 @@
 			char *in, size_t len, size_t prespace,
 			char **out, size_t *outlen, size_t *outprespace)
 {
-	g_return_if_fail (filter != NULL);
+	g_return_if_fail (GMIME_IS_FILTER (filter));
 	
-	filter_run (filter, in, len, prespace, out, outlen, outprespace, filter->complete);
+	filter_run (filter, in, len, prespace, out, outlen, outprespace,
+		    GMIME_FILTER_GET_CLASS (filter)->complete);
+}
+
+
+static void
+filter_reset (GMimeFilter *filter)
+{
+	/* no-op */
 }
 
 
@@ -203,9 +264,9 @@
 void
 g_mime_filter_reset (GMimeFilter *filter)
 {
-	g_return_if_fail (filter != NULL);
+	g_return_if_fail (GMIME_IS_FILTER (filter));
 	
-	filter->reset (filter);
+	GMIME_FILTER_GET_CLASS (filter)->reset (filter);
 	
 	/* could free some buffers, if they are really big? */
 	filter->backlen = 0;
@@ -224,7 +285,7 @@
 void
 g_mime_filter_backup (GMimeFilter *filter, const char *data, size_t length)
 {
-	g_return_if_fail (filter != NULL);
+	g_return_if_fail (GMIME_IS_FILTER (filter));
 	
 	if (filter->backsize < length) {
 		/* g_realloc copies data, unnecessary overhead */
@@ -249,7 +310,7 @@
 void
 g_mime_filter_set_size (GMimeFilter *filter, size_t size, gboolean keep)
 {
-	g_return_if_fail (filter != NULL);
+	g_return_if_fail (GMIME_IS_FILTER (filter));
 	
 	if (filter->outsize < size) {
 		size_t offset = filter->outptr - filter->outreal;
Index: gmime/gmime-filter.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-filter.h,v
retrieving revision 1.5
diff -u -r1.5 gmime-filter.h
--- gmime/gmime-filter.h	30 Dec 2002 16:37:57 -0000	1.5
+++ gmime/gmime-filter.h	7 Jun 2004 06:10:12 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,17 +24,30 @@
 #ifndef __GMIME_FILTER_H__
 #define __GMIME_FILTER_H__
 
+#include <glib.h>
+#include <glib-object.h>
+#include <sys/types.h>
+
+#include <gmime/gmime-type-utils.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include <sys/types.h>
+#define GMIME_TYPE_FILTER            (g_mime_filter_get_type ())
+#define GMIME_FILTER(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_FILTER, GMimeFilter))
+#define GMIME_FILTER_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER, GMimeFilterClass))
+#define GMIME_IS_FILTER(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_FILTER))
+#define GMIME_IS_FILTER_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER))
+#define GMIME_FILTER_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_FILTER, GMimeFilterClass))
 
 typedef struct _GMimeFilter GMimeFilter;
+typedef struct _GMimeFilterClass GMimeFilterClass;
 
 struct _GMimeFilter {
+	GObject parent_object;
+	
 	struct _GMimeFilterPrivate *priv;
 	
 	char *outreal;		/* real malloc'd buffer */
@@ -46,28 +59,26 @@
 	char *backbuf;
 	size_t backsize;
 	size_t backlen;		/* significant data there */
+};
+
+struct _GMimeFilterClass {
+	GObjectClass parent_class;
 	
 	/* virtual functions */
-	void (*destroy)  (GMimeFilter *filter);
-	
-	GMimeFilter *(*copy) (GMimeFilter *filter);
+	GMimeFilter * (* copy) (GMimeFilter *filter);
 	
-	void (*filter)   (GMimeFilter *filter,
-			  char *in, size_t len, size_t prespace,
-			  char **out, size_t *outlen, size_t *outprespace);
+	void (* filter)   (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
 	
-	void (*complete) (GMimeFilter *filter,
-			  char *in, size_t len, size_t prespace,
-			  char **out, size_t *outlen, size_t *outprespace);
+	void (* complete) (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+			   char **out, size_t *outlen, size_t *outprespace);
 	
-	void (*reset)    (GMimeFilter *filter);
+	void (* reset)    (GMimeFilter *filter);
 };
 
-#define GMIME_FILTER(filter) ((GMimeFilter *) filter)
 
-void g_mime_filter_construct (GMimeFilter *filter, GMimeFilter *filter_template);
+GType g_mime_filter_get_type (void);
 
-void g_mime_filter_destroy (GMimeFilter *filter);
 
 GMimeFilter *g_mime_filter_copy (GMimeFilter *filter);
 
@@ -80,6 +91,7 @@
 			     char **out, size_t *outlen, size_t *outprespace);
 
 void g_mime_filter_reset (GMimeFilter *filter);
+
 
 /* sets/returns number of bytes backed up on the input */
 void g_mime_filter_backup (GMimeFilter *filter, const char *data, size_t length);
Index: gmime/gmime-header.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-header.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-header.c
--- gmime/gmime-header.c	30 Mar 2003 11:14:22 -0000	1.8
+++ gmime/gmime-header.c	7 Jun 2004 06:10:12 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -39,9 +39,8 @@
 };
 
 struct _GMimeHeader {
-	GMimeHeaderWriteFunc default_write_func;
 	GHashTable *hash;
-	GHashTable *write_hash;
+	GHashTable *writers;
 	struct raw_header *headers;
 };
 
@@ -49,7 +48,7 @@
 static int
 header_equal (gconstpointer v, gconstpointer v2)
 {
-	return g_strcasecmp ((const char *) v, (const char *) v2) == 0;
+	return strcasecmp ((const char *) v, (const char *) v2) == 0;
 }
 
 static guint
@@ -65,18 +64,6 @@
 	return h;
 }
 
-static gssize
-write_default (GMimeStream *stream, const char *name, const char *value)
-{
-	gssize nwritten;
-	char *val;
-	
-	val = g_mime_utils_header_printf ("%s: %s\n", name, value);
-	nwritten = g_mime_stream_write_string (stream, val);
-	g_free (val);
-	
-	return nwritten;
-}
 
 /**
  * g_mime_header_new:
@@ -91,9 +78,8 @@
 	GMimeHeader *new;
 	
 	new = g_new (GMimeHeader, 1);
-	new->default_write_func = write_default;
 	new->hash = g_hash_table_new (header_hash, header_equal);
-	new->write_hash = g_hash_table_new (header_hash, header_equal);
+	new->writers = g_hash_table_new (header_hash, header_equal);
 	new->headers = NULL;
 	
 	return new;
@@ -129,8 +115,8 @@
 		}
 		
 		g_hash_table_destroy (header->hash);
-		g_hash_table_foreach (header->write_hash, writer_free, NULL);
-		g_hash_table_destroy (header->write_hash);
+		g_hash_table_foreach (header->writers, writer_free, NULL);
+		g_hash_table_destroy (header->writers);
 		g_free (header);
 	}
 }
@@ -163,7 +149,7 @@
 		n->next = NULL;
 		n->name = g_strdup (name);
 		n->value = g_strdup (value);
-	
+		
 		h = header->headers;
 		while (h && h->next)
 			h = h->next;
@@ -272,6 +258,18 @@
 }
 
 
+static ssize_t
+write_default (GMimeStream *stream, const char *name, const char *value)
+{
+	ssize_t nwritten;
+	char *val;
+	
+	val = g_mime_utils_header_printf ("%s: %s\n", name, value);
+	nwritten = g_mime_stream_write_string (stream, val);
+	g_free (val);
+	
+	return nwritten;
+}
 
 
 /**
@@ -283,11 +281,11 @@
  *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_header_write_to_stream (const GMimeHeader *header, GMimeStream *stream)
 {
-	GMimeHeaderWriteFunc header_write;
-	gssize nwritten, total = 0;
+	GMimeHeaderWriter header_write;
+	ssize_t nwritten, total = 0;
 	struct raw_header *h;
 	
 	g_return_val_if_fail (header != NULL, -1);
@@ -296,11 +294,11 @@
 	h = header->headers;
 	while (h) {
 		if (h->value) {
-			header_write = g_hash_table_lookup (header->write_hash, h->name);
+			header_write = g_hash_table_lookup (header->writers, h->name);
 			if (header_write)
 				nwritten = (*header_write) (stream, h->name, h->value);
 			else
-				nwritten = (header->default_write_func)(stream, h->name, h->value);
+				nwritten = write_default (stream, h->name, h->value);
 			
 			if (nwritten == -1)
 				return -1;
@@ -337,7 +335,8 @@
 	stream = g_mime_stream_mem_new ();
 	g_mime_stream_mem_set_byte_array (GMIME_STREAM_MEM (stream), array);
 	g_mime_header_write_to_stream (header, stream);
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
+	
 	g_byte_array_append (array, "", 1);
 	str = array->data;
 	g_byte_array_free (array, FALSE);
@@ -369,36 +368,29 @@
 
 
 /**
- * g_mime_header_set_write_func:
+ * g_mime_header_register_writer:
  * @header: header object
  * @name: header name
- * @func: writer function
+ * @writer: writer function
  *
- * Changes the function used to write @name headers to @func. This is
- * useful if you want to change the default header folding style for a
- * particular header.
+ * Changes the function used to write @name headers to @writer (or the
+ * default if @writer is %NULL). This is useful if you want to change
+ * the default header folding style for a particular header.
  **/
 void
-g_mime_header_set_write_func (GMimeHeader *header, const char *name, GMimeHeaderWriteFunc func)
+g_mime_header_register_writer (GMimeHeader *header, const char *name, GMimeHeaderWriter writer)
 {
 	gpointer okey, oval;
 	
 	g_return_if_fail (header != NULL);
 	g_return_if_fail (name != NULL);
 	
-	if (g_hash_table_lookup (header->write_hash, name)) {
-		g_hash_table_lookup_extended (header->write_hash, name, &okey, &oval);
-		g_hash_table_remove (header->write_hash, name);
+	if (g_hash_table_lookup (header->writers, name)) {
+		g_hash_table_lookup_extended (header->writers, name, &okey, &oval);
+		g_hash_table_remove (header->writers, name);
 		g_free (okey);
 	}
 	
-	g_hash_table_insert (header->write_hash, g_strdup (name), func);
-}
-
-void
-g_mime_header_set_default_write_func (GMimeHeader * header, GMimeHeaderWriteFunc func)
-{
-	g_return_if_fail (header != NULL);
-
-	header->default_write_func = func;
+	if (writer)
+		g_hash_table_insert (header->writers, g_strdup (name), writer);
 }
Index: gmime/gmime-header.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-header.h,v
retrieving revision 1.8
diff -u -r1.8 gmime-header.h
--- gmime/gmime-header.h	30 Dec 2002 16:37:57 -0000	1.8
+++ gmime/gmime-header.h	7 Jun 2004 06:10:12 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -23,18 +23,18 @@
 #ifndef __GMIME_HEADER_H__
 #define __GMIME_HEADER_H__
 
+#include <glib.h>
+#include <gmime/gmime-stream.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include "gmime-stream.h"
-
 typedef struct _GMimeHeader GMimeHeader;
 
-typedef void (*GMimeHeaderForeachFunc) (const char *name, const char *value, gpointer data);
-typedef gssize (*GMimeHeaderWriteFunc) (GMimeStream *stream, const char *name, const char *value);
+typedef void (*GMimeHeaderForeachFunc) (const char *name, const char *value, gpointer user_data);
+typedef ssize_t (*GMimeHeaderWriter) (GMimeStream *stream, const char *name, const char *value);
 
 GMimeHeader *g_mime_header_new (void);
 
@@ -48,15 +48,13 @@
 
 void g_mime_header_remove (GMimeHeader *header, const char *name);
 
-gssize g_mime_header_write_to_stream (const GMimeHeader *header, GMimeStream *stream);
+ssize_t g_mime_header_write_to_stream (const GMimeHeader *header, GMimeStream *stream);
 
 char *g_mime_header_to_string (const GMimeHeader *header);
 
-void g_mime_header_foreach (const GMimeHeader *header, GMimeHeaderForeachFunc func, gpointer data);
-
-void g_mime_header_set_write_func (GMimeHeader *header, const char *name, GMimeHeaderWriteFunc func);
+void g_mime_header_foreach (const GMimeHeader *header, GMimeHeaderForeachFunc func, gpointer user_data);
 
-void g_mime_header_set_default_write_func (GMimeHeader *header, GMimeHeaderWriteFunc func);
+void g_mime_header_register_writer (GMimeHeader *header, const char *name, GMimeHeaderWriter writer);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-iconv-utils.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-iconv-utils.c,v
retrieving revision 1.9
diff -u -r1.9 gmime-iconv-utils.c
--- gmime/gmime-iconv-utils.c	2 Sep 2002 13:58:05 -0000	1.9
+++ gmime/gmime-iconv-utils.c	7 Jun 2004 06:10:12 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -33,6 +33,12 @@
 #include "gmime-iconv-utils.h"
 #include "gmime-charset.h"
 
+#ifdef ENABLE_WARNINGS
+#define w(x) x
+#else
+#define w(x)
+#endif /* ENABLE_WARNINGS */
+
 #ifdef G_THREADS_ENABLED
 static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 #define LOCK()   g_static_mutex_lock (&lock)
@@ -57,8 +63,8 @@
 	
 	g_mime_charset_map_init ();
 	
-	utf8 = g_mime_charset_name ("utf-8");
-	locale = g_mime_charset_name (g_mime_charset_locale_name ());
+	utf8 = g_mime_charset_iconv_name ("utf-8");
+	locale = g_mime_charset_iconv_name (g_mime_locale_charset ());
 	
 	utf8_to_locale = iconv_open (locale, utf8);
 	locale_to_utf8 = iconv_open (utf8, locale);
@@ -99,6 +105,7 @@
 	inleft = n;
 	
 	do {
+		errno = 0;
 		outbuf = out + converted;
 		outleft = outlen - converted;
 		
@@ -147,7 +154,7 @@
 	
  fail:
 	
-	g_warning ("g_mime_iconv_strndup: %s", g_strerror (errno));
+	w(g_warning ("g_mime_iconv_strndup: %s", strerror (errno)));
 	
 	g_free (out);
 	
Index: gmime/gmime-iconv-utils.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-iconv-utils.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-iconv-utils.h
--- gmime/gmime-iconv-utils.h	14 May 2002 03:24:12 -0000	1.4
+++ gmime/gmime-iconv-utils.h	7 Jun 2004 06:10:12 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,13 +24,13 @@
 #ifndef __GMIME_ICONV_UTILS_H__
 #define __GMIME_ICONV_UTILS_H__
 
+#include <sys/types.h>
+#include <iconv.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <sys/types.h>
-#include <iconv.h>
 
 char *g_mime_iconv_strdup (iconv_t cd, const char *string);
 char *g_mime_iconv_strndup (iconv_t cd, const char *string, size_t n);
Index: gmime/gmime-iconv.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-iconv.c,v
retrieving revision 1.9
diff -u -r1.9 gmime-iconv.c
--- gmime/gmime-iconv.c	2 Mar 2003 21:33:27 -0000	1.9
+++ gmime/gmime-iconv.c	7 Jun 2004 06:10:13 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -32,26 +32,35 @@
 
 #include "gmime-charset.h"
 #include "gmime-iconv.h"
-#include "memchunk.h"
+#include "cache.h"
+
+#ifdef ENABLE_WARNINGS
+#define w(x) x
+#else
+#define w(x)
+#endif /* ENABLE_WARNINGS */
 
 
 #define ICONV_CACHE_SIZE   (16)
 
-struct _iconv_cache_bucket {
-	struct _iconv_cache_bucket *next;
-	struct _iconv_cache_bucket *prev;
-	guint32 refcount;
-	gboolean used;
+typedef struct {
+	CacheNode node;
+	guint32 refcount : 31;
+	guint32 used : 1;
 	iconv_t cd;
-	char *key;
-};
+} IconvCacheNode;
 
 
-static MemChunk *cache_chunk;
-static struct _iconv_cache_bucket *iconv_cache_buckets;
-static GHashTable *iconv_cache;
-static GHashTable *iconv_open_hash;
-static unsigned int iconv_cache_size = 0;
+static Cache *iconv_cache = NULL;
+static GHashTable *iconv_open_hash = NULL;
+
+#ifdef GMIME_ICONV_DEBUG
+static int cache_misses = 0;
+static int shutdown = 0;
+#define d(x) x
+#else
+#define d(x)
+#endif /* GMIME_ICONV_DEBUG */
 
 #ifdef G_THREADS_ENABLED
 static GStaticMutex iconv_cache_lock = G_STATIC_MUTEX_INIT;
@@ -67,118 +76,89 @@
 
 
 /**
- * iconv_cache_bucket_new:
+ * iconv_cache_node_new:
  * @key: cache key
  * @cd: iconv descriptor
  *
- * Creates a new cache bucket, inserts it into the cache and
- * increments the cache size.
+ * Creates a new cache node, inserts it into the cache and increments
+ * the cache size.
  *
- * Returns a pointer to the newly allocated cache bucket.
+ * Returns a pointer to the newly allocated cache node.
  **/
-static struct _iconv_cache_bucket *
-iconv_cache_bucket_new (const char *key, iconv_t cd)
+static IconvCacheNode *
+iconv_cache_node_new (const char *key, iconv_t cd)
 {
-	struct _iconv_cache_bucket *bucket;
+	IconvCacheNode *node;
 	
-	bucket = memchunk_alloc (cache_chunk);
-	bucket->prev = NULL;
-	bucket->key = g_strdup (key);
-	bucket->refcount = 1;
-	bucket->used = TRUE;
-	bucket->cd = cd;
-	
-	g_hash_table_insert (iconv_cache, bucket->key, bucket);
-	
-	/* FIXME: Since iconv_cache_expire_unused() traverses the list
-           from head to tail, perhaps it might be better to append new
-           nodes rather than prepending? This way older cache buckets
-           expire first? */
-	bucket->next = iconv_cache_buckets;
-	iconv_cache_buckets = bucket;
-	if (bucket->next)
-		bucket->next->prev = bucket;
+#ifdef GMIME_ICONV_DEBUG
+	cache_misses++;
+#endif
 	
-	iconv_cache_size++;
+	node = (IconvCacheNode *) cache_node_insert (iconv_cache, key);
+	node->refcount = 1;
+	node->used = TRUE;
+	node->cd = cd;
 	
-	return bucket;
+	return node;
 }
 
 
-/**
- * iconv_cache_bucket_expire:
- * @bucket: cache bucket
- *
- * Expires a single cache bucket @bucket. This should only ever be
- * called on a bucket that currently has no used iconv descriptors
- * open.
- **/
 static void
-iconv_cache_bucket_expire (struct _iconv_cache_bucket *bucket)
+iconv_cache_node_free (CacheNode *node)
 {
-	g_hash_table_remove (iconv_cache, bucket->key);
+	IconvCacheNode *inode = (IconvCacheNode *) node;
 	
-	if (bucket->prev) {
-		bucket->prev->next = bucket->next;
-		if (bucket->next)
-			bucket->next->prev = bucket->prev;
-	} else {
-		iconv_cache_buckets = bucket->next;
-		if (bucket->next)
-			bucket->next->prev = NULL;
+#ifdef GMIME_ICONV_DEBUG
+	if (shutdown) {
+		fprintf (stderr, "%s: open=%d; used=%s\n", node->key,
+			 inode->refcount, inode->used ? "yes" : "no");
 	}
+#endif
 	
-	g_free (bucket->key);
-	iconv_close (bucket->cd);
-	memchunk_free (cache_chunk, bucket);
-	
-	iconv_cache_size--;
+	iconv_close (inode->cd);
 }
 
 
 /**
- * iconv_cache_expire_unused:
+ * iconv_cache_node_expire:
+ * @node: cache node
  *
- * Expires as many unused cache buckets as it needs to in order to get
- * the total number of buckets < ICONV_CACHE_SIZE.
+ * Decides whether or not a cache node should be expired.
  **/
-static void
-iconv_cache_expire_unused (void)
+static gboolean
+iconv_cache_node_expire (Cache *cache, CacheNode *node)
 {
-	struct _iconv_cache_bucket *bucket, *next;
+	IconvCacheNode *inode = (IconvCacheNode *) node;
 	
-	bucket = iconv_cache_buckets;
-	while (bucket && iconv_cache_size >= ICONV_CACHE_SIZE) {
-		next = bucket->next;
-		
-		if (bucket->refcount == 0)
-			iconv_cache_bucket_expire (bucket);
-		
-		bucket = next;
-	}
+	if (inode->refcount == 0)
+		return TRUE;
+	
+	return FALSE;
 }
 
 
-static void
+
+/**
+ * g_mime_iconv_shutdown:
+ *
+ * Frees internal iconv caches created in g_mime_iconv_init().
+ **/
+void
 g_mime_iconv_shutdown (void)
 {
-	struct _iconv_cache_bucket *bucket, *next;
+	if (!iconv_cache)
+		return;
 	
-	bucket = iconv_cache_buckets;
-	while (bucket) {
-		next = bucket->next;
-		
-		g_free (bucket->key);
-		iconv_close (bucket->cd);
-		memchunk_free (cache_chunk, bucket);
-		
-		bucket = next;
-	}
+#ifdef GMIME_ICONV_DEBUG
+	fprintf (stderr, "There were %d iconv cache misses\n", cache_misses);
+	fprintf (stderr, "The following %d iconv cache buckets are still open:\n", iconv_cache->size);
+	shutdown = 1;
+#endif
+	cache_free (iconv_cache);
+	iconv_cache = NULL;
 	
-	g_hash_table_destroy (iconv_cache);
 	g_hash_table_destroy (iconv_open_hash);
-	
-	memchunk_destroy (cache_chunk);
+	iconv_open_hash = NULL;
 }
 
 
@@ -191,23 +171,14 @@
 void
 g_mime_iconv_init (void)
 {
-	static gboolean initialized = FALSE;
-	
-	if (initialized)
+	if (iconv_cache)
 		return;
 	
 	g_mime_charset_map_init ();
 	
-	iconv_cache_buckets = NULL;
-	iconv_cache = g_hash_table_new (g_str_hash, g_str_equal);
 	iconv_open_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
-	
-	cache_chunk = memchunk_new (sizeof (struct _iconv_cache_bucket),
-				    ICONV_CACHE_SIZE, FALSE);
-	
-	g_atexit (g_mime_iconv_shutdown);
-	
-	initialized = TRUE;
+	iconv_cache = cache_new (iconv_cache_node_expire, iconv_cache_node_free,
+				 sizeof (IconvCacheNode), ICONV_CACHE_SIZE);
 }
 
 
@@ -218,17 +189,17 @@
  *
  * Allocates a coversion descriptor suitable for converting byte
  * sequences from charset @from to charset @to. The resulting
- * descriptor can be used with iconv (or the g_mime_iconv wrapper) any
- * number of times until closed using g_mime_iconv_close.
+ * descriptor can be used with iconv() (or the g_mime_iconv() wrapper) any
+ * number of times until closed using g_mime_iconv_close().
  *
- * Returns a new conversion descriptor for use with iconv on success
- * or (iconv_t) -1 on fail as well as setting an appropriate errno
- * value.
+ * Returns a new conversion descriptor for use with g_mime_iconv() on
+ * success or (iconv_t) -1 on fail as well as setting an appropriate
+ * errno value.
  **/
 iconv_t
 g_mime_iconv_open (const char *to, const char *from)
 {
-	struct _iconv_cache_bucket *bucket;
+	IconvCacheNode *node;
 	iconv_t cd;
 	char *key;
 	
@@ -237,19 +208,19 @@
 		return (iconv_t) -1;
 	}
 	
-	if (!g_strcasecmp (from, "x-unknown"))
-		from = g_mime_charset_locale_name ();
+	if (!strcasecmp (from, "x-unknown"))
+		from = g_mime_locale_charset ();
 	
-	from = g_mime_charset_name (from);
-	to = g_mime_charset_name (to);
+	from = g_mime_charset_iconv_name (from);
+	to = g_mime_charset_iconv_name (to);
 	key = g_alloca (strlen (from) + strlen (to) + 2);
 	sprintf (key, "%s:%s", from, to);
 	
 	ICONV_CACHE_LOCK ();
 	
-	bucket = g_hash_table_lookup (iconv_cache, key);
-	if (bucket) {
-		if (bucket->used) {
+	node = (IconvCacheNode *) cache_node_lookup (iconv_cache, key, TRUE);
+	if (node) {
+		if (node->used) {
 			cd = iconv_open (to, from);
 			if (cd == (iconv_t) -1)
 				goto exception;
@@ -261,26 +232,23 @@
 			size_t inleft = 0, outleft = 0;
 			char *outbuf = NULL;
 			
-			cd = bucket->cd;
-			bucket->used = TRUE;
+			cd = node->cd;
+			node->used = TRUE;
 			
 			/* reset the descriptor */
 			iconv (cd, NULL, &inleft, &outbuf, &outleft);
 		}
 		
-		bucket->refcount++;
+		node->refcount++;
 	} else {
 		cd = iconv_open (to, from);
 		if (cd == (iconv_t) -1)
 			goto exception;
 		
-		if (iconv_cache_size >= ICONV_CACHE_SIZE)
-			iconv_cache_expire_unused ();
-		
-		bucket = iconv_cache_bucket_new (key, cd);
+		node = iconv_cache_node_new (key, cd);
 	}
 	
-	g_hash_table_insert (iconv_open_hash, cd, bucket->key);
+	g_hash_table_insert (iconv_open_hash, cd, ((CacheNode *) node)->key);
 	
 	ICONV_CACHE_UNLOCK ();
 	
@@ -290,11 +258,13 @@
 	
 	ICONV_CACHE_UNLOCK ();
 	
+#if w(!)0
 	if (errno == EINVAL)
 		g_warning ("Conversion from '%s' to '%s' is not supported", from, to);
 	else
 		g_warning ("Could not open converter from '%s' to '%s': %s",
-			   from, to, g_strerror (errno));
+			   from, to, strerror (errno));
+#endif
 	
 	return cd;
 }
@@ -312,7 +282,7 @@
 int
 g_mime_iconv_close (iconv_t cd)
 {
-	struct _iconv_cache_bucket *bucket;
+	IconvCacheNode *node;
 	const char *key;
 	
 	if (cd == (iconv_t) -1)
@@ -324,24 +294,24 @@
 	if (key) {
 		g_hash_table_remove (iconv_open_hash, cd);
 		
-		bucket = g_hash_table_lookup (iconv_cache, key);
-		g_assert (bucket);
+		node = (IconvCacheNode *) cache_node_lookup (iconv_cache, key, FALSE);
+		g_assert (node);
 		
-		bucket->refcount--;
+		if (iconv_cache->size > ICONV_CACHE_SIZE) {
+			/* expire before unreffing this node so that it wont get uncached */
+			cache_expire_unused (iconv_cache);
+		}
+		
+		node->refcount--;
 		
-		if (cd == bucket->cd)
-			bucket->used = FALSE;
+		if (cd == node->cd)
+			node->used = FALSE;
 		else
 			iconv_close (cd);
-		
-		if (!bucket->refcount && iconv_cache_size > ICONV_CACHE_SIZE) {
-			/* expire this cache bucket */
-			iconv_cache_bucket_expire (bucket);
-		}
 	} else {
 		ICONV_CACHE_UNLOCK ();
 		
-		g_warning ("This iconv context wasn't opened using g_mime_iconv_open()");
+		d(g_warning ("This iconv context wasn't opened using g_mime_iconv_open()"));
 		
 		return iconv_close (cd);
 	}
Index: gmime/gmime-iconv.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-iconv.h,v
retrieving revision 1.2
diff -u -r1.2 gmime-iconv.h
--- gmime/gmime-iconv.h	14 May 2002 03:24:12 -0000	1.2
+++ gmime/gmime-iconv.h	7 Jun 2004 06:10:13 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,17 +24,82 @@
 #ifndef __GMIME_ICONV_H__
 #define __GMIME_ICONV_H__
 
+#include <iconv.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <iconv.h>
-
 void g_mime_iconv_init (void);
+void g_mime_iconv_shutdown (void);
 
 iconv_t g_mime_iconv_open (const char *to, const char *from);
 
+
+/**
+ * g_mime_iconv:
+ * @cd: iconv_t conversion descriptor
+ * @inbuf: input buffer
+ * @inleft: number of bytes left in @inbuf
+ * @outbuf: output buffer
+ * @outleft: number of bytes left in @outbuf
+ *
+ * The argument @cd must be a conversion descriptor created using the
+ * function #g_mime_iconv_open.
+ *
+ * The main case is when @inbuf is not %NULL and *inbuf is not
+ * %NULL. In this case, the #g_mime_iconv function converts the
+ * multibyte sequence starting at *inbuf to a multibyte sequence
+ * starting at *outbuf. At most *inleft bytes, starting at *inbuf,
+ * will be read. At most *outleft bytes, starting at *outbuf, will
+ * be written.
+ *
+ * The #g_mime_iconv function converts one multibyte character at a
+ * time, and for each character conversion it increments *inbuf and
+ * decrements *inleft by the number of converted input bytes, it
+ * increments *outbuf and decrements *outleft by the number of
+ * converted output bytes, and it updates the conversion state
+ * contained in @cd. The conversion can stop for four reasons:
+ *
+ * 1. An invalid multibyte sequence is encountered in the input. In
+ * this case it sets errno to %EILSEQ and returns (size_t)(-1).
+ * *inbuf is left pointing to the beginning of the invalid multibyte
+ * sequence.
+ *
+ * 2. The input byte sequence has been entirely converted, i.e.
+ * *inleft has gone down to %0. In this case #g_mime_iconv returns
+ * the number of non-reversible conversions performed during this
+ * call.
+ *
+ * 3. An incomplete multibyte sequence is encountered in the input,
+ * and the input byte sequence terminates after it. In this case it
+ * sets errno to %EINVAL and returns (size_t)(-1). *inbuf is left
+ * pointing to the beginning of the incomplete multibyte sequence.
+ *
+ * 4. The output buffer has no more room for the next converted
+ * character. In this case it sets errno to %E2BIG and returns
+ * (size_t)(-1).
+ *
+ * A different case is when @inbuf is %NULL or *inbuf is %NULL, but
+ * @outbuf is not %NULL and *outbuf is not %NULL. In this case, the
+ * #g_mime_iconv function attempts to set @cd's conversion state to
+ * the initial state and store a corresponding shift sequence at
+ * *outbuf.  At most *outleft bytes, starting at *outbuf, will be
+ * written.  If the output buffer has no more room for this reset
+ * sequence, it sets errno to %E2BIG and returns (size_t)(-1).
+ * Otherwise it increments *outbuf and decrements *outleft by the
+ * number of bytes written.
+ *
+ * A third case is when @inbuf is %NULL or *inbuf is %NULL, and
+ * @outbuf is %NULL or *outbuf is %NULL. In this case, the
+ * #g_mime_iconv function sets @cd's conversion state to the initial
+ * state.
+ *
+ * Returns the number of characters converted in a nonreversible way
+ * during this call; reversible conversions are not counted. In case
+ * of error, it sets errno and returns (size_t)(-1).
+ **/
 #define g_mime_iconv(cd,inbuf,inleft,outbuf,outleft) iconv (cd, inbuf, inleft, outbuf, outleft)
 
 int g_mime_iconv_close (iconv_t cd);
Index: gmime/gmime-message-part.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-message-part.c,v
retrieving revision 1.4
diff -u -r1.4 gmime-message-part.c
--- gmime/gmime-message-part.c	26 Dec 2002 18:50:14 -0000	1.4
+++ gmime/gmime-message-part.c	7 Jun 2004 06:10:15 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -25,6 +25,8 @@
 #include <config.h>
 #endif
 
+#include <string.h>
+
 #include "gmime-message-part.h"
 
 #define d(x) x
@@ -42,7 +44,7 @@
 static void message_part_remove_header (GMimeObject *object, const char *header);
 static void message_part_set_content_type (GMimeObject *object, GMimeContentType *content_type);
 static char *message_part_get_headers (GMimeObject *object);
-static gssize message_part_write_to_stream (GMimeObject *object, GMimeStream *stream);
+static ssize_t message_part_write_to_stream (GMimeObject *object, GMimeStream *stream);
 
 
 static GMimeObjectClass *parent_class = NULL;
@@ -105,7 +107,7 @@
 	GMimeMessagePart *part = (GMimeMessagePart *) object;
 	
 	if (part->message)
-		g_mime_object_unref (GMIME_OBJECT (part->message));
+		g_object_unref (part->message);
 	
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -124,7 +126,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a message part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->add_header (object, header, value);
 }
 
@@ -132,13 +134,13 @@
 message_part_set_header (GMimeObject *object, const char *header, const char *value)
 {
 	/* RFC 1864 states that you cannot set a Content-MD5 on a message part */
-	if (!g_strcasecmp ("Content-MD5", header))
+	if (!strcasecmp ("Content-MD5", header))
 		return;
 	
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a message part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->set_header (object, header, value);
 }
 
@@ -148,7 +150,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a message part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		return GMIME_OBJECT_CLASS (parent_class)->get_header (object, header);
 	else
 		return NULL;
@@ -160,7 +162,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a message part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->remove_header (object, header);
 }
 
@@ -177,30 +179,27 @@
 	return GMIME_OBJECT_CLASS (parent_class)->get_headers (object);
 }
 
-static gssize
+static ssize_t
 message_part_write_to_stream (GMimeObject *object, GMimeStream *stream)
 {
 	GMimeMessagePart *part = (GMimeMessagePart *) object;
-	gssize nwritten, total = 0;
+	ssize_t nwritten, total = 0;
 	
 	/* write the content headers */
-	nwritten = g_mime_header_write_to_stream (object->headers, stream);
-	if (nwritten == -1)
+	if ((nwritten = g_mime_header_write_to_stream (object->headers, stream)) == -1)
 		return -1;
 	
 	total += nwritten;
 	
 	/* terminate the headers */
-	nwritten = g_mime_stream_write (stream, "\n", 1);
-	if (nwritten == -1)
+	if ((nwritten = g_mime_stream_write (stream, "\n", 1)) == -1)
 		return -1;
 	
 	total += nwritten;
 	
 	/* write the message */
 	if (part->message) {
-		nwritten = g_mime_object_write_to_stream (GMIME_OBJECT (part->message), stream);
-		if (nwritten == -1)
+		if ((nwritten = g_mime_object_write_to_stream (GMIME_OBJECT (part->message), stream)) == -1)
 			return -1;
 		
 		total += nwritten;
@@ -253,7 +252,7 @@
 	
 	part = g_mime_message_part_new (subtype);
 	part->message = message;
-	g_mime_object_ref (GMIME_OBJECT (message));
+	g_object_ref (message);
 	
 	return part;
 }
@@ -272,10 +271,10 @@
 	g_return_if_fail (GMIME_IS_MESSAGE_PART (part));
 	
 	if (message)
-		g_mime_object_ref (GMIME_OBJECT (message));
+		g_object_ref (message);
 	
 	if (part->message)
-		g_mime_object_unref (GMIME_OBJECT (part->message));
+		g_object_unref (part->message);
 	
 	part->message = message;
 }
@@ -293,6 +292,8 @@
 g_mime_message_part_get_message (GMimeMessagePart *part)
 {
 	g_return_val_if_fail (GMIME_IS_MESSAGE_PART (part), NULL);
+	
+	g_object_ref (part->message);
 	
 	return part->message;
 }
Index: gmime/gmime-message-part.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-message-part.h,v
retrieving revision 1.1
diff -u -r1.1 gmime-message-part.h
--- gmime/gmime-message-part.h	29 Jul 2002 20:19:43 -0000	1.1
+++ gmime/gmime-message-part.h	7 Jun 2004 06:10:15 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,13 +24,13 @@
 #ifndef __GMIME_MESSAGE_PART_H__
 #define __GMIME_MESSAGE_PART_H__
 
+#include <gmime/gmime-object.h>
+#include <gmime/gmime-message.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include "gmime-object.h"
-#include "gmime-message.h"
 
 #define GMIME_TYPE_MESSAGE_PART            (g_mime_message_part_get_type ())
 #define GMIME_MESSAGE_PART(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_MESSAGE_PART, GMimeMessagePart))
Index: gmime/gmime-message-partial.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-message-partial.c,v
retrieving revision 1.5
diff -u -r1.5 gmime-message-partial.c
--- gmime/gmime-message-partial.c	6 Sep 2002 16:39:08 -0000	1.5
+++ gmime/gmime-message-partial.c	7 Jun 2004 06:10:15 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -118,13 +118,13 @@
 message_partial_add_header (GMimeObject *object, const char *header, const char *value)
 {
 	/* RFC 1864 states that you cannot set a Content-MD5 on a message part */
-	if (!g_strcasecmp ("Content-MD5", header))
+	if (!strcasecmp ("Content-MD5", header))
 		return;
 	
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a mime part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->add_header (object, header, value);
 }
 
@@ -132,13 +132,13 @@
 message_partial_set_header (GMimeObject *object, const char *header, const char *value)
 {
 	/* RFC 1864 states that you cannot set a Content-MD5 on a message part */
-	if (!g_strcasecmp ("Content-MD5", header))
+	if (!strcasecmp ("Content-MD5", header))
 		return;
 	
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a mime part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->set_header (object, header, value);
 }
 
@@ -329,12 +329,12 @@
 		
 		g_mime_stream_reset (stream);
 		g_mime_stream_cat_add_source (GMIME_STREAM_CAT (cat), stream);
-		g_mime_stream_unref (stream);
+		g_object_unref (stream);
 	}
 	
 	parser = g_mime_parser_new ();
 	g_mime_parser_init_with_stream (parser, cat);
-	g_mime_stream_unref (cat);
+	g_object_unref (cat);
 	
 	message = g_mime_parser_construct_message (parser);
 	g_object_unref (parser);
@@ -343,7 +343,7 @@
 	
  exception:
 	
-	g_mime_stream_unref (cat);
+	g_object_unref (cat);
 	
 	return NULL;
 }
@@ -403,7 +403,7 @@
 	stream = g_mime_stream_mem_new ();
 	
 	if (g_mime_object_write_to_stream (GMIME_OBJECT (message), stream) == -1) {
-		g_mime_stream_unref (stream);
+		g_object_unref (stream);
 		return NULL;
 	}
 	
@@ -413,10 +413,14 @@
 	
 	/* optimization */
 	if (len <= max_size) {
-		g_mime_stream_unref (stream);
+		g_object_unref (stream);
+		g_object_ref (message);
+		
+		messages = g_malloc (sizeof (void *) * 2);
+		messages[0] = message;
 		*nparts = 1;
-		g_mime_object_ref (GMIME_OBJECT (message));
-		return &message;
+		
+		return messages;
 	}
 	
 	parts = g_ptr_array_new ();
@@ -432,16 +436,16 @@
 		partial = g_mime_message_partial_new (id, i + 1, parts->len);
 		wrapper = g_mime_data_wrapper_new_with_stream (GMIME_STREAM (parts->pdata[i]),
 							       GMIME_PART_ENCODING_DEFAULT);
-		g_mime_stream_unref (GMIME_STREAM (parts->pdata[i]));
+		g_object_unref (parts->pdata[i]);
 		g_mime_part_set_content_object (GMIME_PART (partial), wrapper);
 		g_object_unref (wrapper);
 		
 		parts->pdata[i] = message_partial_message_new (message);
 		g_mime_message_set_mime_part (GMIME_MESSAGE (parts->pdata[i]), GMIME_OBJECT (partial));
-		g_mime_object_unref (GMIME_OBJECT (partial));
+		g_object_unref (partial);
 	}
 	
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 	
 	messages = (GMimeMessage **) parts->pdata;
 	*nparts = parts->len;
Index: gmime/gmime-message-partial.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-message-partial.h,v
retrieving revision 1.2
diff -u -r1.2 gmime-message-partial.h
--- gmime/gmime-message-partial.h	30 Dec 2002 16:37:57 -0000	1.2
+++ gmime/gmime-message-partial.h	7 Jun 2004 06:10:15 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,15 +24,15 @@
 #ifndef __GMIME_MESSAGE_PARTIAL_H__
 #define __GMIME_MESSAGE_PARTIAL_H__
 
+#include <glib.h>
+
+#include <gmime/gmime-part.h>
+#include <gmime/gmime-message.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <glib/gtypes.h>
-
-#include "gmime-part.h"
-#include "gmime-message.h"
 
 #define GMIME_TYPE_MESSAGE_PARTIAL            (g_mime_message_partial_get_type ())
 #define GMIME_MESSAGE_PARTIAL(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_MESSAGE_PARTIAL, GMimeMessagePartial))
Index: gmime/gmime-message.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-message.c,v
retrieving revision 1.26
diff -u -r1.26 gmime-message.c
--- gmime/gmime-message.c	12 May 2003 14:52:11 -0000	1.26
+++ gmime/gmime-message.c	7 Jun 2004 06:10:15 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
 #include "gmime-part.h"
 #include "gmime-utils.h"
 #include "gmime-stream-mem.h"
+#include "gmime-table-private.h"
 
 
 static void g_mime_message_class_init (GMimeMessageClass *klass);
@@ -47,13 +48,17 @@
 static const char *message_get_header (GMimeObject *object, const char *header);
 static void message_remove_header (GMimeObject *object, const char *header);
 static char *message_get_headers (GMimeObject *object);
-static gssize message_write_to_stream (GMimeObject *object, GMimeStream *stream);
+static ssize_t message_write_to_stream (GMimeObject *object, GMimeStream *stream);
 
 static void message_set_sender (GMimeMessage *message, const char *sender);
 static void message_set_reply_to (GMimeMessage *message, const char *reply_to);
 static void message_add_recipients_from_string (GMimeMessage *message, char *type, const char *string);
 static void message_set_subject (GMimeMessage *message, const char *subject);
-static void message_set_message_id (GMimeMessage *message, const char *message_id);
+
+static ssize_t write_received (GMimeStream *stream, const char *name, const char *value);
+static ssize_t write_subject (GMimeStream *stream, const char *name, const char *value);
+static ssize_t write_msgid (GMimeStream *stream, const char *name, const char *value);
+
 
 static GMimeObjectClass *parent_class = NULL;
 
@@ -127,6 +132,10 @@
 	message->gmt_offset = 0;
 	message->message_id = NULL;
 	message->mime_part = NULL;
+	
+	g_mime_header_register_writer (((GMimeObject *) message)->headers, "Subject", write_subject);
+	g_mime_header_register_writer (((GMimeObject *) message)->headers, "Received", write_received);
+	g_mime_header_register_writer (((GMimeObject *) message)->headers, "Message-Id", write_msgid);
 }
 
 static gboolean
@@ -157,7 +166,7 @@
 	
 	/* unref child mime part */
 	if (message->mime_part)
-		g_mime_object_unref (GMIME_OBJECT (message->mime_part));
+		g_object_unref (message->mime_part);
 	
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -171,6 +180,364 @@
 }
 
 
+typedef void (*token_skip_t) (const char **in);
+
+struct _received_token {
+	char *token;
+	size_t len;
+	token_skip_t skip;
+};
+
+extern void decode_lwsp   (const char **in);
+
+static void skip_atom     (const char **in);
+static void skip_domain   (const char **in);
+static void skip_addr     (const char **in);
+static void skip_msgid    (const char **in);
+
+static struct _received_token received_tokens[] = {
+	{ "from ", 5, skip_domain   },
+	{ "by ",   3, skip_domain   },
+	{ "via ",  4, skip_atom     },
+	{ "with ", 5, skip_atom     },
+	{ "id ",   3, skip_msgid    },
+	{ "for ",  4, skip_addr     }
+};
+
+static void
+skip_atom (const char **in)
+{
+	register const char *inptr;
+	
+	decode_lwsp (in);
+	inptr = *in;
+	while (is_atom (*inptr))
+		inptr++;
+	*in = inptr;
+}
+
+static void
+skip_comment (const char **in)
+{
+	register const char *inptr = *in;
+	int depth = 1;
+	
+	if (*inptr == '(')
+		inptr++;
+	
+	while (*inptr && depth > 0) {
+		if (*inptr == '(')
+			depth++;
+		else if (*inptr == ')')
+			depth--;
+		inptr++;
+	}
+	
+	if (*inptr == ')')
+		inptr++;
+	
+	*in = inptr;
+}
+
+static void
+skip_quoted_string (const char **in)
+{
+	const char *inptr = *in;
+	
+	decode_lwsp (&inptr);
+	if (*inptr == '"') {
+		inptr++;
+		while (*inptr && *inptr != '"') {
+			if (*inptr == '\\')
+				inptr++;
+			
+			if (*inptr)
+				inptr++;
+		}
+		
+		if (*inptr == '"')
+			inptr++;
+	}
+	
+	*in = inptr;
+}
+
+static void
+skip_word (const char **in)
+{
+	decode_lwsp (in);
+	if (**in == '"') {
+		skip_quoted_string (in);
+	} else {
+		skip_atom (in);
+	}
+}
+
+static void
+skip_domain_subliteral (const char **in)
+{
+	const char *inptr = *in;
+	
+	while (*inptr && *inptr != '.' && *inptr != ']') {
+		if (is_dtext (*inptr)) {
+			inptr++;
+		} else if (is_lwsp (*inptr)) {
+			decode_lwsp (&inptr);
+		} else {
+			break;
+		}
+	}
+	
+	*in = inptr;
+}
+
+static void
+skip_domain_literal (const char **in)
+{
+	const char *inptr = *in;
+	
+	decode_lwsp (&inptr);
+	while (*inptr && *inptr != ']') {
+		skip_domain_subliteral (&inptr);
+		if (*inptr && *inptr != ']')
+			inptr++;
+	}
+	
+	*in = inptr;
+}
+
+static void
+skip_domain (const char **in)
+{
+	const char *save, *inptr = *in;
+	
+	while (inptr && *inptr) {
+		decode_lwsp (&inptr);
+		if (*inptr == '[') {
+			/* domain literal */
+			inptr++;
+			skip_domain_literal (&inptr);
+			if (*inptr == ']')
+				inptr++;
+		} else {
+			skip_atom (&inptr);
+		}
+		
+		save = inptr;
+		decode_lwsp (&inptr);
+		if (*inptr != '.') {
+			inptr = save;
+			break;
+		}
+		
+		inptr++;
+	}
+	
+	*in = inptr;
+}
+
+static void
+skip_addrspec (const char **in)
+{
+	const char *inptr = *in;
+	
+	decode_lwsp (&inptr);
+	skip_word (&inptr);
+	decode_lwsp (&inptr);
+	
+	while (*inptr == '.') {
+		inptr++;
+		skip_word (&inptr);
+		decode_lwsp (&inptr);
+	}
+	
+	if (*inptr == '@') {
+		inptr++;
+		skip_domain (&inptr);
+	}
+	
+	*in = inptr;
+}
+
+static void
+skip_addr (const char **in)
+{
+	const char *inptr = *in;
+	
+	decode_lwsp (&inptr);
+	if (*inptr == '<') {
+		inptr++;
+		skip_addrspec (&inptr);
+		if (*inptr == '>')
+			inptr++;
+	} else {
+		skip_addrspec (&inptr);
+	}
+	
+	*in = inptr;
+}
+
+static void
+skip_msgid (const char **in)
+{
+	const char *inptr = *in;
+	
+	decode_lwsp (&inptr);
+	if (*inptr == '<') {
+		inptr++;
+		skip_addrspec (&inptr);
+		if (*inptr == '>')
+			inptr++;
+	} else {
+		skip_atom (&inptr);
+	}
+	
+	*in = inptr;
+}
+
+
+struct _received_part {
+	struct _received_part *next;
+	const char *start;
+	size_t len;
+};
+
+static ssize_t
+write_received (GMimeStream *stream, const char *name, const char *value)
+{
+	struct _received_part *parts, *part, *tail;
+	const char *lwsp, *inptr;
+	ssize_t nwritten;
+	GString *str;
+	int len, i;
+	
+	while (is_lwsp (*value))
+		value++;
+	
+	if (*value == '\0')
+		return 0;
+	
+	str = g_string_new (name);
+	g_string_append_len (str, ": ", 2);
+	len = 10;
+	
+	tail = parts = part = g_alloca (sizeof (struct _received_part));
+	part->start = inptr = value;
+	part->next = NULL;
+	
+	while (*inptr) {
+		for (i = 0; i < G_N_ELEMENTS (received_tokens); i++) {
+			if (!strncmp (inptr, received_tokens[i].token, received_tokens[i].len)) {
+				if (inptr > part->start) {
+					part->len = lwsp - part->start;
+					
+					part = g_alloca (sizeof (struct _received_part));
+					part->start = inptr;
+					part->next = NULL;
+					
+					tail->next = part;
+					tail = part;
+				}
+				
+				inptr += received_tokens[i].len;
+				received_tokens[i].skip (&inptr);
+				
+				lwsp = inptr;
+				while (is_lwsp (*inptr))
+					inptr++;
+				
+				if (*inptr == ';') {
+					inptr++;
+					
+					part->len = inptr - part->start;
+					
+					lwsp = inptr;
+					while (is_lwsp (*inptr))
+						inptr++;
+					
+					part = g_alloca (sizeof (struct _received_part));
+					part->start = inptr;
+					part->next = NULL;
+					
+					tail->next = part;
+					tail = part;
+				}
+				
+				break;
+			}
+		}
+		
+		if (i == G_N_ELEMENTS (received_tokens)) {
+			while (*inptr && !is_lwsp (*inptr))
+				inptr++;
+			
+			lwsp = inptr;
+			while (is_lwsp (*inptr))
+				inptr++;
+		}
+		
+		if (*inptr == '(') {
+			skip_comment (&inptr);
+			
+			lwsp = inptr;
+			while (is_lwsp (*inptr))
+				inptr++;
+		}
+	}
+	
+	part->len = lwsp - part->start;
+	
+	lwsp = NULL;
+	part = parts;
+	do {
+		len += lwsp ? part->start - lwsp : 0;
+		if (len + part->len > GMIME_FOLD_LEN && part != parts) {
+			g_string_append (str, "\n\t");
+			len = 1;
+		} else if (lwsp) {
+			g_string_append_len (str, lwsp, part->start - lwsp);
+		}
+		
+		g_string_append_len (str, part->start, part->len);
+		lwsp = part->start + part->len;
+		len += part->len;
+		
+		part = part->next;
+	} while (part != NULL);
+	
+	g_string_append_c (str, '\n');
+	
+	nwritten = g_mime_stream_write (stream, str->str, str->len);
+	g_string_free (str, TRUE);
+	
+	return nwritten;
+}
+
+static ssize_t
+write_subject (GMimeStream *stream, const char *name, const char *value)
+{
+	char *unfolded, *folded;
+	ssize_t n;
+	
+	unfolded = g_strdup_printf ("%s: %s\n", name, value);
+	folded = g_mime_utils_unstructured_header_fold (unfolded);
+	g_free (unfolded);
+	
+	n = g_mime_stream_write_string (stream, folded);
+	g_free (folded);
+	
+	return n;
+}
+
+static ssize_t
+write_msgid (GMimeStream *stream, const char *name, const char *value)
+{
+	/* we don't want to wrap the Message-Id header - seems to
+	   break a lot of clients (and servers) */
+	return g_mime_stream_printf (stream, "%s: %s\n", name, value);
+}
+
+
 enum {
 	HEADER_FROM,
 	HEADER_REPLY_TO,
@@ -203,7 +570,7 @@
 	time_t date;
 	
 	for (i = 0; headers[i]; i++) {
-		if (!g_strcasecmp (headers[i], header))
+		if (!strcasecmp (headers[i], header))
 			break;
 	}
 	
@@ -234,7 +601,8 @@
 		}
 		break;
 	case HEADER_MESSAGE_ID:
-		message_set_message_id (message, value);
+		g_free (message->message_id);
+		message->message_id = g_mime_utils_decode_message_id (value);
 		break;
 	default:
 		return FALSE;
@@ -247,13 +615,13 @@
 static void
 message_add_header (GMimeObject *object, const char *header, const char *value)
 {
-	if (!g_strcasecmp ("MIME-Version", header))
+	if (!strcasecmp ("MIME-Version", header))
 		return;
 	
 	/* Make sure that the header is not a Content-* header, else it
            doesn't belong on a message */
 	
-	if (g_strncasecmp ("Content-", header, 8)) {
+	if (strncasecmp ("Content-", header, 8)) {
 		if (process_header (object, header, value))
 			g_mime_header_add (object->headers, header, value);
 		else
@@ -264,13 +632,13 @@
 static void
 message_set_header (GMimeObject *object, const char *header, const char *value)
 {
-	if (!g_strcasecmp ("MIME-Version", header))
+	if (!strcasecmp ("MIME-Version", header))
 		return;
 	
 	/* Make sure that the header is not a Content-* header, else it
            doesn't belong on a message */
 	
-	if (g_strncasecmp ("Content-", header, 8)) {
+	if (strncasecmp ("Content-", header, 8)) {
 		if (process_header (object, header, value))
 			g_mime_header_set (object->headers, header, value);
 		else
@@ -284,10 +652,10 @@
 	/* Make sure that the header is not a Content-* header, else it
            doesn't belong on a message */
 	
-	if (!g_strcasecmp ("MIME-Version", header))
+	if (!strcasecmp ("MIME-Version", header))
 		return "1.0";
 	
-	if (g_strncasecmp ("Content-", header, 8))
+	if (strncasecmp ("Content-", header, 8))
 		return GMIME_OBJECT_CLASS (parent_class)->get_header (object, header);
 	else
 		return NULL;
@@ -300,17 +668,17 @@
 	InternetAddressList *addrlist;
 	int i;
 	
-	if (!g_strcasecmp ("MIME-Version", header))
+	if (!strcasecmp ("MIME-Version", header))
 		return;
 	
 	/* Make sure that the header is not a Content-* header, else it
            doesn't belong on a message */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		return;
 	
 	for (i = 0; headers[i]; i++) {
-		if (!g_strcasecmp (headers[i], header))
+		if (!strcasecmp (headers[i], header))
 			break;
 	}
 	
@@ -376,7 +744,7 @@
 		g_mime_header_write_to_stream (message->mime_part->headers, stream);
 	}
 	
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 	g_byte_array_append (ba, "", 1);
 	str = ba->data;
 	g_byte_array_free (ba, FALSE);
@@ -384,30 +752,27 @@
 	return str;
 }
 
-static gssize
+static ssize_t
 message_write_to_stream (GMimeObject *object, GMimeStream *stream)
 {
 	GMimeMessage *message = (GMimeMessage *) object;
-	gssize nwritten, total = 0;
+	ssize_t nwritten, total = 0;
 	
 	/* write the content headers */
-	nwritten = g_mime_header_write_to_stream (object->headers, stream);
-	if (nwritten == -1)
+	if ((nwritten = g_mime_header_write_to_stream (object->headers, stream)) == -1)
 		return -1;
 	
 	total += nwritten;
 	
 	if (message->mime_part) {
-		nwritten = g_mime_stream_write_string (stream, "MIME-Version: 1.0\n");
-		if (nwritten == -1)
+		if ((nwritten = g_mime_stream_write_string (stream, "MIME-Version: 1.0\n")) == -1)
 			return -1;
 		
 		total += nwritten;
 		
 		nwritten = g_mime_object_write_to_stream (message->mime_part, stream);
 	} else {
-		nwritten = g_mime_stream_write (stream, "\n", 1);
-		if (nwritten == -1)
+		if ((nwritten = g_mime_stream_write (stream, "\n", 1)) == -1)
 			return -1;
 	}
 	
@@ -462,7 +827,7 @@
  * @sender: The name and address of the sender
  *
  * Set the sender's name and address on the MIME Message.
- * (ex: "\"Joe Sixpack\" <joe@sixpack.org>")
+ * (ex: "\"Joe Sixpack\" &lt;joe@sixpack.org&gt;")
  **/
 void
 g_mime_message_set_sender (GMimeMessage *message, const char *sender)
@@ -540,7 +905,7 @@
 static void
 sync_recipient_header (GMimeMessage *message, const char *type)
 {
-	InternetAddressList *recipients;
+	const InternetAddressList *recipients;
 	
 	/* sync the specified recipient header */
 	recipients = g_mime_message_get_recipients (message, type);
@@ -645,7 +1010,7 @@
  * Returns a list of recipients of a chosen type from the MIME
  * Message.
  **/
-InternetAddressList *
+const InternetAddressList *
 g_mime_message_get_recipients (GMimeMessage *message, const char *type)
 {
 	g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
@@ -682,7 +1047,7 @@
 	
 	message_set_subject (message, subject);
 	
-	encoded = g_mime_utils_8bit_header_encode (message->subject);
+	encoded = g_mime_utils_header_encode_text (message->subject);
 	g_mime_header_set (GMIME_OBJECT (message)->headers, "Subject", encoded);
 	g_free (encoded);
 }
@@ -773,32 +1138,29 @@
 }
 
 
-static void
-message_set_message_id (GMimeMessage *message, const char *message_id)
-{
-	if (message->message_id)
-		g_free (message->message_id);
-	
-	message->message_id = g_strstrip (g_strdup (message_id));
-}
-
-
 /**
  * g_mime_message_set_message_id: 
  * @message: MIME Message
- * @message_id: message-id
+ * @message_id: message-id (addr-spec portion)
  *
  * Set the Message-Id on a message.
  **/
 void
 g_mime_message_set_message_id (GMimeMessage *message, const char *message_id)
 {
+	char *msgid;
+	
 	g_return_if_fail (GMIME_IS_MESSAGE (message));
 	g_return_if_fail (message_id != NULL);
 	
-	message_set_message_id (message, message_id);
-	g_mime_header_set (GMIME_OBJECT (message)->headers,
-			   "Message-Id", message->message_id);
+	if (message->message_id)
+		g_free (message->message_id);
+	
+	message->message_id = g_strstrip (g_strdup (message_id));
+	
+	msgid = g_strdup_printf ("<%s>", message_id);
+	g_mime_header_set (GMIME_OBJECT (message)->headers, "Message-Id", msgid);
+	g_free (msgid);
 }
 
 
@@ -880,6 +1242,28 @@
 
 
 /**
+ * g_mime_message_get_mime_part:
+ * @message: MIME Message
+ *
+ * Gets the toplevel MIME part contained within @message.
+ *
+ * Returns the toplevel MIME part of @message.
+ **/
+GMimeObject *
+g_mime_message_get_mime_part (GMimeMessage *message)
+{
+	g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
+	
+	if (message->mime_part == NULL)
+		return NULL;
+	
+	g_object_ref (message->mime_part);
+	
+	return message->mime_part;
+}
+
+
+/**
  * g_mime_message_set_mime_part:
  * @message: MIME Message
  * @mime_part: The root-level MIME Part
@@ -891,10 +1275,10 @@
 {
 	g_return_if_fail (GMIME_IS_MESSAGE (message));
 	
-	g_mime_object_ref (GMIME_OBJECT (mime_part));
+	g_object_ref (mime_part);
 	
 	if (message->mime_part)
-		g_mime_object_unref (GMIME_OBJECT (message->mime_part));
+		g_object_unref (message->mime_part);
 	
 	message->mime_part = mime_part;
 }
@@ -907,15 +1291,18 @@
  *
  * Write the contents of the MIME Message to @stream.
  *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_object_write_to_stream() instead.
+ *
  * Returns -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_message_write_to_stream (GMimeMessage *message, GMimeStream *stream)
 {
 	g_return_val_if_fail (GMIME_IS_MESSAGE (message), -1);
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
 	
-	return g_mime_object_write_to_stream (GMIME_OBJECT (message), stream);
+	return g_mime_object_write_to_stream ((GMimeObject *) message, stream);
 }
 
 
@@ -925,6 +1312,9 @@
  *
  * Allocates a string buffer containing the mime message @message.
  *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_object_to_string() instead.
+ *
  * Returns an allocated string containing the MIME Message.
  **/
 char *
@@ -932,90 +1322,92 @@
 {
 	g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
 	
-	return g_mime_object_to_string (GMIME_OBJECT (message));
+	return g_mime_object_to_string ((GMimeObject *) message);
 }
 
 
-/* Brief explanation of how this function works it's magic:
+/* The proper way to handle a multipart/alternative part is to return
+ * the last part that we know how to render. For our purposes, we are
+ * going to assume:
+ *
+ * If @want_plain is %FALSE then assume we can render text/plain and
+ * text/html, thus the order of preference is text/html, then
+ * text/plain and finally text/<any>.
  *
- * We cycle through the immediate subparts looking for text parts. If
- * the first text part we come accross is exactly what we want then we
- * return it, otherwise keep a reference to it for later use (if we
- * don't find the preferred part later as we continue to cycle through
- * the subparts then we default to the first text part found). If we
- * come to a multipart, we descend into it repeating the process. If
- * we find the 'body' in a sub-multipart, we don't necessarily return
- * that value for it is entirely possible that there could be text
- * parts defined after the sub-multipart. For example, we could have
- * the following MIME structure:
- *
- * multipart/alternative
- *   image/png
- *   multipart/related
- *     text/html
- *     image/png
- *     image/gif
- *     image/jpeg
- *   text/plain
- *   text/html
- *
- * While one can never be certain that the text/html part within the
- * multipart/related isn't the true 'body', it's genrally safe to
- * assume that in cases like this, the outer text part(s) are the
- * message body. Note that this is an assumption and is thus not
- * guarenteed to always be correct.
+ * Otherwise, if @want_plain is %TRUE then we assume that we do not
+ * know how to render text/html and so our order of preference becomes
+ * text/plain and then text/<any>.
  **/
-static char *
-multipart_get_body (GMimeMultipart *multipart, gboolean want_plain, gboolean *is_html)
+static GMimeObject *
+handle_multipart_alternative (GMimeMultipart *multipart, gboolean want_plain, gboolean *is_html)
 {
-	GMimeObject *first = NULL;
-	const char *content;
-	char *body = NULL;
+	GMimeObject *mime_part, *text_part = NULL;
+	const GMimeContentType *type;
 	GList *subpart;
-	guint len;
 	
 	subpart = multipart->subparts;
 	while (subpart) {
-		const GMimeContentType *type;
-		GMimeObject *mime_part;
-		
 		mime_part = subpart->data;
-		type = g_mime_object_get_content_type (mime_part);
 		
-		if (g_mime_content_type_is_type (type, "text", want_plain ? "plain" : "html")) {
-			/* we got what we came for */
-			*is_html = !want_plain;
-			
-			content = g_mime_part_get_content (GMIME_PART (mime_part), &len);
-			g_free (body);
-			body = g_strndup (content, len);
-			break;
-		} else if (g_mime_content_type_is_type (type, "text", "*") && !first) {
-			/* remember what our first text part was */
-			first = mime_part;
-			g_free (body);
-			body = NULL;
-		} else if (g_mime_content_type_is_type (type, "multipart", "*") && !first && !body) {
-			/* look in the multipart for the body */
-			body = multipart_get_body (GMIME_MULTIPART (mime_part), want_plain, is_html);
-			
-			/* You are probably asking: "why don't we break here?"
-			 * The answer is because the real message body could
-			 * be a part after this multipart */
+		type = g_mime_object_get_content_type (mime_part);
+		if (g_mime_content_type_is_type (type, "text", "*")) {
+			if (!text_part || !strcasecmp (type->subtype, want_plain ? "plain" : "html")) {
+				*is_html = !strcasecmp (type->subtype, "html");
+				text_part = mime_part;
+			}
 		}
 		
 		subpart = subpart->next;
 	}
 	
-	if (!body && first) {
-		/* we didn't get the type we wanted but still got the body */
-		*is_html = want_plain;
+	return text_part;
+}
+
+static GMimeObject *
+handle_multipart_mixed (GMimeMultipart *multipart, gboolean want_plain, gboolean *is_html)
+{
+	GMimeObject *mime_part, *text_part = NULL;
+	const GMimeContentType *type, *first_type = NULL;
+	GList *subpart;
+	
+	subpart = multipart->subparts;
+	while (subpart) {
+		mime_part = subpart->data;
 		
-		content = g_mime_part_get_content (GMIME_PART (first), &len);
-		body = g_strndup (content, len);
+		type = g_mime_object_get_content_type (mime_part);
+		if (GMIME_IS_MULTIPART (mime_part)) {
+			multipart = GMIME_MULTIPART (mime_part);
+			if (g_mime_content_type_is_type (type, "multipart", "alternative")) {
+				mime_part = handle_multipart_alternative (multipart, want_plain, is_html);
+				if (mime_part)
+					return mime_part;
+			} else {
+				mime_part = handle_multipart_mixed (multipart, want_plain, is_html);
+				if (mime_part && !text_part)
+					text_part = mime_part;
+			}
+		} else if (g_mime_content_type_is_type (type, "text", "*")) {
+			if (!strcasecmp (type->subtype, want_plain ? "plain" : "html")) {
+				/* we got what we came for */
+				*is_html = !strcasecmp (type->subtype, "html");
+				return mime_part;
+			}
+			
+			/* if we haven't yet found a text part or if
+                           it is a type we can understand and it is
+                           the first of that type, save it */
+			if (!text_part || (!strcasecmp (type->subtype, "plain") && (first_type &&
+					   strcasecmp (type->subtype, first_type->subtype) != 0))) {
+				*is_html = !strcasecmp (type->subtype, "html");
+				text_part = mime_part;
+				first_type = type;
+			}
+		}
+		
+		subpart = subpart->next;
 	}
 	
-	return body;
+	return text_part;
 }
 
 
@@ -1039,27 +1431,37 @@
 char *
 g_mime_message_get_body (const GMimeMessage *message, gboolean want_plain, gboolean *is_html)
 {
+	GMimeObject *mime_part = NULL;
 	const GMimeContentType *type;
+	GMimeMultipart *multipart;
 	const char *content;
 	char *body = NULL;
-	guint len = 0;
+	size_t len = 0;
 	
 	g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
 	g_return_val_if_fail (is_html != NULL, NULL);
 	
 	type = g_mime_object_get_content_type (message->mime_part);
-	if (g_mime_content_type_is_type (type, "text", "*")) {
+	if (GMIME_IS_MULTIPART (message->mime_part)) {
+		/* lets see if we can find a body in the multipart */
+		multipart = GMIME_MULTIPART (message->mime_part);
+		if (g_mime_content_type_is_type (type, "multipart", "alternative"))
+			mime_part = handle_multipart_alternative (multipart, want_plain, is_html);
+		else
+			mime_part = handle_multipart_mixed (multipart, want_plain, is_html);
+	} else if (g_mime_content_type_is_type (type, "text", "*")) {
 		/* this *has* to be the message body */
-		if (g_mime_content_type_is_type (type, "text", want_plain ? "plain" : "html"))
-			*is_html = !want_plain;
+		if (g_mime_content_type_is_type (type, "text", "html"))
+			*is_html = TRUE;
 		else
-			*is_html = want_plain;
+			*is_html = FALSE;
 		
-		content = g_mime_part_get_content (GMIME_PART (message->mime_part), &len);
+		mime_part = message->mime_part;
+	}
+	
+	if (mime_part != NULL) {
+		content = g_mime_part_get_content (GMIME_PART (mime_part), &len);
 		body = g_strndup (content, len);
-	} else if (g_mime_content_type_is_type (type, "multipart", "*")) {
-		/* lets see if we can find a body in the multipart */
-		body = multipart_get_body (GMIME_MULTIPART (message->mime_part), want_plain, is_html);
 	}
 	
 	return body;
Index: gmime/gmime-message.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-message.h,v
retrieving revision 1.11
diff -u -r1.11 gmime-message.h
--- gmime/gmime-message.h	26 Dec 2002 18:50:14 -0000	1.11
+++ gmime/gmime-message.h	7 Jun 2004 06:10:15 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,19 +24,19 @@
 #ifndef __GMIME_MESSAGE_H__
 #define __GMIME_MESSAGE_H__
 
+#include <stdarg.h>
+#include <time.h>
+
+#include <gmime/gmime-object.h>
+#include <gmime/gmime-header.h>
+#include <gmime/gmime-stream.h>
+#include <gmime/internet-address.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <stdarg.h>
-#include <time.h>
-
-#include "gmime-object.h"
-#include "gmime-header.h"
-#include "gmime-stream.h"
-#include "internet-address.h"
-
 #define GMIME_TYPE_MESSAGE            (g_mime_message_get_type ())
 #define GMIME_MESSAGE(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_MESSAGE, GMimeMessage))
 #define GMIME_MESSAGE_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_MESSAGE, GMimeMessageClass))
@@ -108,7 +108,7 @@
 
 void g_mime_message_add_recipient (GMimeMessage *message, char *type, const char *name, const char *address);
 void g_mime_message_add_recipients_from_string (GMimeMessage *message, char *type, const char *string);
-InternetAddressList *g_mime_message_get_recipients (GMimeMessage *message, const char *type);
+const InternetAddressList *g_mime_message_get_recipients (GMimeMessage *message, const char *type);
 
 void g_mime_message_set_subject (GMimeMessage *message, const char *subject);
 const char *g_mime_message_get_subject (GMimeMessage *message);
@@ -124,11 +124,13 @@
 void g_mime_message_set_header (GMimeMessage *message, const char *header, const char *value);
 const char *g_mime_message_get_header (GMimeMessage *message, const char *header);
 
+GMimeObject *g_mime_message_get_mime_part (GMimeMessage *message);
 void g_mime_message_set_mime_part (GMimeMessage *message, GMimeObject *mime_part);
 
-/* utility functions */
-gssize g_mime_message_write_to_stream (GMimeMessage *message, GMimeStream *stream);
+#ifndef GMIME_DISABLE_DEPRECATED
+ssize_t g_mime_message_write_to_stream (GMimeMessage *message, GMimeStream *stream);
 char *g_mime_message_to_string (GMimeMessage *message);
+#endif /* GMIME_DISABLE_DEPRECATED */
 
 char *g_mime_message_get_body (const GMimeMessage *message, gboolean want_plain, gboolean *is_html);
 char *g_mime_message_get_headers (GMimeMessage *message);
Index: gmime/gmime-multipart.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-multipart.c,v
retrieving revision 1.9
diff -u -r1.9 gmime-multipart.c
--- gmime/gmime-multipart.c	26 Dec 2002 18:50:15 -0000	1.9
+++ gmime/gmime-multipart.c	7 Jun 2004 06:10:16 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@
 #include "gmime-utils.h"
 
 
-#define d(x) x
+#define d(x)
 
 /* GObject class methods */
 static void g_mime_multipart_class_init (GMimeMultipartClass *klass);
@@ -50,7 +50,7 @@
 static void multipart_remove_header (GMimeObject *object, const char *header);
 static void multipart_set_content_type (GMimeObject *object, GMimeContentType *content_type);
 static char *multipart_get_headers (GMimeObject *object);
-static gssize multipart_write_to_stream (GMimeObject *object, GMimeStream *stream);
+static ssize_t multipart_write_to_stream (GMimeObject *object, GMimeStream *stream);
 
 /* GMimeMultipart class methods */
 static void multipart_add_part (GMimeMultipart *multipart, GMimeObject *part);
@@ -144,7 +144,7 @@
 		GMimeObject *part;
 		
 		part = node->data;
-		g_mime_object_unref (part);
+		g_object_unref (part);
 		node = node->next;
 	}
 	g_list_free (multipart->subparts);
@@ -166,7 +166,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a multipart */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->add_header (object, header, value);
 }
 
@@ -174,13 +174,13 @@
 multipart_set_header (GMimeObject *object, const char *header, const char *value)
 {
 	/* RFC 1864 states that you cannot set a Content-MD5 on a multipart */
-	if (!g_strcasecmp ("Content-MD5", header))
+	if (!strcasecmp ("Content-MD5", header))
 		return;
 	
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a multipart */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->set_header (object, header, value);
 }
 
@@ -190,7 +190,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a multipart */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		return GMIME_OBJECT_CLASS (parent_class)->get_header (object, header);
 	else
 		return NULL;
@@ -202,7 +202,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a multipart */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->remove_header (object, header);
 }
 
@@ -225,11 +225,11 @@
 	return GMIME_OBJECT_CLASS (parent_class)->get_headers (object);
 }
 
-static gssize
+static ssize_t
 multipart_write_to_stream (GMimeObject *object, GMimeStream *stream)
 {
 	GMimeMultipart *multipart = (GMimeMultipart *) object;
-	gssize nwritten, total = 0;
+	ssize_t nwritten, total = 0;
 	GMimeObject *part;
 	GList *node;
 	
@@ -238,8 +238,7 @@
 		g_mime_multipart_set_boundary (multipart, NULL);
 	
 	/* write the content headers */
-	nwritten = g_mime_header_write_to_stream (object->headers, stream);
-	if (nwritten == -1)
+	if ((nwritten = g_mime_header_write_to_stream (object->headers, stream)) == -1)
 		return -1;
 	
 	total += nwritten;
@@ -252,8 +251,7 @@
 		
 		total++;
 		
-		nwritten = g_mime_stream_write_string (stream, multipart->preface);
-		if (nwritten == -1)
+		if ((nwritten = g_mime_stream_write_string (stream, multipart->preface)) == -1)
 			return -1;
 		
 		total += nwritten;
@@ -264,15 +262,13 @@
 		part = node->data;
 		
 		/* write the boundary */
-		nwritten = g_mime_stream_printf (stream, "\n--%s\n", multipart->boundary);
-		if (nwritten == -1)
+		if ((nwritten = g_mime_stream_printf (stream, "\n--%s\n", multipart->boundary)) == -1)
 			return -1;
 		
 		total += nwritten;
 		
 		/* write this part out */
-		nwritten = g_mime_object_write_to_stream (part, stream);
-		if (nwritten == -1)
+		if ((nwritten = g_mime_object_write_to_stream (part, stream)) == -1)
 			return -1;
 		
 		total += nwritten;
@@ -280,16 +276,14 @@
 		node = node->next;
 	}
 	
-	nwritten = g_mime_stream_printf (stream, "\n--%s--\n", multipart->boundary);
-	if (nwritten == -1)
+	if ((nwritten = g_mime_stream_printf (stream, "\n--%s--\n", multipart->boundary)) == -1)
 		return -1;
 	
 	total += nwritten;
 	
 	/* write the postface */
 	if (multipart->postface) {
-		nwritten = g_mime_stream_write_string (stream, multipart->postface);
-		if (nwritten == -1)
+		if ((nwritten = g_mime_stream_write_string (stream, multipart->postface)) == -1)
 			return -1;
 		
 		total += nwritten;
@@ -419,7 +413,7 @@
 static void
 multipart_add_part (GMimeMultipart *multipart, GMimeObject *part)
 {
-	g_mime_object_ref (part);
+	g_object_ref (part);
 	multipart->subparts = g_list_append (multipart->subparts, part);
 }
 
@@ -444,7 +438,7 @@
 static void
 multipart_add_part_at (GMimeMultipart *multipart, GMimeObject *part, int index)
 {
-	g_mime_object_ref (part);
+	g_object_ref (part);
 	multipart->subparts = g_list_insert (multipart->subparts, part, index);
 }
 
@@ -495,11 +489,12 @@
 	} else {
 		if (node->next)
 			node->next->prev = node->prev;
+		node->prev->next = node->next;
 	}
 	
 	g_list_free_1 (node);
 	
-	g_mime_object_unref (part);
+	g_object_unref (part);
 }
 
 
@@ -526,8 +521,7 @@
 	GMimeObject *part;
 	GList *node;
 	
-	node = g_list_nth (multipart->subparts, index);
-	if (!node) {
+	if (!(node = g_list_nth (multipart->subparts, index))) {
 		d(g_warning ("multipart_remove_part_at: no part at index %d within %p", index, multipart));
 		return NULL;
 	}
@@ -541,6 +535,7 @@
 	} else {
 		if (node->next)
 			node->next->prev = node->prev;
+		node->prev->next = node->next;
 	}
 	
 	g_list_free_1 (node);
@@ -574,15 +569,14 @@
 	GMimeObject *part;
 	GList *node;
 	
-	node = g_list_nth (multipart->subparts, index);
-	if (!node) {
+	if (!(node = g_list_nth (multipart->subparts, index))) {
 		d(g_warning ("multipart_get_part: no part at index %d within %p", index, multipart));
 		return NULL;
 	}
 	
 	part = node->data;
 	
-	g_mime_object_ref (part);
+	g_object_ref (part);
 	
 	return part;
 }
@@ -639,10 +633,8 @@
 {
 	int fd;
 	
-	fd = open ("/dev/urandom", O_RDONLY);
-	if (fd == -1) {
-		fd = open ("/dev/random", O_RDONLY);
-		if (fd == -1)
+	if ((fd = open ("/dev/urandom", O_RDONLY)) == -1) {
+		if ((fd = open ("/dev/random", O_RDONLY)) == -1)
 			return;
 	}
 	
@@ -730,30 +722,23 @@
  * @callback: function to call for @multipart and all of its subparts
  * @user_data: extra data to pass to the callback
  * 
- * Calls @callback on @multipart and each of its subparts.
+ * Calls @callback on each of @multipart's subparts.
  **/
 void
 g_mime_multipart_foreach (GMimeMultipart *multipart, GMimePartFunc callback, gpointer user_data)
 {
+	GList *subpart;
+	
 	g_return_if_fail (GMIME_IS_MULTIPART (multipart));
 	g_return_if_fail (callback != NULL);
 	
-	callback (GMIME_OBJECT (multipart), user_data);
-	
-	if (multipart->subparts) {
-		GList *subpart;
+	subpart = multipart->subparts;
+	while (subpart) {
+		GMimeObject *part = subpart->data;
+		
+		callback (part, user_data);
 		
-		subpart = multipart->subparts;
-		while (subpart) {
-			GMimeObject *part = subpart->data;
-			
-			if (GMIME_IS_MULTIPART (part))
-				g_mime_multipart_foreach (GMIME_MULTIPART (part), callback, user_data);
-			else
-				callback (part, user_data);
-			
-			subpart = subpart->next;
-		}
+		subpart = subpart->next;
 	}
 }
 
@@ -769,7 +754,7 @@
  * Returns the GMimeObject whose content-id matches the search string,
  * or %NULL if a match cannot be found.
  **/
-const GMimeObject *
+GMimeObject *
 g_mime_multipart_get_subpart_from_content_id (GMimeMultipart *multipart, const char *content_id)
 {
 	GMimeObject *object = (GMimeObject *) multipart;
@@ -778,22 +763,22 @@
 	g_return_val_if_fail (GMIME_IS_MULTIPART (multipart), NULL);
 	g_return_val_if_fail (content_id != NULL, NULL);
 	
-	if (object->content_id && !strcmp (object->content_id, content_id))
+	if (object->content_id && !strcmp (object->content_id, content_id)) {
+		g_object_ref (object);
 		return object;
+	}
 	
 	subparts = multipart->subparts;
 	while (subparts) {
-		const GMimeContentType *type;
-		const GMimeObject *part;
+		GMimeObject *part = NULL;
 		GMimeObject *subpart;
 		
 		subpart = subparts->data;
-		type = g_mime_object_get_content_type (GMIME_OBJECT (subpart));
 		
-		if (g_mime_content_type_is_type (type, "multipart", "*")) {
-			part = g_mime_multipart_get_subpart_from_content_id (GMIME_MULTIPART (subpart),
-									     content_id);
+		if (GMIME_IS_MULTIPART (subpart)) {
+			part = g_mime_multipart_get_subpart_from_content_id (GMIME_MULTIPART (subpart), content_id);
 		} else if (subpart->content_id && !strcmp (subpart->content_id, content_id)) {
+			g_object_ref (subpart);
 			part = subpart;
 		}
 		
Index: gmime/gmime-multipart.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-multipart.h,v
retrieving revision 1.2
diff -u -r1.2 gmime-multipart.h
--- gmime/gmime-multipart.h	30 Dec 2002 16:37:57 -0000	1.2
+++ gmime/gmime-multipart.h	7 Jun 2004 06:10:16 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,15 +24,15 @@
 #ifndef __GMIME_MULTIPART_H__
 #define __GMIME_MULTIPART_H__
 
+#include <glib.h>
+
+#include <gmime/gmime-object.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-
-#include "gmime-object.h"
-
 #define GMIME_TYPE_MULTIPART            (g_mime_multipart_get_type ())
 #define GMIME_MULTIPART(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_MULTIPART, GMimeMultipart))
 #define GMIME_MULTIPART_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_MULTIPART, GMimeMultipartClass))
@@ -57,17 +57,17 @@
 struct _GMimeMultipartClass {
 	GMimeObjectClass parent_class;
 	
-	void (*add_part) (GMimeMultipart *multipart, GMimeObject *part);
-	void (*add_part_at) (GMimeMultipart *multipart, GMimeObject *part, int index);
-	void (*remove_part) (GMimeMultipart *multipart, GMimeObject *part);
+	void (* add_part) (GMimeMultipart *multipart, GMimeObject *part);
+	void (* add_part_at) (GMimeMultipart *multipart, GMimeObject *part, int index);
+	void (* remove_part) (GMimeMultipart *multipart, GMimeObject *part);
 	
-	GMimeObject * (*remove_part_at) (GMimeMultipart *multipart, int index);
-	GMimeObject * (*get_part) (GMimeMultipart *multipart, int index);
+	GMimeObject * (* remove_part_at) (GMimeMultipart *multipart, int index);
+	GMimeObject * (* get_part) (GMimeMultipart *multipart, int index);
 	
-	int  (*get_number) (GMimeMultipart *multipart);
+	int  (* get_number) (GMimeMultipart *multipart);
 	
-	void (*set_boundary) (GMimeMultipart *multipart, const char *boundary);
-	const char * (*get_boundary) (GMimeMultipart *multipart);
+	void (* set_boundary) (GMimeMultipart *multipart, const char *boundary);
+	const char * (* get_boundary) (GMimeMultipart *multipart);
 };
 
 
@@ -97,8 +97,8 @@
 
 void g_mime_multipart_foreach (GMimeMultipart *multipart, GMimePartFunc callback, gpointer user_data);
 
-const GMimeObject *g_mime_multipart_get_subpart_from_content_id (GMimeMultipart *multipart,
-								 const char *content_id);
+GMimeObject *g_mime_multipart_get_subpart_from_content_id (GMimeMultipart *multipart,
+							   const char *content_id);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-object.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-object.c,v
retrieving revision 1.9
diff -u -r1.9 gmime-object.c
--- gmime/gmime-object.c	26 Dec 2002 18:50:15 -0000	1.9
+++ gmime/gmime-object.c	7 Jun 2004 06:10:16 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001-2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -28,8 +28,10 @@
 #include <ctype.h>
 #include <string.h>
 
+#include "gmime-common.h"
 #include "gmime-object.h"
 #include "gmime-stream-mem.h"
+#include "gmime-utils.h"
 
 struct _type_bucket {
 	char *type;
@@ -53,10 +55,8 @@
 static void remove_header (GMimeObject *object, const char *header);
 static void set_content_type (GMimeObject *object, GMimeContentType *content_type);
 static char *get_headers (GMimeObject *object);
-static gssize write_to_stream (GMimeObject *object, GMimeStream *stream);
+static ssize_t write_to_stream (GMimeObject *object, GMimeStream *stream);
 
-static int strcase_equal (gconstpointer v, gconstpointer v2);
-static guint strcase_hash (gconstpointer key);
 static void type_registry_init (void);
 
 static GHashTable *type_hash = NULL;
@@ -140,13 +140,15 @@
  * @object: mime object
  *
  * Ref's a MIME object.
+ *
+ * WARNING: This method is deprecated. Use g_object_ref() instead.
  **/
 void
 g_mime_object_ref (GMimeObject *object)
 {
 	g_return_if_fail (GMIME_IS_OBJECT (object));
 	
-	g_object_ref ((GObject *) object);
+	g_object_ref (object);
 }
 
 
@@ -155,13 +157,15 @@
  * @object: mime object
  *
  * Unref's a MIME object.
+ *
+ * WARNING: This method is deprecated. Use g_object_unref() instead.
  **/
 void
 g_mime_object_unref (GMimeObject *object)
 {
 	g_return_if_fail (GMIME_IS_OBJECT (object));
 	
-	g_object_unref ((GObject *) object);
+	g_object_unref (object);
 }
 
 
@@ -172,7 +176,7 @@
  * @object_type: object type
  *
  * Registers the object type @object_type for use with the
- * #g_mime_object_new_type convenience function.
+ * g_mime_object_new_type() convenience function.
  *
  * Note: You may use the wildcard "*" to match any type and/or
  * subtype.
@@ -194,7 +198,7 @@
 		bucket = g_new (struct _type_bucket, 1);
 		bucket->type = g_strdup (type);
 		bucket->object_type = *type == '*' ? object_type : 0;
-		bucket->subtype_hash = g_hash_table_new (strcase_hash, strcase_equal);
+		bucket->subtype_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
 		g_hash_table_insert (type_hash, bucket->type, bucket);
 	}
 	
@@ -218,7 +222,7 @@
  * @subtype: mime subtype
  *
  * Performs a lookup of registered #GMimeObject subclasses, registered
- * using #g_mime_object_register_type, to find an appropriate class
+ * using g_mime_object_register_type(), to find an appropriate class
  * capable of handling MIME parts of type @type/@subtype. If no class
  * has been registered to handle that type, it looks for a registered
  * class that can handle @type. If that also fails, then it will use
@@ -390,19 +394,23 @@
 /**
  * g_mime_object_set_content_id:
  * @object: MIME object
- * @content_id: content-id
+ * @content_id: content-id (addr-spec portion)
  *
  * Sets the Content-Id of the MIME object.
  **/
 void
 g_mime_object_set_content_id (GMimeObject *object, const char *content_id)
 {
+	char *msgid;
+	
 	g_return_if_fail (GMIME_IS_OBJECT (object));
 	
 	g_free (object->content_id);
 	object->content_id = g_strdup (content_id);
 	
-	g_mime_object_set_header (object, "Content-Id", content_id);
+	msgid = g_strdup_printf ("<%s>", content_id);
+	g_mime_object_set_header (object, "Content-Id", msgid);
+	g_free (msgid);
 }
 
 
@@ -442,7 +450,7 @@
 	int i;
 	
 	for (i = 0; headers[i]; i++) {
-		if (!g_strcasecmp (headers[i], header))
+		if (!strcasecmp (headers[i], header))
 			break;
 	}
 	
@@ -455,8 +463,7 @@
 		break;
 	case HEADER_CONTENT_ID:
 		g_free (object->content_id);
-		object->content_id = g_strdup (value);
-		g_strstrip (object->content_id);
+		object->content_id = g_mime_utils_decode_message_id (value);
 		break;
 	default:
 		break;
@@ -594,7 +601,7 @@
 }
 
 
-static gssize
+static ssize_t
 write_to_stream (GMimeObject *object, GMimeStream *stream)
 {
 	return -1;
@@ -610,7 +617,7 @@
  *
  * Returns -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_object_write_to_stream (GMimeObject *object, GMimeStream *stream)
 {
 	g_return_val_if_fail (GMIME_IS_OBJECT (object), -1);
@@ -644,7 +651,7 @@
 	
 	g_mime_object_write_to_stream (object, stream);
 	
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 	g_byte_array_append (array, "", 1);
 	str = array->data;
 	g_byte_array_free (array, FALSE);
@@ -653,25 +660,6 @@
 }
 
 
-static int
-strcase_equal (gconstpointer v, gconstpointer v2)
-{
-	return g_strcasecmp ((const char *) v, (const char *) v2) == 0;
-}
-
-static guint
-strcase_hash (gconstpointer key)
-{
-	const char *p = key;
-	guint h = tolower (*p);
-	
-	if (h)
-		for (p += 1; *p != '\0'; p++)
-			h = (h << 5) - h + tolower (*p);
-	
-	return h;
-}
-
 static void
 subtype_bucket_foreach (gpointer key, gpointer value, gpointer user_data)
 {
@@ -706,7 +694,7 @@
 	if (type_hash)
 		return;
 	
-	type_hash = g_hash_table_new (strcase_hash, strcase_equal);
+	type_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
 	
 	g_atexit (type_registry_shutdown);
 }
Index: gmime/gmime-object.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-object.h,v
retrieving revision 1.7
diff -u -r1.7 gmime-object.h
--- gmime/gmime-object.h	30 Dec 2002 16:37:57 -0000	1.7
+++ gmime/gmime-object.h	7 Jun 2004 06:10:16 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001-2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,19 +24,19 @@
 #ifndef __GMIME_OBJECT_H__
 #define __GMIME_OBJECT_H__
 
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gmime/gmime-type-utils.h>
+#include <gmime/gmime-content-type.h>
+#include <gmime/gmime-stream.h>
+#include <gmime/gmime-header.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include <glib-object.h>
-
-#include "gmime-type-utils.h"
-#include "gmime-content-type.h"
-#include "gmime-stream.h"
-#include "gmime-header.h"
-
 #define GMIME_TYPE_OBJECT            (g_mime_object_get_type ())
 #define GMIME_OBJECT(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_OBJECT, GMimeObject))
 #define GMIME_OBJECT_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_OBJECT, GMimeObjectClass))
@@ -59,18 +59,18 @@
 struct _GMimeObjectClass {
 	GObjectClass parent_class;
 	
-	void         (*init)          (GMimeObject *object);
+	void         (* init)          (GMimeObject *object);
 	
-	void         (*add_header)    (GMimeObject *object, const char *header, const char *value);
-	void         (*set_header)    (GMimeObject *object, const char *header, const char *value);
-	const char * (*get_header)    (GMimeObject *object, const char *header);
-	void         (*remove_header) (GMimeObject *object, const char *header);
+	void         (* add_header)    (GMimeObject *object, const char *header, const char *value);
+	void         (* set_header)    (GMimeObject *object, const char *header, const char *value);
+	const char * (* get_header)    (GMimeObject *object, const char *header);
+	void         (* remove_header) (GMimeObject *object, const char *header);
 	
-	void         (*set_content_type) (GMimeObject *object, GMimeContentType *content_type);
+	void         (* set_content_type) (GMimeObject *object, GMimeContentType *content_type);
 	
-	char *       (*get_headers)   (GMimeObject *object);
+	char *       (* get_headers)   (GMimeObject *object);
 	
-	gssize      (*write_to_stream) (GMimeObject *object, GMimeStream *stream);
+	ssize_t      (* write_to_stream) (GMimeObject *object, GMimeStream *stream);
 };
 
 
@@ -82,8 +82,10 @@
 void g_mime_object_register_type (const char *type, const char *subtype, GType object_type);
 GMimeObject *g_mime_object_new_type (const char *type, const char *subtype);
 
+#ifndef GMIME_DISABLE_DEPRECATED
 void g_mime_object_ref (GMimeObject *object);
 void g_mime_object_unref (GMimeObject *object);
+#endif
 
 void g_mime_object_set_content_type (GMimeObject *object, GMimeContentType *mime_type);
 const GMimeContentType *g_mime_object_get_content_type (GMimeObject *object);
@@ -101,7 +103,7 @@
 
 char *g_mime_object_get_headers (GMimeObject *object);
 
-gssize g_mime_object_write_to_stream (GMimeObject *object, GMimeStream *stream);
+ssize_t g_mime_object_write_to_stream (GMimeObject *object, GMimeStream *stream);
 char *g_mime_object_to_string (GMimeObject *object);
 
 #ifdef __cplusplus
Index: gmime/gmime-param.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-param.c,v
retrieving revision 1.16
diff -u -r1.16 gmime-param.c
--- gmime/gmime-param.c	30 Dec 2002 16:37:57 -0000	1.16
+++ gmime/gmime-param.c	7 Jun 2004 06:10:17 -0000
@@ -1,8 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -30,19 +30,21 @@
 #include <errno.h>
 
 #include "gmime-param.h"
+#include "gmime-common.h"
 #include "gmime-table-private.h"
 #include "gmime-charset.h"
 #include "gmime-utils.h"
 #include "gmime-iconv.h"
 #include "gmime-iconv-utils.h"
 
-#include <glib/gmem.h>
-#include <glib/gmessages.h>
-#include <glib/gstrfuncs.h>
-#include <glib/galloca.h>
 
-#define d(x) x
+#ifdef ENABLE_WARNINGS
+#define w(x) x
+#else
 #define w(x)
+#endif /* ENABLE_WARNINGS */
+
+#define d(x)
 
 
 static unsigned char tohex[16] = {
@@ -74,104 +76,6 @@
 	return param;
 }
 
-
-#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
-
-static size_t
-hex_decode (const unsigned char *in, size_t len, unsigned char *out)
-{
-	register const unsigned char *inptr;
-	register unsigned char *outptr;
-	const unsigned char *inend;
-	
-	inptr = in;
-	inend = in + len;
-	
-	outptr = out;
-	
-	while (inptr < inend) {
-		if (*inptr == '%') {
-			if (isxdigit (inptr[1]) && isxdigit (inptr[2])) {
-				*outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
-				inptr += 3;
-			} else
-				*outptr++ = *inptr++;
-		} else
-			*outptr++ = *inptr++;
-	}
-	
-	*outptr = '\0';
-	
-	return outptr - out;
-}
-
-/* an rfc2184 encoded string looks something like:
- * us-ascii'en'This%20is%20even%20more%20
- */
-static char *
-rfc2184_decode (const char *in, size_t len)
-{
-	const char *inptr = in;
-	const char *inend = in + len;
-	const char *charset = NULL;
-	char *decoded = NULL;
-	char *charenc;
-	
-	/* skips to the end of the charset / beginning of the locale */
-	inptr = memchr (inptr, '\'', len);
-	if (!inptr)
-		return NULL;
-	
-	/* save the charset */
-	len = inptr - in;
-	charenc = g_alloca (len + 1);
-	memcpy (charenc, in, len);
-	charenc[len] = '\0';
-	charset = charenc;
-	
-	/* skip to the end of the locale */
-	inptr = memchr (inptr + 1, '\'', (unsigned int) (inend - inptr - 1));
-	if (!inptr)
-		return NULL;
-	
-	inptr++;
-	if (inptr < inend) {
-		len = inend - inptr;
-		if (g_strcasecmp (charset, "UTF-8") != 0) {
-			char *udecoded;
-			iconv_t cd;
-			
-			decoded = g_alloca (len + 1);
-			len = hex_decode (inptr, len, decoded);
-			
-			cd = g_mime_iconv_open ("UTF-8", charset);
-			if (cd == (iconv_t) -1) {
-				d(g_warning ("Cannot convert from %s to UTF-8, param display may "
-					     "be corrupt: %s", charset, g_strerror (errno)));
-				charset = g_mime_charset_locale_name ();
-				cd = g_mime_iconv_open ("UTF-8", charset);
-				if (cd == (iconv_t) -1)
-					return NULL;
-			}
-			
-			udecoded = g_mime_iconv_strndup (cd, decoded, len);
-			g_mime_iconv_close (cd);
-			
-			if (!udecoded) {
-				d(g_warning ("Failed to convert \"%.*s\" to UTF-8, display may be "
-					     "corrupt: %s", (int)len, decoded, g_strerror (errno)));
-			}
-			
-			decoded = udecoded;
-		} else {
-			decoded = g_malloc (len + 1);
-			hex_decode (inptr, len, decoded);
-		}
-	}
-	
-	return decoded;
-}
-
 static void
 decode_lwsp (const char **in)
 {
@@ -308,13 +212,13 @@
 }
 
 static gboolean
-decode_rfc2184_param (const char **in, char **paramp, int *part, gboolean *value_is_encoded)
+decode_rfc2184_param (const char **in, char **paramp, int *part, gboolean *encoded)
 {
 	gboolean is_rfc2184 = FALSE;
 	const char *inptr = *in;
 	char *param;
 	
-	*value_is_encoded = FALSE;
+	*encoded = FALSE;
 	*part = -1;
 	
 	param = decode_param_token (&inptr);
@@ -328,8 +232,7 @@
 		decode_lwsp (&inptr);
 		if (*inptr == '=') {
 			/* form := param*=value */
-			if (value_is_encoded)
-				*value_is_encoded = TRUE;
+			*encoded = TRUE;
 		} else {
 			/* form := param*#=value or param*#*=value */
 			*part = decode_int (&inptr);
@@ -337,9 +240,8 @@
 			decode_lwsp (&inptr);
 			if (*inptr == '*') {
 				/* form := param*#*=value */
-				if (value_is_encoded)
-					*value_is_encoded = TRUE;
 				inptr++;
+				*encoded = TRUE;
 				decode_lwsp (&inptr);
 			}
 		}
@@ -354,171 +256,351 @@
 	return is_rfc2184;
 }
 
-static int
-decode_param (const char **in, char **paramp, char **valuep, gboolean *is_rfc2184_param)
+static gboolean
+decode_param (const char **in, char **paramp, char **valuep, int *id, gboolean *encoded)
 {
-	gboolean is_rfc2184_encoded = FALSE;
 	gboolean is_rfc2184 = FALSE;
 	const char *inptr = *in;
 	char *param, *value = NULL;
-	int rfc2184_part = -1;
-	
-	*is_rfc2184_param = FALSE;
+	char *val;
 	
-	is_rfc2184 = decode_rfc2184_param (&inptr, &param, &rfc2184_part,
-					   &is_rfc2184_encoded);
+	is_rfc2184 = decode_rfc2184_param (&inptr, &param, id, encoded);
 	
 	if (*inptr == '=') {
 		inptr++;
 		value = decode_value (&inptr);
 		
-		if (is_rfc2184) {
-			/* We have ourselves an rfc2184 parameter */
-			if (rfc2184_part == -1) {
-				/* rfc2184 allows the value to be broken into
-				 * multiple parts - this isn't one of them so
-				 * it is safe to decode it.
+		if (!is_rfc2184 && value) {
+			if (!strncmp (value, "=?", 2)) {
+				/* We have a broken param value that is rfc2047 encoded.
+				 * Since both Outlook and Netscape/Mozilla do this, we
+				 * should handle this case.
 				 */
-				char *val;
 				
-				val = rfc2184_decode (value, strlen (value));
-				if (val) {
+				if ((val = g_mime_utils_header_decode_text (value))) {
 					g_free (value);
 					value = val;
 				}
-			} else {
-				/* Since we are expecting to find the rest of
-				 * this paramter value later, let our caller know.
-				 */
-				*is_rfc2184_param = TRUE;
 			}
-		} else if (value && !strncmp (value, "=?", 2)) {
-			/* We have a broken param value that is rfc2047 encoded.
-			 * Since both Outlook and Netscape/Mozilla do this, we
-			 * should handle this case.
-			 */
-			char *val;
 			
-			val = g_mime_utils_8bit_header_decode (value);
-			if (val) {
-				g_free (value);
-				value = val;
+			if (!g_utf8_validate (value, -1, NULL)) {
+				/* A (broken) mailer has sent us an unencoded 8bit value.
+				 * Attempt to save it by assuming it's in the user's
+				 * locale and converting to UTF-8 */
+				
+				if ((val = g_mime_iconv_locale_to_utf8 (value))) {
+					g_free (value);
+					value = val;
+				} else {
+					d(g_warning ("Failed to convert %s param value (\"%s\") to UTF-8: %s",
+						     param, value, g_strerror (errno)));
+				}
 			}
 		}
 	}
 	
-	if (value && !g_utf8_validate (value, -1, NULL)) {
-		/* A (broken) mailer has sent us an unencoded 8bit value.
-		 * Attempt to save it by assuming it's in the user's
-		 * locale and converting to UTF-8 */
-		char *buf;
-		
-		buf = g_mime_iconv_locale_to_utf8 (value);
-		if (buf) {
-			g_free (value);
-			value = buf;
-		} else {
-			d(g_warning ("Failed to convert %s param value (\"%s\") to UTF-8: %s",
-				     param, value, g_strerror (errno)));
-		}
-	}
-	
 	if (param && value) {
 		*paramp = param;
 		*valuep = value;
 		*in = inptr;
-		return 0;
+		return TRUE;
 	} else {
 		g_free (param);
 		g_free (value);
-		return 1;
+		return FALSE;
 	}
 }
 
+
+struct _rfc2184_part {
+	char *value;
+	int id;
+};
+
+struct _rfc2184_param {
+	struct _rfc2184_param *next;
+	const char *charset;
+	GMimeParam *param;
+	GPtrArray *parts;
+	char *lang;
+};
+
+static int
+rfc2184_sort_cb (const void *v0, const void *v1)
+{
+	const struct _rfc2184_part *p0 = *((struct _rfc2184_part **) v0);
+	const struct _rfc2184_part *p1 = *((struct _rfc2184_part **) v1);
+	
+	return p0->id - p1->id;
+}
+
+#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
+
+static size_t
+hex_decode (const unsigned char *in, size_t len, unsigned char *out)
+{
+	register const unsigned char *inptr;
+	register unsigned char *outptr;
+	const unsigned char *inend;
+	
+	inptr = in;
+	inend = in + len;
+	
+	outptr = out;
+	
+	while (inptr < inend) {
+		if (*inptr == '%') {
+			if (isxdigit (inptr[1]) && isxdigit (inptr[2])) {
+				*outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
+				inptr += 3;
+			} else
+				*outptr++ = *inptr++;
+		} else
+			*outptr++ = *inptr++;
+	}
+	
+	*outptr = '\0';
+	
+	return outptr - out;
+}
+
+static const char *
+rfc2184_param_charset (const char **in, char **langp)
+{
+	const char *lang, *inptr = *in;
+	char *charset;
+	size_t len;
+	
+	if (langp)
+		*langp = NULL;
+	
+	while (*inptr != '\0' && *inptr != '\'')
+		inptr++;
+	
+	if (*inptr != '\'')
+		return NULL;
+	
+	len = inptr - *in;
+	charset = g_alloca (len + 1);
+	memcpy (charset, *in, len);
+	charset[len] = '\0';
+	
+	lang = ++inptr;
+	while (*inptr != '\0' && *inptr != '\'')
+		inptr++;
+	
+	if (*inptr == '\'') {
+		if (langp)
+			*langp = g_strndup (lang, inptr - lang);
+		
+		inptr++;
+	}
+	
+	*in = inptr;
+	
+	return g_mime_charset_canon_name (charset);
+}
+
+static char *
+charset_convert (const char *charset, char *in, size_t inlen)
+{
+	gboolean locale = FALSE;
+	char *result = NULL;
+	iconv_t cd;
+	
+	if (!charset || !strcasecmp (charset, "UTF-8") || !strcasecmp (charset, "us-ascii")) {
+		/* we shouldn't need any charset conversion here... */
+		if (g_utf8_validate (in, inlen, NULL))
+			return in;
+		
+		charset = g_mime_locale_charset ();
+		locale = TRUE;
+	}
+	
+	/* need charset conversion */
+	cd = g_mime_iconv_open ("UTF-8", charset);
+	if (cd == (iconv_t) -1 && !locale) {
+		charset = g_mime_locale_charset ();
+		cd = g_mime_iconv_open ("UTF-8", charset);
+	}
+	
+	if (cd != (iconv_t) -1) {
+		result = g_mime_iconv_strndup (cd, in, inlen);
+		g_mime_iconv_close (cd);
+	}
+	
+	if (result == NULL)
+		result = in;
+	else
+		g_free (in);
+	
+	return result;
+}
+
+static char *
+rfc2184_decode (const char *value)
+{
+	const char *inptr = value;
+	const char *charset;
+	char *udecoded;
+	char *decoded;
+	size_t len;
+	
+	charset = rfc2184_param_charset (&inptr, NULL);
+	
+	len = strlen (inptr);
+	decoded = g_alloca (len + 1);
+	len = hex_decode (inptr, len, decoded);
+	
+	return charset_convert (charset, g_strdup (decoded), len);
+}
+
+static void
+rfc2184_param_add_part (struct _rfc2184_param *rfc2184, char *value, int id, gboolean encoded)
+{
+	struct _rfc2184_part *part;
+	size_t len;
+	
+	part = g_new (struct _rfc2184_part, 1);
+	g_ptr_array_add (rfc2184->parts, part);
+	part->id = id;
+	
+	if (encoded) {
+		len = strlen (value);
+		part->value = g_malloc (len + 1);
+		hex_decode (value, len, part->value);
+		g_free (value);
+	} else {
+		part->value = value;
+	}
+}
+
+static struct _rfc2184_param *
+rfc2184_param_new (char *name, char *value, int id, gboolean encoded)
+{
+	struct _rfc2184_param *rfc2184;
+	struct _rfc2184_part *part;
+	const char *inptr = value;
+	
+	rfc2184 = g_new (struct _rfc2184_param, 1);
+	rfc2184->parts = g_ptr_array_new ();
+	rfc2184->next = NULL;
+	
+	rfc2184->charset = rfc2184_param_charset (&inptr, &rfc2184->lang);
+	
+	if (inptr == value) {
+		rfc2184_param_add_part (rfc2184, value, id, encoded);
+	} else {
+		rfc2184_param_add_part (rfc2184, g_strdup (inptr), id, encoded);
+		g_free (value);
+	}
+	
+	rfc2184->param = g_new (GMimeParam, 1);
+	rfc2184->param->next = NULL;
+	rfc2184->param->name = name;
+	rfc2184->param->value = NULL;
+	
+	return rfc2184;
+}
+
 static GMimeParam *
-decode_param_list (const char **in)
+decode_param_list (const char *in)
 {
-	const char *inptr = *in;
-	GMimeParam *head = NULL, *tail = NULL;
-	gboolean last_was_rfc2184 = FALSE;
-	gboolean is_rfc2184 = FALSE;
+	struct _rfc2184_param *rfc2184, *list, *t;
+	GMimeParam *param, *params, *tail;
+	struct _rfc2184_part *part;
+	GHashTable *rfc2184_hash;
+	const char *inptr = in;
+	char *name, *value;
+	gboolean encoded;
+	GString *gvalue;
+	int id, i;
+	
+	params = NULL;
+	tail = (GMimeParam *) &params;
+	
+	list = NULL;
+	t = (struct _rfc2184_param *) &list;
+	rfc2184_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
 	
 	decode_lwsp (&inptr);
 	
 	do {
-		GMimeParam *param = NULL;
-		char *name, *value;
-		
 		/* invalid format? */
-		if (decode_param (&inptr, &name, &value, &is_rfc2184) != 0) {
-			if (*inptr == ';') {
+		if (!decode_param (&inptr, &name, &value, &id, &encoded)) {
+			decode_lwsp (&inptr);
+			
+			if (*inptr == ';')
 				continue;
-			}
+			
 			break;
 		}
 		
-		if (is_rfc2184 && tail && !g_strcasecmp (name, tail->name)) {
-			/* rfc2184 allows a parameter to be broken into multiple parts
-			 * and it looks like we've found one. Append this value to the
-			 * last value.
-			 */
-			GString *gvalue;
-			
-			gvalue = g_string_new (tail->value);
-			g_string_append (gvalue, value);
-			g_free (tail->value);
-			g_free (value);
-			g_free (name);
-			
-			tail->value = gvalue->str;
-			g_string_free (gvalue, FALSE);
-		} else {
-			if (last_was_rfc2184) {
-				/* We've finished gathering the values for the last param
-				 * so it is now safe to decode it.
-				 */
-				char *val;
+		if (id != -1) {
+			/* we have a multipart rfc2184 param */
+			if (!(rfc2184 = g_hash_table_lookup (rfc2184_hash, name))) {
+				rfc2184 = rfc2184_param_new (name, value, id, encoded);
+				param = rfc2184->param;
+				t->next = rfc2184;
+				t = rfc2184;
 				
-				val = rfc2184_decode (tail->value, strlen (tail->value));
-				if (val) {
-					g_free (tail->value);
-					tail->value = val;
-				}
+				g_hash_table_insert (rfc2184_hash, param->name, rfc2184);
+				
+				tail->next = param;
+				tail = param;
+			} else {
+				rfc2184_param_add_part (rfc2184, value, id, encoded);
+				g_free (name);
 			}
-			
+		} else {
 			param = g_new (GMimeParam, 1);
 			param->next = NULL;
 			param->name = name;
-			param->value = value;
 			
-			if (head == NULL)
-				head = param;
-			if (tail)
-				tail->next = param;
+			if (encoded) {
+				/* singleton encoded rfc2184 param value */
+				param->value = rfc2184_decode (value);
+				g_free (value);
+			} else {
+				/* normal parameter value */
+				param->value = value;
+			}
+			
+			tail->next = param;
 			tail = param;
 		}
 		
-		last_was_rfc2184 = is_rfc2184;
-		
 		decode_lwsp (&inptr);
 	} while (*inptr++ == ';');
 	
-	if (last_was_rfc2184) {
-		/* We've finished gathering the values for the last param
-		 * so it is now safe to decode it.
-		 */
-		char *val;
-		
-		val = rfc2184_decode (tail->value, strlen (tail->value));
-		if (val) {
-			g_free (tail->value);
-			tail->value = val;
+	g_hash_table_destroy (rfc2184_hash);
+	
+	rfc2184 = list;
+	while (rfc2184 != NULL) {
+		t = rfc2184->next;
+		
+		param = rfc2184->param;
+		gvalue = g_string_new ("");
+		
+		g_ptr_array_sort (rfc2184->parts, rfc2184_sort_cb);
+		for (i = 0; i < rfc2184->parts->len; i++) {
+			part = rfc2184->parts->pdata[i];
+			g_string_append (gvalue, part->value);
+			g_free (part->value);
+			g_free (part);
 		}
+		
+		g_ptr_array_free (rfc2184->parts, TRUE);
+		
+		param->value = charset_convert (rfc2184->charset, gvalue->str, gvalue->len);
+		g_string_free (gvalue, FALSE);
+		
+		g_free (rfc2184->lang);
+		g_free (rfc2184);
+		rfc2184 = t;
 	}
 	
-	*in = inptr;
-	
-	return head;
+	return params;
 }
 
 
@@ -535,7 +617,7 @@
 {
 	g_return_val_if_fail (string != NULL, NULL);
 	
-	return decode_param_list (&string);
+	return decode_param_list (string);
 }
 
 
@@ -630,6 +712,7 @@
 	unsigned char *outbuf = NULL;
 	iconv_t cd = (iconv_t) -1;
 	const char *charset = NULL;
+	unsigned char c;
 	char *outstr;
 	GString *out;
 	
@@ -648,13 +731,18 @@
 	if (!charset)
 		charset = "iso-8859-1";
 	
-	if (g_strcasecmp (charset, "UTF-8") != 0)
+	if (strcasecmp (charset, "UTF-8") != 0)
 		cd = g_mime_iconv_open (charset, "UTF-8");
 	
 	if (cd != (iconv_t) -1) {
 		outbuf = g_mime_iconv_strdup (cd, in);
 		g_mime_iconv_close (cd);
-		inptr = outbuf;
+		if (outbuf == NULL) {
+			charset = "UTF-8";
+			inptr = in;
+		} else {
+			inptr = outbuf;
+		}
 	} else {
 		charset = "UTF-8";
 		inptr = in;
@@ -662,20 +750,13 @@
 	
 	/* FIXME: set the 'language' as well, assuming we can get that info...? */
 	out = g_string_new ("");
-	g_string_sprintfa (out, "%s''", charset);
+	g_string_append_printf (out, "%s''", charset);
 	
-	while (inptr && *inptr) {
-		unsigned char c = *inptr++;
-		
-		/* FIXME: make sure that '\'', '*', and ';' are also encoded */
-		
-		if (c > 127) {
-			g_string_sprintfa (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
-		} else if (is_lwsp (c) || !(gmime_special_table[c] & IS_ESAFE)) {
-			g_string_sprintfa (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
-		} else {
+	while ((c = *inptr++)) {
+		if (!is_attrchar (c))
+			g_string_append_printf (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
+		else
 			g_string_append_c (out, c);
-		}
 	}
 	
 	g_free (outbuf);
@@ -717,9 +798,9 @@
 	
 	while (param) {
 		gboolean encoded = FALSE;
-		gboolean quote = FALSE;
 		unsigned nlen, vlen;
 		int here = out->len;
+		int quote = 0;
 		char *value;
 		
 		if (!param->value) {
@@ -727,8 +808,7 @@
 			continue;
 		}
 		
-		value = encode_param (param->value, &encoded);
-		if (!value) {
+		if (!(value = encode_param (param->value, &encoded))) {
 			w(g_warning ("appending parameter %s=%s violates rfc2184",
 				     param->name, param->value));
 			value = g_strdup (param->value);
@@ -738,30 +818,27 @@
 			char *ch;
 			
 			for (ch = value; *ch; ch++) {
-				if (is_tspecial (*ch) || is_lwsp (*ch))
-					break;
+				if (!is_attrchar (*ch) || is_lwsp (*ch))
+					quote++;
 			}
-			
-			quote = ch && *ch;
 		}
 		
 		nlen = strlen (param->name);
 		vlen = strlen (value);
 		
-		if (used + nlen + vlen > GMIME_FOLD_LEN - 8) {
-			if (fold)
-				g_string_append (out, ";\n\t");
-			else
-				g_string_append (out, "; ");
-			
+		if (fold && (used + nlen + vlen + quote > GMIME_FOLD_LEN - 2)) {
+			g_string_append (out, ";\n\t");
 			here = out->len;
-			used = 0;
-		} else
-			out = g_string_append (out, "; ");
+			used = 1;
+		} else {
+			g_string_append (out, "; ");
+			here = out->len;
+			used += 2;
+		}
 		
-		if (nlen + vlen > GMIME_FOLD_LEN - 10) {
+		if (nlen + vlen + quote > GMIME_FOLD_LEN - 2) {
 			/* we need to do special rfc2184 parameter wrapping */
-			int maxlen = GMIME_FOLD_LEN - (nlen + 10);
+			int maxlen = GMIME_FOLD_LEN - (nlen + 6);
 			char *inptr, *inend;
 			int i = 0;
 			
@@ -788,10 +865,11 @@
 						g_string_append (out, "; ");
 					
 					here = out->len;
-					used = 0;
+					used = 1;
 				}
 				
-				g_string_sprintfa (out, "%s*%d%s=", param->name, i++, encoded ? "*" : "");
+				g_string_append_printf (out, "%s*%d%s=", param->name,
+							i++, encoded ? "*" : "");
 				
 				if (encoded || !quote)
 					g_string_append_len (out, inptr, ptr - inptr);
@@ -803,7 +881,7 @@
 				inptr = ptr;
 			}
 		} else {
-			g_string_sprintfa (out, "%s%s=", param->name, encoded ? "*" : "");
+			g_string_append_printf (out, "%s%s=", param->name, encoded ? "*" : "");
 			
 			if (encoded || !quote)
 				g_string_append_len (out, value, vlen);
Index: gmime/gmime-param.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-param.h,v
retrieving revision 1.3
diff -u -r1.3 gmime-param.h
--- gmime/gmime-param.h	30 Dec 2002 16:37:57 -0000	1.3
+++ gmime/gmime-param.h	7 Jun 2004 06:10:17 -0000
@@ -1,8 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,16 +20,16 @@
  *
  */
 
+
 #ifndef __GMIME_PARAM_H__
 #define __GMIME_PARAM_H__
 
+#include <glib.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <glib/gtypes.h>
-#include <glib/gstring.h>
 
 struct _GMimeParam {
 	struct _GMimeParam *next;
Index: gmime/gmime-parser.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-parser.c,v
retrieving revision 1.26
diff -u -r1.26 gmime-parser.c
--- gmime/gmime-parser.c	10 Dec 2003 22:15:38 -0000	1.26
+++ gmime/gmime-parser.c	7 Jun 2004 06:10:18 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@
 
 #include <string.h>
 #include <sys/types.h>
-#include <pcreposix.h>
+#include <regex.h>
 #include <ctype.h>
 
 #include "gmime-parser.h"
@@ -41,6 +41,12 @@
 #define isblank(c) ((c) == ' ' || (c) == '\t')
 #endif /* HAVE_ISBLANK */
 
+#ifdef ENABLE_WARNINGS
+#define w(x) x
+#else
+#define w(x)
+#endif /* ENABLE_WARNINGS */
+
 #define d(x)
 
 static void g_mime_parser_class_init (GMimeParserClass *klass);
@@ -96,11 +102,13 @@
 	off_t headers_start;
 	off_t header_start;
 	
-	unsigned int unstep:28;
+	unsigned int unstep:26;
 	unsigned int midline:1;
 	unsigned int seekable:1;
 	unsigned int scan_from:1;
 	unsigned int have_regex:1;
+	unsigned int persist_stream:1;
+	unsigned int respect_content_length:1;
 	
 	GMimeContentType *content_type;
 	struct _header_raw *headers;
@@ -111,9 +119,10 @@
 struct _boundary_stack {
 	struct _boundary_stack *parent;
 	unsigned char *boundary;
-	unsigned int boundarylen;
-	unsigned int boundarylenfinal;
-	unsigned int boundarylenmax;
+	size_t boundarylen;
+	size_t boundarylenfinal;
+	size_t boundarylenmax;
+	off_t content_end;
 };
 
 static void
@@ -121,7 +130,7 @@
 {
 	struct _GMimeParserPrivate *priv = parser->priv;
 	struct _boundary_stack *s;
-	unsigned int max;
+	size_t max;
 	
 	max = priv->bounds ? priv->bounds->boundarylenmax : 0;
 	
@@ -136,10 +145,12 @@
 	} else {
 		s->boundary = g_strdup_printf ("--%s--", boundary);
 		s->boundarylen = strlen (boundary) + 2;
-		s->boundarylenfinal = strlen (s->boundary);
+		s->boundarylenfinal = s->boundarylen + 2;
 	}
 	
 	s->boundarylenmax = MAX (s->boundarylenfinal, max);
+	
+	s->content_end = -1;
 }
 
 static void
@@ -149,7 +160,7 @@
 	struct _boundary_stack *s;
 	
 	if (!priv->bounds) {
-		g_warning ("boundary stack underflow");
+		d(g_warning ("boundary stack underflow"));
 		return;
 	}
 	
@@ -174,7 +185,7 @@
 	
 	h = headers;
 	while (h) {
-		if (!g_strcasecmp (h->name, name)) {
+		if (!strcasecmp (h->name, name)) {
 			if (offset)
 				*offset = h->offset;
 			return h->value;
@@ -243,6 +254,11 @@
 {
 	parser->priv = g_new (struct _GMimeParserPrivate, 1);
 	parser_init (parser, NULL);
+	
+	parser->priv->scan_from = FALSE;
+	parser->priv->have_regex = FALSE;
+	parser->priv->persist_stream = TRUE;
+	parser->priv->respect_content_length = FALSE;
 }
 
 static void
@@ -268,7 +284,7 @@
 	off_t offset = -1;
 	
 	if (stream) {
-		g_mime_stream_ref (stream);
+		g_object_ref (stream);
 		offset = g_mime_stream_tell (stream);
 	}
 	
@@ -295,8 +311,6 @@
 	priv->unstep = 0;
 	priv->midline = FALSE;
 	priv->seekable = offset != -1;
-	priv->scan_from = FALSE;
-	priv->have_regex = FALSE;
 	
 	priv->headers = NULL;
 	
@@ -309,7 +323,7 @@
 	struct _GMimeParserPrivate *priv = parser->priv;
 	
 	if (priv->stream)
-		g_mime_stream_unref (priv->stream);
+		g_object_unref (priv->stream);
 	
 	g_byte_array_free (priv->from_line, TRUE);
 	
@@ -350,6 +364,26 @@
 
 
 /**
+ * g_mime_parser_new_with_stream:
+ * @stream: raw message or part stream
+ *
+ * Creates a new parser object preset to parse @stream.
+ *
+ * Returns a new parser object.
+ **/
+GMimeParser *
+g_mime_parser_new_with_stream (GMimeStream *stream)
+{
+	GMimeParser *parser;
+	
+	parser = g_mime_parser_new ();
+	g_mime_parser_init_with_stream (parser, stream);
+	
+	return parser;
+}
+
+
+/**
  * g_mime_parser_init_with_stream:
  * @parser: MIME parser object
  * @stream: raw message or part stream
@@ -364,11 +398,11 @@
  * or resetting of the stream. Anything that will/could change the
  * current stream's offset is PROHIBITED.
  *
- * It is also recommended that you not use #g_mime_stream_tell because
- * it will not necessarily give you the current @parser offset since
- * @parser handles its own internal read-ahead buffer. Instead, it is
- * recommended that you use #g_mime_parser_tell if you have a reason
- * to need the current offset of the @parser.
+ * It is also recommended that you not use g_mime_stream_tell()
+ * because it will not necessarily give you the current @parser offset
+ * since @parser handles its own internal read-ahead buffer. Instead,
+ * it is recommended that you use g_mime_parser_tell() if you have a
+ * reason to need the current offset of the @parser.
  **/
 void
 g_mime_parser_init_with_stream (GMimeParser *parser, GMimeStream *stream)
@@ -382,18 +416,47 @@
 
 
 /**
- * g_mime_parser_set_scan_from:
+ * g_mime_parser_get_persist_stream:
  * @parser: MIME parser object
- * @scan_from: %TRUE to scan From-lines or %FALSE otherwise
  *
- * Sets whether or not @parser should scan mbox-style From-lines.
+ * Gets whether or not the underlying stream is persistant.
+ *
+ * Returns %TRUE if the @parser will leave the content on disk or
+ * %FALSE if it will load the content into memory.
+ **/
+gboolean
+g_mime_parser_get_persist_stream (GMimeParser *parser)
+{
+	g_return_val_if_fail (GMIME_IS_PARSER (parser), FALSE);
+	
+	return (parser->priv->persist_stream && parser->priv->seekable);
+}
+
+
+/**
+ * g_mime_parser_set_persist_stream:
+ * @parser: MIME parser object
+ * @persist: persist attribute
+ *
+ * Sets whether or not the @parser's underlying stream is persistant.
+ *
+ * If @persist is %TRUE, the @parser will attempt to construct
+ * messages/parts whos content will remain on disk rather than being
+ * loaded into memory so as to reduce memory usage. This is the default.
+ *
+ * If @persist is %FALSE, the @parser will always load message content
+ * into memory.
+ *
+ * Note: This attribute only serves as a hint to the @parser. If the
+ * underlying stream does not support seeking, then this attribute
+ * will be ignored.
  **/
 void
-g_mime_parser_set_scan_from (GMimeParser *parser, gboolean scan_from)
+g_mime_parser_set_persist_stream (GMimeParser *parser, gboolean persist)
 {
 	g_return_if_fail (GMIME_IS_PARSER (parser));
 	
-	parser->priv->scan_from = scan_from ? 1 : 0;
+	parser->priv->persist_stream = persist;
 }
 
 
@@ -416,6 +479,62 @@
 
 
 /**
+ * g_mime_parser_set_scan_from:
+ * @parser: MIME parser object
+ * @scan_from: %TRUE to scan From-lines or %FALSE otherwise
+ *
+ * Sets whether or not @parser should scan mbox-style From-lines.
+ **/
+void
+g_mime_parser_set_scan_from (GMimeParser *parser, gboolean scan_from)
+{
+	g_return_if_fail (GMIME_IS_PARSER (parser));
+	
+	parser->priv->scan_from = scan_from ? 1 : 0;
+}
+
+
+/**
+ * g_mime_parser_get_respect_content_length:
+ * @parser: MIME parser object
+ *
+ * Gets whether or not @parser is set to use Content-Length for
+ * determining the offset of the end of the message.
+ *
+ * Returns whether or not @parser is set to use Content-Length for
+ * determining the offset of the end of the message.
+ **/
+gboolean
+g_mime_parser_get_respect_content_length (GMimeParser *parser)
+{
+	g_return_val_if_fail (GMIME_IS_PARSER (parser), FALSE);
+	
+	return parser->priv->respect_content_length;
+}
+
+
+/**
+ * g_mime_parser_set_respect_content_length:
+ * @parser: MIME parser object
+ * @respect_content_length: %TRUE if the parser should use Content-Length headers or %FALSE otherwise.
+ *
+ * Sets whether or not @parser should respect Content-Length headers
+ * when deciding where to look for the start of the next message. Only
+ * used when the parser is also set to scan for From-lines.
+ *
+ * Most notably useful when parsing broken Solaris mbox files (See
+ * http://www.jwz.org/doc/content-length.html for details).
+ **/
+void
+g_mime_parser_set_respect_content_length (GMimeParser *parser, gboolean respect_content_length)
+{
+	g_return_if_fail (GMIME_IS_PARSER (parser));
+	
+	parser->priv->respect_content_length = respect_content_length ? 1 : 0;
+}
+
+
+/**
  * g_mime_parser_set_header_regex:
  * @parser: MIME parser object
  * @regex: regular expression
@@ -451,13 +570,13 @@
 }
 
 
-static gssize
+static ssize_t
 parser_fill (GMimeParser *parser)
 {
 	struct _GMimeParserPrivate *priv = parser->priv;
 	unsigned char *inbuf, *inptr, *inend;
 	size_t inlen, atleast = SCAN_HEAD;
-	gssize nread;
+	ssize_t nread;
 	
 	inbuf = priv->inbuf;
 	inptr = priv->inptr;
@@ -493,8 +612,7 @@
 	priv->inend = inbuf;
 	inend = priv->realbuf + SCAN_HEAD + SCAN_BUF - 1;
 	
-	nread = g_mime_stream_read (priv->stream, inbuf, inend - inbuf);
-	if (nread > 0)
+	if ((nread = g_mime_stream_read (priv->stream, inbuf, inend - inbuf)) > 0)
 		priv->inend += nread;
 	
 	priv->offset = g_mime_stream_tell (priv->stream);
@@ -504,10 +622,10 @@
 
 
 static off_t
-parser_offset (GMimeParser *parser, unsigned char *cur)
+parser_offset (struct _GMimeParserPrivate *priv, unsigned char *inptr)
 {
-	struct _GMimeParserPrivate *priv = parser->priv;
-	unsigned char *inptr = cur;
+	if (priv->offset == -1)
+		return -1;
 	
 	if (!inptr)
 		inptr = priv->inptr;
@@ -531,7 +649,7 @@
 	g_return_val_if_fail (GMIME_IS_PARSER (parser), -1);
 	g_return_val_if_fail (GMIME_IS_STREAM (parser->priv->stream), -1);
 	
-	return parser_offset (parser, NULL);
+	return parser_offset (parser->priv, NULL);
 }
 
 
@@ -592,7 +710,7 @@
 			inptr++;
 			
 			if (len >= 5 && !strncmp (start, "From ", 5)) {
-				priv->from_offset = parser_offset (parser, start);
+				priv->from_offset = parser_offset (priv, start);
 				g_byte_array_append (priv->from_line, start, len);
 				goto got_from;
 			}
@@ -612,7 +730,7 @@
 
 #define header_backup(priv, start, len) G_STMT_START {                    \
 	if (priv->headerleft <= len) {                                    \
-		unsigned int hlen, hoff;                                  \
+		size_t hlen, hoff;                                        \
 		                                                          \
 		hlen = hoff = priv->headerptr - priv->headerbuf;          \
 		hlen = hlen ? hlen : 1;                                   \
@@ -633,7 +751,7 @@
 #define header_parse(parser, priv, hend) G_STMT_START {                   \
 	register unsigned char *colon;                                    \
 	struct _header_raw *header;                                       \
-	unsigned int hlen;                                                \
+	size_t hlen;                                                      \
 	                                                                  \
 	header = g_new (struct _header_raw, 1);                           \
 	header->next = NULL;                                              \
@@ -648,7 +766,7 @@
 	header->name = g_strndup (priv->headerbuf, hlen);                 \
 	g_strstrip (header->name);                                        \
 	if (*colon != ':') {                                              \
-		g_warning ("Invalid header: %s", header->name);           \
+		w(g_warning ("Invalid header: %s", header->name));        \
 		header->value = header->name;                             \
 		header->name = g_strdup ("X-Invalid-Header");             \
 	} else {                                                          \
@@ -680,8 +798,8 @@
 	
 	priv->midline = FALSE;
 	hend = (struct _header_raw *) &priv->headers;
-	priv->headers_start = parser_offset (parser, NULL);
-	priv->header_start = parser_offset (parser, NULL);
+	priv->headers_start = parser_offset (priv, NULL);
+	priv->header_start = parser_offset (priv, NULL);
 	
 	inptr = priv->inptr;
 	inend = priv->inend;
@@ -704,7 +822,7 @@
 			while (*inptr != '\n')
 				inptr++;
 			
-			if (inptr + 1 >= inend) {
+			if (inptr == inend) {
 				/* we don't have enough data to tell if we
 				   got all of the header or not... */
 				priv->inptr = start;
@@ -727,7 +845,7 @@
 				} else {
 					priv->midline = FALSE;
 					header_parse (parser, priv, hend);
-					priv->header_start = parser_offset (parser, inptr);
+					priv->header_start = parser_offset (priv, inptr);
 				}
 			} else {
 				priv->midline = TRUE;
@@ -742,7 +860,6 @@
 	inend = priv->inend;
 	
 	header_backup (priv, inptr, inend - inptr);
-	/*header_parse (priv, hend);*/
 	
  headers_end:
 	
@@ -764,8 +881,7 @@
 	struct _GMimeParserPrivate *priv = parser->priv;
 	const char *content_type;
 	
-	content_type = header_raw_find (priv->headers, "Content-Type", NULL);
-	if (content_type)
+	if ((content_type = header_raw_find (priv->headers, "Content-Type", NULL)))
 		return g_mime_content_type_new_from_string (content_type);
 	
 	return NULL;
@@ -837,7 +953,7 @@
 }
 
 enum {
-	FOUND_EOS,
+	FOUND_EOS          = 1,
 	FOUND_BOUNDARY,
 	FOUND_END_BOUNDARY
 };
@@ -851,6 +967,44 @@
                          ((scan_from && len >= 5 && !strncmp (start, "From ", 5)) ||  \
 			  (len >= 2 && (start[0] == '-' && start[1] == '-')))
 
+static int
+check_boundary (struct _GMimeParserPrivate *priv, const unsigned char *start, size_t len)
+{
+	off_t offset = parser_offset (priv, (unsigned char *) start);
+	
+	if (possible_boundary (priv->scan_from, start, len)) {
+		struct _boundary_stack *s;
+		
+		d(printf ("checking boundary '%.*s'\n", len, start));
+		
+		s = priv->bounds;
+		while (s) {
+			/* we use >= here because From lines are > 5 chars */
+			if (offset >= s->content_end &&
+			    len >= s->boundarylenfinal &&
+			    !strncmp (s->boundary, start,
+				      s->boundarylenfinal)) {
+				d(printf ("found %s\n", s->content_end != -1 && offset >= s->content_end ?
+					  "end of content" : "end boundary"));
+				return FOUND_END_BOUNDARY;
+			}
+			
+			if (len == s->boundarylen &&
+			    !strncmp (s->boundary, start,
+				      s->boundarylen)) {
+				d(printf ("found boundary\n"));
+				return FOUND_BOUNDARY;
+			}
+			
+			s = s->parent;
+		}
+		
+		d(printf ("'%.*s' not a boundary\n", len, start));
+	}
+	
+	return 0;
+}
+
 /* Optimization Notes:
  *
  * 1. By making the priv->realbuf char array 1 extra char longer, we
@@ -867,9 +1021,8 @@
 	struct _GMimeParserPrivate *priv = parser->priv;
 	register unsigned char *inptr;
 	unsigned char *start, *inend;
-	gboolean found_eos = FALSE;
 	size_t nleft, len;
-	int found;
+	int found = 0;
 	
 	d(printf ("scan-content\n"));
 	
@@ -877,7 +1030,7 @@
 	
 	g_assert (priv->inptr <= priv->inend);
 	
-	inptr = priv->inptr;
+	start = inptr = priv->inptr;
 	
 	do {
 	refill:
@@ -893,8 +1046,10 @@
 		/* Note: see optimization comment [1] */
 		*inend = '\n';
 		
-		if (inend - inptr == nleft)
-			found_eos = TRUE;
+		if (priv->midline && inend - inptr == nleft)
+			found = FOUND_EOS;
+		
+		priv->midline = FALSE;
 		
 		while (inptr < inend) {
 			start = inptr;
@@ -906,47 +1061,30 @@
 			
 			if (inptr < inend) {
 				inptr++;
-				if (possible_boundary (priv->scan_from, start, len)) {
-					struct _boundary_stack *s;
-					
-					d(printf ("checking boundary '%.*s'\n", len, start));
-					
-					s = priv->bounds;
-					while (s) {
-						/* we use >= here because From lines are > 5 chars */
-						if (len >= s->boundarylenfinal &&
-						    !strncmp (s->boundary, start,
-							      s->boundarylenfinal)) {
-							d(printf ("found end boundary\n"));
-							found = FOUND_END_BOUNDARY;
-							goto boundary;
-						}
-						
-						if (len == s->boundarylen &&
-						    !strncmp (s->boundary, start,
-							      s->boundarylen)) {
-							d(printf ("found boundary\n"));
-							found = FOUND_BOUNDARY;
-							goto boundary;
-						}
-						
-						s = s->parent;
-					}
-					
-					d(printf ("'%.*s' not a boundary\n", len, start));
-				}
+				if ((found = check_boundary (priv, start, len)))
+					goto boundary;
 				len++;
-			} else if (!found_eos) {
-				/* not enough to tell if we found a boundary */
-				priv->inptr = start;
-				goto refill;
+			} else {
+				/* didn't find an end-of-line */
+				priv->midline = TRUE;
+				
+				if (!found) {
+					/* not enough to tell if we found a boundary */
+					priv->inptr = start;
+					inptr = start;
+					goto refill;
+				}
+				
+				/* check for a boundary not ending in a \n */
+				if ((found = check_boundary (priv, start, len)))
+					goto boundary;
 			}
 			
 			content_save (content, start, len);
 		}
 		
 		priv->inptr = inptr;
-	} while (1);
+	} while (!found);
 	
  boundary:
 	
@@ -966,25 +1104,25 @@
 	GMimeStream *stream;
 	off_t start, end;
 	
-	if (priv->seekable)
-		start = parser_offset (parser, NULL);
+	if (priv->persist_stream && priv->seekable)
+		start = parser_offset (priv, NULL);
 	else
 		content = g_byte_array_new ();
 	
 	*found = parser_scan_content (parser, content);
 	if (*found != FOUND_EOS) {
 		/* last '\n' belongs to the boundary */
-		if (priv->seekable)
-			end = parser_offset (parser, NULL) - 1;
+		if (priv->persist_stream && priv->seekable)
+			end = parser_offset (priv, NULL) - 1;
 		else
 			g_byte_array_set_size (content, MAX (content->len - 1, 0));
-	} else if (priv->seekable) {
-		end = parser_offset (parser, NULL);
+	} else if (priv->persist_stream && priv->seekable) {
+		end = parser_offset (priv, NULL);
 	}
 	
 	encoding = g_mime_part_get_encoding (mime_part);
 	
-	if (priv->seekable) {
+	if (priv->persist_stream && priv->seekable) {
 		stream = g_mime_stream_substream (priv->stream, start, end);
 	} else {
 		stream = g_mime_stream_mem_new_with_byte_array (content);
@@ -992,8 +1130,8 @@
 	
 	wrapper = g_mime_data_wrapper_new_with_stream (stream, encoding);
 	g_mime_part_set_content_object (mime_part, wrapper);
-	g_mime_stream_unref (stream);
 	g_object_unref (wrapper);
+	g_object_unref (stream);
 }
 
 static void
@@ -1013,7 +1151,7 @@
 	message = g_mime_message_new (FALSE);
 	header = priv->headers;
 	while (header) {
-		g_mime_object_add_header (GMIME_OBJECT (message), header->name, header->value);
+		g_mime_object_add_header ((GMimeObject *) message, header->name, header->value);
 		header = header->next;
 	}
 	
@@ -1029,10 +1167,10 @@
 	}
 	
 	g_mime_message_set_mime_part (message, object);
-	g_mime_object_unref (object);
+	g_object_unref (object);
 	
 	g_mime_message_part_set_message (mpart, message);
-	g_mime_object_unref (GMIME_OBJECT (message));
+	g_object_unref (message);
 }
 
 static GMimeObject *
@@ -1041,14 +1179,14 @@
 	struct _GMimeParserPrivate *priv = parser->priv;
 	struct _header_raw *header;
 	GMimeObject *object;
+	const char *ctype;
 	
 	/* get the headers */
 	while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
 		;
 	
 	if (!content_type) {
-		content_type = parser_content_type (parser);
-		if (!content_type)
+		if (!(content_type = parser_content_type (parser)))
 			content_type = g_mime_content_type_new ("text", "plain");
 	}
 	
@@ -1061,7 +1199,8 @@
 	
 	header_raw_clear (&priv->headers);
 	
-	g_mime_object_set_content_type (object, content_type);
+	g_mime_content_type_destroy (object->content_type);
+	object->content_type = content_type;
 	
 	/* skip empty line after headers */
 	parser_skip_line (parser);
@@ -1131,12 +1270,20 @@
 		}
 		
 		g_mime_multipart_add_part (multipart, subpart);
-		g_mime_object_unref (subpart);
+		g_object_unref (subpart);
 	} while (found == FOUND_BOUNDARY);
 	
 	return found;
 }
 
+static gboolean
+found_immediate_boundary (struct _GMimeParserPrivate *priv)
+{
+	struct _boundary_stack *s = priv->bounds;
+	
+	return !strncmp (s->boundary, priv->inptr, s->boundarylenfinal);
+}
+
 static GMimeObject *
 parser_construct_multipart (GMimeParser *parser, GMimeContentType *content_type, int *found)
 {
@@ -1159,7 +1306,8 @@
 	
 	header_raw_clear (&priv->headers);
 	
-	g_mime_object_set_content_type (object, content_type);
+	g_mime_content_type_destroy (object->content_type);
+	object->content_type = content_type;
 	
 	multipart = (GMimeMultipart *) object;
 	
@@ -1174,15 +1322,17 @@
 		
 		if (*found == FOUND_BOUNDARY)
 			*found = parser_scan_multipart_subparts (parser, multipart);
-		parser_pop_boundary (parser);
 		
-		/* eat end boundary */
-		parser_skip_line (parser);
-		
-		if (*found == FOUND_END_BOUNDARY)
+		if (*found == FOUND_END_BOUNDARY && found_immediate_boundary (priv)) {
+			/* eat end boundary */
+			parser_skip_line (parser);
+			parser_pop_boundary (parser);
 			*found = parser_scan_multipart_postface (parser, multipart);
+		} else {
+			parser_pop_boundary (parser);
+		}
 	} else {
-		g_warning ("multipart without boundary encountered");
+		w(g_warning ("multipart without boundary encountered"));
 		/* this will scan everything into the preface */
 		*found = parser_scan_multipart_preface (parser, multipart);
 	}
@@ -1239,6 +1389,7 @@
 	struct _GMimeParserPrivate *priv = parser->priv;
 	GMimeContentType *content_type;
 	struct _header_raw *header;
+	int content_length = -1;
 	GMimeMessage *message;
 	GMimeObject *object;
 	int found;
@@ -1250,12 +1401,18 @@
 	message = g_mime_message_new (FALSE);
 	header = priv->headers;
 	while (header) {
-		g_mime_object_add_header (GMIME_OBJECT (message), header->name, header->value);
+		if (priv->respect_content_length && !strcasecmp (header->name, "Content-Length"))
+			content_length = strtoul (header->value, NULL, 10);
+		
+		g_mime_object_add_header ((GMimeObject *) message, header->name, header->value);
 		header = header->next;
 	}
 	
-	if (priv->scan_from)
+	if (priv->scan_from) {
 		parser_push_boundary (parser, "From ");
+		if (priv->respect_content_length && content_length != -1)
+			priv->bounds->content_end = parser_offset (priv, NULL) + content_length;
+	}
 	
 	content_type = parser_content_type (parser);
 	if (!content_type)
@@ -1269,7 +1426,7 @@
 	}
 	
 	g_mime_message_set_mime_part (message, object);
-	g_mime_object_unref (object);
+	g_object_unref (object);
 	
 	if (priv->scan_from) {
 		priv->state = GMIME_PARSER_STATE_FROM;
@@ -1302,7 +1459,7 @@
  * @parser: MIME parser object
  *
  * Gets the mbox-style From-line of the most recently parsed message
- * (gotten from #g_mime_parser_construct_message).
+ * (gotten from g_mime_parser_construct_message()).
  *
  * Returns the mbox-style From-line of the most recently parsed
  * message or %NULL on error.
@@ -1330,7 +1487,7 @@
  * @parser: MIME parser object
  *
  * Gets the offset of the most recently parsed mbox-style From-line
- * (gotten from #g_mime_parser_construct_message).
+ * (gotten from g_mime_parser_construct_message()).
  *
  * Returns the offset of the most recently parsed mbox-style From-line
  * or -1 on error.
Index: gmime/gmime-parser.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-parser.h,v
retrieving revision 1.9
diff -u -r1.9 gmime-parser.h
--- gmime/gmime-parser.h	30 Dec 2002 16:37:57 -0000	1.9
+++ gmime/gmime-parser.h	7 Jun 2004 06:10:18 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -29,14 +29,14 @@
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
+#include <glib.h>
 #include <glib-object.h>
 #include <errno.h>
 
-#include "gmime-object.h"
-#include "gmime-message.h"
-#include "gmime-content-type.h"
-#include "gmime-stream.h"
+#include <gmime/gmime-object.h>
+#include <gmime/gmime-message.h>
+#include <gmime/gmime-content-type.h>
+#include <gmime/gmime-stream.h>
 
 #define GMIME_TYPE_PARSER            (g_mime_parser_get_type ())
 #define GMIME_PARSER(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_PARSER, GMimeParser))
@@ -67,11 +67,18 @@
 GType g_mime_parser_get_type (void);
 
 GMimeParser *g_mime_parser_new (void);
+GMimeParser *g_mime_parser_new_with_stream (GMimeStream *stream);
 
 void g_mime_parser_init_with_stream (GMimeParser *parser, GMimeStream *stream);
 
-void g_mime_parser_set_scan_from (GMimeParser *parser, gboolean scan_from);
+gboolean g_mime_parser_get_persist_stream (GMimeParser *parser);
+void g_mime_parser_set_persist_stream (GMimeParser *parser, gboolean persist);
+
 gboolean g_mime_parser_get_scan_from (GMimeParser *parser);
+void g_mime_parser_set_scan_from (GMimeParser *parser, gboolean scan_from);
+
+gboolean g_mime_parser_get_respect_content_length (GMimeParser *parser);
+void g_mime_parser_set_respect_content_length (GMimeParser *parser, gboolean respect_content_length);
 
 void g_mime_parser_set_header_regex (GMimeParser *parser, const char *regex,
 				     GMimeParserHeaderRegexFunc header_cb,
Index: gmime/gmime-part.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-part.c,v
retrieving revision 1.34
diff -u -r1.34 gmime-part.c
--- gmime/gmime-part.c	12 May 2003 14:52:11 -0000	1.34
+++ gmime/gmime-part.c	7 Jun 2004 06:10:20 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -36,9 +36,13 @@
 #include "gmime-part.h"
 #include "gmime-utils.h"
 #include "gmime-stream-mem.h"
+#include "gmime-stream-null.h"
 #include "gmime-stream-filter.h"
 #include "gmime-filter-basic.h"
-#include "md5-utils.h"
+#include "gmime-filter-crlf.h"
+#include "gmime-filter-md5.h"
+
+#define d(x)
 
 /* GObject class methods */
 static void g_mime_part_class_init (GMimePartClass *klass);
@@ -52,7 +56,7 @@
 static const char *mime_part_get_header (GMimeObject *object, const char *header);
 static void mime_part_remove_header (GMimeObject *object, const char *header);
 static char *mime_part_get_headers (GMimeObject *object);
-static gssize mime_part_write_to_stream (GMimeObject *object, GMimeStream *stream);
+static ssize_t mime_part_write_to_stream (GMimeObject *object, GMimeStream *stream);
 
 #define NEEDS_DECODING(encoding) (((GMimePartEncodingType) encoding) == GMIME_PART_ENCODING_BASE64 ||   \
 				  ((GMimePartEncodingType) encoding) == GMIME_PART_ENCODING_UUENCODE || \
@@ -170,6 +174,18 @@
 	mime_part->disposition = g_mime_disposition_new (disposition);
 }
 
+static void
+sync_content_disposition (GMimePart *mime_part)
+{
+	char *str;
+	
+	if (mime_part->disposition) {
+		str = g_mime_disposition_header (mime_part->disposition, FALSE);
+		g_mime_header_set (GMIME_OBJECT (mime_part)->headers, "Content-Disposition", str);
+		g_free (str);
+	}
+}
+
 static gboolean
 process_header (GMimeObject *object, const char *header, const char *value)
 {
@@ -178,7 +194,7 @@
 	int i;
 	
 	for (i = 0; headers[i]; i++) {
-		if (!g_strcasecmp (headers[i], header))
+		if (!strcasecmp (headers[i], header))
 			break;
 	}
 	
@@ -219,7 +235,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a mime part */
 	
-	if (!g_strncasecmp ("Content-", header, 8)) {
+	if (!strncasecmp ("Content-", header, 8)) {
 		if (process_header (object, header, value))
 			g_mime_header_add (object->headers, header, value);
 		else
@@ -233,7 +249,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a mime part */
 	
-	if (!g_strncasecmp ("Content-", header, 8)) {
+	if (!strncasecmp ("Content-", header, 8)) {
 		if (process_header (object, header, value))
 			g_mime_header_set (object->headers, header, value);
 		else
@@ -247,7 +263,7 @@
 	/* Make sure that the header is a Content-* header, else it
            doesn't belong on a mime part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		return GMIME_OBJECT_CLASS (parent_class)->get_header (object, header);
 	else
 		return NULL;
@@ -259,7 +275,7 @@
 	/* Make sure that the header is a Content-* header, else it
 	   doesn't belong on a mime part */
 	
-	if (!g_strncasecmp ("Content-", header, 8))
+	if (!strncasecmp ("Content-", header, 8))
 		GMIME_OBJECT_CLASS (parent_class)->remove_header (object, header);
 }
 
@@ -269,10 +285,10 @@
 	return GMIME_OBJECT_CLASS (parent_class)->get_headers (object);
 }
 
-static gssize
+static ssize_t
 write_content (GMimePart *part, GMimeStream *stream)
 {
-	gssize nwritten, total = 0;
+	ssize_t nwritten, total = 0;
 	
 	if (!part->content)
 		return 0;
@@ -293,17 +309,19 @@
 		case GMIME_PART_ENCODING_BASE64:
 			filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_BASE64_ENC);
 			g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+			g_object_unref (filter);
 			break;
 		case GMIME_PART_ENCODING_QUOTEDPRINTABLE:
 			filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_QP_ENC);
 			g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+			g_object_unref (filter);
 			break;
 		case GMIME_PART_ENCODING_UUENCODE:
 			filename = g_mime_part_get_filename (part);
 			nwritten = g_mime_stream_printf (stream, "begin 0644 %s\n",
 							 filename ? filename : "unknown");
 			if (nwritten == -1) {
-				g_mime_stream_unref (filtered_stream);
+				g_object_unref (filtered_stream);
 				return -1;
 			}
 			
@@ -311,6 +329,7 @@
 			
 			filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_UU_ENC);
 			g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+			g_object_unref (filter);
 			break;
 		default:
 			break;
@@ -318,7 +337,7 @@
 		
 		nwritten = g_mime_data_wrapper_write_to_stream (part->content, filtered_stream);
 		g_mime_stream_flush (filtered_stream);
-		g_mime_stream_unref (filtered_stream);
+		g_object_unref (filtered_stream);
 		
 		if (nwritten == -1)
 			return -1;
@@ -339,7 +358,7 @@
 		content_stream = g_mime_data_wrapper_get_stream (part->content);
 		g_mime_stream_reset (content_stream);
 		nwritten = g_mime_stream_write_to_stream (content_stream, stream);
-		g_mime_stream_unref (content_stream);
+		g_object_unref (content_stream);
 		
 		if (nwritten == -1)
 			return -1;
@@ -350,15 +369,14 @@
 	return total;
 }
 
-static gssize
+static ssize_t
 mime_part_write_to_stream (GMimeObject *object, GMimeStream *stream)
 {
 	GMimePart *mime_part = (GMimePart *) object;
-	gssize nwritten, total = 0;
+	ssize_t nwritten, total = 0;
 	
 	/* write the content headers */
-	nwritten = g_mime_header_write_to_stream (object->headers, stream);
-	if (nwritten == -1)
+	if ((nwritten = g_mime_header_write_to_stream (object->headers, stream)) == -1)
 		return -1;
 	
 	total += nwritten;
@@ -369,8 +387,7 @@
 	
 	total++;
 	
-	nwritten = write_content (mime_part, stream);
-	if (nwritten == -1)
+	if ((nwritten = write_content (mime_part, stream)) == -1)
 		return -1;
 	
 	total += nwritten;
@@ -547,46 +564,57 @@
 void
 g_mime_part_set_content_md5 (GMimePart *mime_part, const char *content_md5)
 {
+	unsigned char digest[16], b64digest[32];
+	const GMimeContentType *content_type;
+	GMimeStreamFilter *filtered_stream;
+	GMimeFilter *md5_filter;
+	GMimeStream *stream;
+	int state, save;
+	size_t len;
+	
 	g_return_if_fail (GMIME_IS_PART (mime_part));
 	
 	if (mime_part->content_md5)
 		g_free (mime_part->content_md5);
 	
-	if (content_md5) {
-		mime_part->content_md5 = g_strdup (content_md5);
-	} else if (mime_part->content && mime_part->content->stream) {
-		GMimePartEncodingType encoding;
-		char digest[16], b64digest[32];
-		GMimeStream *stream;
-		GByteArray *buf;
-		int state, save;
-		size_t len;
-		
-		encoding = g_mime_data_wrapper_get_encoding (mime_part->content);
-		stream = g_mime_data_wrapper_get_stream (mime_part->content);
-		if (!GMIME_IS_STREAM_MEM (stream) || NEEDS_DECODING (encoding)) {
-			g_mime_stream_unref (stream);
-			stream = g_mime_stream_mem_new ();
-			g_mime_data_wrapper_write_to_stream (mime_part->content, stream);
-		}
-		
-		buf = GMIME_STREAM_MEM (stream)->buffer;
-		len = g_mime_stream_length (stream);
+	if (!content_md5) {
+		/* compute a md5sum */
+		stream = g_mime_stream_null_new ();
+		filtered_stream = (GMimeStreamFilter *) g_mime_stream_filter_new_with_stream (stream);
+		g_object_unref (stream);
 		
-		if (len != (size_t) -1) {
-			md5_get_digest (buf->data + stream->bound_start, len, digest);
+		content_type = g_mime_object_get_content_type ((GMimeObject *) mime_part);
+		if (g_mime_content_type_is_type (content_type, "text", "*")) {
+			GMimeFilter *crlf_filter;
 			
-			state = save = 0;
-			len = g_mime_utils_base64_encode_close (digest, 16, b64digest, &state, &save);
-			b64digest[len] = '\0';
+			crlf_filter = g_mime_filter_crlf_new (GMIME_FILTER_CRLF_ENCODE,
+							      GMIME_FILTER_CRLF_MODE_CRLF_ONLY);
 			
-			mime_part->content_md5 = g_strdup (b64digest);
-			
-			g_mime_header_set (GMIME_OBJECT (mime_part)->headers, "Content-Md5", b64digest);
+			g_mime_stream_filter_add (filtered_stream, crlf_filter);
+			g_object_unref (crlf_filter);
 		}
 		
-		g_mime_stream_unref (stream);
+		md5_filter = g_mime_filter_md5_new ();
+		g_mime_stream_filter_add (filtered_stream, md5_filter);
+		
+		stream = (GMimeStream *) filtered_stream;
+		g_mime_data_wrapper_write_to_stream (mime_part->content, stream);
+		g_object_unref (stream);
+		
+		memset (digest, 0, 16);
+		g_mime_filter_md5_get_digest ((GMimeFilterMd5 *) md5_filter, digest);
+		g_object_unref (md5_filter);
+		
+		state = save = 0;
+		len = g_mime_utils_base64_encode_close (digest, 16, b64digest, &state, &save);
+		b64digest[len] = '\0';
+		g_strstrip (b64digest);
+		
+		content_md5 = (const char *) b64digest;
 	}
+	
+	mime_part->content_md5 = g_strdup (content_md5);
+	g_mime_header_set (GMIME_OBJECT (mime_part)->headers, "Content-Md5", content_md5);
 }
 
 
@@ -602,10 +630,11 @@
 gboolean
 g_mime_part_verify_content_md5 (GMimePart *mime_part)
 {
-	GMimePartEncodingType encoding;
-	char digest[16], b64digest[32];
+	unsigned char digest[16], b64digest[32];
+	const GMimeContentType *content_type;
+	GMimeStreamFilter *filtered_stream;
+	GMimeFilter *md5_filter;
 	GMimeStream *stream;
-	GByteArray *buf;
 	int state, save;
 	size_t len;
 	
@@ -615,28 +644,36 @@
 	if (!mime_part->content_md5)
 		return FALSE;
 	
-	encoding = g_mime_data_wrapper_get_encoding (mime_part->content);
-	stream = g_mime_data_wrapper_get_stream (mime_part->content);
-	if (!GMIME_IS_STREAM_MEM (stream) || NEEDS_DECODING (encoding)) {
-		g_mime_stream_unref (stream);
-		stream = g_mime_stream_mem_new ();
-		g_mime_data_wrapper_write_to_stream (mime_part->content, stream);
-	}
-	
-	buf = GMIME_STREAM_MEM (stream)->buffer;
-	len = g_mime_stream_length (stream);
-	
-	if (len != (size_t) -1) {
-		md5_get_digest (buf->data + stream->bound_start, len, digest);
+	stream = g_mime_stream_null_new ();
+	filtered_stream = (GMimeStreamFilter *) g_mime_stream_filter_new_with_stream (stream);
+	g_object_unref (stream);
+	
+	content_type = g_mime_object_get_content_type ((GMimeObject *) mime_part);
+	if (g_mime_content_type_is_type (content_type, "text", "*")) {
+		GMimeFilter *crlf_filter;
 		
-		state = save = 0;
-		len = g_mime_utils_base64_encode_close (digest, 16, b64digest, &state, &save);
-		b64digest[len] = '\0';
-	} else {
-		b64digest[0] = '\0';
+		crlf_filter = g_mime_filter_crlf_new (GMIME_FILTER_CRLF_ENCODE,
+						      GMIME_FILTER_CRLF_MODE_CRLF_ONLY);
+		
+		g_mime_stream_filter_add (filtered_stream, crlf_filter);
+		g_object_unref (crlf_filter);
 	}
 	
-	g_mime_stream_unref (GMIME_STREAM (stream));
+	md5_filter = g_mime_filter_md5_new ();
+	g_mime_stream_filter_add (filtered_stream, md5_filter);
+	
+	stream = (GMimeStream *) filtered_stream;
+	g_mime_data_wrapper_write_to_stream (mime_part->content, stream);
+	g_object_unref (stream);
+	
+	memset (digest, 0, 16);
+	g_mime_filter_md5_get_digest ((GMimeFilterMd5 *) md5_filter, digest);
+	g_object_unref (md5_filter);
+	
+	state = save = 0;
+	len = g_mime_utils_base64_encode_close (digest, 16, b64digest, &state, &save);
+	b64digest[len] = '\0';
+	g_strstrip (b64digest);
 	
 	return !strcmp (b64digest, mime_part->content_md5);
 }
@@ -830,33 +867,22 @@
 GMimePartEncodingType
 g_mime_part_encoding_from_string (const char *encoding)
 {
-	if (!g_strcasecmp (encoding, "7bit"))
+	if (!strcasecmp (encoding, "7bit"))
 		return GMIME_PART_ENCODING_7BIT;
-	else if (!g_strcasecmp (encoding, "8bit"))
+	else if (!strcasecmp (encoding, "8bit"))
 		return GMIME_PART_ENCODING_8BIT;
-	else if (!g_strcasecmp (encoding, "binary"))
+	else if (!strcasecmp (encoding, "binary"))
 		return GMIME_PART_ENCODING_BINARY;
-	else if (!g_strcasecmp (encoding, "base64"))
+	else if (!strcasecmp (encoding, "base64"))
 		return GMIME_PART_ENCODING_BASE64;
-	else if (!g_strcasecmp (encoding, "quoted-printable"))
+	else if (!strcasecmp (encoding, "quoted-printable"))
 		return GMIME_PART_ENCODING_QUOTEDPRINTABLE;
-	else if (!g_strcasecmp (encoding, "x-uuencode"))
+	else if (!strcasecmp (encoding, "x-uuencode"))
 		return GMIME_PART_ENCODING_UUENCODE;
 	else return GMIME_PART_ENCODING_DEFAULT;
 }
 
 
-static void
-sync_content_disposition (GMimePart *mime_part)
-{
-	char *str;
-	
-	str = g_mime_disposition_header (mime_part->disposition, FALSE);
-	g_mime_header_set (GMIME_OBJECT (mime_part)->headers, "Content-Disposition", str);
-	g_free (str);
-}
-
-
 /**
  * g_mime_part_set_content_disposition_object:
  * @mime_part: Mime part
@@ -1022,6 +1048,9 @@
  * @content: raw mime part content
  * @len: raw content length
  *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_part_set_content_object() instead.
+ *
  * Sets the content of the Mime Part (only non-multiparts)
  **/
 void
@@ -1037,7 +1066,7 @@
 	stream = g_mime_stream_mem_new_with_buffer (content, len);
 	g_mime_data_wrapper_set_stream (mime_part->content, stream);
 	g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_PART_ENCODING_DEFAULT);
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 }
 
 
@@ -1046,6 +1075,9 @@
  * @mime_part: Mime part
  * @content: raw mime part content.
  *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_part_set_content_object() instead.
+ *
  * Sets the content of the Mime Part (only non-multiparts)
  **/
 void
@@ -1061,7 +1093,7 @@
 	stream = g_mime_stream_mem_new_with_byte_array (content);
 	g_mime_data_wrapper_set_stream (mime_part->content, stream);
 	g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_PART_ENCODING_DEFAULT);
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 }
 
 
@@ -1072,7 +1104,11 @@
  * @len: length of the content
  * @encoding: content encoding
  *
- * Sets the encoding type and raw content on the mime part after decoding the content.
+ * WARNING: This interface is deprecated. Use
+ * g_mime_part_set_content_object() instead.
+ *
+ * Sets the encoding type and raw content on the mime part after
+ * decoding the content.
  **/
 void
 g_mime_part_set_pre_encoded_content (GMimePart *mime_part, const char *content,
@@ -1093,14 +1129,17 @@
 	case GMIME_PART_ENCODING_BASE64:
 		filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_BASE64_DEC);
 		g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
 		break;
 	case GMIME_PART_ENCODING_QUOTEDPRINTABLE:
 		filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_QP_DEC);
 		g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
 		break;
 	case GMIME_PART_ENCODING_UUENCODE:
 		filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_UU_DEC);
 		g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
+		g_object_unref (filter);
 		break;
 	default:
 		break;
@@ -1108,12 +1147,12 @@
 	
 	g_mime_stream_write (filtered_stream, (char *) content, len);
 	g_mime_stream_flush (filtered_stream);
-	g_mime_stream_unref (filtered_stream);
+	g_object_unref (filtered_stream);
 	
 	g_mime_stream_reset (stream);
 	g_mime_data_wrapper_set_stream (mime_part->content, stream);
 	g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_PART_ENCODING_DEFAULT);
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 	
 	mime_part->encoding = encoding;
 }
@@ -1169,12 +1208,19 @@
  *
  * Gets the raw contents of the mime part and sets @len to the length
  * of the raw data buffer.
+ *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_get_content_object() instead.
  * 
  * Returns a const char * pointer to the raw contents of the MIME Part
- * and sets %len to the length of the buffer.
+ * and sets @len to the length of the buffer. Note: textual content
+ * will not be converted to UTF-8. Also note that this buffer will not
+ * be nul-terminated and may in fact contain nul bytes mid-buffer so
+ * you MUST treat the data returned as raw binary data even if the
+ * content type is text.
  **/
 const char *
-g_mime_part_get_content (const GMimePart *mime_part, guint *len)
+g_mime_part_get_content (const GMimePart *mime_part, size_t *len)
 {
 	const char *retval = NULL;
 	GMimeStream *stream;
@@ -1182,7 +1228,7 @@
 	g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
 	
 	if (!mime_part->content || !mime_part->content->stream) {
-		g_warning ("no content set on this mime part");
+		d(g_warning ("no content set on this mime part"));
 		return NULL;
 	}
 	
@@ -1199,7 +1245,7 @@
 		
 		g_mime_data_wrapper_set_stream (mime_part->content, cache);
 		g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_PART_ENCODING_DEFAULT);
-		g_mime_stream_unref (cache);
+		g_object_unref (cache);
 		
 		*len = buf->len;
 		retval = buf->data;
@@ -1231,15 +1277,18 @@
  *
  * Writes the contents of the MIME Part to @stream.
  *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_object_write_to_stream() instead.
+ *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_part_write_to_stream (GMimePart *mime_part, GMimeStream *stream)
 {
 	g_return_val_if_fail (GMIME_IS_PART (mime_part), -1);
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
 	
-	return g_mime_object_write_to_stream (GMIME_OBJECT (mime_part), stream);
+	return g_mime_object_write_to_stream ((GMimeObject *) mime_part, stream);
 }
 
 
@@ -1249,6 +1298,9 @@
  *
  * Allocates a string buffer containing the MIME Part.
  *
+ * WARNING: This interface is deprecated. Use
+ * g_mime_object_to_string() instead.
+ *
  * Returns an allocated string containing the MIME Part.
  **/
 char *
@@ -1256,5 +1308,5 @@
 {
 	g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
 	
-	return g_mime_object_to_string (GMIME_OBJECT (mime_part));
+	return g_mime_object_to_string ((GMimeObject *) mime_part);
 }
Index: gmime/gmime-part.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-part.h,v
retrieving revision 1.19
diff -u -r1.19 gmime-part.h
--- gmime/gmime-part.h	12 May 2003 14:52:11 -0000	1.19
+++ gmime/gmime-part.h	7 Jun 2004 06:10:20 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,19 +24,19 @@
 #ifndef __GMIME_PART_H__
 #define __GMIME_PART_H__
 
+#include <glib.h>
+#include <stdio.h>
+
+#include <gmime/gmime-object.h>
+#include <gmime/gmime-param.h>
+#include <gmime/gmime-disposition.h>
+#include <gmime/gmime-data-wrapper.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include <stdio.h>
-
-#include "gmime-object.h"
-#include "gmime-param.h"
-#include "gmime-disposition.h"
-#include "gmime-data-wrapper.h"
-
 #define GMIME_TYPE_PART            (g_mime_part_get_type ())
 #define GMIME_PART(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_PART, GMimePart))
 #define GMIME_PART_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_PART, GMimePartClass))
@@ -107,20 +107,21 @@
 void g_mime_part_set_filename (GMimePart *mime_part, const char *filename);
 const char *g_mime_part_get_filename (const GMimePart *mime_part);
 
+#ifndef GMIME_DISABLE_DEPRECATED
 void g_mime_part_set_content_byte_array (GMimePart *mime_part, GByteArray *content);
 void g_mime_part_set_content (GMimePart *mime_part, const char *content, size_t len);
 void g_mime_part_set_pre_encoded_content (GMimePart *mime_part, const char *content,
 					  size_t len, GMimePartEncodingType encoding);
-
-/*void g_mime_part_set_content_stream (GMimePart *mime_part, GMimeStream *content);*/
+const char *g_mime_part_get_content (const GMimePart *mime_part, size_t *len);
+#endif /* GMIME_DISABLE_DEPRECATED */
 
 void g_mime_part_set_content_object (GMimePart *mime_part, GMimeDataWrapper *content);
 GMimeDataWrapper *g_mime_part_get_content_object (const GMimePart *mime_part);
-const char *g_mime_part_get_content (const GMimePart *mime_part, guint *len);
 
-/* utility functions */
-gssize g_mime_part_write_to_stream (GMimePart *mime_part, GMimeStream *stream);
+#ifndef GMIME_DISABLE_DEPRECATED
+ssize_t g_mime_part_write_to_stream (GMimePart *mime_part, GMimeStream *stream);
 char *g_mime_part_to_string (GMimePart *mime_part);
+#endif /* GMIME_DISABLE_DEPRECATED */
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-stream-buffer.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-buffer.c,v
retrieving revision 1.7
diff -u -r1.7 gmime-stream-buffer.c
--- gmime/gmime-stream-buffer.c	26 Dec 2002 18:50:15 -0000	1.7
+++ gmime/gmime-stream-buffer.c	7 Jun 2004 06:10:20 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -36,15 +36,15 @@
 static void g_mime_stream_buffer_init (GMimeStreamBuffer *stream, GMimeStreamBufferClass *klass);
 static void g_mime_stream_buffer_finalize (GObject *object);
 
-static gssize stream_read (GMimeStream *stream, char *buf, size_t len);
-static gssize stream_write (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
 static int stream_flush (GMimeStream *stream);
 static int stream_close (GMimeStream *stream);
 static gboolean stream_eos (GMimeStream *stream);
 static int stream_reset (GMimeStream *stream);
 static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 static off_t stream_tell (GMimeStream *stream);
-static gssize stream_length (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
 static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
 
 
@@ -115,7 +115,7 @@
 	GMimeStreamBuffer *stream = (GMimeStreamBuffer *) object;
 	
 	if (stream->source)
-		g_mime_stream_unref (stream->source);
+		g_object_unref (stream->source);
 	
 	g_free (stream->buffer);
 	
@@ -123,12 +123,12 @@
 }
 
 
-static gssize
+static ssize_t
 stream_read (GMimeStream *stream, char *buf, size_t len)
 {
 	/* FIXME: this could be better optimized in the case where @len > the block size */
 	GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
-	gssize n, nread = 0;
+	ssize_t n, nread = 0;
 	
  again:
 	switch (buffer->mode) {
@@ -194,28 +194,30 @@
 	return nread;
 }
 
-static gssize
-stream_write (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t len)
 {
 	/* FIXME: this could be better optimized for the case where @len > block size */
 	GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
-	gssize written = 0, n;
+	ssize_t written = 0, n;
 	
- again:
 	switch (buffer->mode) {
 	case GMIME_STREAM_BUFFER_BLOCK_WRITE:
+	again:
 		n = MIN (BLOCK_BUFFER_LEN - buffer->buflen, len);
 		memcpy (buffer->buffer + buffer->buflen, buf, n);
 		buffer->buflen += n;
 		written += n;
+		buf += n;
 		len -= n;
 		if (len) {
 			/* flush our buffer... */
-			n = g_mime_stream_write (buffer->source, buffer->buffer, BLOCK_BUFFER_LEN);
-			if (n > 0) {
+			if ((n = g_mime_stream_write (buffer->source, buffer->buffer, BLOCK_BUFFER_LEN)) != -1) {
 				memmove (buffer->buffer, buffer->buffer + n, BLOCK_BUFFER_LEN - n);
+				buffer->buflen -= n;
 				goto again;
-			}
+			} else
+				return -1;
 		}
 		break;
 	default:
@@ -234,7 +236,7 @@
 	GMimeStreamBuffer *buffer = (GMimeStreamBuffer *) stream;
 	
 	if (buffer->mode == GMIME_STREAM_BUFFER_BLOCK_WRITE && buffer->buflen > 0) {
-		gssize written = 0;
+		ssize_t written = 0;
 		
 		written = g_mime_stream_write (buffer->source, buffer->buffer, buffer->buflen);
 		if (written > 0) {
@@ -357,7 +359,7 @@
 		if (real > stream->position) {
 			/* buffer any data between position and real */
 			size_t len, total = 0;
-			gssize nread;
+			ssize_t nread;
 			off_t pos;
 			
 			len = real - (stream->bound_start + (buffer->bufend - buffer->bufptr));
@@ -412,7 +414,7 @@
 	return stream->position;
 }
 
-static gssize
+static ssize_t
 stream_length (GMimeStream *stream)
 {
 	return g_mime_stream_length (GMIME_STREAM_BUFFER (stream)->source);
@@ -451,7 +453,7 @@
 	buffer = g_object_new (GMIME_TYPE_STREAM_BUFFER, NULL, NULL);
 	
 	buffer->source = source;
-	g_mime_stream_ref (source);
+	g_object_ref (source);
 	
 	buffer->mode = mode;
 	
@@ -493,12 +495,12 @@
  * Returns the number of characters read into @buf on success and -1
  * on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_buffer_gets (GMimeStream *stream, char *buf, size_t max)
 {
 	register char *inptr, *outptr;
 	char *inend, *outend;
-	gssize nread;
+	ssize_t nread;
 	char c = '\0';
 	
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
@@ -589,6 +591,10 @@
 			*outptr++ = c;
 	}
 	
+	/* strip \r */
+	if (c == '\n' && outptr > buf && outptr[-1] == '\r')
+		outptr--;
+	
 	if (outptr <= outend) {
 		/* this should always be true unless @max == 0 */
 		*outptr = '\0';
@@ -609,7 +615,7 @@
 g_mime_stream_buffer_readln (GMimeStream *stream, GByteArray *buffer)
 {
 	char linebuf[1024];
-	gssize len;
+	ssize_t len;
 	
 	g_return_if_fail (GMIME_IS_STREAM (stream));
 	
Index: gmime/gmime-stream-buffer.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-buffer.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-stream-buffer.h
--- gmime/gmime-stream-buffer.h	26 Dec 2002 18:50:15 -0000	1.4
+++ gmime/gmime-stream-buffer.h	7 Jun 2004 06:10:20 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,13 +24,13 @@
 #ifndef __GMIME_STREAM_BUFFER_H__
 #define __GMIME_STREAM_BUFFER_H__
 
+#include <gmime/gmime-stream.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include "gmime-stream.h"
-
 #define GMIME_TYPE_STREAM_BUFFER            (g_mime_stream_buffer_get_type ())
 #define GMIME_STREAM_BUFFER(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM_BUFFER, GMimeStreamBuffer))
 #define GMIME_STREAM_BUFFER_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_STREAM_BUFFER, GMimeStreamBufferClass))
@@ -42,7 +42,7 @@
 typedef enum {
 	GMIME_STREAM_BUFFER_CACHE_READ,
 	GMIME_STREAM_BUFFER_BLOCK_READ,
-	GMIME_STREAM_BUFFER_BLOCK_WRITE,
+	GMIME_STREAM_BUFFER_BLOCK_WRITE
 } GMimeStreamBufferMode;
 
 typedef struct _GMimeStreamBuffer GMimeStreamBuffer;
@@ -56,7 +56,7 @@
 	unsigned char *buffer;
 	unsigned char *bufptr;
 	unsigned char *bufend;
-	gssize buflen;
+	size_t buflen;
 	
 	GMimeStreamBufferMode mode;
 };
@@ -71,7 +71,7 @@
 
 GMimeStream *g_mime_stream_buffer_new (GMimeStream *source, GMimeStreamBufferMode mode);
 
-gssize g_mime_stream_buffer_gets (GMimeStream *stream, char *buf, size_t max);
+ssize_t g_mime_stream_buffer_gets (GMimeStream *stream, char *buf, size_t max);
 
 void    g_mime_stream_buffer_readln (GMimeStream *stream, GByteArray *buffer);
 
Index: gmime/gmime-stream-cat.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-cat.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-stream-cat.c
--- gmime/gmime-stream-cat.c	26 Dec 2002 18:50:15 -0000	1.8
+++ gmime/gmime-stream-cat.c	7 Jun 2004 06:10:21 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -31,15 +31,15 @@
 static void g_mime_stream_cat_init (GMimeStreamCat *stream, GMimeStreamCatClass *klass);
 static void g_mime_stream_cat_finalize (GObject *object);
 
-static gssize stream_read (GMimeStream *stream, char *buf, size_t len);
-static gssize stream_write (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
 static int stream_flush (GMimeStream *stream);
 static int stream_close (GMimeStream *stream);
 static gboolean stream_eos (GMimeStream *stream);
 static int stream_reset (GMimeStream *stream);
 static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 static off_t stream_tell (GMimeStream *stream);
-static gssize stream_length (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
 static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
 
 
@@ -49,7 +49,7 @@
 struct _cat_node {
 	struct _cat_node *next;
 	GMimeStream *stream;
-	gssize length;
+	ssize_t length;
 };
 
 GType
@@ -115,7 +115,7 @@
 	p = cat->sources;
 	while (p) {
 		n = p->next;
-		g_mime_stream_unref (p->stream);
+		g_object_unref (p->stream);
 		g_free (p);
 		p = n;
 	}
@@ -124,12 +124,12 @@
 }
 
 
-static gssize
+static ssize_t
 stream_read (GMimeStream *stream, char *buf, size_t len)
 {
 	GMimeStreamCat *cat = (GMimeStreamCat *) stream;
 	struct _cat_node *current;
-	gssize n, nread = 0;
+	ssize_t n, nread = 0;
 	
 	/* check for end-of-stream */
 	if (stream->bound_end != -1 && stream->position >= stream->bound_end)
@@ -137,7 +137,7 @@
 	
 	/* don't allow our caller to read past the end of the stream */
 	if (stream->bound_end != -1)
-		len = MIN (stream->bound_end - stream->position, len);
+		len = MIN (stream->bound_end - stream->position, (off_t) len);
 	
 	/* make sure our stream position is where it should be */
 	if (stream_seek (stream, stream->position, GMIME_STREAM_SEEK_SET) == -1)
@@ -174,12 +174,12 @@
 	return nread;
 }
  
-static gssize
-stream_write (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t len)
 {
 	GMimeStreamCat *cat = (GMimeStreamCat *) stream;
 	struct _cat_node *current;
-	gssize n, nwritten = 0;
+	ssize_t n, nwritten = 0;
 	
 	/* check for end-of-stream */
 	if (stream->bound_end != -1 && stream->position >= stream->bound_end)
@@ -187,7 +187,7 @@
 	
 	/* don't allow our caller to write past the end of the stream */
 	if (stream->bound_end != -1)
-		len = MIN (stream->bound_end - stream->position, len);
+		len = MIN (stream->bound_end - stream->position, (off_t) len);
 	
 	/* make sure our stream position is where it should be */
 	if (stream_seek (stream, stream->position, GMIME_STREAM_SEEK_SET) == -1)
@@ -197,8 +197,9 @@
 		return -1;
 	
 	do {
+		n = -1;
 		while (!g_mime_stream_eos (current->stream) && nwritten < len) {
-			n = g_mime_stream_read (current->stream, buf + nwritten, len - nwritten);
+			n = g_mime_stream_write (current->stream, buf + nwritten, len - nwritten);
 			if (n > 0)
 				nwritten += n;
 		}
@@ -371,12 +372,12 @@
 	return stream->position;
 }
 
-static gssize
+static ssize_t
 stream_length (GMimeStream *stream)
 {
 	GMimeStreamCat *cat = GMIME_STREAM_CAT (stream);
 	struct _cat_node *p;
-	gssize len = 0;
+	ssize_t len = 0;
 	
 	if (stream->bound_end != -1)
 		return stream->bound_end - stream->bound_start;
@@ -443,7 +444,7 @@
 g_mime_stream_cat_add_source (GMimeStreamCat *cat, GMimeStream *source)
 {
 	struct _cat_node *p, *node;
-	gssize len;
+	ssize_t len;
 	
 	g_return_val_if_fail (GMIME_IS_STREAM_CAT (cat), -1);
 	g_return_val_if_fail (GMIME_IS_STREAM (source), -1);
@@ -456,7 +457,7 @@
 	node->next = NULL;
 	node->stream = source;
 	node->length = len;
-	g_mime_stream_ref (source);
+	g_object_ref (source);
 	
 	p = cat->sources;
 	while (p && p->next)
Index: gmime/gmime-stream-cat.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-cat.h,v
retrieving revision 1.2
diff -u -r1.2 gmime-stream-cat.h
--- gmime/gmime-stream-cat.h	30 Dec 2002 16:37:57 -0000	1.2
+++ gmime/gmime-stream-cat.h	7 Jun 2004 06:10:21 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,13 +24,13 @@
 #ifndef __GMIME_STREAM_CAT_H__
 #define __GMIME_STREAM_CAT_H__
 
+#include <glib.h>
+#include <gmime/gmime-stream.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <glib/gtypes.h>
-#include "gmime-stream.h"
 
 #define GMIME_TYPE_STREAM_CAT            (g_mime_stream_cat_get_type ())
 #define GMIME_STREAM_CAT(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM_CAT, GMimeStreamCat))
Index: gmime/gmime-stream-file.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-file.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-stream-file.c
--- gmime/gmime-stream-file.c	26 Dec 2002 18:50:15 -0000	1.8
+++ gmime/gmime-stream-file.c	7 Jun 2004 06:10:21 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -31,15 +31,15 @@
 static void g_mime_stream_file_init (GMimeStreamFile *stream, GMimeStreamFileClass *klass);
 static void g_mime_stream_file_finalize (GObject *object);
 
-static gssize stream_read (GMimeStream *stream, char *buf, size_t len);
-static gssize stream_write (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
 static int stream_flush (GMimeStream *stream);
 static int stream_close (GMimeStream *stream);
 static gboolean stream_eos (GMimeStream *stream);
 static int stream_reset (GMimeStream *stream);
 static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 static off_t stream_tell (GMimeStream *stream);
-static gssize stream_length (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
 static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
 
 
@@ -112,17 +112,17 @@
 }
 
 
-static gssize
+static ssize_t
 stream_read (GMimeStream *stream, char *buf, size_t len)
 {
 	GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
-	gssize nread;
+	ssize_t nread;
 	
 	if (stream->bound_end != -1 && stream->position >= stream->bound_end)
 		return -1;
 	
 	if (stream->bound_end != -1)
-		len = MIN (stream->bound_end - stream->position, len);
+		len = MIN (stream->bound_end - stream->position, (off_t) len);
 	
 	/* make sure we are at the right position */
 	fseek (fstream->fp, stream->position, SEEK_SET);
@@ -135,17 +135,17 @@
 	return nread;
 }
 
-static gssize
-stream_write (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t len)
 {
 	GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
-	gssize nwritten;
+	ssize_t nwritten;
 	
 	if (stream->bound_end != -1 && stream->position >= stream->bound_end)
 		return -1;
 	
 	if (stream->bound_end != -1)
-		len = MIN (stream->bound_end - stream->position, len);
+		len = MIN (stream->bound_end - stream->position, (off_t) len);
 	
 	/* make sure we are at the right position */
 	fseek (fstream->fp, stream->position, SEEK_SET);
@@ -264,7 +264,7 @@
 	return stream->position;
 }
 
-static gssize
+static ssize_t
 stream_length (GMimeStream *stream)
 {
 	GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
@@ -304,6 +304,9 @@
  *
  * Creates a new GMimeStreamFile object around @fp.
  *
+ * Note: The created GMimeStreamFile object will own the FILE pointer
+ * passed in.
+ *
  * Returns a stream using @fp.
  **/
 GMimeStream *
@@ -330,6 +333,9 @@
  * Creates a new GMimeStreamFile object around @fp with bounds @start
  * and @end.
  *
+ * Note: The created GMimeStreamFile object will own the FILE pointer
+ * passed in.
+ *
  * Returns a stream using @fp with bounds @start and @end.
  **/
 GMimeStream *
@@ -344,4 +350,41 @@
 	g_mime_stream_construct (GMIME_STREAM (fstream), start, end);
 	
 	return GMIME_STREAM (fstream);
+}
+
+
+/**
+ * g_mime_stream_file_get_owner:
+ * @stream: file stream
+ *
+ * Gets whether or not @stream owns the backend FILE pointer.
+ *
+ * Returns %TRUE if @stream owns the backend FILE pointer or %FALSE
+ * otherwise.
+ **/
+gboolean
+g_mime_stream_file_get_owner (GMimeStreamFile *stream)
+{
+	g_return_val_if_fail (GMIME_IS_STREAM_FILE (stream), FALSE);
+	
+	return stream->owner;
+}
+
+
+/**
+ * g_mime_stream_file_set_owner:
+ * @stream: file stream
+ * @owner: owner
+ *
+ * Sets whether or not @stream owns the backend FILE pointer.
+ *
+ * Note: @owner should be %TRUE if the stream should fclose() the
+ * backend FILE pointer when destroyed or %FALSE otherwise.
+ **/
+void
+g_mime_stream_file_set_owner (GMimeStreamFile *stream, gboolean owner)
+{
+	g_return_if_fail (GMIME_IS_STREAM_FILE (stream));
+	
+	stream->owner = owner;
 }
Index: gmime/gmime-stream-file.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-file.h,v
retrieving revision 1.3
diff -u -r1.3 gmime-stream-file.h
--- gmime/gmime-stream-file.h	30 Dec 2002 16:37:57 -0000	1.3
+++ gmime/gmime-stream-file.h	7 Jun 2004 06:10:21 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,15 +24,15 @@
 #ifndef __GMIME_STREAM_FILE_H__
 #define __GMIME_STREAM_FILE_H__
 
+#include <glib.h>
+#include <stdio.h>
+#include <gmime/gmime-stream.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include <stdio.h>
-#include "gmime-stream.h"
-
 #define GMIME_TYPE_STREAM_FILE            (g_mime_stream_file_get_type ())
 #define GMIME_STREAM_FILE(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM_FILE, GMimeStreamFile))
 #define GMIME_STREAM_FILE_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_STREAM_FILE, GMimeStreamFileClass))
@@ -60,6 +60,9 @@
 
 GMimeStream *g_mime_stream_file_new (FILE *fp);
 GMimeStream *g_mime_stream_file_new_with_bounds (FILE *fp, off_t start, off_t end);
+
+gboolean g_mime_stream_file_get_owner (GMimeStreamFile *stream);
+void g_mime_stream_file_set_owner (GMimeStreamFile *stream, gboolean owner);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-stream-filter.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-filter.c,v
retrieving revision 1.8
diff -u -r1.8 gmime-stream-filter.c
--- gmime/gmime-stream-filter.c	26 Dec 2002 18:50:15 -0000	1.8
+++ gmime/gmime-stream-filter.c	7 Jun 2004 06:10:22 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -50,22 +50,23 @@
 	char *filtered;		/* the filtered data */
 	size_t filteredlen;
 	
-	int last_was_read;	/* was the last op read or write? */
+	int last_was_read:1;	/* was the last op read or write? */
+	int flushed:1;          /* have the filters been flushed? */
 };
 
 static void g_mime_stream_filter_class_init (GMimeStreamFilterClass *klass);
 static void g_mime_stream_filter_init (GMimeStreamFilter *stream, GMimeStreamFilterClass *klass);
 static void g_mime_stream_filter_finalize (GObject *object);
 
-static gssize stream_read (GMimeStream *stream, char *buf, size_t len);
-static gssize stream_write (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t n);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t n);
 static int stream_flush (GMimeStream *stream);
 static int stream_close (GMimeStream *stream);
 static gboolean stream_eos (GMimeStream *stream);
 static int stream_reset (GMimeStream *stream);
 static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 static off_t stream_tell (GMimeStream *stream);
-static gssize stream_length (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
 static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
 
 
@@ -130,6 +131,7 @@
 	stream->priv->buffer = stream->priv->realbuffer + READ_PAD;
 	stream->priv->last_was_read = TRUE;
 	stream->priv->filteredlen = 0;
+	stream->priv->flushed = FALSE;
 }
 
 static void
@@ -142,7 +144,7 @@
 	f = p->filters;
 	while (f) {
 		fn = f->next;
-		g_mime_filter_destroy (f->filter);
+		g_object_unref (f->filter);
 		g_free (f);
 		f = fn;
 	}
@@ -151,82 +153,83 @@
 	g_free (p);
 	
 	if (filter->source)
-		g_mime_stream_unref (filter->source);
+		g_object_unref (filter->source);
 	
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 
-static gssize
-stream_read (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_read (GMimeStream *stream, char *buf, size_t n)
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
-	struct _GMimeStreamFilterPrivate *p = filter->priv;
+	struct _GMimeStreamFilterPrivate *priv = filter->priv;
 	struct _filter *f;
-	gssize size;
+	ssize_t nread;
 	
-	p->last_was_read = TRUE;
+	priv->last_was_read = TRUE;
 	
-	if (p->filteredlen <= 0) {
+	if (priv->filteredlen <= 0) {
 		size_t presize = READ_SIZE;
 		
-		size = g_mime_stream_read (filter->source, p->buffer, READ_SIZE);
-		if (size <= 0) {
+		nread = g_mime_stream_read (filter->source, priv->buffer, READ_SIZE);
+		if (nread <= 0) {
 			/* this is somewhat untested */
-			if (g_mime_stream_eos (filter->source)) {
-				f = p->filters;
-				p->filtered = p->buffer;
-				p->filteredlen = 0;
-				while (f) {
-					g_mime_filter_complete (f->filter, p->filtered, p->filteredlen,
-								presize, &p->filtered, &p->filteredlen,
+			if (g_mime_stream_eos (filter->source) && !priv->flushed) {
+				priv->filtered = priv->buffer;
+				priv->filteredlen = 0;
+				f = priv->filters;
+				
+				while (f != NULL) {
+					g_mime_filter_complete (f->filter, priv->filtered, priv->filteredlen,
+								presize, &priv->filtered, &priv->filteredlen,
 								&presize);
 					f = f->next;
 				}
-				size = p->filteredlen;
+				
+				nread = priv->filteredlen;
+				priv->flushed = TRUE;
 			}
-			if (size <= 0)
-				return size;
+			
+			if (nread <= 0)
+				return nread;
 		} else {
-			f = p->filters;
-			p->filtered = p->buffer;
-			p->filteredlen = size;
+			priv->filtered = priv->buffer;
+			priv->filteredlen = nread;
+			f = priv->filters;
 			
-			while (f) {
-				g_mime_filter_filter (f->filter, p->filtered, p->filteredlen, presize,
-						      &p->filtered, &p->filteredlen, &presize);
+			while (f != NULL) {
+				g_mime_filter_filter (f->filter, priv->filtered, priv->filteredlen, presize,
+						      &priv->filtered, &priv->filteredlen, &presize);
 				
 				f = f->next;
 			}
 		}
 	}
 	
-	size = MIN (len, p->filteredlen);
-	memcpy (buf, p->filtered, size);
-	p->filteredlen -= size;
-	p->filtered += size;
+	nread = MIN (n, priv->filteredlen);
+	memcpy (buf, priv->filtered, nread);
+	priv->filteredlen -= nread;
+	priv->filtered += nread;
 	
-	return size;
+	return nread;
 }
 
-static gssize
-stream_write (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t n)
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
-	struct _GMimeStreamFilterPrivate *p = filter->priv;
+	struct _GMimeStreamFilterPrivate *priv = filter->priv;
+	char *buffer = (char *) buf;
+	ssize_t nwritten = n;
 	struct _filter *f;
 	size_t presize;
-	char *buffer;
-	size_t n;
 	
-	p->last_was_read = FALSE;
+	priv->last_was_read = FALSE;
 	
-	buffer = buf;
-	n = len;
-	
-	f = p->filters;
+	f = priv->filters;
 	presize = 0;
-	while (f) {
+	while (f != NULL) {
 		g_mime_filter_filter (f->filter, buffer, n, presize, &buffer, &n, &presize);
 		
 		f = f->next;
@@ -235,20 +238,20 @@
 	if (g_mime_stream_write (filter->source, buffer, n) != n)
 		return -1;
 	
-	/* return 'len' because that's what our caller expects */
-	return len;
+	/* return original input len because that's what our caller expects */
+	return nwritten;
 }
 
 static int
 stream_flush (GMimeStream *stream)
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
-	struct _GMimeStreamFilterPrivate *p = filter->priv;
-	size_t len, presize;
+	struct _GMimeStreamFilterPrivate *priv = filter->priv;
+	size_t presize, len;
 	struct _filter *f;
 	char *buffer;
 	
-	if (p->last_was_read) {
+	if (priv->last_was_read) {
 		/* no-op */
 		return 0;
 	}
@@ -256,9 +259,9 @@
 	buffer = "";
 	len = 0;
 	presize = 0;
-	f = p->filters;
+	f = priv->filters;
 	
-	while (f) {
+	while (f != NULL) {
 		g_mime_filter_complete (f->filter, buffer, len, presize, &buffer, &len, &presize);
 		
 		f = f->next;
@@ -274,11 +277,10 @@
 stream_close (GMimeStream *stream)
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
-	struct _GMimeStreamFilterPrivate *p = filter->priv;
+	struct _GMimeStreamFilterPrivate *priv = filter->priv;
 	
-	if (!p->last_was_read) {
+	if (!priv->last_was_read)
 		stream_flush (stream);
-	}
 	
 	return g_mime_stream_close (filter->source);
 }
@@ -287,9 +289,12 @@
 stream_eos (GMimeStream *stream)
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
-	struct _GMimeStreamFilterPrivate *p = filter->priv;
+	struct _GMimeStreamFilterPrivate *priv = filter->priv;
+	
+	if (priv->filteredlen > 0)
+		return FALSE;
 	
-	if (p->filteredlen > 0)
+	if (!priv->flushed)
 		return FALSE;
 	
 	return g_mime_stream_eos (filter->source);
@@ -299,14 +304,15 @@
 stream_reset (GMimeStream *stream)
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
-	struct _GMimeStreamFilterPrivate *p = filter->priv;
+	struct _GMimeStreamFilterPrivate *priv = filter->priv;
 	struct _filter *f;
 	
-	p->filteredlen = 0;
+	priv->filteredlen = 0;
+	priv->flushed = FALSE;
 	
 	/* and reset filters */
-	f = p->filters;
-	while (f) {
+	f = priv->filters;
+	while (f != NULL) {
 		g_mime_filter_reset (f->filter);
 		f = f->next;
 	}
@@ -317,8 +323,6 @@
 static off_t
 stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence)
 {
-	/*GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;*/
-	
 	return -1;
 }
 
@@ -328,7 +332,7 @@
 	return -1;
 }
 
-static gssize
+static ssize_t
 stream_length (GMimeStream *stream)
 {
 	return stream->bound_end - stream->bound_start;
@@ -339,11 +343,10 @@
 {
 	GMimeStreamFilter *filter = (GMimeStreamFilter *) stream;
 	GMimeStreamFilter *sub;
-	/*struct _filter *f, *fn;*/
 	
 	sub = g_object_new (GMIME_TYPE_STREAM_FILTER, NULL, NULL);
 	sub->source = filter->source;
-	g_mime_stream_ref (sub->source);
+	g_object_ref (sub->source);
 	
 	if (filter->priv->filters) {
 		struct _filter *f, *sn, *s = NULL;
@@ -395,7 +398,7 @@
 	
 	filter = g_object_new (GMIME_TYPE_STREAM_FILTER, NULL, NULL);
 	filter->source = stream;
-	g_mime_stream_ref (stream);
+	g_object_ref (stream);
 	
 	g_mime_stream_construct (GMIME_STREAM (filter),
 				 stream->bound_start,
@@ -417,20 +420,22 @@
 int
 g_mime_stream_filter_add (GMimeStreamFilter *fstream, GMimeFilter *filter)
 {
-	struct _GMimeStreamFilterPrivate *p;
+	struct _GMimeStreamFilterPrivate *priv;
 	struct _filter *f, *fn;
 	
 	g_return_val_if_fail (GMIME_IS_STREAM_FILTER (fstream), -1);
-	g_return_val_if_fail (filter != NULL, -1);
+	g_return_val_if_fail (GMIME_IS_FILTER (filter), -1);
+	
+	g_object_ref (filter);
 	
-	p = fstream->priv;
+	priv = fstream->priv;
 	
 	fn = g_new (struct _filter, 1);
 	fn->next = NULL;
 	fn->filter = filter;
-	fn->id = p->filterid++;
+	fn->id = priv->filterid++;
 	
-	f = (struct _filter *) &p->filters;
+	f = (struct _filter *) &priv->filters;
 	while (f->next)
 		f = f->next;
 	
@@ -452,22 +457,22 @@
 void
 g_mime_stream_filter_remove (GMimeStreamFilter *fstream, int id)
 {
-	struct _GMimeStreamFilterPrivate *p;
+	struct _GMimeStreamFilterPrivate *priv;
 	struct _filter *f, *fn;
 	
 	g_return_if_fail (GMIME_IS_STREAM_FILTER (fstream));
 	
-	p = fstream->priv;
+	priv = fstream->priv;
 	
 	if (id == -1)
 		return;
 	
-	f = (struct _filter *) &p->filters;
+	f = (struct _filter *) &priv->filters;
 	while (f && f->next) {
 		fn = f->next;
 		if (fn->id == id) {
 			f->next = fn->next;
-			g_mime_filter_destroy (fn->filter);
+			g_object_unref (fn->filter);
 			g_free (fn);
 		}
 		f = f->next;
Index: gmime/gmime-stream-filter.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-filter.h,v
retrieving revision 1.2
diff -u -r1.2 gmime-stream-filter.h
--- gmime/gmime-stream-filter.h	14 May 2002 03:24:13 -0000	1.2
+++ gmime/gmime-stream-filter.h	7 Jun 2004 06:10:22 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,13 +24,13 @@
 #ifndef __GMIME_STREAM_FILTER_H__
 #define __GMIME_STREAM_FILTER_H__
 
+#include <gmime/gmime-stream.h>
+#include <gmime/gmime-filter.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include "gmime-stream.h"
-#include "gmime-filter.h"
 
 #define GMIME_TYPE_STREAM_FILTER            (g_mime_stream_filter_get_type ())
 #define GMIME_STREAM_FILTER(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM_FILTER, GMimeStreamFilter))
Index: gmime/gmime-stream-mem.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-mem.c,v
retrieving revision 1.7
diff -u -r1.7 gmime-stream-mem.c
--- gmime/gmime-stream-mem.c	15 Jan 2004 21:50:15 -0000	1.7
+++ gmime/gmime-stream-mem.c	7 Jun 2004 06:10:22 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -33,15 +33,15 @@
 static void g_mime_stream_mem_init (GMimeStreamMem *stream, GMimeStreamMemClass *klass);
 static void g_mime_stream_mem_finalize (GObject *object);
 
-static gssize stream_read (GMimeStream *stream, char *buf, size_t len);
-static gssize stream_write (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
 static int stream_flush (GMimeStream *stream);
 static int stream_close (GMimeStream *stream);
 static gboolean stream_eos (GMimeStream *stream);
 static int stream_reset (GMimeStream *stream);
 static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 static off_t stream_tell (GMimeStream *stream);
-static gssize stream_length (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
 static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
 
 
@@ -114,7 +114,7 @@
 }
 
 
-static gssize
+static ssize_t
 stream_read (GMimeStream *stream, char *buf, size_t len)
 {
 	GMimeStreamMem *mem = (GMimeStreamMem *) stream;
@@ -137,12 +137,12 @@
 	return n;
 }
 
-static gssize
-stream_write (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t len)
 {
 	GMimeStreamMem *mem = (GMimeStreamMem *) stream;
 	off_t bound_end;
-	gssize n;
+	ssize_t n;
 	
 	g_return_val_if_fail (mem->buffer != NULL, -1);
 	
@@ -152,7 +152,7 @@
 	} else
 		bound_end = stream->bound_end;
 	
-	n = MIN (bound_end - stream->position, len);
+	n = MIN (bound_end - stream->position, (off_t) len);
 	if (n > 0) {
 		memcpy (mem->buffer->data + stream->position, buf, n);
 		stream->position += n;
@@ -252,7 +252,7 @@
 	return stream->position;
 }
 
-static gssize
+static ssize_t
 stream_length (GMimeStream *stream)
 {
 	GMimeStreamMem *mem = GMIME_STREAM_MEM (stream);
@@ -357,8 +357,10 @@
  * @mem: memory stream
  * @array: stream data
  *
- * Sets the byte array on the memory stream. Note: The memory stream
- * is not responsible for freeing the byte array.
+ * Sets the byte array on the memory stream.
+ *
+ * Note: The memory stream is not responsible for freeing the byte
+ * array.
  **/
 void
 g_mime_stream_mem_set_byte_array (GMimeStreamMem *mem, GByteArray *array)
@@ -379,4 +381,41 @@
 	stream->position = 0;
 	stream->bound_start = 0;
 	stream->bound_end = -1;
+}
+
+
+/**
+ * g_mime_stream_mem_get_owner:
+ * @mem: memory stream
+ *
+ * Gets whether or not @mem owns the backend memory buffer.
+ *
+ * Returns %TRUE if @mem owns the backend memory buffer or %FALSE
+ * otherwise.
+ **/
+gboolean
+g_mime_stream_mem_get_owner (GMimeStreamMem *mem)
+{
+	g_return_val_if_fail (GMIME_IS_STREAM_MEM (mem), FALSE);
+	
+	return mem->owner;
+}
+
+
+/**
+ * g_mime_stream_mem_set_owner:
+ * @mem: memory stream
+ * @owner: owner
+ *
+ * Sets whether or not @mem owns the backend memory buffer.
+ *
+ * Note: @owner should be %TRUE if the stream should free the backend
+ * memory buffer when destroyed or %FALSE otherwise.
+ **/
+void
+g_mime_stream_mem_set_owner (GMimeStreamMem *mem, gboolean owner)
+{
+	g_return_if_fail (GMIME_IS_STREAM_MEM (mem));
+	
+	mem->owner = owner;
 }
Index: gmime/gmime-stream-mem.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream-mem.h,v
retrieving revision 1.3
diff -u -r1.3 gmime-stream-mem.h
--- gmime/gmime-stream-mem.h	30 Dec 2002 16:37:57 -0000	1.3
+++ gmime/gmime-stream-mem.h	7 Jun 2004 06:10:22 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,14 +24,14 @@
 #ifndef __GMIME_STREAM_MEM_H__
 #define __GMIME_STREAM_MEM_H__
 
+#include <glib.h>
+#include <gmime/gmime-stream.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-#include "gmime-stream.h"
-
 #define GMIME_TYPE_STREAM_MEM            (g_mime_stream_mem_get_type ())
 #define GMIME_STREAM_MEM(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM_MEM, GMimeStreamMem))
 #define GMIME_STREAM_MEM_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_STREAM_MEM, GMimeStreamMemClass))
@@ -62,6 +62,9 @@
 GMimeStream *g_mime_stream_mem_new_with_buffer (const char *buffer, size_t len);
 
 void g_mime_stream_mem_set_byte_array (GMimeStreamMem *mem, GByteArray *array);
+
+gboolean g_mime_stream_mem_get_owner (GMimeStreamMem *mem);
+void g_mime_stream_mem_set_owner (GMimeStreamMem *mem, gboolean owner);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-stream-null.c
===================================================================
RCS file: gmime/gmime-stream-null.c
diff -N gmime/gmime-stream-null.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-stream-null.c	7 Jun 2004 06:10:22 -0000
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "gmime-stream-null.h"
+
+static void g_mime_stream_null_class_init (GMimeStreamNullClass *klass);
+static void g_mime_stream_null_init (GMimeStreamNull *stream, GMimeStreamNullClass *klass);
+static void g_mime_stream_null_finalize (GObject *object);
+
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
+static int stream_flush (GMimeStream *stream);
+static int stream_close (GMimeStream *stream);
+static gboolean stream_eos (GMimeStream *stream);
+static int stream_reset (GMimeStream *stream);
+static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
+static off_t stream_tell (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
+static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
+
+
+static GMimeStreamClass *parent_class = NULL;
+
+
+GType
+g_mime_stream_null_get_type (void)
+{
+	static GType type = 0;
+	
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof (GMimeStreamNullClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc) g_mime_stream_null_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GMimeStreamNull),
+			16,   /* n_preallocs */
+			(GInstanceInitFunc) g_mime_stream_null_init,
+		};
+		
+		type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamNull", &info, 0);
+	}
+	
+	return type;
+}
+
+
+static void
+g_mime_stream_null_class_init (GMimeStreamNullClass *klass)
+{
+	GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	
+	parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
+	
+	object_class->finalize = g_mime_stream_null_finalize;
+	
+	stream_class->read = stream_read;
+	stream_class->write = stream_write;
+	stream_class->flush = stream_flush;
+	stream_class->close = stream_close;
+	stream_class->eos = stream_eos;
+	stream_class->reset = stream_reset;
+	stream_class->seek = stream_seek;
+	stream_class->tell = stream_tell;
+	stream_class->length = stream_length;
+	stream_class->substream = stream_substream;
+}
+
+static void
+g_mime_stream_null_init (GMimeStreamNull *stream, GMimeStreamNullClass *klass)
+{
+	stream->written = 0;
+	stream->newlines = 0;
+}
+
+static void
+g_mime_stream_null_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static ssize_t
+stream_read (GMimeStream *stream, char *buf, size_t len)
+{
+	memset (buf, 0, len);
+	
+	stream->position += len;
+	
+	return len;
+}
+
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t len)
+{
+	GMimeStreamNull *null = (GMimeStreamNull *) stream;
+	register const char *inptr = buf;
+	const char *inend = buf + len;
+	
+	while (inptr < inend) {
+		if (*inptr == '\n')
+			null->newlines++;
+		inptr++;
+	}
+	
+	null->written += len;
+	stream->position += len;
+	
+	return len;
+}
+
+static int
+stream_flush (GMimeStream *stream)
+{
+	return 0;
+}
+
+static int
+stream_close (GMimeStream *stream)
+{
+	return 0;
+}
+
+static gboolean
+stream_eos (GMimeStream *stream)
+{
+	return TRUE;
+}
+
+static int
+stream_reset (GMimeStream *stream)
+{
+	GMimeStreamNull *null = (GMimeStreamNull *) stream;
+	
+	null->written = 0;
+	null->newlines = 0;
+	
+	return 0;
+}
+
+static off_t
+stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence)
+{
+	GMimeStreamNull *null = (GMimeStreamNull *) stream;
+	off_t bound_end;
+	
+	bound_end = stream->bound_end != -1 ? stream->bound_end : null->written;
+	
+	switch (whence) {
+	case GMIME_STREAM_SEEK_SET:
+		stream->position = MIN (offset + stream->bound_start, bound_end);
+		break;
+	case GMIME_STREAM_SEEK_END:
+		stream->position = MAX (offset + bound_end, 0);
+		break;
+	case GMIME_STREAM_SEEK_CUR:
+		stream->position += offset;
+		if (stream->position < stream->bound_start)
+			stream->position = stream->bound_start;
+		else if (stream->position > bound_end)
+			stream->position = bound_end;
+	}
+	
+	return stream->position;
+}
+
+static off_t
+stream_tell (GMimeStream *stream)
+{
+	return stream->position;
+}
+
+static ssize_t
+stream_length (GMimeStream *stream)
+{
+	GMimeStreamNull *null = GMIME_STREAM_NULL (stream);
+	off_t bound_end;
+	
+	bound_end = stream->bound_end != -1 ? stream->bound_end : null->written;
+	
+	return bound_end - stream->bound_start;
+}
+
+static GMimeStream *
+stream_substream (GMimeStream *stream, off_t start, off_t end)
+{
+	GMimeStreamNull *null;
+	
+	null = g_object_new (GMIME_TYPE_STREAM_NULL, NULL, NULL);
+	
+	g_mime_stream_construct (GMIME_STREAM (null), start, end);
+	
+	return GMIME_STREAM (null);
+}
+
+
+/**
+ * g_mime_stream_null_new:
+ *
+ * Creates a new GMimeStreamNull object.
+ *
+ * Returns a new null stream (similar to /dev/null on Unix).
+ **/
+GMimeStream *
+g_mime_stream_null_new (void)
+{
+	GMimeStreamNull *null;
+	
+	null = g_object_new (GMIME_TYPE_STREAM_NULL, NULL, NULL);
+	
+	g_mime_stream_construct (GMIME_STREAM (null), 0, -1);
+	
+	return GMIME_STREAM (null);
+}
Index: gmime/gmime-stream-null.h
===================================================================
RCS file: gmime/gmime-stream-null.h
diff -N gmime/gmime-stream-null.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gmime-stream-null.h	7 Jun 2004 06:10:22 -0000
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __GMIME_STREAM_NULL_H__
+#define __GMIME_STREAM_NULL_H__
+
+#include <glib.h>
+#include <gmime/gmime-stream.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define GMIME_TYPE_STREAM_NULL            (g_mime_stream_null_get_type ())
+#define GMIME_STREAM_NULL(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM_NULL, GMimeStreamNull))
+#define GMIME_STREAM_NULL_CLASS(klass)    (GMIME_CHECK_CLASS_CAST ((klass), GMIME_TYPE_STREAM_NULL, GMimeStreamNullClass))
+#define GMIME_IS_STREAM_NULL(obj)         (GMIME_CHECK_TYPE ((obj), GMIME_TYPE_STREAM_NULL))
+#define GMIME_IS_STREAM_NULL_CLASS(klass) (GMIME_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_STREAM_NULL))
+#define GMIME_STREAM_NULL_GET_CLASS(obj)  (GMIME_CHECK_GET_CLASS ((obj), GMIME_TYPE_STREAM_NULL, GMimeStreamNullClass))
+
+typedef struct _GMimeStreamNull GMimeStreamNull;
+typedef struct _GMimeStreamNullClass GMimeStreamNullClass;
+
+struct _GMimeStreamNull {
+	GMimeStream parent_object;
+	
+	size_t written;
+	size_t newlines;
+};
+
+struct _GMimeStreamNullClass {
+	GMimeStreamClass parent_class;
+	
+};
+
+
+GType g_mime_stream_null_get_type (void);
+
+GMimeStream *g_mime_stream_null_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GMIME_STREAM_NULL_H__ */
Index: gmime/gmime-stream.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream.c,v
retrieving revision 1.11
diff -u -r1.11 gmime-stream.c
--- gmime/gmime-stream.c	26 Dec 2002 18:50:15 -0000	1.11
+++ gmime/gmime-stream.c	7 Jun 2004 06:10:23 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -29,21 +29,21 @@
 
 #include "gmime-stream.h"
 
-#define d(x) x
+#define d(x)
 
 static void g_mime_stream_class_init (GMimeStreamClass *klass);
 static void g_mime_stream_init (GMimeStream *stream, GMimeStreamClass *klass);
 static void g_mime_stream_finalize (GObject *object);
 
-static gssize stream_read (GMimeStream *stream, char *buf, size_t len);
-static gssize stream_write (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
+static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
 static int stream_flush (GMimeStream *stream);
 static int stream_close (GMimeStream *stream);
 static gboolean stream_eos (GMimeStream *stream);
 static int stream_reset (GMimeStream *stream);
 static off_t stream_seek (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 static off_t stream_tell (GMimeStream *stream);
-static gssize stream_length (GMimeStream *stream);
+static ssize_t stream_length (GMimeStream *stream);
 static GMimeStream *stream_substream (GMimeStream *stream, off_t start, off_t end);
 
 
@@ -112,7 +112,7 @@
 	GMimeStream *stream = (GMimeStream *) object;
 	
 	if (stream->super_stream)
-		g_mime_stream_unref (stream->super_stream);
+		g_object_unref (stream->super_stream);
 	
 	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -135,7 +135,7 @@
 }
 
 
-static gssize
+static ssize_t
 stream_read (GMimeStream *stream, char *buf, size_t len)
 {
 	d(g_warning ("Invoked default stream_read implementation."));
@@ -153,18 +153,21 @@
  *
  * Returns the number of bytes read or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_read (GMimeStream *stream, char *buf, size_t len)
 {
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
 	g_return_val_if_fail (buf != NULL, -1);
 	
+	if (len == 0)
+		return 0;
+	
 	return GMIME_STREAM_GET_CLASS (stream)->read (stream, buf, len);
 }
 
 
-static gssize
-stream_write (GMimeStream *stream, char *buf, size_t len)
+static ssize_t
+stream_write (GMimeStream *stream, const char *buf, size_t len)
 {
 	d(g_warning ("Invoked default stream_write implementation."));
 	return 0;
@@ -181,12 +184,15 @@
  *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
-g_mime_stream_write (GMimeStream *stream, char *buf, size_t len)
+ssize_t
+g_mime_stream_write (GMimeStream *stream, const char *buf, size_t len)
 {
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
 	g_return_val_if_fail (buf != NULL, -1);
 	
+	if (len == 0)
+		return 0;
+	
 	return GMIME_STREAM_GET_CLASS (stream)->write (stream, buf, len);
 }
 
@@ -357,7 +363,7 @@
 }
 
 
-static gssize
+static ssize_t
 stream_length (GMimeStream *stream)
 {
 	d(g_warning ("Invoked default stream_length implementation."));
@@ -373,7 +379,7 @@
  *
  * Returns the length of the stream or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_length (GMimeStream *stream)
 {
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
@@ -410,7 +416,7 @@
 	sub = GMIME_STREAM_GET_CLASS (stream)->substream (stream, start, end);
 	if (sub) {
 		sub->super_stream = stream;
-		g_mime_stream_ref (stream);
+		g_object_ref (stream);
 	}
 	
 	return sub;
@@ -422,13 +428,15 @@
  * @stream: stream
  *
  * Ref's a stream.
+ *
+ * WARNING: This method is deprecated. Use #g_object_ref instead.
  **/
 void
 g_mime_stream_ref (GMimeStream *stream)
 {
 	g_return_if_fail (GMIME_IS_STREAM (stream));
 	
-	g_object_ref ((GObject *) stream);
+	g_object_ref (stream);
 }
 
 
@@ -437,13 +445,15 @@
  * @stream: stream
  *
  * Unref's a stream.
+ *
+ * WARNING: This method is deprecated. Use g_object_unref instead.
  **/
 void
 g_mime_stream_unref (GMimeStream *stream)
 {
 	g_return_if_fail (GMIME_IS_STREAM (stream));
 	
-	g_object_unref ((GObject *) stream);
+	g_object_unref (stream);
 }
 
 
@@ -479,7 +489,7 @@
  *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_write_string (GMimeStream *stream, const char *string)
 {
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
@@ -499,12 +509,12 @@
  *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_printf (GMimeStream *stream, const char *fmt, ...)
 {
 	va_list args;
 	char *string;
-	gssize ret;
+	ssize_t ret;
 	
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
 	g_return_val_if_fail (fmt != NULL, -1);
@@ -532,10 +542,10 @@
  *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_write_to_stream (GMimeStream *src, GMimeStream *dest)
 {
-	gssize nread, nwritten, total = 0;
+	ssize_t nread, nwritten, total = 0;
 	char buf[4096];
 	
 	g_return_val_if_fail (GMIME_IS_STREAM (src), -1);
@@ -549,7 +559,7 @@
 		if (nread > 0) {
 			nwritten = 0;
 			while (nwritten < nread) {
-				gssize len;
+				ssize_t len;
 				
 				len = g_mime_stream_write (dest, buf + nwritten, nread - nwritten);
 				if (len < 0)
@@ -576,19 +586,20 @@
  *
  * Returns the number of bytes written or -1 on fail.
  **/
-gssize
+ssize_t
 g_mime_stream_writev (GMimeStream *stream, GMimeStreamIOVector *vector, size_t count)
 {
-	gssize total = 0;
+	ssize_t total = 0;
 	int i;
 	
 	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
 	
 	for (i = 0; i < count; i++) {
-		gssize n, nwritten = 0;
+		char *buffer = vector[i].data;
+		ssize_t n, nwritten = 0;
 		
 		while (nwritten < vector[i].len) {
-			n = g_mime_stream_write (stream, (char*)vector[i].data + nwritten,
+			n = g_mime_stream_write (stream, buffer + nwritten,
 						 vector[i].len - nwritten);
 			if (n > 0)
 				nwritten += n;
Index: gmime/gmime-stream.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-stream.h,v
retrieving revision 1.7
diff -u -r1.7 gmime-stream.h
--- gmime/gmime-stream.h	30 Dec 2002 16:37:57 -0000	1.7
+++ gmime/gmime-stream.h	7 Jun 2004 06:10:23 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,18 +24,18 @@
 #ifndef __GMIME_STREAM_H__
 #define __GMIME_STREAM_H__
 
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus */
-
-#include <glib/gtypes.h>
+#include <glib.h>
 #include <glib-object.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdarg.h>
 
-#include "gmime-type-utils.h"
+#include <gmime/gmime-type-utils.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
 
 #define GMIME_TYPE_STREAM            (g_mime_stream_get_type ())
 #define GMIME_STREAM(obj)            (GMIME_CHECK_CAST ((obj), GMIME_TYPE_STREAM, GMimeStream))
@@ -47,7 +47,7 @@
 typedef enum {
 	GMIME_STREAM_SEEK_SET = SEEK_SET,
 	GMIME_STREAM_SEEK_CUR = SEEK_CUR,
-	GMIME_STREAM_SEEK_END = SEEK_END,
+	GMIME_STREAM_SEEK_END = SEEK_END
 } GMimeSeekWhence;
 
 typedef struct {
@@ -72,16 +72,16 @@
 struct _GMimeStreamClass {
 	GObjectClass parent_class;
 	
-	gssize  (*read)    (GMimeStream *stream, char *buf, size_t len);
-	gssize  (*write)   (GMimeStream *stream, char *buf, size_t len);
-	int      (*flush)   (GMimeStream *stream);
-	int      (*close)   (GMimeStream *stream);
-	gboolean (*eos)     (GMimeStream *stream);
-	int      (*reset)   (GMimeStream *stream);
-	off_t    (*seek)    (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
-	off_t    (*tell)    (GMimeStream *stream);
-	gssize  (*length)  (GMimeStream *stream);
-	GMimeStream *(*substream) (GMimeStream *stream, off_t start, off_t end);
+	ssize_t  (* read)   (GMimeStream *stream, char *buf, size_t len);
+	ssize_t  (* write)  (GMimeStream *stream, const char *buf, size_t len);
+	int      (* flush)  (GMimeStream *stream);
+	int      (* close)  (GMimeStream *stream);
+	gboolean (* eos)    (GMimeStream *stream);
+	int      (* reset)  (GMimeStream *stream);
+	off_t    (* seek)   (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
+	off_t    (* tell)   (GMimeStream *stream);
+	ssize_t  (* length) (GMimeStream *stream);
+	GMimeStream * (* substream) (GMimeStream *stream, off_t start, off_t end);
 };
 
 
@@ -91,30 +91,32 @@
 
 
 /* public methods */
-gssize   g_mime_stream_read    (GMimeStream *stream, char *buf, size_t len);
-gssize   g_mime_stream_write   (GMimeStream *stream, char *buf, size_t len);
+ssize_t   g_mime_stream_read    (GMimeStream *stream, char *buf, size_t len);
+ssize_t   g_mime_stream_write   (GMimeStream *stream, const char *buf, size_t len);
 int       g_mime_stream_flush   (GMimeStream *stream);
 int       g_mime_stream_close   (GMimeStream *stream);
 gboolean  g_mime_stream_eos     (GMimeStream *stream);
 int       g_mime_stream_reset   (GMimeStream *stream);
 off_t     g_mime_stream_seek    (GMimeStream *stream, off_t offset, GMimeSeekWhence whence);
 off_t     g_mime_stream_tell    (GMimeStream *stream);
-gssize   g_mime_stream_length  (GMimeStream *stream);
+ssize_t   g_mime_stream_length  (GMimeStream *stream);
 
 GMimeStream *g_mime_stream_substream (GMimeStream *stream, off_t start, off_t end);
 
+#ifndef GMIME_DISABLE_DEPRECATED
 void      g_mime_stream_ref     (GMimeStream *stream);
 void      g_mime_stream_unref   (GMimeStream *stream);
+#endif
 
 void      g_mime_stream_set_bounds (GMimeStream *stream, off_t start, off_t end);
 
-gssize   g_mime_stream_write_string (GMimeStream *stream, const char *string);
-gssize   g_mime_stream_printf       (GMimeStream *stream, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
+ssize_t   g_mime_stream_write_string (GMimeStream *stream, const char *string);
+ssize_t   g_mime_stream_printf       (GMimeStream *stream, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
 
-gssize   g_mime_stream_write_to_stream (GMimeStream *src, GMimeStream *dest);
+ssize_t   g_mime_stream_write_to_stream (GMimeStream *src, GMimeStream *dest);
 
 
-gssize    g_mime_stream_writev (GMimeStream *stream, GMimeStreamIOVector *vector, size_t count);
+ssize_t    g_mime_stream_writev (GMimeStream *stream, GMimeStreamIOVector *vector, size_t count);
 
 #ifdef __cplusplus
 }
Index: gmime/gmime-table-private.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-table-private.h,v
retrieving revision 1.4
diff -u -r1.4 gmime-table-private.h
--- gmime/gmime-table-private.h	13 Jun 2002 01:44:09 -0000	1.4
+++ gmime/gmime-table-private.h	7 Jun 2004 06:10:23 -0000
@@ -9,12 +9,12 @@
 static unsigned short gmime_special_table[256] = {
 	  5,  5,  5,  5,  5,  5,  5,  5,  5,103,  7,  5,  5, 39,  5,  5,
 	  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
-	242,448, 76,192,192,192,192,192, 76, 76,448,448, 76,448, 72,324,
-	448,448,448,448,448,448,448,448,448,448, 76, 76, 76,260, 76, 68,
-	 76,448,448,448,448,448,448,448,448,448,448,448,448,448,448,448,
-	448,448,448,448,448,448,448,448,448,448,448,108,236,108,192,320,
-	192,448,448,448,448,448,448,448,448,448,448,448,448,448,448,448,
-	448,448,448,448,448,448,448,448,448,448,448,192,192,192,192,  5,
+	242,960, 76,704,704,192,704,192, 76, 76,448,960, 76,960,584,324,
+	960,960,960,960,960,960,960,960,960,960, 76, 76, 76,260, 76, 68,
+	 76,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,
+	960,960,960,960,960,960,960,960,960,960,960,108,236,108,704,832,
+	704,960,960,960,960,960,960,960,960,960,960,960,960,960,960,960,
+	960,960,960,960,960,960,960,960,960,960,960,704,704,704,704,  5,
 	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
@@ -26,15 +26,16 @@
 };
 
 enum {
-	IS_CTRL    	= (1 << 0),
-	IS_LWSP    	= (1 << 1),
-	IS_TSPECIAL	= (1 << 2),
-	IS_SPECIAL 	= (1 << 3),
-	IS_SPACE   	= (1 << 4),
-	IS_DSPECIAL	= (1 << 5),
-	IS_QPSAFE  	= (1 << 6),
-	IS_ESAFE   	= (1 << 7), /* encoded word safe */
-	IS_PSAFE   	= (1 << 8)  /* encode word in phrase safe */
+	IS_CTRL     = (1 << 0),
+	IS_LWSP     = (1 << 1),
+	IS_TSPECIAL = (1 << 2),
+	IS_SPECIAL  = (1 << 3),
+	IS_SPACE    = (1 << 4),
+	IS_DSPECIAL = (1 << 5),
+	IS_QPSAFE   = (1 << 6),
+	IS_ESAFE    = (1 << 7), /* encoded word safe */
+	IS_PSAFE    = (1 << 8), /* encode word in phrase safe */
+	IS_ATTRCHAR = (1 << 9)  /* attribute-char from rfc2184 */
 };
 
 #define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0)
@@ -48,6 +49,7 @@
 #define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0)
 #define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESAFE) != 0)
 #define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0)
+#define is_attrchar(x) ((gmime_special_table[(unsigned char)(x)] & IS_ATTRCHAR) != 0)
 
 #define CHARS_LWSP " \t\n\r"               /* linear whitespace chars */
 #define CHARS_TSPECIAL "()<>@,;:\\\"/[]?="
@@ -56,5 +58,6 @@
 #define CHARS_DSPECIAL "[]\\\r \t"         /* not in domains */
 #define CHARS_ESPECIAL "()<>@,;:\"/[]?.=_" /* encoded word specials (rfc2047 5.1) */
 #define CHARS_PSPECIAL "!*+-/=_"           /* encoded phrase specials (rfc2047 5.3) */
+#define CHARS_ATTRCHAR "*'% "              /* attribute-char from rfc2184 */
 
 #define GMIME_FOLD_LEN 76
Index: gmime/gmime-type-utils.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-type-utils.h,v
retrieving revision 1.1
diff -u -r1.1 gmime-type-utils.h
--- gmime/gmime-type-utils.h	14 May 2002 03:24:13 -0000	1.1
+++ gmime/gmime-type-utils.h	7 Jun 2004 06:10:23 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
Index: gmime/gmime-utils.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-utils.c,v
retrieving revision 1.38
diff -u -r1.38 gmime-utils.c
--- gmime/gmime-utils.c	10 Mar 2003 21:22:56 -0000	1.38
+++ gmime/gmime-utils.c	7 Jun 2004 06:10:25 -0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Michael Zucchi <notzed@helixcode.com>
- *           Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Michael Zucchi <notzed@ximian.com>
+ *           Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -26,9 +26,21 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#else
+#define MAXHOSTNAMELEN 64
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
 #include <ctype.h>
 #include <errno.h>
 
@@ -39,13 +51,18 @@
 #include "gmime-iconv.h"
 #include "gmime-iconv-utils.h"
 
-#define d(x)
-#define w(x) x
-
 #ifndef HAVE_ISBLANK
 #define isblank(c) (c == ' ' || c == '\t')
 #endif
 
+#ifdef ENABLE_WARNINGS
+#define w(x) x
+#else
+#define w(x)
+#endif /* ENABLE_WARNINGS */
+
+#define d(x)
+
 #define GMIME_UUENCODE_CHAR(c) ((c) ? (c) + ' ' : '`')
 #define	GMIME_UUDECODE_CHAR(c) (((c) - ' ') & 077)
 
@@ -218,14 +235,14 @@
 		/* kill leading whitespace */
 		while (*start && isspace ((int) *start))
 			start++;
-	
+		
 		if (*start == '\0')
 			break;
-
+		
 		mask = gmime_datetok_table[*start];
 		
 		/* find the end of this token */
-		end = start+1;
+		end = start + 1;
 		while (*end && !strchr ("-/,\t\r\n ", *end))
 			mask |= gmime_datetok_table[*end++];
 		
@@ -317,7 +334,7 @@
 		return -1;
 	
 	for (wday = 0; wday < 7; wday++)
-		if (!g_strncasecmp (in, tm_days[wday], 3))
+		if (!strncasecmp (in, tm_days[wday], 3))
 			return wday;
 	
 	return -1;  /* unknown week day */
@@ -349,7 +366,7 @@
 		return -1;
 	
 	for (i = 0; i < 12; i++)
-		if (!g_strncasecmp (in, tm_months[i], 3))
+		if (!strncasecmp (in, tm_months[i], 3))
 			return i;
 	
 	return -1;  /* unknown month */
@@ -411,22 +428,31 @@
 static int
 get_tzone (struct _date_token **token)
 {
-	int i;
+	const unsigned char *inptr, *inend;
+	unsigned int inlen;
+	int i, t;
 	
 	for (i = 0; *token && i < 2; *token = (*token)->next, i++) {
-		const unsigned char *inptr = (*token)->start;
-		unsigned int inlen = (*token)->len;
+		inptr = (*token)->start;
+		inlen = (*token)->len;
+		inend = inptr + inlen;
 		
 		if (*inptr == '+' || *inptr == '-') {
 			return decode_int (inptr, inlen);
 		} else {
-			int t;
-			
-			if (*inptr == '(')
+			if (*inptr == '(') {
 				inptr++;
+				if (*(inend - 1) == ')')
+					inlen -= 2;
+				else
+					inlen--;
+			}
 			
 			for (t = 0; t < 15; t++) {
-				unsigned int len = MIN (strlen (tz_offsets[t].name), inlen - 1);
+				unsigned int len = strlen (tz_offsets[t].name);
+				
+				if (len != inlen)
+					continue;
 				
 				if (!strncmp (inptr, tz_offsets[t].name, len))
 					return tz_offsets[t].offset;
@@ -438,6 +464,32 @@
 }
 
 static time_t
+mktime_utc (struct tm *tm)
+{
+	time_t tt;
+	
+	tm->tm_isdst = -1;
+	tt = mktime (tm);
+	
+#if defined (HAVE_TM_GMTOFF)
+	tt += tm->tm_gmtoff;
+#elif defined (HAVE_TIMEZONE)
+	if (tm->tm_isdst > 0) {
+#if defined (HAVE_ALTZONE)
+		tt -= altzone;
+#else /* !defined (HAVE_ALTZONE) */
+		tt -= (timezone - 3600);
+#endif
+	} else
+		tt -= timezone;
+#else
+#error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
+#endif
+	
+	return tt;
+}
+
+static time_t
 parse_rfc822_date (struct _date_token *tokens, int *tzone)
 {
 	int hour, min, sec, offset, n;
@@ -495,14 +547,7 @@
 		offset = n;
 	}
 	
-	t = mktime (&tm);
-#if defined(HAVE_TIMEZONE)
-	t -= timezone;
-#elif defined(HAVE_TM_GMTOFF)
-	t += tm.tm_gmtoff;
-#else
-#error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
-#endif
+	t = mktime_utc (&tm);
 	
 	/* t is now GMT of the time we want, but not offset by the timezone ... */
 	
@@ -532,7 +577,7 @@
 	int hour, min, sec, offset, n;
 	struct _date_token *token;
 	struct tm tm;
-	time_t time;
+	time_t t;
 	
 	memset ((void *) &tm, 0, sizeof (struct tm));
 	got_wday = got_month = got_tzone = FALSE;
@@ -542,7 +587,7 @@
 	while (token) {
 		if (is_weekday (token) && !got_wday) {
 			if ((n = get_wday (token->start, token->len)) != -1) {
-				d(printf ("weekday; ");)
+				d(printf ("weekday; "));
 				got_wday = TRUE;
 				tm.tm_wday = n;
 				goto next_token;
@@ -551,7 +596,7 @@
 		
 		if (is_month (token) && !got_month) {
 			if ((n = get_month (token->start, token->len)) != -1) {
-				d(printf ("month; ");)
+				d(printf ("month; "));
 				got_month = TRUE;
 				tm.tm_mon = n;
 				goto next_token;
@@ -560,7 +605,7 @@
 		
 		if (is_time (token) && !tm.tm_hour && !tm.tm_min && !tm.tm_sec) {
 			if (get_time (token->start, token->len, &hour, &min, &sec)) {
-				d(printf ("time; ");)
+				d(printf ("time; "));
 				tm.tm_hour = hour;
 				tm.tm_min = min;
 				tm.tm_sec = sec;
@@ -572,7 +617,7 @@
 			struct _date_token *t = token;
 			
 			if ((n = get_tzone (&t)) != -1) {
-				d(printf ("tzone; ");)
+				d(printf ("tzone; "));
 				got_tzone = TRUE;
 				offset = n;
 				goto next_token;
@@ -582,23 +627,23 @@
 		if (is_numeric (token)) {
 			if (token->len == 4 && !tm.tm_year) {
 				if ((n = get_year (token->start, token->len)) != -1) {
-					d(printf ("year; ");)
+					d(printf ("year; "));
 					tm.tm_year = n - 1900;
 					goto next_token;
 				}
 			} else {
 				if (!got_month && !got_wday && token->next && is_numeric (token->next)) {
-					d(printf ("mon; ");)
+					d(printf ("mon; "));
 					n = decode_int (token->start, token->len);
 					got_month = TRUE;
 					tm.tm_mon = n - 1;
 					goto next_token;
 				} else if (!tm.tm_mday && (n = get_mday (token->start, token->len)) != -1) {
-					d(printf ("mday; ");)
+					d(printf ("mday; "));
 					tm.tm_mday = n;
 					goto next_token;
 				} else if (!tm.tm_year) {
-					d(printf ("2-digit year; ");)
+					d(printf ("2-digit year; "));
 					n = get_year (token->start, token->len);
 					tm.tm_year = n - 1900;
 					goto next_token;
@@ -606,33 +651,26 @@
 			}
 		}
 		
-		d(printf ("???; ");)
+		d(printf ("???; "));
 		
 	next_token:
 		
 		token = token->next;
 	}
 	
-	d(printf ("\n");)
+	d(printf ("\n"));
 		
-	time = mktime (&tm);
-#if defined(HAVE_TIMEZONE)
-	time -= timezone;
-#elif defined(HAVE_TM_GMTOFF)
-	time += tm.tm_gmtoff;
-#else
-#error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
-#endif
+	t = mktime_utc (&tm);
 	
 	/* t is now GMT of the time we want, but not offset by the timezone ... */
 	
 	/* this should convert the time to the GMT equiv time */
-	time -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
+	t -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
 	
 	if (tzone)
 		*tzone = offset;
 	
-	return time;
+	return t;
 }
 
 #if 0
@@ -715,16 +753,168 @@
 }
 
 
+/* external symbols from internet-address.c */
+extern void decode_lwsp (const char **in);
+extern char *decode_word (const char **in);
+extern char *decode_addrspec (const char **in);
+
+
+static char *
+decode_msgid (const char **in)
+{
+	const char *inptr = *in;
+	char *msgid = NULL;
+	
+	decode_lwsp (&inptr);
+	if (*inptr != '<') {
+		w(g_warning ("Invalid msg-id; missing '<': %s", *in));
+	} else {
+		inptr++;
+	}
+	
+	decode_lwsp (&inptr);
+	if ((msgid = decode_addrspec (&inptr))) {
+		decode_lwsp (&inptr);
+		if (*inptr != '>') {
+			w(g_warning ("Invalid msg-id; missing '>': %s", *in));
+		} else {
+			inptr++;
+		}
+		
+		*in = inptr;
+	} else {
+		w(g_warning ("Invalid msg-id; missing addr-spec: %s", *in));
+		*in = inptr;
+		while (*inptr && *inptr != '>')
+			inptr++;
+		
+		msgid = g_strndup (*in, inptr - *in);
+		*in = inptr;
+	}
+	
+	return msgid;
+}
+
+
 /**
- * g_mime_utils_header_fold:
- * @in: input header string
+ * g_mime_utils_decode_message_id:
+ * @message_id: string containing a message-id
  *
- * Folds a header according to the rules in rfc822.
+ * Decodes a msg-id as defined by rfc822.
  *
- * Returns an allocated string containing the folded header.
+ * Returns the addr-spec portion of the msg-id.
  **/
 char *
-g_mime_utils_header_fold (const char *in)
+g_mime_utils_decode_message_id (const char *message_id)
+{
+	g_return_val_if_fail (message_id != NULL, NULL);
+	
+	return decode_msgid (&message_id);
+}
+
+
+/**
+ * g_mime_references_decode:
+ * @text: string containing a list of msg-ids
+ *
+ * Decodes a list of msg-ids as in the References and/or In-Reply-To
+ * headers defined in rfc822.
+ *
+ * Returns a list of referenced msg-ids.
+ **/
+GMimeReferences *
+g_mime_references_decode (const char *text)
+{
+	GMimeReferences *refs, *tail, *ref;
+	const char *inptr = text;
+	char *word, *msgid;
+	
+	g_return_val_if_fail (text != NULL, NULL);
+	
+	refs = NULL;
+	tail = (GMimeReferences *) &refs;
+	
+	while (*inptr) {
+		decode_lwsp (&inptr);
+		if (*inptr == '<') {
+			/* looks like a msg-id */
+			if ((msgid = decode_msgid (&inptr))) {
+				ref = g_new (GMimeReferences, 1);
+				ref->next = NULL;
+				ref->msgid = msgid;
+				tail->next = ref;
+				tail = ref;
+			} else {
+				w(g_warning ("Invalid References header: %s", inptr));
+				break;
+			}
+		} else if (*inptr) {
+			/* looks like part of a phrase */
+			if ((word = decode_word (&inptr))) {
+				g_free (word);
+			} else {
+				w(g_warning ("Invalid References header: %s", inptr));
+				break;
+			}
+		}
+	}
+	
+	return refs;
+}
+
+
+/**
+ * g_mime_references_append:
+ * @refs: the address of a GMimeReferences list
+ * @msgid: a message-id string
+ *
+ * Appends a reference to msgid to the list of references.
+ **/
+void
+g_mime_references_append (GMimeReferences **refs, const char *msgid)
+{
+	GMimeReferences *ref;
+	
+	g_return_if_fail (refs != NULL);
+	g_return_if_fail (msgid != NULL);
+	
+	ref = (GMimeReferences *) refs;
+	while (ref->next)
+		ref = ref->next;
+	
+	ref->next = g_new (GMimeReferences, 1);
+	ref->next->msgid = g_strdup (msgid);
+	ref->next->next = NULL;
+}
+
+
+/**
+ * g_mime_references_clear:
+ * @refs: address of a GMimeReferences list
+ *
+ * Clears the GMimeReferences list and resets it to %NULL.
+ **/
+void
+g_mime_references_clear (GMimeReferences **refs)
+{
+	GMimeReferences *ref, *next;
+	
+	g_return_if_fail (refs != NULL);
+	
+	ref = *refs;
+	while (ref) {
+		next = ref->next;
+		g_free (ref->msgid);
+		g_free (ref);
+		ref = next;
+	}
+	
+	*refs = NULL;
+}
+
+
+static char *
+header_fold (const char *in, gboolean structured)
 {
 	gboolean last_was_lwsp = FALSE;
 	register const char *inptr;
@@ -734,20 +924,25 @@
 	
 	inptr = in;
 	len = strlen (in);
-	if (len <= GMIME_FOLD_LEN)
+	if (len <= GMIME_FOLD_LEN + 1)
 		return g_strdup (in);
 	
 	out = g_string_new ("");
 	outlen = 0;
-	while (*inptr) {
-		len = strcspn (inptr, " \t");
+	while (*inptr && *inptr != '\n') {
+		len = strcspn (inptr, " \t\n");
 		
-		if (outlen + len > GMIME_FOLD_LEN) {			
-			if (last_was_lwsp)
-				g_string_truncate (out, out->len - 1);
-			
-			g_string_append (out, "\n\t");
-			outlen = 1;
+		if (outlen + len > GMIME_FOLD_LEN) {
+			if (outlen > 1) {
+				if (last_was_lwsp) {
+					if (structured)
+						out->str[out->len - 1] = '\t';
+					
+					g_string_insert_c (out, out->len - 1, '\n');
+				} else
+					g_string_append (out, "\n\t");
+				outlen = 1;
+			}
 			
 			/* check for very long words, just cut them up */
 			while (outlen + len > GMIME_FOLD_LEN) {
@@ -765,22 +960,24 @@
 			inptr += len;
 			last_was_lwsp = FALSE;
 		} else {
+			last_was_lwsp = TRUE;
 			if (*inptr == '\t') {
 				/* tabs are a good place to fold, odds
-                                   are that this is where the previous
-                                   mailer folded it */
+				   are that this is where the previous
+				   mailer folded it */
 				g_string_append (out, "\n\t");
 				outlen = 1;
 				inptr++;
-				last_was_lwsp = FALSE;
 			} else {
 				g_string_append_c (out, *inptr++);
 				outlen++;
-				last_was_lwsp = TRUE;
 			}
 		}
 	}
 	
+	if (*inptr == '\n' && out->str[out->len - 1] != '\n')
+		g_string_append_c (out, '\n');
+	
 	ret = out->str;
 	g_string_free (out, FALSE);
 	
@@ -789,6 +986,51 @@
 
 
 /**
+ * g_mime_utils_structured_header_fold:
+ * @in: input header string
+ *
+ * Folds a structured header according to the rules in rfc822.
+ *
+ * Returns an allocated string containing the folded header.
+ **/
+char *
+g_mime_utils_structured_header_fold (const char *in)
+{
+	return header_fold (in, TRUE);
+}
+
+
+/**
+ * g_mime_utils_unstructured_header_fold:
+ * @in: input header string
+ *
+ * Folds an unstructured header according to the rules in rfc822.
+ *
+ * Returns an allocated string containing the folded header.
+ **/
+char *
+g_mime_utils_unstructured_header_fold (const char *in)
+{
+	return header_fold (in, FALSE);
+}
+
+
+/**
+ * g_mime_utils_header_fold:
+ * @in: input header string
+ *
+ * Folds a structured header according to the rules in rfc822.
+ *
+ * Returns an allocated string containing the folded header.
+ **/
+char *
+g_mime_utils_header_fold (const char *in)
+{
+	return header_fold (in, TRUE);
+}
+
+
+/**
  * g_mime_utils_header_printf:
  * @format: string format
  * @Varargs: arguments
@@ -809,7 +1051,7 @@
 	buf = g_strdup_vprintf (format, ap);
 	va_end (ap);
 	
-	ret = g_mime_utils_header_fold (buf);
+	ret = header_fold (buf, TRUE);
 	g_free (buf);
 	
 	return ret;
@@ -932,13 +1174,14 @@
 gboolean
 g_mime_utils_text_is_8bit (const unsigned char *text, size_t len)
 {
-	const unsigned char *c, *inend;
+	register const unsigned char *inptr;
+	const unsigned char *inend;
 	
 	g_return_val_if_fail (text != NULL, FALSE);
 	
 	inend = text + len;
-	for (c = text; c < inend; c++)
-		if (*c > (unsigned char) 127)
+	for (inptr = text; *inptr && inptr < inend; inptr++)
+		if (*inptr > (unsigned char) 127)
 			return TRUE;
 	
 	return FALSE;
@@ -975,7 +1218,7 @@
 }
 
 /* this decodes rfc2047's version of quoted-printable */
-static gssize
+static ssize_t
 quoted_decode (const unsigned char *in, size_t len, unsigned char *out)
 {
 	register const unsigned char *inptr;
@@ -1029,7 +1272,7 @@
 	inptr = memchr (inptr, '?', inend - inptr);
 	if (inptr && inptr[2] == '?') {
 		unsigned char *decoded;
-		gssize declen;
+		ssize_t declen;
 		int state = 0;
 		int save = 0;
 		
@@ -1077,14 +1320,14 @@
 			*p = '\0';
 		
 		/* slight optimization */
-		if (!g_strcasecmp (charset, "UTF-8"))
+		if (!strcasecmp (charset, "UTF-8"))
 			return g_strndup (decoded, declen);
 		
 		cd = g_mime_iconv_open ("UTF-8", charset);
 		if (cd == (iconv_t) -1) {
 			w(g_warning ("Cannot convert from %s to UTF-8, header display may "
 				     "be corrupt: %s", charset, g_strerror (errno)));
-			charset = g_mime_charset_locale_name ();
+			charset = g_mime_locale_charset ();
 			cd = g_mime_iconv_open ("UTF-8", charset);
 			if (cd == (iconv_t) -1)
 				return NULL;
@@ -1106,15 +1349,134 @@
 
 
 /**
- * g_mime_utils_8bit_header_decode:
+ * g_mime_utils_header_decode_text:
  * @in: header to decode
  *
- * Decodes and rfc2047 encoded header.
+ * Decodes an rfc2047 encoded 'text' header.
  *
- * Returns the mime encoded header as 8bit text.
+ * Returns the decoded header (which will be in UTF-8 if at all
+ * possible).
  **/
 char *
-g_mime_utils_8bit_header_decode (const unsigned char *in)
+g_mime_utils_header_decode_text (const unsigned char *in)
+{
+	GString *out, *lwsp, *text;
+	const unsigned char *inptr;
+	unsigned char *decoded;
+	gboolean last_was_encoded = FALSE;
+	gboolean last_was_space = FALSE;
+	
+	out = g_string_sized_new (256);
+	lwsp = g_string_sized_new (256);
+	text = g_string_sized_new (256);
+	inptr = in;
+	
+	while (inptr && *inptr) {
+		unsigned char c = *inptr++;
+		
+		if (is_lwsp ((int) c) && !last_was_space) {
+			/* we reached the end of an atom */
+			unsigned char *dword = NULL;
+			const unsigned char *word;
+			gboolean was_encoded;
+			
+			if ((was_encoded = is_rfc2047_encoded_word (text->str, text->len)))
+				word = dword = rfc2047_decode_word (text->str, text->len);
+			else
+				word = text->str;
+			
+			if (word) {
+				if (!(last_was_encoded && was_encoded)) {
+					/* rfc2047 states that you
+                                           must ignore all whitespace
+                                           between encoded words */
+					g_string_append (out, lwsp->str);
+				}
+				
+				g_string_append (out, word);
+				g_free (dword);
+			} else {
+				was_encoded = FALSE;
+				g_string_append (out, lwsp->str);
+				g_string_append (out, text->str);
+			}
+			
+			last_was_encoded = was_encoded;
+			
+			g_string_truncate (lwsp, 0);
+			g_string_truncate (text, 0);
+			
+			if (is_lwsp (c)) {
+				g_string_append_c (lwsp, c);
+				last_was_space = TRUE;
+			} else {
+				/* This is mostly here for interoperability with broken
+                                   mailers that might do something stupid like:
+                                   =?iso-8859-1?Q?blah?=:\t=?iso-8859-1?Q?I_am_broken?= */
+				g_string_append_c (out, c);
+				last_was_encoded = FALSE;
+				last_was_space = FALSE;
+			}
+			
+			continue;
+		}
+		
+		if (!is_lwsp (c)) {
+			g_string_append_c (text, c);
+			last_was_space = FALSE;
+		} else {
+			g_string_append_c (lwsp, c);
+			last_was_space = TRUE;
+		}
+	}
+	
+	if (text->len || lwsp->len) {
+		unsigned char *dword = NULL;
+		const unsigned char *word;
+		gboolean was_encoded;
+		
+		if ((was_encoded = is_rfc2047_encoded_word (text->str, text->len)))
+			word = dword = rfc2047_decode_word (text->str, text->len);
+		else
+			word = text->str;
+		
+		if (word) {
+			if (!(last_was_encoded && was_encoded)) {
+				/* rfc2047 states that you
+				   must ignore all whitespace
+				   between encoded words */
+				g_string_append (out, lwsp->str);
+			}
+			
+			g_string_append (out, word);
+			g_free (dword);
+		} else {
+			g_string_append (out, lwsp->str);
+			g_string_append (out, text->str);
+		}
+	}
+	
+	g_string_free (lwsp, TRUE);
+	g_string_free (text, TRUE);
+	
+	decoded = out->str;
+	g_string_free (out, FALSE);
+	
+	return (char *) decoded;
+}
+
+
+/**
+ * g_mime_utils_header_decode_phrase:
+ * @in: header to decode
+ *
+ * Decodes an rfc2047 encoded 'phrase' header.
+ *
+ * Returns the decoded header (which will be in UTF-8 if at all
+ * possible).
+ **/
+char *
+g_mime_utils_header_decode_phrase (const unsigned char *in)
 {
 	GString *out, *lwsp, *atom;
 	const unsigned char *inptr;
@@ -1262,8 +1624,7 @@
 	int save = 0;
 	char encoding;
 	
-	
-	if (g_strcasecmp (charset, "UTF-8") != 0)
+	if (strcasecmp (charset, "UTF-8") != 0)
 		cd = g_mime_iconv_open (charset, "UTF-8");
 	
 	if (cd != (iconv_t) -1) {
@@ -1281,7 +1642,7 @@
 	switch (g_mime_utils_best_encoding (word, len)) {
 	case GMIME_PART_ENCODING_BASE64:
 		enclen = BASE64_ENCODE_LEN (len);
-		encoded = g_alloca (enclen);
+		encoded = g_alloca (enclen + 1);
 		
 		encoding = 'b';
 		
@@ -1296,7 +1657,7 @@
 		break;
 	case GMIME_PART_ENCODING_QUOTEDPRINTABLE:
 		enclen = QP_ENCODE_LEN (len);
-		encoded = g_alloca (enclen);
+		encoded = g_alloca (enclen + 1);
 		
 		encoding = 'q';
 		
@@ -1305,69 +1666,38 @@
 		
 		break;
 	default:
+		encoded = NULL;
+		encoding = '\0';
 		g_assert_not_reached ();
 	}
 	
 	g_free (uword);
 	
-	g_string_sprintfa (string, "=?%s?%c?%s?=", charset, encoding, encoded);
-}
-
-
-/**
- * g_mime_utils_8bit_header_encode_phrase:
- * @in: header to encode
- *
- * Encodes a header phrase according to the rules in rfc2047.
- *
- * Returns the header phrase as 1 encoded atom. Useful for encoding
- * internet addresses.
- **/
-char *
-g_mime_utils_8bit_header_encode_phrase (const unsigned char *in)
-{
-	const char *charset;
-	GString *string;
-	size_t len;
-	char *str;
-	
-	if (in == NULL)
-		return NULL;
-	
-	len = strlen (in);
-	
-	charset = g_mime_charset_best (in, len);
-	charset = charset ? charset : "iso-8859-1";
-	
-	string = g_string_new ("");
-	
-	rfc2047_encode_word (string, in, strlen (in), charset, IS_ESAFE);
-	
-	str = string->str;
-	g_string_free (string, FALSE);
-	
-	return str;
+	g_string_append_printf (string, "=?%s?%c?%s?=", charset, encoding, encoded);
 }
 
 
-enum _phrase_word_t {
+enum _rfc822_word_t {
 	WORD_ATOM,
+	WORD_QSTRING,
 	WORD_2047
 };
 
-struct _phrase_word {
-	struct _phrase_word *next;
+struct _rfc822_word {
+	struct _rfc822_word *next;
 	const unsigned char *start, *end;
-	enum _phrase_word_t type;
+	enum _rfc822_word_t type;
 	int encoding;
 };
 
 static gboolean
-word_types_compatable (enum _phrase_word_t type1, enum _phrase_word_t type2)
+word_types_compatable (enum _rfc822_word_t type1, enum _rfc822_word_t type2)
 {
 	switch (type1) {
 	case WORD_ATOM:
-		return FALSE;
+		return type2 != WORD_ATOM;
+	case WORD_QSTRING:
+		return type2 != WORD_2047;
 	case WORD_2047:
 		return type2 == WORD_2047;
 	default:
@@ -1375,16 +1705,17 @@
 	}
 }
 
-static struct _phrase_word *
-rfc2047_encode_phrase_get_words (const unsigned char *in)
+/* okay, so 'text' fields don't actually contain 'word's, but we can group stuff similarly */
+static struct _rfc822_word *
+rfc2047_encode_get_rfc822_words (const unsigned char *in, gboolean phrase)
 {
 	const unsigned char *inptr, *start, *last;
-	struct _phrase_word *words, *tail, *word;
-	enum _phrase_word_t type = WORD_ATOM;
+	struct _rfc822_word *words, *tail, *word;
+	enum _rfc822_word_t type = WORD_ATOM;
 	int count = 0, encoding = 0;
 	
 	words = NULL;
-	tail = (struct _phrase_word *) &words;
+	tail = (struct _rfc822_word *) &words;
 	
 	last = start = inptr = in;
 	while (inptr && *inptr) {
@@ -1394,7 +1725,7 @@
 		newinptr = g_utf8_next_char (inptr);
 		c = g_utf8_get_char (inptr);
 		if (newinptr == NULL || !g_unichar_validate (c)) {
-			d(g_warning ("Invalid UTF-8 sequence encountered"));
+			w(g_warning ("Invalid UTF-8 sequence encountered"));
 			inptr++;
 			continue;
 		}
@@ -1403,7 +1734,7 @@
 		
 		if (g_unichar_isspace (c)) {
 			if (count > 0) {
-				word = g_new (struct _phrase_word, 1);
+				word = g_new (struct _rfc822_word, 1);
 				word->next = NULL;
 				word->start = start;
 				word->end = last;
@@ -1420,9 +1751,13 @@
 			encoding = 0;
 		} else {
 			count++;
-			if (c > 127 && c < 256) {
+			if (phrase && c < 128) {
+				/* phrases can have qstring words */
+				if (!is_atom (c))
+					type = MAX (type, WORD_QSTRING);
+			} else if (c > 127 && c < 256) {
 				type = WORD_2047;
-				encoding = MAX (encoding, 2);
+				encoding = MAX (encoding, 1);
 			} else if (c >= 256) {
 				type = WORD_2047;
 				encoding = 2;
@@ -1433,7 +1768,7 @@
 	}
 	
 	if (count > 0) {
-		word = g_new (struct _phrase_word, 1);
+		word = g_new (struct _rfc822_word, 1);
 		word->next = NULL;
 		word->start = start;
 		word->end = last;
@@ -1447,10 +1782,12 @@
 	return words;
 }
 
+#define MERGED_WORD_LT_FOLDLEN(wlen, type) ((type) == WORD_2047 ? (wlen) < GMIME_FOLD_PREENCODED : (wlen) < (GMIME_FOLD_LEN - 8))
+
 static gboolean
-rfc2047_encode_phrase_merge_words (struct _phrase_word **wordsp)
+rfc2047_encode_merge_rfc822_words (struct _rfc822_word **wordsp)
 {
-	struct _phrase_word *word, *next, *words = *wordsp;
+	struct _rfc822_word *word, *next, *words = *wordsp;
 	gboolean merged = FALSE;
 	
 	/* scan the list, checking for words of similar types that can be merged */
@@ -1461,7 +1798,7 @@
 		while (next) {
 			/* merge nodes of the same type AND we are not creating too long a string */
 			if (word_types_compatable (word->type, next->type)) {
-				if (next->end - word->start < GMIME_FOLD_PREENCODED) {
+				if (MERGED_WORD_LT_FOLDLEN (next->end - word->start, MAX (word->type, next->type))) {
 					/* the resulting word type is the MAX of the 2 types */
 					word->type = MAX (word->type, next->type);
 					
@@ -1492,21 +1829,40 @@
 	return merged;
 }
 
+static void
+g_string_append_len_quoted (GString *out, const char *in, size_t len)
+{
+	register const char *inptr;
+	const char *inend;
+	
+	g_string_append_c (out, '"');
+	
+	inptr = in;
+	inend = in + len;
+	
+	while (inptr < inend) {
+		if ((*inptr == '"') || *inptr == '\\')
+			g_string_append_c (out, '\\');
+		
+		g_string_append_c (out, *inptr);
+		
+		inptr++;
+	}
+	
+	g_string_append_c (out, '"');
+}
+
 static char *
-rfc2047_encode_phrase (const unsigned char *in)
+rfc2047_encode (const unsigned char *in, gushort safemask)
 {
-	struct _phrase_word *words, *word, *prev = NULL;
+	struct _rfc822_word *words, *word, *prev = NULL;
 	GString *out;
 	char *outstr;
 	
-	if (in == NULL)
-		return NULL;
-	
-	words = rfc2047_encode_phrase_get_words (in);
-	if (!words)
-		return NULL;
+	if (!(words = rfc2047_encode_get_rfc822_words (in, safemask & IS_PSAFE)))
+		return g_strdup (in);
 	
-	while (rfc2047_encode_phrase_merge_words (&words))
+	while (rfc2047_encode_merge_rfc822_words (&words))
 		;
 	
 	out = g_string_new ("");
@@ -1528,10 +1884,14 @@
 		case WORD_ATOM:
 			g_string_append_len (out, word->start, word->end - word->start);
 			break;
+		case WORD_QSTRING:
+			g_assert (safemask & IS_PSAFE);
+			g_string_append_len_quoted (out, word->start, word->end - word->start);
+			break;
 		case WORD_2047:
 			if (prev && prev->type == WORD_2047) {
 				/* include the whitespace chars between these 2 words in the
-                                   resulting rfc2047 encoded word. */
+				   resulting rfc2047 encoded word. */
 				len = word->end - prev->end;
 				start = prev->end;
 				
@@ -1543,10 +1903,9 @@
 			}
 			
 			if (word->encoding == 1)
-				rfc2047_encode_word (out, start, len, "iso-8859-1", IS_PSAFE);
+				rfc2047_encode_word (out, start, len, "iso-8859-1", safemask);
 			else
-				rfc2047_encode_word (out, start, len,
-						     g_mime_charset_best (start, len), IS_PSAFE);
+				rfc2047_encode_word (out, start, len, g_mime_charset_best (start, len), safemask);
 			break;
 		}
 		
@@ -1565,18 +1924,97 @@
 
 
 /**
+ * g_mime_utils_header_encode_phrase:
+ * @in: header to encode
+ *
+ * Encodes a 'phrase' header according to the rules in rfc2047.
+ *
+ * Returns the encoded 'phrase'. Useful for encoding internet
+ * addresses.
+ **/
+char *
+g_mime_utils_header_encode_phrase (const unsigned char *in)
+{
+	if (in == NULL)
+		return NULL;
+	
+	return rfc2047_encode (in, IS_PSAFE);
+}
+
+
+/**
+ * g_mime_utils_header_encode_text:
+ * @in: header to encode
+ *
+ * Encodes a 'text' header according to the rules in rfc2047.
+ *
+ * Returns the encoded header. Useful for encoding
+ * headers like "Subject".
+ **/
+char *
+g_mime_utils_header_encode_text (const unsigned char *in)
+{
+	if (in == NULL)
+		return NULL;
+	
+	return rfc2047_encode (in, IS_ESAFE);
+}
+
+
+/**
+ * g_mime_utils_8bit_header_decode:
+ * @in: header to decode
+ *
+ * Decodes an rfc2047 encoded header.
+ *
+ * WARNING: This function is deprecated. Use
+ * g_mime_utils_header_decode_text() instead.
+ *
+ * Returns the decoded header (which will be in UTF-8 if at all
+ * possible).
+ **/
+char *
+g_mime_utils_8bit_header_decode (const unsigned char *in)
+{
+	return g_mime_utils_header_decode_text (in);
+}
+
+
+/**
  * g_mime_utils_8bit_header_encode:
  * @in: header to encode
  *
- * Encodes a header according to the rules in rfc2047.
+ * Encodes a 'text' header according to the rules in rfc2047.
+ *
+ * WARNING: This function is deprecated. Use
+ * g_mime_utils_header_encode_text() instead.
  *
- * Returns the header as several encoded atoms. Useful for encoding
+ * Returns the encoded header. Useful for encoding
  * headers like "Subject".
  **/
 char *
 g_mime_utils_8bit_header_encode (const unsigned char *in)
 {
-	return rfc2047_encode_phrase (in);
+	return g_mime_utils_header_encode_text (in);
+}
+
+
+/**
+ * g_mime_utils_8bit_header_encode_phrase:
+ * @in: header to encode
+ *
+ * Encodes a 'phrase' header according to the rules in rfc2047.
+ *
+ * WARNING: This function is deprecated. Use
+ * g_mime_utils_header_encode_phrase() instead.
+ *
+ * Returns the encoded 'phrase'. Useful for encoding internet
+ * addresses.
+ **/
+char *
+g_mime_utils_8bit_header_encode_phrase (const unsigned char *in)
+{
+	return g_mime_utils_header_encode_phrase (in);
 }
 
 
@@ -1589,7 +2027,7 @@
  * @save: leftover bits that have not yet been encoded
  *
  * Base64 encodes the input stream to the output stream. Call this
- * when finished encoding data with #g_mime_utils_base64_encode_step
+ * when finished encoding data with g_mime_utils_base64_encode_step()
  * to flush off the last little bit.
  *
  * Returns the number of bytes encoded.
@@ -1790,7 +2228,7 @@
  * @save: leftover bits that have not yet been encoded
  *
  * Uuencodes a chunk of data. Call this when finished encoding data
- * with #g_mime_utils_uuencode_step to flush off the last little bit.
+ * with g_mime_utils_uuencode_step() to flush off the last little bit.
  *
  * Returns the number of bytes encoded.
  **/
@@ -1948,7 +2386,7 @@
  * @save: leftover bits that have not yet been decoded
  *
  * Uudecodes a chunk of data. Performs a 'decode step' on a chunk of
- * uuencoded data. Assumes the "begin <mode> <file name>" line has
+ * uuencoded data. Assumes the "begin mode filename" line has
  * been stripped off.
  *
  * Returns the number of bytes decoded.
@@ -2051,7 +2489,7 @@
  * @save: leftover bits that have not yet been encoded
  *
  * Quoted-printable encodes a block of text. Call this when finished
- * encoding data with #g_mime_utils_quoted_encode_step to flush off
+ * encoding data with g_mime_utils_quoted_encode_step() to flush off
  * the last little bit.
  *
  * Returns the number of bytes encoded.
Index: gmime/gmime-utils.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime-utils.h,v
retrieving revision 1.11
diff -u -r1.11 gmime-utils.h
--- gmime/gmime-utils.h	30 Dec 2002 16:37:57 -0000	1.11
+++ gmime/gmime-utils.h	7 Jun 2004 06:10:25 -0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Michael Zucchi <notzed@helixcode.com>
- *           Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Michael Zucchi <notzed@ximian.com>
+ *           Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,17 +21,18 @@
  *
  */
 
+
 #ifndef __GMIME_UTILS_H__
 #define __GMIME_UTILS_H__
 
+#include <glib.h>
+#include <time.h>
+#include <stdarg.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
-#endif /* __cplusplus }*/
-
-#include <glib/gtypes.h>
-#include <time.h>
-#include <stdarg.h>
+#endif /* __cplusplus */
 
 typedef enum {
 	GMIME_PART_ENCODING_DEFAULT,
@@ -44,6 +45,13 @@
 	GMIME_PART_NUM_ENCODINGS
 } GMimePartEncodingType;
 
+struct _GMimeReferences {
+	struct _GMimeReferences *next;
+	char *msgid;
+};
+
+typedef struct _GMimeReferences GMimeReferences;
+
 #define BASE64_ENCODE_LEN(x) ((size_t) ((x) * 5 / 3) + 4)  /* conservative would be ((x * 4 / 3) + 4) */
 #define QP_ENCODE_LEN(x)     ((size_t) ((x) * 7 / 2) + 4)  /* conservative would be ((x * 3) + 4) */
 
@@ -52,10 +60,19 @@
 #define GMIME_UUDECODE_STATE_END    (1 << 17)
 #define GMIME_UUDECODE_STATE_MASK   (GMIME_UUDECODE_STATE_BEGIN | GMIME_UUDECODE_STATE_END)
 
-
 time_t g_mime_utils_header_decode_date (const char *in, int *saveoffset);
 char  *g_mime_utils_header_format_date (time_t time, int offset);
 
+/* decode a message-id */
+char *g_mime_utils_decode_message_id (const char *message_id);
+
+/* decode a References or In-Reply-To header */
+GMimeReferences *g_mime_references_decode (const char *text);
+void g_mime_references_append (GMimeReferences **refs, const char *msgid);
+void g_mime_references_clear (GMimeReferences **refs);
+
+char  *g_mime_utils_structured_header_fold (const char *in);
+char  *g_mime_utils_unstructured_header_fold (const char *in);
 char  *g_mime_utils_header_fold (const char *in);
 char  *g_mime_utils_header_printf (const char *format, ...);
 
@@ -67,9 +84,17 @@
 GMimePartEncodingType g_mime_utils_best_encoding (const unsigned char *text, size_t len);
 
 /* utilities to (de/en)code headers */
+char *g_mime_utils_header_decode_text (const unsigned char *in);
+char *g_mime_utils_header_encode_text (const unsigned char *in);
+
+char *g_mime_utils_header_decode_phrase (const unsigned char *in);
+char *g_mime_utils_header_encode_phrase (const unsigned char *in);
+
+#ifndef GMIME_DISABLE_DEPRECATED
 char *g_mime_utils_8bit_header_decode (const unsigned char *in);
 char *g_mime_utils_8bit_header_encode (const unsigned char *in);
 char *g_mime_utils_8bit_header_encode_phrase (const unsigned char *in);
+#endif
 
 /* do incremental base64 (de/en)coding */
 size_t g_mime_utils_base64_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save);
@@ -85,6 +110,7 @@
 size_t g_mime_utils_quoted_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *savestate, int *saved);
 size_t g_mime_utils_quoted_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save);
 size_t g_mime_utils_quoted_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save);
+
 
 #ifdef __cplusplus
 }
Index: gmime/gmime.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime.c,v
retrieving revision 1.4
diff -u -r1.4 gmime.c
--- gmime/gmime.c	2 Sep 2002 13:58:05 -0000	1.4
+++ gmime/gmime.c	7 Jun 2004 06:10:25 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -28,17 +28,24 @@
 #include "gmime.h"
 
 
+GQuark gmime_error_quark;
+
+
+static int initialized = FALSE;
+
+
 /**
  * g_mime_init:
  * @flags: initialization flags
  *
  * Initializes GMime.
+ *
+ * Note: Calls g_mime_charset_map_init() and g_mime_iconv_init() as
+ * well.
  **/
 void
 g_mime_init (guint32 flags)
 {
-	static int initialized = FALSE;
-	
 	if (initialized)
 		return;
 	
@@ -50,6 +57,8 @@
 	
 	g_mime_iconv_init ();
 	
+	gmime_error_quark = g_quark_from_static_string ("gmime");
+	
 	/* register our default mime object types */
 	g_mime_object_register_type ("*", "*", g_mime_part_get_type ());
 	g_mime_object_register_type ("multipart", "*", g_mime_multipart_get_type ());
@@ -57,4 +66,23 @@
 	g_mime_object_register_type ("message", "rfc2822", g_mime_message_part_get_type ());
 	g_mime_object_register_type ("message", "news", g_mime_message_part_get_type ());
 	g_mime_object_register_type ("message", "partial", g_mime_message_partial_get_type ());
+}
+
+
+/**
+ * g_mime_shutdown:
+ *
+ * Frees internally allocated tables created in g_mime_init(). Also
+ * calls g_mime_charset_map_shutdown() and g_mime_iconv_shutdown().
+ **/
+void
+g_mime_shutdown (void)
+{
+	if (!initialized)
+		return;
+	
+	g_mime_charset_map_shutdown ();
+	g_mime_iconv_shutdown ();
+	
+	initialized = FALSE;
 }
Index: gmime/gmime.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/gmime.h,v
retrieving revision 1.15
diff -u -r1.15 gmime.h
--- gmime/gmime.h	30 Dec 2002 16:37:57 -0000	1.15
+++ gmime/gmime.h	7 Jun 2004 06:10:25 -0000
@@ -1,8 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,50 +20,60 @@
  *
  */
 
+
 #ifndef __GMIME_H__
 #define __GMIME_H__
 
-#include <glib/gtypes.h>
-#include "gmime-charset.h"
-#include "gmime-iconv.h"
-#include "gmime-iconv-utils.h"
-#include "gmime-param.h"
-#include "gmime-content-type.h"
-#include "gmime-disposition.h"
-#include "gmime-data-wrapper.h"
-#include "gmime-object.h"
-#include "gmime-part.h"
-#include "gmime-multipart.h"
-#include "gmime-message.h"
-#include "gmime-message-part.h"
-#include "gmime-message-partial.h"
-#include "internet-address.h"
-#include "gmime-parser.h"
-#include "gmime-utils.h"
-#include "gmime-stream.h"
-#include "gmime-stream-buffer.h"
-#include "gmime-stream-cat.h"
-#include "gmime-stream-file.h"
-#include "gmime-stream-filter.h"
-#include "gmime-stream-mem.h"
-#include "gmime-filter.h"
-#include "gmime-filter-basic.h"
-#include "gmime-filter-charset.h"
-#include "gmime-filter-html.h"
-#include "gmime-filter-yenc.h"
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <glib.h>
+#include <gmime/gmime-error.h>
+#include <gmime/gmime-charset.h>
+#include <gmime/gmime-iconv.h>
+#include <gmime/gmime-iconv-utils.h>
+#include <gmime/gmime-param.h>
+#include <gmime/gmime-content-type.h>
+#include <gmime/gmime-disposition.h>
+#include <gmime/gmime-data-wrapper.h>
+#include <gmime/gmime-object.h>
+#include <gmime/gmime-part.h>
+#include <gmime/gmime-multipart.h>
+#include <gmime/gmime-message.h>
+#include <gmime/gmime-message-part.h>
+#include <gmime/gmime-message-partial.h>
+#include <gmime/internet-address.h>
+#include <gmime/gmime-parser.h>
+#include <gmime/gmime-utils.h>
+#include <gmime/gmime-stream.h>
+#include <gmime/gmime-stream-buffer.h>
+#include <gmime/gmime-stream-cat.h>
+#include <gmime/gmime-stream-file.h>
+#include <gmime/gmime-stream-filter.h>
+#include <gmime/gmime-stream-mem.h>
+#include <gmime/gmime-stream-null.h>
+#include <gmime/gmime-filter.h>
+#include <gmime/gmime-filter-basic.h>
+#include <gmime/gmime-filter-charset.h>
+#include <gmime/gmime-filter-crlf.h>
+#include <gmime/gmime-filter-html.h>
+#include <gmime/gmime-filter-md5.h>
+#include <gmime/gmime-filter-yenc.h>
 
 /* GMIME version */
-static const guint gmime_major_version = 1;
-static const guint gmime_minor_version = 90;
-static const guint gmime_micro_version = 0;
+static const guint gmime_major_version = 2;
+static const guint gmime_minor_version = 1;
+static const guint gmime_micro_version = 6;
 static const guint gmime_interface_age = 0;
 static const guint gmime_binary_age    = 0;
 
 #define GMIME_CHECK_VERSION(major,minor,micro)    \
-    (gmime_major_version > (major) || \
-     (gmime_major_version == (major) && gmime_minor_version > (minor)) || \
-     (gmime_major_version == (major) && gmime_minor_version == (minor) && \
-      gmime_micro_version >= (micro)))
+    (2 > (major) || \
+     (2 == (major) && 1 > (minor)) || \
+     (2 == (major) && 1 == (minor) && \
+      6 >= (micro)))
 
 
 /**
@@ -78,5 +88,10 @@
 #define GMIME_INIT_FLAG_UTF8  (1 << 0)
 
 void g_mime_init (guint32 flags);
+void g_mime_shutdown (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
 
 #endif /* __GMIME_H__ */
Index: gmime/gtrie.c
===================================================================
RCS file: gmime/gtrie.c
diff -N gmime/gtrie.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gtrie.c	7 Jun 2004 06:10:26 -0000
@@ -0,0 +1,409 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gtrie.h"
+#include "memchunk.h"
+
+#define d(x)
+
+struct _trie_state {
+	struct _trie_state *next;
+	struct _trie_state *fail;
+	struct _trie_match *match;
+	unsigned int final;
+	int id;
+};
+
+struct _trie_match {
+	struct _trie_match *next;
+	struct _trie_state *state;
+	gunichar c;
+};
+
+struct _GTrie {
+	struct _trie_state root;
+	GPtrArray *fail_states;
+	gboolean icase;
+	
+	MemChunk *match_chunks;
+	MemChunk *state_chunks;
+};
+
+
+static inline gunichar
+trie_utf8_getc (const unsigned char **in, size_t inlen)
+{
+	register const unsigned char *inptr = *in;
+	const unsigned char *inend = inptr + inlen;
+	register unsigned char c, r;
+	register gunichar m, u = 0;
+	
+	if (inlen == 0)
+		return 0;
+	
+ again:
+	r = *inptr++;
+ loop:
+	if (r < 0x80) {
+		*in = inptr;
+		u = r;
+	} else if (r < 0xfe) { /* valid start char? */
+		u = r;
+		m = 0x7f80;	/* used to mask out the length bits */
+		do {
+			if (inptr >= inend)
+				return 0;
+			
+			c = *inptr++;
+			if ((c & 0xc0) != 0x80)
+				goto error;
+			
+			u = (u << 6) | (c & 0x3f);
+			r <<= 1;
+			m <<= 5;
+		} while (r & 0x40);
+		
+		*in = inptr;
+		
+		u &= ~m;
+	} else {
+	error:
+		*in = (*in) + 1;
+		u = 0xfffe;
+	}
+	
+	return u;
+}
+
+
+GTrie *
+g_trie_new (gboolean icase)
+{
+	GTrie *trie;
+	
+	trie = g_new (GTrie, 1);
+	trie->root.next = NULL;
+	trie->root.fail = NULL;
+	trie->root.match = NULL;
+	trie->root.final = 0;
+	
+	trie->fail_states = g_ptr_array_sized_new (8);
+	trie->icase = icase;
+	
+	trie->match_chunks = memchunk_new (sizeof (struct _trie_match), 8, FALSE);
+	trie->state_chunks = memchunk_new (sizeof (struct _trie_state), 8, FALSE);
+	
+	return trie;
+}
+
+void
+g_trie_free (GTrie *trie)
+{
+	g_ptr_array_free (trie->fail_states, TRUE);
+	memchunk_destroy (trie->match_chunks);
+	memchunk_destroy (trie->state_chunks);
+	g_free (trie);
+}
+
+
+
+static struct _trie_match *
+g (struct _trie_state *s, gunichar c)
+{
+	struct _trie_match *m = s->match;
+	
+	while (m && m->c != c)
+		m = m->next;
+	
+	return m;
+}
+
+static struct _trie_state *
+trie_insert (GTrie *trie, int depth, struct _trie_state *q, gunichar c)
+{
+	struct _trie_match *m;
+	
+	m = memchunk_alloc (trie->match_chunks);
+	m->next = q->match;
+	m->c = c;
+	
+	q->match = m;
+	q = m->state = memchunk_alloc (trie->state_chunks);
+	q->match = NULL;
+	q->fail = &trie->root;
+	q->final = 0;
+	q->id = 0;
+	
+	if (trie->fail_states->len < depth + 1) {
+		unsigned int size = trie->fail_states->len;
+		
+		size = MAX (size + 64, depth + 1);
+		g_ptr_array_set_size (trie->fail_states, size);
+	}
+	
+	q->next = trie->fail_states->pdata[depth];
+	trie->fail_states->pdata[depth] = q;
+	
+	return q;
+}
+
+
+#if d(!)0
+static void 
+dump_trie (struct _trie_state *s, int depth)
+{
+	char *p = g_alloca ((depth * 2) + 1);
+	struct _trie_match *m;
+	
+	memset (p, ' ', depth * 2);
+	p[depth * 2] = '\0';
+	
+	fprintf (stderr, "%s[state] %p: final=%d; pattern-id=%s; fail=%p\n",
+		 p, s, s->final, s->id, s->fail);
+	m = s->match;
+	while (m) {
+		fprintf (stderr, " %s'%c' -> %p\n", p, m->c, m->state);
+		if (m->state)
+			dump_trie (m->state, depth + 1);
+		
+		m = m->next;
+	}
+}
+#endif
+
+
+/*
+ * final = empty set
+ * FOR p = 1 TO #pat
+ *   q = root
+ *   FOR j = 1 TO m[p]
+ *     IF g(q, pat[p][j]) == null
+ *       insert(q, pat[p][j])
+ *     ENDIF
+ *     q = g(q, pat[p][j])
+ *   ENDFOR
+ *   final = union(final, q)
+ * ENDFOR
+*/
+
+void
+g_trie_add (GTrie *trie, const char *pattern, int pattern_id)
+{
+	const unsigned char *inptr = (const unsigned char *) pattern;
+	struct _trie_state *q, *q1, *r;
+	struct _trie_match *m, *n;
+	int i, depth = 0;
+	gunichar c;
+	
+	/* Step 1: add the pattern to the trie */
+	
+	q = &trie->root;
+	
+	while ((c = trie_utf8_getc (&inptr, -1))) {
+		if (c == 0xfffe) {
+			g_warning ("Invalid UTF-8 sequence in pattern '%s' at %s",
+				   pattern, (inptr - 1));
+			continue;
+		}
+		
+		if (trie->icase)
+			c = g_unichar_tolower (c);
+		
+		m = g (q, c);
+		if (m == NULL) {
+			q = trie_insert (trie, depth, q, c);
+		} else {
+			q = m->state;
+		}
+		
+		depth++;
+	}
+	
+	q->final = depth;
+	q->id = pattern_id;
+	
+	/* Step 2: compute failure graph */
+	
+	for (i = 0; i < trie->fail_states->len; i++) {
+		q = trie->fail_states->pdata[i];
+		while (q) {
+			m = q->match;
+			while (m) {
+				c = m->c;
+				q1 = m->state;
+				r = q->fail;
+				while (r && (n = g (r, c)) == NULL)
+					r = r->fail;
+				
+				if (r != NULL) {
+					q1->fail = n->state;
+					if (q1->fail->final > q1->final)
+						q1->final = q1->fail->final;
+				} else {
+					if ((n = g (&trie->root, c)))
+						q1->fail = n->state;
+					else
+						q1->fail = &trie->root;
+				}
+				
+				m = m->next;
+			}
+			
+			q = q->next;
+		}
+	}
+	
+	d(fprintf (stderr, "\nafter adding pattern '%s' to trie %p:\n", pattern, trie));
+	d(dump_trie (&trie->root, 0));
+}
+
+/*
+ * Aho-Corasick
+ *
+ * q = root
+ * FOR i = 1 TO n
+ *   WHILE q != fail AND g(q, text[i]) == fail
+ *     q = h(q)
+ *   ENDWHILE
+ *   IF q == fail
+ *     q = root
+ *   ELSE
+ *     q = g(q, text[i])
+ *   ENDIF
+ *   IF isElement(q, final)
+ *     RETURN TRUE
+ *   ENDIF
+ * ENDFOR
+ * RETURN FALSE
+ */
+
+const char *
+g_trie_search (GTrie *trie, const char *buffer, size_t buflen, int *matched_id)
+{
+	const unsigned char *inptr, *inend, *prev, *pat;
+	register size_t inlen = buflen;
+	struct _trie_state *q;
+	struct _trie_match *m;
+	gunichar c;
+	
+	inptr = (const unsigned char *) buffer;
+	inend = inptr + buflen;
+	
+	q = &trie->root;
+	pat = prev = inptr;
+	while ((c = trie_utf8_getc (&inptr, inlen))) {
+		inlen = (inend - inptr);
+		
+		if (c == 0xfffe) {
+			prev = (inptr - 1);
+			pat = (const unsigned char *) buffer + buflen;
+			g_warning ("Invalid UTF-8 in buffer '%.*s' at %.*s",
+				   buflen, buffer, pat - prev, prev);
+			pat = prev = inptr;
+		}
+		
+		if (trie->icase)
+			c = g_unichar_tolower (c);
+		
+		while (q != NULL && (m = g (q, c)) == NULL)
+			q = q->fail;
+		
+		if (q == &trie->root)
+			pat = prev;
+		
+		if (q == NULL) {
+			q = &trie->root;
+			pat = inptr;
+		} else if (m != NULL) {
+			q = m->state;
+			
+			if (q->final) {
+				if (matched_id)
+					*matched_id = q->id;
+				
+				return (const char *) pat;
+			}
+		}
+		
+		prev = inptr;
+	}
+	
+	return NULL;
+}
+
+
+#ifdef TEST
+
+static char *patterns[] = {
+	"news://",
+	"nntp://",
+	"telnet://",
+	"file://",
+	"ftp://",
+	"http://",
+	"https://",
+	"www.",
+	"ftp.",
+	"mailto:",
+	"@"
+};
+
+static char *haystacks[] = {
+	"try this url: http://www.ximian.com",
+	"or, feel free to email me at fejj@ximian.com",
+	"don't forget to check out www.ximian.com",
+	"I've attached a file (file:///cvs/gmime/gmime/gtrie.c)",
+};
+
+int main (int argc, char **argv)
+{
+	const char *match;
+	GTrie *trie;
+	int id, i;
+	
+	trie = g_trie_new (TRUE);
+	for (i = 0; i < (sizeof (patterns) / sizeof (patterns[0])); i++)
+		g_trie_add (trie, patterns[i], i);
+	
+	for (i = 0; i < (sizeof (haystacks) / sizeof (haystacks[0])); i++) {
+		if ((match = g_trie_search (trie, haystacks[i], -1, &id))) {
+			fprintf (stderr, "matched @ '%s' with pattern '%s'\n", match, patterns[id]);
+		} else {
+			fprintf (stderr, "no match\n");
+		}
+	}
+	
+	fflush (stdout);
+	
+	g_trie_free (trie);
+	
+	return match == NULL ? 0 : 1;
+}
+#endif /* TEST */
Index: gmime/gtrie.h
===================================================================
RCS file: gmime/gtrie.h
diff -N gmime/gtrie.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/gtrie.h	7 Jun 2004 06:10:26 -0000
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __G_TRIE_H__
+#define __G_TRIE_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+typedef struct _GTrie GTrie;
+
+GTrie *g_trie_new (gboolean icase);
+void g_trie_free (GTrie *trie);
+
+void g_trie_add (GTrie *trie, const char *pattern, int pattern_id);
+
+const char *g_trie_search (GTrie *trie, const char *buffer, size_t buflen, int *matched_id);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __G_TRIE_H__ */
Index: gmime/internet-address.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/internet-address.c,v
retrieving revision 1.14
diff -u -r1.14 internet-address.c
--- gmime/internet-address.c	30 Dec 2002 16:37:57 -0000	1.14
+++ gmime/internet-address.c	7 Jun 2004 06:10:27 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -34,14 +34,21 @@
 #include "gmime-utils.h"
 #include "gmime-iconv-utils.h"
 
-#include <glib/gmem.h>
-#include <glib/gmessages.h>
-#include <glib/gstring.h>
-#include <glib/gstrfuncs.h>
 
+#ifdef ENABLE_WARNINGS
 #define w(x) x
+#else
+#define w(x)
+#endif /* ENABLE_WARNINGS */
+
 #define d(x)
 
+/* public functions that we don't care for the user of GMime to know or care about */
+void decode_lwsp (const char **in);
+char *decode_word (const char **in);
+char *decode_addrspec (const char **in);
+
+
 /**
  * internet_address_new:
  *
@@ -137,7 +144,7 @@
 	ia = internet_address_new ();
 	ia->type = INTERNET_ADDRESS_NAME;
 	if (name) {
-		ia->name = g_mime_utils_8bit_header_decode (name);
+		ia->name = g_mime_utils_header_decode_phrase (name);
 		g_mime_utils_unquote_string (ia->name);
 	}
 	ia->value.addr = g_strdup (addr);
@@ -162,7 +169,7 @@
 	ia = internet_address_new ();
 	ia->type = INTERNET_ADDRESS_GROUP;
 	if (name) {
-		ia->name = g_mime_utils_8bit_header_decode (name);
+		ia->name = g_mime_utils_header_decode_phrase (name);
 		g_mime_utils_unquote_string (ia->name);
 	}
 	
@@ -184,7 +191,7 @@
 	
 	g_free (ia->name);
 	if (name) {
-		ia->name = g_mime_utils_8bit_header_decode (name);
+		ia->name = g_mime_utils_header_decode_phrase (name);
 		g_mime_utils_unquote_string (ia->name);
 	} else
 		ia->name = NULL;
@@ -265,7 +272,7 @@
 {
 	InternetAddressList *node;
 	
-	g_return_val_if_fail (ia!=NULL, NULL);
+	g_return_val_if_fail (ia != NULL, NULL);
 	
 	internet_address_ref (ia);
 	node = g_new (InternetAddressList, 1);
@@ -291,7 +298,7 @@
 {
 	InternetAddressList *node, *n;
 	
-	g_return_val_if_fail (ia!=NULL, NULL);
+	g_return_val_if_fail (ia != NULL, NULL);
 	
 	internet_address_ref (ia);
 	node = g_new (InternetAddressList, 1);
@@ -363,9 +370,9 @@
  * Returns the number of internet addresses in @list.
  **/
 int
-internet_address_list_length (InternetAddressList *list)
+internet_address_list_length (const InternetAddressList *list)
 {
-	InternetAddressList *node;
+	const InternetAddressList *node;
 	int len = 0;
 	
 	node = list;
@@ -406,8 +413,8 @@
 	
 	g_return_val_if_fail (raw != NULL, NULL);
 	
-	if (rfc2047_encode && g_mime_utils_text_is_8bit (raw, strlen (raw))) {
-		name = g_mime_utils_8bit_header_encode_phrase (raw);
+	if (rfc2047_encode) {
+		name = g_mime_utils_header_encode_phrase (raw);
 	} else {
 		name = g_mime_utils_quote_string (raw);
 	}
@@ -416,7 +423,7 @@
 }
 
 static void
-internet_address_list_to_string_internal (InternetAddressList *list, gboolean encode, GString *string)
+internet_address_list_to_string_internal (const InternetAddressList *list, gboolean encode, GString *string)
 {
 	while (list) {
 		char *addr;
@@ -446,12 +453,12 @@
  * rfc822 format.
  **/
 char *
-internet_address_to_string (InternetAddress *ia, gboolean encode)
+internet_address_to_string (const InternetAddress *ia, gboolean encode)
 {
 	char *str = NULL;
 	
 	if (ia->type == INTERNET_ADDRESS_NAME) {
-		if (ia->name) {
+		if (ia->name && *ia->name) {
 			char *name;
 			
 			name = encoded_name (ia->name, encode);
@@ -490,7 +497,7 @@
  * Returns a string containing the list of addresses in rfc822 format.
  **/
 char *
-internet_address_list_to_string (InternetAddressList *list, gboolean encode)
+internet_address_list_to_string (const InternetAddressList *list, gboolean encode)
 {
 	GString *string;
 	char *str;
@@ -504,7 +511,7 @@
 }
 
 
-static void
+void
 decode_lwsp (const char **in)
 {
 	const char *inptr = *in;
@@ -580,7 +587,7 @@
 		return NULL;
 }
 
-static char *
+char *
 decode_word (const char **in)
 {
 	const char *inptr = *in;
@@ -667,7 +674,7 @@
 			if (!(atom = decode_atom (&inptr))) {
 				w(g_warning ("Unexpected char '%c' in domain: %s", *inptr, *in));
 				/* remove the last '.' */
-				if (domain->str[domain->len - 1] == '.')
+				if (domain->len && domain->str[domain->len - 1] == '.')
 					g_string_truncate (domain, domain->len - 1);
 				break;
 			}
@@ -697,6 +704,68 @@
 	*in = inptr;
 	
 	return dom;
+}
+
+char *
+decode_addrspec (const char **in)
+{
+	char *domain, *word, *str = NULL;
+	const char *inptr;
+	GString *addrspec;
+	
+	decode_lwsp (in);
+	inptr = *in;
+	
+	if (!(word = decode_word (&inptr))) {
+		w(g_warning ("No local-part in addr-spec: %s", *in));
+		return NULL;
+	}
+	
+	addrspec = g_string_new (word);
+	g_free (word);
+	
+	/* get the rest of the local-part */
+	decode_lwsp (&inptr);
+	while (*inptr == '.') {
+		g_string_append_c (addrspec, *inptr++);
+		word = decode_word (&inptr);
+		if (word) {
+			g_string_append (addrspec, word);
+			decode_lwsp (&inptr);
+			g_free (word);
+		} else {
+			w(g_warning ("Invalid local-part in addr-spec: %s", *in));
+			goto exception;
+		}
+	}
+	
+	/* we should be at the '@' now... */
+	if (*inptr++ != '@') {
+		w(g_warning ("Invalid addr-spec; missing '@': %s", *in));
+		goto exception;
+	}
+	
+	if (!(domain = decode_domain (&inptr))) {
+		w(g_warning ("No domain in addr-spec: %s", *in));
+		goto exception;
+	}
+	
+	g_string_append_c (addrspec, '@');
+	g_string_append (addrspec, domain);
+	g_free (domain);
+	
+	str = addrspec->str;
+	g_string_free (addrspec, FALSE);
+	
+	*in = inptr;
+	
+	return str;
+	
+ exception:
+	
+	g_string_free (addrspec, TRUE);
+	
+	return NULL;
 }
 
 static InternetAddress *
Index: gmime/internet-address.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/internet-address.h,v
retrieving revision 1.6
diff -u -r1.6 internet-address.h
--- gmime/internet-address.h	30 Dec 2002 16:37:57 -0000	1.6
+++ gmime/internet-address.h	7 Jun 2004 06:10:27 -0000
@@ -1,8 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- *  Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *  Copyright 2000-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,16 +20,17 @@
  *
  */
 
+
 #ifndef __INTERNET_ADDRESS_H__
 #define __INTERNET_ADDRESS_H__
 
+#include <glib.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
 
-#include <glib/gtypes.h>
-
 typedef enum {
 	INTERNET_ADDRESS_NONE,
 	INTERNET_ADDRESS_NAME,
@@ -69,13 +70,13 @@
 InternetAddressList *internet_address_list_prepend (InternetAddressList *list, InternetAddress *ia);
 InternetAddressList *internet_address_list_append (InternetAddressList *list, InternetAddress *ia);
 InternetAddressList *internet_address_list_concat (InternetAddressList *a, InternetAddressList *b);
-int internet_address_list_length (InternetAddressList *list);
+int internet_address_list_length (const InternetAddressList *list);
 void internet_address_list_destroy (InternetAddressList *list);
 
 InternetAddressList *internet_address_parse_string (const char *string);
 
-char *internet_address_to_string (InternetAddress *ia, gboolean encode);
-char *internet_address_list_to_string (InternetAddressList *list, gboolean encode);
+char *internet_address_to_string (const InternetAddress *ia, gboolean encode);
+char *internet_address_list_to_string (const InternetAddressList *list, gboolean encode);
 
 #ifdef __cplusplus
 }
Index: gmime/list.c
===================================================================
RCS file: gmime/list.c
diff -N gmime/list.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/list.c	7 Jun 2004 06:10:27 -0000
@@ -0,0 +1,120 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2003-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "list.h"
+
+void
+list_init (List *list)
+{
+	list->head = (ListNode *) &list->tail;
+	list->tail = NULL;
+	list->tailpred = (ListNode *) &list->head;
+}
+
+int
+list_is_empty (List *list)
+{
+	return list->head == (ListNode *) &list->tail;
+}
+
+int
+list_length (List *list)
+{
+	ListNode *node;
+	int n = 0;
+	
+	node = list->head;
+	while (node->next) {
+		node = node->next;
+		n++;
+	}
+	
+	return n;
+}
+
+ListNode *
+list_unlink_head (List *list)
+{
+	ListNode *n, *nn;
+	
+	n = list->head;
+	nn = n->next;
+	if (nn) {
+		nn->prev = n->prev;
+		list->head = nn;
+		return n;
+	}
+	
+	return NULL;
+}
+
+ListNode *
+list_unlink_tail (List *list)
+{
+	ListNode *n, *np;
+	
+	n = list->tailpred;
+	np = n->prev;
+	if (np) {
+		np->next = n->next;
+		list->tailpred = np;
+		return n;
+	}
+	
+	return NULL;
+}
+
+ListNode *
+list_prepend_node (List *list, ListNode *node)
+{
+	node->next = list->head;
+	node->prev = (ListNode *) &list->head;
+	list->head->prev = node;
+	list->head = node;
+	
+	return node;
+}
+
+ListNode *
+list_append_node (List *list, ListNode *node)
+{
+	node->next = (ListNode *) &list->tail;
+	node->prev = list->tailpred;
+	list->tailpred->next = node;
+	list->tailpred = node;
+	
+	return node;
+}
+
+ListNode *
+list_node_unlink (ListNode *node)
+{
+	node->next->prev = node->prev;
+        node->prev->next = node->next;
+	
+	return node;
+}
Index: gmime/list.h
===================================================================
RCS file: gmime/list.h
diff -N gmime/list.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/list.h	7 Jun 2004 06:10:27 -0000
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2003-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __LIST_H__
+#define __LIST_H__
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+typedef struct _ListNode {
+	struct _ListNode *next;
+	struct _ListNode *prev;
+} ListNode;
+
+typedef struct {
+	ListNode *head;
+	ListNode *tail;
+	ListNode *tailpred;
+} List;
+
+#define LIST_INITIALIZER(l) { (ListNode *) &l.tail, NULL, (ListNode *) &l.head }
+
+void list_init (List *list);
+
+int list_is_empty (List *list);
+
+int list_length (List *list);
+
+ListNode *list_unlink_head (List *list);
+ListNode *list_unlink_tail (List *list);
+
+ListNode *list_prepend_node (List *list, ListNode *node);
+ListNode *list_append_node  (List *list, ListNode *node);
+
+ListNode *list_node_unlink (ListNode *node);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __LIST_H__ */
Index: gmime/memchunk.c
===================================================================
RCS file: /cvs/gnome/pan/gmime/memchunk.c,v
retrieving revision 1.10
diff -u -r1.10 memchunk.c
--- gmime/memchunk.c	30 Dec 2002 16:37:57 -0000	1.10
+++ gmime/memchunk.c	7 Jun 2004 06:10:27 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -30,10 +30,6 @@
 
 #include "memchunk.h"
 
-#include <glib/gmem.h>
-#include <glib/garray.h>
-#include <glib/gtree.h>
-#include <glib/galloca.h>
 
 /* in glib2, GSearchFunc was combined with GCompareFunc */
 typedef GCompareFunc GSearchFunc;
@@ -112,16 +108,14 @@
 	char *block;
 	
 	if (memchunk->free) {
-		node = memchunk->free;
+		block = (char *) (node = memchunk->free);
 		node->atoms--;
-		if (node->atoms > 0) {
-			block = (char *) node + (node->atoms * memchunk->atomsize);
-			return (void *) block;
-		}
-	
+		if (node->atoms > 0)
+			return (void *) (block + (node->atoms * memchunk->atomsize));
+		
 		memchunk->free = node->next;
 		
-		return (void *) node;
+		return (void *) block;
 	} else {
 		block = g_malloc (memchunk->blocksize);
 		g_ptr_array_add (memchunk->blocks, block);
Index: gmime/memchunk.h
===================================================================
RCS file: /cvs/gnome/pan/gmime/memchunk.h,v
retrieving revision 1.3
diff -u -r1.3 memchunk.h
--- gmime/memchunk.h	30 Dec 2002 16:37:57 -0000	1.3
+++ gmime/memchunk.h	7 Jun 2004 06:10:27 -0000
@@ -2,7 +2,7 @@
 /*
  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
  *
- *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *  Copyright 2001-2004 Ximian, Inc. (www.ximian.com)
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,13 +24,13 @@
 #ifndef __MEMCHUNK_H__
 #define __MEMCHUNK_H__
 
+#include <glib.h>
+#include <sys/types.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
 #endif /* __cplusplus */
-
-#include <glib/gtypes.h>
-#include <sys/types.h>
 
 typedef struct _MemChunk MemChunk;
 
Index: gmime/url-scanner.c
===================================================================
RCS file: gmime/url-scanner.c
diff -N gmime/url-scanner.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/url-scanner.c	7 Jun 2004 06:10:27 -0000
@@ -0,0 +1,495 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gtrie.h"
+#include "url-scanner.h"
+
+
+struct _GUrlScanner {
+	GPtrArray *patterns;
+	GTrie *trie;
+};
+
+
+GUrlScanner *
+g_url_scanner_new (void)
+{
+	GUrlScanner *scanner;
+	
+	scanner = g_new (GUrlScanner, 1);
+	scanner->patterns = g_ptr_array_new ();
+	scanner->trie = g_trie_new (TRUE);
+	
+	return scanner;
+}
+
+
+void
+g_url_scanner_free (GUrlScanner *scanner)
+{
+	g_return_if_fail (scanner != NULL);
+	
+	g_ptr_array_free (scanner->patterns, TRUE);
+	g_trie_free (scanner->trie);
+	g_free (scanner);
+}
+
+
+void
+g_url_scanner_add (GUrlScanner *scanner, urlpattern_t *pattern)
+{
+	g_return_if_fail (scanner != NULL);
+	
+	g_trie_add (scanner->trie, pattern->pattern, scanner->patterns->len);
+	g_ptr_array_add (scanner->patterns, pattern);
+}
+
+
+gboolean
+g_url_scanner_scan (GUrlScanner *scanner, const char *in, size_t inlen, urlmatch_t *match)
+{
+	const char *pos, *inend;
+	urlpattern_t *pat;
+	int pattern_id;
+	
+	g_return_val_if_fail (scanner != NULL, FALSE);
+	g_return_val_if_fail (in != NULL, FALSE);
+	
+	if (!(pos = g_trie_search (scanner->trie, in, inlen, &pattern_id)))
+		return FALSE;
+	
+	pat = g_ptr_array_index (scanner->patterns, pattern_id);
+	
+	match->pattern = pat->pattern;
+	match->prefix = pat->prefix;
+	
+	inend = in + inlen;
+	if (!pat->start (in, pos, inend, match))
+		return FALSE;
+	
+	if (!pat->end (in, pos, inend, match))
+		return FALSE;
+	
+	return TRUE;
+}
+
+
+static unsigned char url_scanner_table[256] = {
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  9,  9,  1,  1,  9,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	 24,128,160,128,128,128,128,128,160,160,128,128,160,192,160,160,
+	 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,160,160, 32,128, 32,128,
+	160, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+	 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,160,160,160,128,128,
+	128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+	 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,128,128,128,128,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+	  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1
+};
+
+enum {
+	IS_CTRL		= (1 << 0),
+	IS_ALPHA        = (1 << 1),
+	IS_DIGIT        = (1 << 2),
+	IS_LWSP		= (1 << 3),
+	IS_SPACE	= (1 << 4),
+	IS_SPECIAL	= (1 << 5),
+	IS_DOMAIN       = (1 << 6),
+	IS_URLSAFE      = (1 << 7),
+};
+
+#define is_ctrl(x) ((url_scanner_table[(unsigned char)(x)] & IS_CTRL) != 0)
+#define is_lwsp(x) ((url_scanner_table[(unsigned char)(x)] & IS_LWSP) != 0)
+#define is_atom(x) ((url_scanner_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0)
+#define is_alpha(x) ((url_scanner_table[(unsigned char)(x)] & IS_ALPHA) != 0)
+#define is_digit(x) ((url_scanner_table[(unsigned char)(x)] & IS_DIGIT) != 0)
+#define is_domain(x) ((url_scanner_table[(unsigned char)(x)] & IS_DOMAIN) != 0)
+#define is_urlsafe(x) ((url_scanner_table[(unsigned char)(x)] & (IS_ALPHA|IS_DIGIT|IS_URLSAFE)) != 0)
+
+static struct {
+	char open;
+	char close;
+} url_braces[] = {
+	{ '(', ')' },
+	{ '{', '}' },
+	{ '[', ']' },
+	{ '<', '>' },
+	{ '|', '|' },
+};
+
+static gboolean
+is_open_brace (char c)
+{
+	int i;
+	
+	for (i = 0; i < G_N_ELEMENTS (url_braces); i++) {
+		if (c == url_braces[i].open)
+			return TRUE;
+	}
+	
+	return FALSE;
+}
+
+static char
+url_stop_at_brace (const char *in, size_t so)
+{
+	int i;
+	
+	if (so > 0) {
+		for (i = 0; i < 4; i++) {
+			if (in[so - 1] == url_braces[i].open)
+				return url_braces[i].close;
+		}
+	}
+	
+	return '\0';
+}
+
+
+gboolean
+g_url_addrspec_start (const char *in, const char *pos, const char *inend, urlmatch_t *match)
+{
+	register const char *inptr = pos;
+	
+	g_assert (*inptr == '@');
+	
+	inptr--;
+	
+	while (inptr > in) {
+		if (is_atom (*inptr))
+			inptr--;
+		else
+			break;
+		
+		while (inptr > in && is_atom (*inptr))
+			inptr--;
+		
+		if (inptr > in && *inptr == '.')
+			inptr--;
+	}
+	
+	if (!is_atom (*inptr) || is_open_brace (*inptr))
+		inptr++;
+	
+	if (inptr == pos)
+		return FALSE;
+	
+	match->um_so = (inptr - in);
+	
+	return TRUE;
+}
+
+gboolean
+g_url_addrspec_end (const char *in, const char *pos, const char *inend, urlmatch_t *match)
+{
+	const char *inptr = pos;
+	int parts = 0, digits;
+	gboolean got_dot = FALSE;
+	
+	g_assert (*inptr == '@');
+	
+	inptr++;
+	
+	if (*inptr == '[') {
+		/* domain literal */
+		do {
+			inptr++;
+			
+			digits = 0;
+			while (inptr < inend && is_digit (*inptr) && digits < 3) {
+				inptr++;
+				digits++;
+			}
+			
+			parts++;
+			
+			if (*inptr != '.' && parts != 4)
+				return FALSE;
+		} while (parts < 4);
+		
+		if (inptr < inend && *inptr == ']')
+			inptr++;
+		else
+			return FALSE;
+		
+		got_dot = TRUE;
+	} else {
+		while (inptr < inend) {
+			if (is_domain (*inptr))
+				inptr++;
+			else
+				break;
+			
+			while (inptr < inend && is_domain (*inptr))
+				inptr++;
+			
+			if (inptr < inend && *inptr == '.' && is_domain (inptr[1])) {
+				if (*inptr == '.')
+					got_dot = TRUE;
+				inptr++;
+			}
+		}
+	}
+	
+	if (inptr == pos + 1 || !got_dot)
+		return FALSE;
+	
+	match->um_eo = (inptr - in);
+	
+	return TRUE;
+}
+
+
+gboolean
+g_url_file_start (const char *in, const char *pos, const char *inend, urlmatch_t *match)
+{
+	match->um_so = (pos - in);
+	
+	return TRUE;
+}
+
+gboolean
+g_url_file_end (const char *in, const char *pos, const char *inend, urlmatch_t *match)
+{
+	register const char *inptr = pos;
+	char close_brace;
+	
+	inptr += strlen (match->pattern);
+	
+	if (*inptr == '/')
+		inptr++;
+	
+	close_brace = url_stop_at_brace (in, match->um_so);
+	
+	while (inptr < inend && is_urlsafe (*inptr) && *inptr != close_brace)
+		inptr++;
+	
+	if (inptr == pos)
+		return FALSE;
+	
+	match->um_eo = (inptr - in);
+	
+	return TRUE;
+}
+
+gboolean
+g_url_web_start (const char *in, const char *pos, const char *inend, urlmatch_t *match)
+{
+	match->um_so = (pos - in);
+	
+	return TRUE;
+}
+
+gboolean
+g_url_web_end (const char *in, const char *pos, const char *inend, urlmatch_t *match)
+{
+	register const char *inptr = pos;
+	int parts = 0, digits, port;
+	char close_brace;
+	
+	inptr += strlen (match->pattern);
+	
+	close_brace = url_stop_at_brace (in, match->um_so);
+	
+	/* find the end of the domain */
+	if (is_digit (*inptr)) {
+		/* domain-literal */
+		do {
+			digits = 0;
+			while (inptr < inend && is_digit (*inptr) && digits < 3) {
+				inptr++;
+				digits++;
+			}
+			
+			parts++;
+			
+			if (*inptr != '.' && parts != 4)
+				return FALSE;
+			else if (*inptr == '.')
+				inptr++;
+			
+		} while (parts < 4);
+	} else if (is_atom (*inptr)) {
+		/* might be a domain or user@domain */
+		const char *save = inptr;
+		
+		while (inptr < inend) {
+			if (!is_atom (*inptr))
+				break;
+			
+			inptr++;
+			
+			while (inptr < inend && is_atom (*inptr))
+				inptr++;
+			
+			if (inptr < inend && *inptr == '.' && is_atom (inptr[1]))
+				inptr++;
+		}
+		
+		if (*inptr != '@')
+			inptr = save;
+		else
+			inptr++;
+		
+		goto domain;
+	} else if (is_domain (*inptr)) {
+	domain:
+		while (inptr < inend) {
+			if (is_domain (*inptr))
+				inptr++;
+			else
+				break;
+			
+			while (inptr < inend && is_domain (*inptr))
+				inptr++;
+			
+			if (inptr < inend && *inptr == '.' && is_domain (inptr[1]))
+				inptr++;
+		}
+	} else {
+		return FALSE;
+	}
+	
+	if (inptr < inend) {
+		switch (*inptr) {
+		case ':': /* port notation */
+			inptr++;
+			port = 0;
+			
+			while (inptr < inend && is_digit (*inptr) && port < 65536)
+				port = (port * 10) + (*inptr++ - '0');
+			
+			if (port >= 65536)
+				inptr--;
+			
+			if (inptr >= inend || *inptr != '/')
+				break;
+			
+			/* we have a '/' so there could be a path - fall through */
+		case '/': /* we've detected a path component to our url */
+			inptr++;
+			
+			while (inptr < inend && is_urlsafe (*inptr) && *inptr != close_brace)
+				inptr++;
+			
+			break;
+		default:
+			break;
+		}
+	}
+	
+	/* urls are extremely unlikely to end with any
+	 * punctuation, so strip any trailing
+	 * punctuation off. Also strip off any closing
+	 * braces or quotes. */
+	while (inptr > pos && strchr (",.:;?!-|)}]'\"", inptr[-1]))
+		inptr--;
+	
+	match->um_eo = (inptr - in);
+	
+	return TRUE;
+}
+
+
+#ifdef BUILD_TABLE
+
+#include <stdio.h>
+
+/* got these from rfc1738 */
+#define CHARS_LWSP " \t\n\r"               /* linear whitespace chars */
+#define CHARS_SPECIAL "()<>@,;:\\\".[]"
+
+/* got these from rfc1738 */
+#define CHARS_URLSAFE "$-_.+!*'(),{}|\\^~[]`#%\";/?:@&="
+
+
+static void
+table_init_bits (unsigned int mask, const unsigned char *vals)
+{
+	int i;
+	
+	for (i = 0; vals[i] != '\0'; i++)
+		url_scanner_table[vals[i]] |= mask;
+}
+
+static void
+url_scanner_table_init (void)
+{
+	int i;
+	
+	for (i = 0; i < 256; i++) {
+		url_scanner_table[i] = 0;
+		if (i < 32)
+			url_scanner_table[i] |= IS_CTRL;
+		if ((i >= '0' && i <= '9'))
+			url_scanner_table[i] |= IS_DIGIT | IS_DOMAIN;
+		if ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z'))
+			url_scanner_table[i] |= IS_ALPHA | IS_DOMAIN;
+		if (i >= 127)
+			url_scanner_table[i] |= IS_CTRL;
+	}
+	
+	url_scanner_table[' '] |= IS_SPACE;
+	url_scanner_table['-'] |= IS_DOMAIN;
+	
+	/* not defined to be special in rfc0822, but when scanning
+           backwards to find the beginning of the email address we do
+           not want to include this char if we come accross it - so
+           this is kind of a hack, but it's ok */
+	url_scanner_table['/'] |= IS_SPECIAL;
+	
+	table_init_bits (IS_LWSP, CHARS_LWSP);
+	table_init_bits (IS_SPECIAL, CHARS_SPECIAL);
+	table_init_bits (IS_URLSAFE, CHARS_URLSAFE);
+}
+
+int main (int argc, char **argv)
+{
+	int i;
+	
+	url_scanner_table_init ();
+	
+	printf ("static unsigned char url_scanner_table[256] = {");
+	for (i = 0; i < 256; i++) {
+		printf ("%s%3d%s", (i % 16) ? "" : "\n\t",
+			url_scanner_table[i], i != 255 ? "," : "\n");
+	}
+	printf ("};\n\n");
+	
+	return 0;
+}
+
+#endif /* BUILD_TABLE */
Index: gmime/url-scanner.h
===================================================================
RCS file: gmime/url-scanner.h
diff -N gmime/url-scanner.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gmime/url-scanner.h	7 Jun 2004 06:10:27 -0000
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ *  Copyright 2002-2004 Ximian, Inc. (www.ximian.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __URL_SCANNER_H__
+#define __URL_SCANNER_H__
+
+#include <glib.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+typedef struct {
+	const char *pattern;
+	const char *prefix;
+	off_t um_so;
+	off_t um_eo;
+} urlmatch_t;
+
+typedef gboolean (*GUrlScanFunc) (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+
+/* some default GUrlScanFunc's */
+gboolean g_url_file_start (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+gboolean g_url_file_end (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+gboolean g_url_web_start (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+gboolean g_url_web_end (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+gboolean g_url_addrspec_start (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+gboolean g_url_addrspec_end (const char *in, const char *pos, const char *inend, urlmatch_t *match);
+
+typedef struct {
+	char *pattern;
+	char *prefix;
+	GUrlScanFunc start;
+	GUrlScanFunc end;
+} urlpattern_t;
+
+typedef struct _GUrlScanner GUrlScanner;
+
+GUrlScanner *g_url_scanner_new (void);
+void g_url_scanner_free (GUrlScanner *scanner);
+
+void g_url_scanner_add (GUrlScanner *scanner, urlpattern_t *pattern);
+
+gboolean g_url_scanner_scan (GUrlScanner *scanner, const char *in, size_t inlen, urlmatch_t *match);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __URL_SCANNER_H__ */
Index: pan/message-window.c
===================================================================
RCS file: /cvs/gnome/pan/pan/message-window.c,v
retrieving revision 1.389
diff -u -r1.389 message-window.c
--- pan/message-window.c	24 Nov 2003 21:33:39 -0000	1.389
+++ pan/message-window.c	7 Jun 2004 06:10:30 -0000
@@ -2456,9 +2456,9 @@
 			charset_filter = g_mime_filter_charset_new ("UTF-8", charset);
 			g_mime_stream_filter_add (GMIME_STREAM_FILTER(charset_stream), charset_filter);
 			g_mime_data_wrapper_set_stream (data_wrapper, charset_stream);
-
-			g_mime_stream_unref (charset_stream);
-			g_mime_stream_unref (original_stream);
+			g_object_unref (charset_filter);
+			g_object_unref (charset_stream);
+			g_object_unref (original_stream);
 			g_object_unref (data_wrapper);
 		}
 
Index: pan/smtp.c
===================================================================
RCS file: /cvs/gnome/pan/pan/smtp.c,v
retrieving revision 1.84
diff -u -r1.84 smtp.c
--- pan/smtp.c	24 Jul 2003 23:20:48 -0000	1.84
+++ pan/smtp.c	7 Jun 2004 06:10:31 -0000
@@ -209,14 +209,14 @@
 	maybe_okay = FALSE;
 	if (okay)
 	{
-		InternetAddressList * addresses = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);
-		InternetAddressList * march;
+		const InternetAddressList * addresses = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);
+		const InternetAddressList * march;
 
 		for (march=addresses; march!=NULL; march=march->next)
 		{
 			if (okay && march->address->type==INTERNET_ADDRESS_NAME)
 			{
-				const char * fmt = "RCPT TO: <%s>\r\n";
+				const char * fmt = "RCPT TO:<%s>\r\n";
 				okay = !smtp_send_line_va (item, sock, fmt, march->address->value.addr);
 			}
 			if (okay)
Index: pan/task-post.c
===================================================================
RCS file: /cvs/gnome/pan/pan/task-post.c,v
retrieving revision 1.24
diff -u -r1.24 task-post.c
--- pan/task-post.c	7 Aug 2003 22:05:52 -0000	1.24
+++ pan/task-post.c	7 Jun 2004 06:10:31 -0000
@@ -103,6 +103,7 @@
 		g_ptr_array_add (((GPtrArray*)(user_data)), (gpointer)name);
 }
 
+#if 0
 static gssize
 write_header_nofold (GMimeStream *stream, const char *name, const char *value)
 {
@@ -115,6 +116,7 @@
 
 	return nwritten;
 }
+#endif
 
 
 /**
@@ -140,8 +142,8 @@
 		GMimeStream * stream;
 		GMimeParser * parser;
 
-		g_mime_header_set_default_write_func (GMIME_OBJECT(message)->headers,
-		                                      write_header_nofold);
+		/*g_mime_header_set_default_write_func (GMIME_OBJECT(message)->headers,
+		  write_header_nofold);*/
 
 		str = g_mime_message_to_string (message);
 		message = NULL;
@@ -168,8 +170,8 @@
 		g_mime_object_remove_header (GMIME_OBJECT(clone), HEADER_MESSAGE_ID);
 
 	/* turn off folding. */
-	g_mime_header_set_default_write_func (GMIME_OBJECT(clone)->headers,
-	                                      write_header_nofold);
+	/*g_mime_header_set_default_write_func (GMIME_OBJECT(clone)->headers,
+	  write_header_nofold);*/
 
 
 	/* remove the internal headers */
Index: pan/text.c
===================================================================
RCS file: /cvs/gnome/pan/pan/text.c,v
retrieving revision 1.371
diff -u -r1.371 text.c
--- pan/text.c	5 May 2004 03:04:58 -0000	1.371
+++ pan/text.c	7 Jun 2004 06:10:33 -0000
@@ -27,6 +27,7 @@
 #include <gdk/gdkkeysyms.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gdk-pixbuf/gdk-pixbuf-loader.h>
+#include <gmime/url-scanner.h>
 #include <gmime/gmime.h>
 
 #include <pan/base/acache.h>
@@ -131,29 +132,19 @@
 	gtk_text_buffer_delete (buffer, &start, &end);
 }
 
-/**
- * Returns the pointer to the beginning of the next URL
- * in searchme, or NULL if no URL is found.
- * @param searchme the zero-terminated string to search for URLS.
- */
-static const char*
-find_next_url (const char * searchme)
-{
-	register const char * pch = searchme;
 
-	for (; pch && *pch; ++pch)
-	{
-		if (*pch == 'h')
-		{
-			if (!strncmp (pch, "http://", 7))
-				return pch;
-			if (!strncmp (pch, "https://", 8))
-				return pch;
-		}
-	}
-
-	return NULL;
-}
+static urlpattern_t patterns[] = {
+	{ "file://",   "",        g_url_file_start,     g_url_file_end     },
+	{ "ftp://",    "",        g_url_web_start,      g_url_web_end      },
+	{ "http://",   "",        g_url_web_start,      g_url_web_end      },
+	{ "https://",  "",        g_url_web_start,      g_url_web_end      },
+	{ "news://",   "",        g_url_web_start,      g_url_web_end      },
+	{ "nntp://",   "",        g_url_web_start,      g_url_web_end      },
+	{ "telnet://", "",        g_url_web_start,      g_url_web_end      },
+	{ "www.",      "http://", g_url_web_start,      g_url_web_end      },
+	{ "ftp.",      "ftp://",  g_url_web_start,      g_url_web_end      },
+	{ "@",         "mailto:", g_url_addrspec_start, g_url_addrspec_end },
+};
 
 /**
  * Appends the specified body into the text buffer.
@@ -174,12 +165,17 @@
 	const char * last_quote_begin = NULL;
 	const char * quote_tag = NULL;
 	const char * last_quote_tag = NULL;
+	GUrlScanner *url_scanner;
 	GtkTextIter mark_start;
 	GtkTextIter mark_end;
 	GtkTextIter start;
 	GtkTextIter end;
 	GtkTextMark * mark;
 	gboolean is_sig = FALSE;
+	urlmatch_t match;
+	size_t bodylen;
+	int i;
+	
 	debug_enter ("append_text_buffer_nolock");
 
 	/* sanity checks */
@@ -234,28 +230,31 @@
 		gtk_text_buffer_get_end_iter (text_buffer, &mark_end);
 		gtk_text_buffer_apply_tag_by_name (text_buffer, last_quote_tag, &mark_start, &mark_end);
 	}
-
-	/* markup URLs */
+	
+	url_scanner = g_url_scanner_new ();
+	for (i = 0; i < G_N_ELEMENTS (patterns); i++)
+		g_url_scanner_add (url_scanner, &patterns[i]);
+	
 	pch = body;
-	while ((pch = find_next_url (pch))) {
-		const char * url_start = pch;
-		char * url = url_extract (&pch, strlen(pch), TRUE, FALSE);
-		if (url == NULL)
-			++pch;
-		else {
-			mark_start = start;
-			gtk_text_iter_forward_chars (&mark_start, g_utf8_strlen(body,url_start-body));
-			mark_end = mark_start;
-			gtk_text_iter_forward_chars (&mark_end, g_utf8_strlen(url,-1));
-			gtk_text_buffer_remove_all_tags (text_buffer, &mark_start, &mark_end);
-			gtk_text_buffer_apply_tag_by_name (text_buffer, "url", &mark_start, &mark_end);
-			g_free (url);
-		}
+	bodylen = strlen (body);
+	while (g_url_scanner_scan (url_scanner, pch, bodylen, &match)) {
+		const char *url_start = pch + match.um_so;
+		int urllen = match.um_eo - match.um_so;
+		
+		gtk_text_iter_forward_chars (&mark_start, g_utf8_strlen (pch, url_start - pch));
+		mark_end = mark_start;
+		gtk_text_iter_forward_chars (&mark_end, g_utf8_strlen (url_start, urllen));
+		gtk_text_buffer_remove_all_tags (text_buffer, &mark_start, &mark_end);
+		gtk_text_buffer_apply_tag_by_name (text_buffer, "url", &mark_start, &mark_end);
+		mark_start = mark_end;
+		bodylen -= match.um_eo;
+		pch += match.um_eo;
 	}
-
+	
 	/* cleanup */
 	g_free (freeme1);
 	g_free (freeme2);
+	g_url_scanner_free (url_scanner);
 	debug_exit ("append_text_buffer_nolock");
 }
 
@@ -531,13 +530,16 @@
 		&& g_object_get_data(G_OBJECT(part),"Y_DECODER_INSTALLED")==NULL)
 	{
 		GMimeStream * stream;
-	       
+		GMimeFilter *yenc;
+		
+		yenc = g_mime_filter_yenc_new (GMIME_FILTER_YENC_DIRECTION_DECODE);
 		stream = g_mime_stream_filter_new_with_stream (part->content->stream);
-		g_mime_stream_filter_add (GMIME_STREAM_FILTER(stream), g_mime_filter_yenc_new (GMIME_FILTER_YENC_DIRECTION_DECODE));
+		g_mime_stream_filter_add (GMIME_STREAM_FILTER(stream), yenc);
 		g_mime_data_wrapper_set_stream (part->content, stream);
 		g_object_set_data (G_OBJECT(part), "Y_DECODER_INSTALLED", GINT_TO_POINTER(1));
-
-		g_mime_stream_unref (stream);
+		
+		g_object_unref (stream);
+		g_object_unref (yenc);
 	}
 
 	is_image = g_mime_content_type_is_type (type, "image", "*");
Index: pan/base/decode.c
===================================================================
RCS file: /cvs/gnome/pan/pan/base/decode.c,v
retrieving revision 1.61
diff -u -r1.61 decode.c
--- pan/base/decode.c	4 Feb 2004 22:21:57 -0000	1.61
+++ pan/base/decode.c	7 Jun 2004 06:10:33 -0000
@@ -195,7 +195,7 @@
 						filter = g_mime_filter_yenc_new (GMIME_FILTER_YENC_DIRECTION_DECODE);
 						g_mime_stream_filter_add (GMIME_STREAM_FILTER(stream), filter);
 						yenc_filter = (GMimeFilterYenc*) filter;
-						g_mime_stream_unref (inner_stream);
+						g_object_unref (inner_stream);
 					}
 				}
 				else {
@@ -271,6 +271,8 @@
 	/* cleanup */
 	if (stream)
 		g_object_unref (stream);
+	if (yenc_filter)
+		g_object_unref (yenc_filter);
 	stream = NULL;
 	g_free (filename);
 	filename = NULL;
Index: pan/base/util-mime.c
===================================================================
RCS file: /cvs/gnome/pan/pan/base/util-mime.c,v
retrieving revision 1.61
diff -u -r1.61 util-mime.c
--- pan/base/util-mime.c	19 Feb 2004 15:55:40 -0000	1.61
+++ pan/base/util-mime.c	7 Jun 2004 06:10:34 -0000
@@ -462,7 +462,7 @@
 					{
 						GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
 						g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cur->cat), stream);
-						g_mime_stream_unref (stream);
+						g_object_unref (stream);
 						g_ptr_array_add (appendme_file_parts, cur);
 						cur = NULL;
 					}
@@ -483,7 +483,7 @@
 					{
 						GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
 						g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cur->cat), stream);
-						g_mime_stream_unref (stream);
+						g_object_unref (stream);
 						g_ptr_array_add (appendme_file_parts, cur);
 						cur = NULL;
 					}
@@ -522,7 +522,7 @@
 						sub_begin = linestart_pos;
 					stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
 					g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cur->cat), stream);
-					g_mime_stream_unref (stream);
+					g_object_unref (stream);
                         		g_ptr_array_add (appendme_file_parts, cur);
 
 					cur = NULL;
@@ -535,7 +535,7 @@
 					{
 						GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
 						g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cur->cat), stream);
-						g_mime_stream_unref (stream);
+						g_object_unref (stream);
 					}
 
 					sub_begin = -1;
@@ -558,7 +558,7 @@
 				{
 					GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
 					g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cur->cat), stream);
-					g_mime_stream_unref (stream);
+					g_object_unref (stream);
 					yenc_parse_end_line (line_str, &cur->y_size, NULL, &cur->y_pcrc, &cur->y_crc);
                         		g_ptr_array_add (appendme_file_parts, cur);
 
@@ -587,7 +587,7 @@
 		{
 			GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
 			g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cur->cat), stream);
-			g_mime_stream_unref (stream);
+			g_object_unref (stream);
 		}
 
 		/* just in case someone started with "yenc" or "begin 644 asf" in a text message to fuck with unwary newsreaders */
@@ -723,7 +723,7 @@
 	stream = g_mime_data_wrapper_get_stream (content);
 	g_mime_stream_reset (stream);
 	istream = g_mime_stream_buffer_new (stream, GMIME_STREAM_BUFFER_BLOCK_READ);
-	g_mime_stream_unref (stream);
+	g_object_unref (stream);
 	g_object_unref (content);
 
 	parts = g_ptr_array_new ();
@@ -752,6 +752,8 @@
 
 			if (enc_part->type == ENC_UU)
 			{
+				GMimeFilter *filter;
+				
 				guess_part_type_from_filename (enc_part->decoded_filename, &type, &subtype);
 				
 				subpart = g_mime_part_new_with_type (type, subtype);
@@ -759,12 +761,14 @@
 				/* set part's attributes */
 				g_mime_part_set_filename (subpart, enc_part->decoded_filename);
 
+				filter = g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_UU_DEC);
 				filter_stream = g_mime_stream_filter_new_with_stream (substream);
-				g_mime_stream_filter_add (GMIME_STREAM_FILTER (filter_stream), g_mime_filter_basic_new_type (GMIME_FILTER_BASIC_UU_DEC));
+				g_mime_stream_filter_add (GMIME_STREAM_FILTER (filter_stream), filter);
 				content = g_mime_data_wrapper_new_with_stream (filter_stream, GMIME_PART_ENCODING_DEFAULT);
 				g_mime_part_set_content_object (subpart, content);
 				g_object_unref (content);
-				g_mime_stream_unref (filter_stream);
+				g_object_unref (filter_stream);
+				g_object_unref (filter);
 			}
 			else if (enc_part->type == ENC_YENC)
 			{
@@ -816,11 +820,11 @@
 		*part = GMIME_OBJECT (multipart);
 	}
 
-	g_mime_stream_unref (istream);
+	g_object_unref (istream);
 
 	for (i=0; i<parts->len; ++i) {	
 		EncTempFilePart * enc_part = parts->pdata[i];
-		g_mime_stream_unref (enc_part->cat);
+		g_object_unref (enc_part->cat);
 		g_free (enc_part->decoded_filename);
 		g_free (enc_part);
 	}
@@ -871,7 +875,7 @@
 		/* cat stream holds headers + body */
 		cat = g_mime_stream_cat_new ();
 		g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cat), stream);
-		g_mime_stream_unref (stream);
+		g_object_unref (stream);
 		g_mime_stream_cat_add_source (GMIME_STREAM_CAT(cat), content);
 
 		/* override the content variable s.t. headers get written too */
@@ -879,7 +883,7 @@
 		wrapper = g_mime_data_wrapper_new_with_stream (cat, GMIME_PART_ENCODING_DEFAULT);
 		g_mime_part_set_content_object (GMIME_PART(partial), wrapper);
 		g_object_unref (wrapper);
-		g_mime_stream_unref (cat);
+		g_object_unref (cat);
 	}
 	else
 	{
@@ -953,7 +957,7 @@
 				partial = pan_fake_partial (message, stream, message_id, i+1, qty);
 				g_mime_stream_reset (stream);
 
-				g_mime_stream_unref (stream);
+				g_object_unref (stream);
 				g_object_unref (wrapper);
 				g_object_unref (message);
 
