? 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"); - 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, ""); + 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, "
"); - - *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, "
"); + 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, ""); + } + } 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
"); + } + + *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, "
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