? README.windows ? autom4te.cache ? iconv-detect.h ? pan-updated-gmime.patch ? 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 04:40:13 -0000 @@ -1,3 +1,30 @@ +2004-06-07 Jeffrey Stedfast + + * 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. + + * 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 * gmime/gmime-charset.[c,h]: Synced up with GMime CVS to fix bug 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 04:40:13 -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 04:40:14 -0000 @@ -0,0 +1,140 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#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 04:40:14 -0000 @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:14 -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 04:40:14 -0000 @@ -0,0 +1,109 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#endif + +#include + +#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 04:40:15 -0000 @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:15 -0000 @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast + * Authors: Jeffrey Stedfast * - * 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 - #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 04:40:15 -0000 @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast + * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include -#include -#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 04:40:15 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:15 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#include - -#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 04:40:15 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 -#include -#include 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 04:40:16 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include -#include -#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 04:40:16 -0000 @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:16 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #endif @@ -29,63 +30,77 @@ #include "gmime-filter-basic.h" #include "gmime-utils.h" -#include -#include +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 04:40:16 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #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 04:40:16 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 -#include -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 04:40:16 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include "gmime-filter.h" -#include +#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 04:40:16 -0000 @@ -0,0 +1,227 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#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 04:40:17 -0000 @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:17 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #endif -#include "gmime-filter-html.h" - #include #include -#include -#include -#include -#include +#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, "
");
-		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, "", 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, "");
-				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, "> ");
-		}
+		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, "%s",
-						   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 ("%s",
-							   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, "<");
 			html->column++;
 			break;
-			
 		case '>':
 			outptr = g_stpcpy (outptr, ">");
 			html->column++;
 			break;
-			
 		case '&':
 			outptr = g_stpcpy (outptr, "&");
 			html->column++;
 			break;
-			
 		case '"':
 			outptr = g_stpcpy (outptr, """);
 			html->column++;
 			break;
-			
-		case '\n':
-			if (html->flags & GMIME_FILTER_HTML_CONVERT_NL)
-				outptr = g_stpcpy (outptr, "
"); - - *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, " "); 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, " "); 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, "
"); + 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, "
");
+		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, "", 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, "> ");
+			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, "");
+					
+					/* 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, "");
+				} 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, "");
+		}
+		
+		if (html->flags & GMIME_FILTER_HTML_CONVERT_NL) {
+			outptr = check_size (filter, outptr, &outend, 5);
+			outptr = g_stpcpy (outptr, "
"); + } + + *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, "
"); + } + } 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 04:40:17 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #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
 tags.
+ * Wrap stream in <pre> tags.
  **/
 #define GMIME_FILTER_HTML_PRE               (1 << 0)
 
@@ -43,7 +53,7 @@
 /**
  * GMIME_FILTER_HTML_CONVERT_NL:
  *
- * Convert new-lines ('\n') into 
tags. + * Convert new-lines ('\n') into <br> tags. **/ #define GMIME_FILTER_HTML_CONVERT_NL (1 << 1) @@ -60,7 +70,7 @@ /** * GMIME_FILTER_HTML_CONVERT_URLS: * - * Wrap detected URLs in tags. + * Wrap detected URLs in <a href=...> 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 "> " 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 04:40:17 -0000 @@ -0,0 +1,173 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#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 04:40:17 -0000 @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:18 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 -#include +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 04:40:18 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #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 04:40:18 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 /* for memcpy */ + #include "gmime-filter.h" -#include -#include 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 04:40:18 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include +#include + +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#include +#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 04:40:19 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:19 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#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 04:40:19 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:19 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include -#include 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 04:40:19 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:19 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include - 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 04:40:20 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #endif +#include + #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 04:40:20 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #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 04:40:20 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:20 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 + +#include +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include - -#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 04:40:22 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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\" ") + * (ex: "\"Joe Sixpack\" <address@hidden>") **/ 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/. * - * 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/. **/ -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 04:40:22 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#include - -#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 04:40:22 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:22 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 + +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include - -#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 04:40:23 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #include +#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 04:40:23 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#include - -#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 04:40:24 -0000 @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast + * Authors: Jeffrey Stedfast * - * 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 #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 -#include -#include -#include -#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, ¶m, &rfc2184_part, - &is_rfc2184_encoded); + is_rfc2184 = decode_rfc2184_param (&inptr, ¶m, 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 *) ¶ms; + + 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 04:40:24 -0000 @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast + * Authors: Jeffrey Stedfast * - * 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 + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include -#include 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 04:40:25 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 #include -#include +#include #include #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 04:40:25 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include #include #include -#include "gmime-object.h" -#include "gmime-message.h" -#include "gmime-content-type.h" -#include "gmime-stream.h" +#include +#include +#include +#include #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 04:40:26 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:26 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#include - -#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 04:40:27 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:27 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 + #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 04:40:27 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:28 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include -#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 04:40:28 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:28 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#include -#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 04:40:28 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:28 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #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 04:40:29 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:29 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include -#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 04:40:29 -0000 @@ -0,0 +1,243 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#endif + +#include + +#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 04:40:29 -0000 @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#include + +#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 04:40:30 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:30 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include #include #include #include #include -#include "gmime-type-utils.h" +#include + +#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 04:40:30 -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 04:40:30 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:32 -0000 @@ -1,9 +1,9 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Michael Zucchi - * Jeffrey Stedfast + * Authors: Michael Zucchi + * Jeffrey Stedfast * - * 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 #endif +#define _GNU_SOURCE + #include #include #include +#ifdef HAVE_SYS_PARAM_H +#include /* for MAXHOSTNAMELEN */ +#else +#define MAXHOSTNAMELEN 64 +#endif +#include +#include +#ifdef HAVE_NETDB_H +#include +#endif #include #include @@ -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 " 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 04:40:32 -0000 @@ -1,9 +1,9 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Michael Zucchi - * Jeffrey Stedfast + * Authors: Michael Zucchi + * Jeffrey Stedfast * - * 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 +#include +#include + #ifdef __cplusplus extern "C" { #pragma } -#endif /* __cplusplus }*/ - -#include -#include -#include +#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 04:40:32 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 04:40:32 -0000 @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast + * Authors: Jeffrey Stedfast * - * 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 -#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* 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 04:40:32 -0000 @@ -0,0 +1,409 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#endif + +#include +#include + +#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 address@hidden", + "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 04:40:33 -0000 @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:33 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 -#include -#include -#include +#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 04:40:33 -0000 @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast + * Authors: Jeffrey Stedfast * - * 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 + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -#include - 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 04:40:33 -0000 @@ -0,0 +1,120 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#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 04:40:33 -0000 @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 + +#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 04:40:34 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 -#include -#include -#include /* 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 04:40:34 -0000 @@ -2,7 +2,7 @@ /* * Authors: Jeffrey Stedfast * - * 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 +#include + #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ - -#include -#include 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 04:40:34 -0000 @@ -0,0 +1,495 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#endif + +#include +#include + +#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 address@hidden */ + 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 + +/* 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 04:40:34 -0000 @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * 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 +#include + +#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 04:40:37 -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 04:40:38 -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 04:40:38 -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 04:40:39 -0000 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -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,16 @@ 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; + int i; + debug_enter ("append_text_buffer_nolock"); /* sanity checks */ @@ -234,28 +229,27 @@ 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); - } + while (g_url_scanner_scan (url_scanner, pch, strlen (pch), &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 (body, url_start - body)); + 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); } - + /* cleanup */ g_free (freeme1); g_free (freeme2); + g_url_scanner_free (url_scanner); debug_exit ("append_text_buffer_nolock"); } @@ -531,13 +525,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 04:40:40 -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 04:40:41 -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; ilen; ++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);