diff options
author | Jeffrey Stedfast <fejj@ximian.com> | 2001-04-02 04:43:38 +0000 |
---|---|---|
committer | Jeffrey Stedfast <fejj@src.gnome.org> | 2001-04-02 04:43:38 +0000 |
commit | d6e232c69c4fc7867be520311bf060c939f1fb67 (patch) | |
tree | 8dfdeb85cea8392ba8abfb333a674b6e39af6dbb | |
parent | dc49c2e85b28b0ee5b43149cefdd92492956bceb (diff) | |
download | gmime-d6e232c69c4fc7867be520311bf060c939f1fb67.tar.gz |
Made smarter.
2001-04-01 Jeffrey Stedfast <fejj@ximian.com>
* gmime-utils.c (g_mime_utils_quote_string): Made smarter.
2001-03-31 Jeffrey Stedfast <fejj@ximian.com>
* Makefile.am: Added rules to build gen-table.c and added
gmime-table-private.h to the build.
* gen-table.c: New file to generate gmime-table-private.h if need
be.
* gmime-table-private.h: New file that contains
gmime_special_table.
* gmime-utils.c: Remove the gmime_special_table from here and
instead #include gmime-table-private.h.
* internet-address.c: #include gmime-table-private.h
2001-03-30 Jeffrey Stedfast <fejj@ximian.com>
* gmime-message.c (create_header): Simplified a tad.
(g_mime_message_add_recipient): Updated for the new
InternetAddress API.
(g_mime_message_add_recipients_from_string): Simplified greatly
using the new InternetAddress API.
* internet-address.[c,h]: Completely rewritten to be a lot more
rfc0822 compliant.
2001-03-28 Jeffrey Stedfast <fejj@ximian.com>
* README, configure.in: Updated version to 0.5.0
-rw-r--r-- | ChangeLog | 35 | ||||
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | gen-table.c | 183 | ||||
-rw-r--r-- | gmime-content-type.c | 9 | ||||
-rw-r--r-- | gmime-message.c | 55 | ||||
-rw-r--r-- | gmime-parser.c | 567 | ||||
-rw-r--r-- | gmime-table-private.h | 76 | ||||
-rw-r--r-- | gmime-utils.c | 169 | ||||
-rw-r--r-- | gmime/gen-table.c | 183 | ||||
-rw-r--r-- | gmime/gmime-content-type.c | 9 | ||||
-rw-r--r-- | gmime/gmime-message.c | 55 | ||||
-rw-r--r-- | gmime/gmime-parser.c | 567 | ||||
-rw-r--r-- | gmime/gmime-table-private.h | 76 | ||||
-rw-r--r-- | gmime/gmime-utils.c | 169 | ||||
-rw-r--r-- | gmime/internet-address.c | 782 | ||||
-rw-r--r-- | gmime/internet-address.h | 24 | ||||
-rw-r--r-- | internet-address.c | 782 | ||||
-rw-r--r-- | internet-address.h | 24 | ||||
-rw-r--r-- | test-mime.c | 85 | ||||
-rw-r--r-- | tests/test-mime.c | 85 |
22 files changed, 2690 insertions, 1257 deletions
@@ -1,3 +1,38 @@ +2001-04-01 Jeffrey Stedfast <fejj@ximian.com> + + * gmime-utils.c (g_mime_utils_quote_string): Made smarter. + +2001-03-31 Jeffrey Stedfast <fejj@ximian.com> + + * Makefile.am: Added rules to build gen-table.c and added + gmime-table-private.h to the build. + + * gen-table.c: New file to generate gmime-table-private.h if need + be. + + * gmime-table-private.h: New file that contains + gmime_special_table. + + * gmime-utils.c: Remove the gmime_special_table from here and + instead #include gmime-table-private.h. + + * internet-address.c: #include gmime-table-private.h + +2001-03-30 Jeffrey Stedfast <fejj@ximian.com> + + * gmime-message.c (create_header): Simplified a tad. + (g_mime_message_add_recipient): Updated for the new + InternetAddress API. + (g_mime_message_add_recipients_from_string): Simplified greatly + using the new InternetAddress API. + + * internet-address.[c,h]: Completely rewritten to be a lot more + rfc0822 compliant. + +2001-03-28 Jeffrey Stedfast <fejj@ximian.com> + + * README, configure.in: Updated version to 0.5.0 + 2001-03-29 Charles Kerr <charles@rebelbase.com> * gmime-parser.c (g_mime_parser_construct_message_from_file): new diff --git a/Makefile.am b/Makefile.am index 7d7820e6..0f3700ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ INCLUDES = -I@srcdir@ $(VERSION_FLAGS) @GLIB_CFLAGS@ -DG_LOG_DOMAIN=\"gmime\" VERSION_FLAGS = -DGMIME_VERSION=\"@GMIME_VERSION@\" -noinst_PROGRAMS = test-mime test-parser +noinst_PROGRAMS = gen-table test-mime test-parser bin_SCRIPTS = gmime-config @@ -27,6 +27,7 @@ libgmime_la_SOURCES = \ gmimeincdir = $(includedir)/gmime gmimeinc_HEADERS = \ gmime.h \ + gmime-table-private.h \ gmime-content-type.h \ gmime-message.h \ gmime-param.h \ @@ -49,6 +50,11 @@ test_parser_LDFLAGS = test_parser_DEPENDENCIES = $(DEPS) test_parser_LDADD = $(LDADDS) +gen_table_SOURCES = gen-table.c +gen_table_LDFLAGS = +gen_table_DEPENDENCIES = +gen_table_LDADD = + check-local: tests testall : tests @@ -1,4 +1,4 @@ - GMime, version 0.4 + GMime, version 0.5 by Jeffrey Stedfast <fejj@helixcode.com> diff --git a/configure.in b/configure.in index 00d9940d..829425d0 100644 --- a/configure.in +++ b/configure.in @@ -11,7 +11,7 @@ AM_CONFIG_HEADER(config.h) # glib_libs: libs to store in gmime-config GMIME_MAJOR_VERSION=0 -GMIME_MINOR_VERSION=4 +GMIME_MINOR_VERSION=5 GMIME_MICRO_VERSION=0 GMIME_VERSION=$GMIME_MAJOR_VERSION.$GMIME_MINOR_VERSION.$GMIME_MICRO_VERSION GMIME_VERSION_INFO=`expr $GMIME_MAJOR_VERSION + $GMIME_MINOR_VERSION`:$GMIME_MICRO_VERSION:$GMIME_MINOR_VERSION diff --git a/gen-table.c b/gen-table.c new file mode 100644 index 00000000..672ef9a9 --- /dev/null +++ b/gen-table.c @@ -0,0 +1,183 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 Ximain, 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. + * + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +enum { + FALSE, + TRUE +}; + +#define CHARS_LWSP " \t\n\r" /* linear whitespace chars */ +#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" +#define CHARS_SPECIAL "()<>@,;:\\\".[]" +#define CHARS_CSPECIAL "()\\\r" /* not in comments */ +#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) */ + +static unsigned char gmime_special_table[256]; + +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, /* encoded word in phrase safe */ +}; + +#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0) +#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0) +#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0) +#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0) +#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0) +#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0) +#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0) +#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0) +#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0) +#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0) +#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0) + +/* code to rebuild the gmime_special_table */ +static void +header_remove_bits (unsigned short bit, unsigned char *vals) +{ + int i; + + for (i = 0; vals[i]; i++) + gmime_special_table[vals[i]] &= ~bit; +} + +static void +header_init_bits (unsigned short bit, unsigned short bitcopy, int remove, unsigned char *vals) +{ + int i, len = strlen (vals); + + if (!remove) { + for (i = 0; i < len; i++) { + gmime_special_table[vals[i]] |= bit; + } + if (bitcopy) { + for (i = 0; i < 256; i++) { + if (gmime_special_table[i] & bitcopy) + gmime_special_table[i] |= bit; + } + } + } else { + for (i = 0; i < 256; i++) + gmime_special_table[i] |= bit; + for (i = 0; i < len; i++) { + gmime_special_table[vals[i]] &= ~bit; + } + if (bitcopy) { + for (i = 0; i < 256; i++) { + if (gmime_special_table[i] & bitcopy) + gmime_special_table[i] &= ~bit; + } + } + } +} + +static void +header_decode_init (void) +{ + int i; + + for (i = 0; i < 256; i++) { + gmime_special_table[i] = 0; + if (i < 32) + gmime_special_table[i] |= IS_CTRL; + if ((i >= 33 && i <= 60) || (i >= 62 && i <= 126) || i == 32 || i == 9) + gmime_special_table[i] |= (IS_QPSAFE | IS_ESAFE); + if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')) + gmime_special_table[i] |= IS_PSAFE; + } + + gmime_special_table[127] |= IS_CTRL; + gmime_special_table[' '] |= IS_SPACE; + header_init_bits (IS_LWSP, 0, FALSE, CHARS_LWSP); + header_init_bits (IS_TSPECIAL, IS_CTRL, FALSE, CHARS_TSPECIAL); + header_init_bits (IS_SPECIAL, 0, FALSE, CHARS_SPECIAL); + header_init_bits (IS_DSPECIAL, 0, FALSE, CHARS_DSPECIAL); + header_remove_bits (IS_ESAFE, CHARS_ESPECIAL); + header_init_bits (IS_PSAFE, 0, FALSE, CHARS_PSPECIAL); +} + +int main (int argc, char **argv) +{ + int i; + + header_decode_init (); + + printf ("/* THIS FILE IS AUTOGENERATED: DO NOT EDIT! */\n\n"); + printf ("/**\n * To regenerate:\n * make gen-table\n"); + printf (" * ./gen-table > gmime-table-private.h\n **/\n\n"); + + /* print out the table */ + printf ("static unsigned short gmime_special_table[256] = {"); + for (i = 0; i < 256; i++) { + printf ("%s%3d%s", (i % 16) ? "" : "\n\t", + gmime_special_table[i], i != 255 ? "," : "\n"); + } + printf ("};\n\n"); + + /* print out the enum */ + printf ("enum {\n"); + printf ("\tIS_CTRL \t= 1 << 0,\n"); + printf ("\tIS_LWSP \t= 1 << 1,\n"); + printf ("\tIS_TSPECIAL\t= 1 << 2,\n"); + printf ("\tIS_SPECIAL \t= 1 << 3,\n"); + printf ("\tIS_SPACE \t= 1 << 4,\n"); + printf ("\tIS_DSPECIAL\t= 1 << 5,\n"); + printf ("\tIS_QPSAFE \t= 1 << 6,\n"); + printf ("\tIS_ESAFE \t= 1 << 7, /* encoded word safe */\n"); + printf ("\tIS_PPSAFE \t= 1 << 8 /* encode word in phrase safe */\n"); + printf ("};\n\n"); + + printf ("#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0)\n"); + printf ("#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0)\n"); + printf ("#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0)\n"); + printf ("#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0)\n"); + printf ("#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0)\n"); + printf ("#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0)\n"); + printf ("#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0)\n"); + printf ("#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0)\n"); + printf ("#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0)\n"); + printf ("#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0)\n"); + printf ("#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0)\n\n"); + + printf ("#define CHARS_LWSP \" \\t\\n\\r\" /* linear whitespace chars */\n"); + printf ("#define CHARS_TSPECIAL \"%s\"\n", CHARS_TSPECIAL); + printf ("#define CHARS_SPECIAL \"%s\"\n", CHARS_SPECIAL); + printf ("#define CHARS_CSPECIAL \"()\\\\\\r\" /* not in comments */\n"); + printf ("#define CHARS_DSPECIAL \"[]\\\\\\r \\t\" /* not in domains */\n"); + printf ("#define CHARS_ESPECIAL \"()<>@,;:\\\"/[]?.=\" /* encoded word specials (rfc2047 5.1) */\n"); + printf ("#define CHARS_PSPECIAL \"!*+-/\" /* encoded phrase specials (rfc2047 5.3) */\n"); + + return 0; +} diff --git a/gmime-content-type.c b/gmime-content-type.c index de4d76d2..a37b0974 100644 --- a/gmime-content-type.c +++ b/gmime-content-type.c @@ -49,10 +49,15 @@ g_mime_content_type_new (const gchar *type, const gchar *subtype) } else { if (type && *type) { mime_type->type = g_strdup (type); - if (!g_strcasecmp (type, "text")) + if (!g_strcasecmp (type, "text")) { mime_type->subtype = g_strdup ("plain"); - else + } else if (!g_strcasecmp (type, "multipart")) { + mime_type->subtype = g_strdup ("mixed"); + } else { + g_free (mime_type->type); + mime_type->type = g_strdup ("application"); mime_type->subtype = g_strdup ("octet-stream"); + } } else { mime_type->type = g_strdup ("application"); mime_type->subtype = g_strdup ("octet-stream"); diff --git a/gmime-message.c b/gmime-message.c index 726fdeeb..02f74eef 100644 --- a/gmime-message.c +++ b/gmime-message.c @@ -20,10 +20,14 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "gmime-message.h" #include "gmime-utils.h" #include "internet-address.h" -#include <config.h> + #include <string.h> #include <ctype.h> #include <locale.h> @@ -202,7 +206,7 @@ g_mime_message_add_recipient (GMimeMessage *message, gchar *type, const gchar *n InternetAddress *ia; GList *recipients; - ia = internet_address_new (name, address); + ia = internet_address_new_name (name, address); recipients = g_hash_table_lookup (message->header->recipients, type); g_hash_table_remove (message->header->recipients, type); @@ -227,9 +231,7 @@ g_mime_message_add_recipient (GMimeMessage *message, gchar *type, const gchar *n void g_mime_message_add_recipients_from_string (GMimeMessage *message, gchar *type, const gchar *string) { - InternetAddress *ia; - GList *recipients; - gchar *ptr, *eptr, *recipient; + GList *recipients, *addrlist; g_return_if_fail (message != NULL); g_return_if_fail (string != NULL); @@ -237,38 +239,9 @@ g_mime_message_add_recipients_from_string (GMimeMessage *message, gchar *type, c recipients = g_hash_table_lookup (message->header->recipients, type); g_hash_table_remove (message->header->recipients, type); - ptr = (gchar *) string; - - while (*ptr) { - gboolean in_quotes = FALSE; - - /* skip through leading whitespace */ - for ( ; *ptr && isspace (*ptr); ptr++); - - if (*ptr == '"') - in_quotes = TRUE; - - /* find the end of this address */ - eptr = ptr + 1; - while (*eptr) { - if (*eptr == '"' && *(eptr - 1) != '\\') - in_quotes = !in_quotes; - else if (*eptr == ',' && !in_quotes) - break; - - eptr++; - } - - recipient = g_strndup (ptr, (gint) (eptr - ptr)); - ia = internet_address_new_from_string (recipient); - g_free (recipient); - recipients = g_list_append (recipients, ia); - - if (*eptr) - ptr = eptr + 1; - else - break; - } + addrlist = internet_address_parse_string (string); + if (addrlist) + recipients = g_list_concat (recipients, addrlist); g_hash_table_insert (message->header->recipients, type, recipients); } @@ -523,7 +496,7 @@ create_header (GMimeMessage *message) GString *recip; recip = g_string_new ("To: "); - while (TRUE) { + while (recipients) { InternetAddress *ia; gchar *address; @@ -535,8 +508,6 @@ create_header (GMimeMessage *message) recipients = recipients->next; if (recipients) g_string_append (recip, ", "); - else - break; } g_string_append (recip, "\n"); buf = g_mime_utils_header_fold (recip->str); @@ -551,7 +522,7 @@ create_header (GMimeMessage *message) GString *recip; recip = g_string_new ("Cc: "); - while (TRUE) { + while (recipients) { InternetAddress *ia; gchar *address; @@ -563,8 +534,6 @@ create_header (GMimeMessage *message) recipients = recipients->next; if (recipients) g_string_append (recip, ", "); - else - break; } g_string_append (recip, "\n"); buf = g_mime_utils_header_fold (recip->str); diff --git a/gmime-parser.c b/gmime-parser.c index 98326ac0..f2c69a79 100644 --- a/gmime-parser.c +++ b/gmime-parser.c @@ -21,9 +21,13 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "gmime-parser.h" #include "gmime-utils.h" -#include <config.h> + #include <string.h> #include <ctype.h> @@ -114,51 +118,56 @@ g_strstrbound (const gchar *haystack, const gchar *needle, const gchar *end) * leave fp_in pointing at the message body that comes after the * headers. **/ -static GArray* +static GArray * get_header_block_from_file (FILE *fp) { - GArray * a; + GArray *a; gchar buf [GMIME_PARSER_MAX_LINE_WIDTH]; - - g_return_val_if_fail (fp!=NULL, NULL); - + + g_return_val_if_fail (fp != NULL, NULL); + a = g_array_new (TRUE, FALSE, 1); for (;;) { - gchar * pch = fgets (buf, sizeof(buf), fp); - if (pch == NULL) { /* eof reached before end-of-headers! */ + gchar *pch; + + pch = fgets (buf, sizeof (buf), fp); + if (pch == NULL) { + /* eof reached before end-of-headers! */ g_array_free (a, TRUE); a = NULL; break; } - if (!strcmp(buf,"\n")) /* empty line -- end of headers */ + + /* empty line -- end of headers */ + if (!strcmp (buf,"\n")) break; - + g_array_append_vals (a, buf, strlen(buf)); } - + return a; } /** * get_header_block: Get the header block from a message. - * @message the message text; that is, header + body. + * @message: the message text; that is, header + body. * * This will read all of the headers into an unparsed GArray. **/ -static GArray* +static GArray * get_header_block (const gchar *pch) { - GArray * a = NULL; - const gchar * header_end; - - g_return_val_if_fail (pch!=NULL, NULL); - + GArray *a = NULL; + const gchar *header_end; + + g_return_val_if_fail (pch != NULL, NULL); + header_end = strstr (pch, "\n\n"); if (header_end != NULL) { a = g_array_new (TRUE, FALSE, 1); - g_array_append_vals (a, pch, header_end-pch); + g_array_append_vals (a, pch, header_end - pch); } - + return a; } @@ -170,235 +179,234 @@ get_header_block (const gchar *pch) * * Returns a pointer to the end of a header. **/ -static const gchar* +static const gchar * find_header_end (const gchar *header, const gchar *boundary) { const gchar * eptr; - - g_return_val_if_fail (header!=NULL, NULL); - g_return_val_if_fail (boundary!=NULL, NULL); - - for (eptr=header; eptr<boundary; ++eptr) - if (*eptr=='\n' && !isblank(eptr[1])) + + g_return_val_if_fail (header != NULL, NULL); + g_return_val_if_fail (boundary != NULL, NULL); + + for (eptr = header; eptr < boundary; eptr++) + if (*eptr == '\n' && !isblank (eptr[1])) break; - - return (gchar*)eptr; + + return eptr; } /** - * parse_content_heaaders: picks the Content-* headers from a header block and extracts info from them. + * parse_content_heaaders: picks the Content-* headers from a header block + * and extracts info from them. * @headers: string containing the zero-terminated header block. * @headers_len: length of the header block. - * @mime_part: mime_part to populate with the information we get from the Content-* headers. - * @is_multipart: set to TRUE if the part is a multipart, FALSE otherwise - * @boundary: if this is a multipart, *boundary is set with the boundary string, which must be g_free()d by the caller. - * @end_boundary: if this is a multipart, *boundary is set with the end boundary string, which must be g_free()d by the caller. + * @mime_part: mime part to populate with the information we get from the Content-* headers. + * @is_multipart: set to TRUE if the part is a multipart, FALSE otherwise (to be set by function) + * @boundary: multipart boundary string (to be set by function) + * @end_boundary: multipart end boundary string (to be set by function) * * Parse a header block for content information. */ static void -parse_content_headers (const gchar *headers, - gint headers_len, - GMimePart *mime_part, - gboolean *is_multipart, - gchar **boundary, - gchar **end_boundary) +parse_content_headers (const gchar *headers, gint headers_len, + GMimePart *mime_part, gboolean *is_multipart, + gchar **boundary, gchar **end_boundary) { - const gchar * headers_ptr = headers; - const gchar * headers_end = headers + headers_len; - + const gchar *headers_ptr = headers; + const gchar *headers_end = headers + headers_len; + while (headers_ptr < headers_end) { const gint type = content_header (headers_ptr); - + switch (type) { - case CONTENT_DESCRIPTION: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *desc_enc = g_strndup (pch, (gint)(end - pch)); - gchar *desc_dec = g_mime_utils_8bit_header_decode (desc_enc); - g_strstrip (desc_dec); - g_mime_part_set_content_description (mime_part, desc_dec); - g_free (desc_enc); - g_free (desc_dec); - - headers_ptr = end + 1; - break; - } - case CONTENT_LOCATION: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar * loc = g_strndup (pch, (gint)(end - pch)); - g_strstrip (loc); - g_mime_part_set_content_location (mime_part, loc); - g_free (loc); - - headers_ptr = end + 1; - break; - } - case CONTENT_MD5: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *md5 = g_strndup (pch, (gint) (end - pch)); - g_strstrip (md5); - g_mime_part_set_content_md5 (mime_part, md5); - g_free (md5); - - headers_ptr = end + 1; - break; - } - case CONTENT_ID: { - const gchar* pch = headers_ptr + strlen(content_headers[type]); - const gchar* end = find_header_end (pch, headers_end); - gchar *id = g_strndup (pch, (gint) (end - pch)); - g_strstrip (id); - g_mime_part_set_content_id (mime_part, id); - g_free (id); - - headers_ptr = end + 1; - break; - } - case CONTENT_TRANSFER_ENCODING: { - const gchar* pch = headers_ptr + strlen(content_headers[type]); - const gchar* end = find_header_end (pch, headers_end); - gchar *encoding = g_strndup (pch, (gint) (end - pch)); - g_strstrip (encoding); - g_mime_part_set_encoding (mime_part, g_mime_part_encoding_from_string(encoding)); - g_free (encoding); + case CONTENT_DESCRIPTION: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *desc_enc = g_strndup (pch, (gint)(end - pch)); + gchar *desc_dec = g_mime_utils_8bit_header_decode (desc_enc); - headers_ptr = end + 1; - break; - } - case CONTENT_TYPE: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *type = g_strndup (pch, (gint) (end - pch)); - GMimeContentType *mime_type; - g_strstrip (type); - mime_type = g_mime_content_type_new_from_string (type); - g_free (type); - - *is_multipart = g_mime_content_type_is_type (mime_type, "multipart", "*"); - if (*is_multipart) { - const gchar *b; - b = g_mime_content_type_get_parameter (mime_type, "boundary"); - if (b != NULL) { - /* create our temp boundary vars */ - *boundary = g_strdup_printf ("--%s\n", b); - *end_boundary = g_strdup_printf ("--%s--\n", b); - } else { - g_warning ("Invalid MIME structure: boundary not found for multipart" - " - defaulting to text/plain."); - - /* let's continue onward as if this was not a multipart */ - g_mime_content_type_destroy (mime_type); - mime_type = g_mime_content_type_new ("text", "plain"); - is_multipart = FALSE; - } + g_strstrip (desc_dec); + g_mime_part_set_content_description (mime_part, desc_dec); + g_free (desc_enc); + g_free (desc_dec); + + headers_ptr = end + 1; + break; + } + case CONTENT_LOCATION: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar * loc = g_strndup (pch, (gint)(end - pch)); + + g_strstrip (loc); + g_mime_part_set_content_location (mime_part, loc); + g_free (loc); + + headers_ptr = end + 1; + break; + } + case CONTENT_MD5: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *md5 = g_strndup (pch, (gint) (end - pch)); + + g_strstrip (md5); + g_mime_part_set_content_md5 (mime_part, md5); + g_free (md5); + + headers_ptr = end + 1; + break; + } + case CONTENT_ID: { + const gchar* pch = headers_ptr + strlen (content_headers[type]); + const gchar* end = find_header_end (pch, headers_end); + gchar *id = g_strndup (pch, (gint) (end - pch)); + + g_strstrip (id); + g_mime_part_set_content_id (mime_part, id); + g_free (id); + + headers_ptr = end + 1; + break; + } + case CONTENT_TRANSFER_ENCODING: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *encoding = g_strndup (pch, (gint) (end - pch)); + + g_strstrip (encoding); + g_mime_part_set_encoding (mime_part, g_mime_part_encoding_from_string (encoding)); + g_free (encoding); + + headers_ptr = end + 1; + break; + } + case CONTENT_TYPE: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *type = g_strndup (pch, (gint) (end - pch)); + GMimeContentType *mime_type; + + g_strstrip (type); + mime_type = g_mime_content_type_new_from_string (type); + g_free (type); + + *is_multipart = g_mime_content_type_is_type (mime_type, "multipart", "*"); + if (*is_multipart) { + const gchar *b; + + b = g_mime_content_type_get_parameter (mime_type, "boundary"); + if (b != NULL) { + /* create our temp boundary vars */ + *boundary = g_strdup_printf ("--%s\n", b); + *end_boundary = g_strdup_printf ("--%s--\n", b); + } else { + g_warning ("Invalid MIME structure: boundary not found for multipart" + " - defaulting to text/plain."); + + /* let's continue onward as if this was not a multipart */ + g_mime_content_type_destroy (mime_type); + mime_type = g_mime_content_type_new ("text", "plain"); + is_multipart = FALSE; } - g_mime_part_set_content_type (mime_part, mime_type); - - headers_ptr = end + 1; - break; } - case CONTENT_DISPOSITION: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *disposition = g_strndup (pch, (gint) (end - pch)); - gchar *ptr; - gchar *disp; - - /* get content disposition part */ - for (ptr=disposition; *ptr && *ptr!=';'; ++ptr); /* find ; or \0 */ - disp = g_strndup (disposition, (gint)(ptr - disposition)); - g_strstrip (disp); - g_mime_part_set_content_disposition (mime_part, disp); - g_free (disp); - - /* parse the parameters, if any */ - while (*ptr==';') { - gchar *name; - gchar *value; - - /* get the param name */ - for (name = ptr + 1; *name && !isspace((int)*name); ++name); - for (ptr = name; *ptr && *ptr != '='; ++ptr); - name = g_strndup (name, (gint) (ptr - name)); - g_strstrip (name); - - /* convert param name to lowercase */ - g_strdown (name); + g_mime_part_set_content_type (mime_part, mime_type); + + headers_ptr = end + 1; + break; + } + case CONTENT_DISPOSITION: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *disposition = g_strndup (pch, (gint) (end - pch)); + gchar *ptr; + gchar *disp; + + /* get content disposition part */ + for (ptr = disposition; *ptr && *ptr != ';'; ptr++); /* find ; or \0 */ + disp = g_strndup (disposition, (gint)(ptr - disposition)); + g_strstrip (disp); + g_mime_part_set_content_disposition (mime_part, disp); + g_free (disp); + + /* parse the parameters, if any */ + while (*ptr == ';') { + gchar *name; + gchar *value; + + /* get the param name */ + for (name = ptr + 1; *name && !isspace ((int)*name); name++); + for (ptr = name; *ptr && *ptr != '='; ptr++); + name = g_strndup (name, (gint) (ptr - name)); + g_strstrip (name); + + /* convert param name to lowercase */ + g_strdown (name); - /* skip any whitespace */ - for (value = ptr + 1; *value && isspace((int)*value); value++); + /* skip any whitespace */ + for (value = ptr + 1; *value && isspace ((int)*value); value++); - if (*value == '"') /* value is in quotes */ - { - value++; - for (ptr = value; *ptr; ptr++) - if (*ptr == '"' && *(ptr - 1) != '\\') - break; - value = g_strndup (value, (gint) (ptr - value)); - g_strstrip (value); - g_mime_utils_unquote_string (value); + if (*value == '"') { + /* value is in quotes */ + value++; + for (ptr = value; *ptr; ptr++) + if (*ptr == '"' && *(ptr - 1) != '\\') + break; + value = g_strndup (value, (gint) (ptr - value)); + g_strstrip (value); + g_mime_utils_unquote_string (value); - for ( ; *ptr && *ptr != ';'; ptr++); - } - else /* value is not in quotes */ - { - for (ptr = value; *ptr && *ptr != ';'; ptr++); - value = g_strndup (value, (gint) (ptr - value)); - g_strstrip (value); - } - - g_mime_part_add_content_disposition_parameter (mime_part, name, value); - - g_free (name); - g_free (value); + for ( ; *ptr && *ptr != ';'; ptr++); + } else { + /* value is not in quotes */ + for (ptr = value; *ptr && *ptr != ';'; ptr++); + value = g_strndup (value, (gint) (ptr - value)); + g_strstrip (value); } - - g_free (disposition); - headers_ptr = end + 1; - break; - } - default: { /* ignore this header */ - const gchar *pch = headers_ptr; - const gchar *end = find_header_end (pch, headers_end); - headers_ptr = end + 1; - break; + g_mime_part_add_content_disposition_parameter (mime_part, name, value); + + g_free (name); + g_free (value); } + + g_free (disposition); + + headers_ptr = end + 1; + break; + } + default: { /* ignore this header */ + const gchar *pch = headers_ptr; + const gchar *end = find_header_end (pch, headers_end); + + headers_ptr = end + 1; + break; + } } } } -typedef enum -{ +typedef enum { PARSER_EOF, PARSER_BOUNDARY, PARSER_END_BOUNDARY, PARSER_LINE -} -ParserState; +} ParserState; static ParserState -get_next_line (gchar *buf, - guint buf_len, - FILE *fp, - const gchar *boundary, - const gchar *end_boundary) +get_next_line (gchar *buf, guint buf_len, FILE *fp, const gchar *boundary, const gchar *end_boundary) { ParserState state; - + *buf = '\0'; - if (fgets(buf, buf_len, fp) == NULL) + if (fgets (buf, buf_len, fp) == NULL) state = PARSER_EOF; - else if (boundary!=NULL && !strcmp (buf, boundary)) + else if (boundary != NULL && !strcmp (buf, boundary)) state = PARSER_BOUNDARY; - else if (end_boundary!=NULL && !strcmp (buf, end_boundary)) + else if (end_boundary != NULL && !strcmp (buf, end_boundary)) state = PARSER_END_BOUNDARY; else state = PARSER_LINE; - + return state; } @@ -420,49 +428,47 @@ g_mime_parser_construct_part_from_file (const gchar *headers, gchar *end_boundary; gboolean is_multipart; GMimePart *mime_part; - - g_return_val_if_fail (headers!=NULL, NULL); - g_return_val_if_fail (headers_len>0, NULL); - g_return_val_if_fail (fp!=NULL, NULL); - g_return_val_if_fail (setme_state!=NULL, NULL); - - /** - *** Headers - **/ - + + g_return_val_if_fail (headers != NULL, NULL); + g_return_val_if_fail (headers_len > 0, NULL); + g_return_val_if_fail (fp != NULL, NULL); + g_return_val_if_fail (setme_state != NULL, NULL); + + /* Headers */ boundary = NULL; end_boundary = NULL; is_multipart = FALSE; mime_part = g_mime_part_new (); parse_content_headers (headers, headers_len, mime_part, &is_multipart, &boundary, &end_boundary); - - /** - *** Body - **/ - + + /* Body */ if (is_multipart && boundary!=NULL && end_boundary!=NULL) { /* is a multipart */ /* get all the subparts */ gchar buf[GMIME_PARSER_MAX_LINE_WIDTH]; - + for (;;) { /* get the next line, we're looking for the beginning of a subpart */ - ParserState ps = get_next_line (buf, sizeof(buf), fp, parent_boundary, parent_end_boundary); + ParserState ps = get_next_line (buf, sizeof (buf), fp, parent_boundary, + parent_end_boundary); if (ps != PARSER_LINE) { *setme_state = ps; break; } - + /* is the beginning of a subpart? */ if (strcmp (buf, boundary)) continue; - + /* add subparts as long as we keep getting boundaries */ for (;;) { GArray *h = get_header_block_from_file (fp); if (h != NULL) { ParserState ps = 0; - GMimePart * part = g_mime_parser_construct_part_from_file ( - h->data, h->len, fp, boundary, end_boundary, &ps); + GMimePart *part; + + part = g_mime_parser_construct_part_from_file (h->data, h->len, + fp, boundary, + end_boundary, &ps); g_array_free (h, TRUE); if (part != NULL) g_mime_part_add_subpart (mime_part, part); @@ -471,36 +477,38 @@ g_mime_parser_construct_part_from_file (const gchar *headers, } } } - } - else { /* not a multipart */ + } else { + /* not a multipart */ GMimePartEncodingType encoding = g_mime_part_get_encoding (mime_part); - GArray * a = g_array_new (TRUE, FALSE, 1); + GArray *a = g_array_new (TRUE, FALSE, 1); gchar buf [GMIME_PARSER_MAX_LINE_WIDTH]; - + /* keep reading lines until we reach a boundary or EOF, we're populating a part */ for (;;) { - ParserState ps = get_next_line (buf, sizeof(buf), fp, parent_boundary, parent_end_boundary); - if (ps == PARSER_LINE) - g_array_append_vals (a, buf, strlen(buf)); - else { + ParserState ps = get_next_line (buf, sizeof (buf), fp, + parent_boundary, + parent_end_boundary); + if (ps == PARSER_LINE) { + g_array_append_vals (a, buf, strlen (buf)); + } else { *setme_state = ps; break; } - } - + /* trim off excess trailing \n's */ - while (a->len>2 && a->data[a->len-1]=='\n' && a->data[a->len-2]=='\n') - g_array_set_size (a, a->len-1); - + while (a->len > 2 && a->data[a->len-1] == '\n' && a->data[a->len - 2] == '\n') + g_array_set_size (a, a->len - 1); + if (a->len > 0) g_mime_part_set_pre_encoded_content (mime_part, a->data, a->len, encoding); - + g_array_free (a, TRUE); } - + g_free (boundary); g_free (end_boundary); + return mime_part; } @@ -523,33 +531,27 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) gboolean is_multipart; const gchar *inptr; const gchar *inend = in + inlen; - - g_return_val_if_fail (in!=NULL, NULL); - g_return_val_if_fail (inlen!=0, NULL); - /** - *** Headers - **/ - + g_return_val_if_fail (in != NULL, NULL); + g_return_val_if_fail (inlen != 0, NULL); + + /* Headers */ headers = get_header_block (in); mime_part = g_mime_part_new (); is_multipart = FALSE; boundary = NULL; end_boundary = NULL; - parse_content_headers (headers->data, headers->len, mime_part, &is_multipart, &boundary, &end_boundary); - - /** - *** Body - **/ - + parse_content_headers (headers->data, headers->len, mime_part, + &is_multipart, &boundary, &end_boundary); + + /* Body */ inptr = in + headers->len; - - if (is_multipart && boundary && end_boundary) - { + + if (is_multipart && boundary && end_boundary) { /* get all the subparts */ - GMimePart * subpart; - const gchar * part_begin; - const gchar * part_end; + GMimePart *subpart; + const gchar *part_begin; + const gchar *part_end; part_begin = g_strstrbound (inptr, boundary, inend); while (part_begin && part_begin < inend) { @@ -560,7 +562,8 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) /* find the end of this part */ part_end = g_strstrbound (part_begin + strlen (boundary), boundary, inend); if (!part_end || part_end >= inend) { - part_end = g_strstrbound (part_begin + strlen (boundary), end_boundary, inend); + part_end = g_strstrbound (part_begin + strlen (boundary), + end_boundary, inend); if (!part_end || part_end >= inend) part_end = inend; } @@ -578,12 +581,12 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) g_free (end_boundary); } else { GMimePartEncodingType encoding; - const gchar * content = NULL; + const gchar *content = NULL; guint len = 0; /* from here to the end is the content */ if (inptr < inend) { - for (inptr++; inptr < inend && isspace((int)*inptr); inptr++); + for (inptr++; inptr < inend && isspace ((int)*inptr); inptr++); len = inend - inptr; content = inptr; @@ -600,8 +603,9 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) if (len > 0) g_mime_part_set_pre_encoded_content (mime_part, content, len, encoding); } - + g_array_free (headers, TRUE); + return mime_part; } @@ -734,21 +738,20 @@ construct_headers (GMimeMessage *message, const gchar *headers, gint inlen, gboo GMimeMessage * g_mime_parser_construct_message (const gchar *data, gboolean save_extra_headers) { - GMimeMessage * message = NULL; - GArray * headers; + GMimeMessage *message = NULL; + GArray *headers; g_return_val_if_fail (data != NULL, NULL); - + headers = get_header_block (data); - if (headers != NULL) - { + if (headers != NULL) { GMimePart * part; - + message = g_mime_message_new (); construct_headers (message, headers->data, headers->len, save_extra_headers); part = g_mime_parser_construct_part (data, strlen(data)); g_mime_message_set_mime_part (message, part); - + g_array_free (headers, TRUE); } @@ -765,24 +768,24 @@ g_mime_parser_construct_message (const gchar *data, gboolean save_extra_headers) GMimeMessage * g_mime_parser_construct_message_from_file (FILE *fp, gboolean save_extra_headers) { - GMimeMessage * message = NULL; - GArray * headers; - - g_return_val_if_fail (fp!=NULL, NULL); - + GMimeMessage *message = NULL; + GArray *headers; + + g_return_val_if_fail (fp != NULL, NULL); + headers = get_header_block_from_file (fp); - if (headers != NULL) - { - GMimePart * part; + if (headers != NULL) { + GMimePart *part; ParserState state = -1; - + message = g_mime_message_new (); construct_headers (message, headers->data, headers->len, save_extra_headers); - part = g_mime_parser_construct_part_from_file (headers->data, headers->len, fp, NULL, NULL, &state); + part = g_mime_parser_construct_part_from_file (headers->data, headers->len, fp, + NULL, NULL, &state); g_mime_message_set_mime_part (message, part); if (state != PARSER_EOF) g_warning ("Didn't reach end of file - parser error?"); - + g_array_free (headers, TRUE); } diff --git a/gmime-table-private.h b/gmime-table-private.h new file mode 100644 index 00000000..7350960a --- /dev/null +++ b/gmime-table-private.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 Ximain, 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. + * + */ + +static unsigned short gmime_special_table[256] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5,231, 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, 4, 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, 64, + 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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +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, /* encoded word in phrase safe */ +}; + +#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0) +#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0) +#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0) +#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0) +#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0) +#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0) +#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0) +#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0) +#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0) +#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0) +#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0) + +#ifndef HAVE_ISBLANK +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif /* HAVE_ISBLANK */ + +#define CHARS_LWSP " \t\n\r" /* linear whitespace chars */ +#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" +#define CHARS_SPECIAL "()<>@,;:\\\".[]" +#define CHARS_CSPECIAL "()\\\r" /* not in comments */ +#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) */ diff --git a/gmime-utils.c b/gmime-utils.c index 540ff670..f0c062bd 100644 --- a/gmime-utils.c +++ b/gmime-utils.c @@ -21,9 +21,14 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "gmime-utils.h" +#include "gmime-table-private.h" #include "gmime-part.h" -#include <config.h> + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -35,6 +40,10 @@ #define GMIME_FOLD_LEN 76 +#ifndef HAVE_ISBLANK +#define isblank(c) (c == ' ' || c == '\t') +#endif + static char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -43,25 +52,6 @@ static unsigned char tohex[16] = { '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; -static unsigned short gmime_special_table[256] = { - 5, 5, 5, 5, 5, 5, 5, 5, 5,231, 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, 4, 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, 64, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - static unsigned char gmime_base64_rank[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, @@ -101,109 +91,6 @@ static unsigned char gmime_uu_rank[256] = { }; -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, /* encoded word in phrase safe */ -}; - -#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0) -#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0) -#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0) -#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0) -#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0) -#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0) -#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0) -#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0) -#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0) -#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0) -#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0) - -#ifndef HAVE_ISBLANK -#define isblank(c) ((c) == ' ' || (c) == '\t') -#endif /* HAVE_ISBLANK */ - -#define CHARS_LWSP " \t\n\r" /* linear whitespace chars */ -#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" -#define CHARS_SPECIAL "()<>@,;:\\\".[]" -#define CHARS_CSPECIAL "()\\\r" /* not in comments */ -#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) */ - -#ifdef BUILD_TABLE -/* code to rebuild the gmime_special_table */ -static void -header_remove_bits (gushort bit, guchar *vals) -{ - gint i; - - for (i = 0; vals[i]; i++) - gmime_special_table[vals[i]] &= ~bit; -} - -static void -header_init_bits (gushort bit, gushort bitcopy, gboolean remove, guchar *vals) -{ - gint i, len = strlen (vals); - - if (!remove) { - for (i = 0; i < len; i++) { - gmime_special_table[vals[i]] |= bit; - } - if (bitcopy) { - for (i = 0; i < 256; i++) { - if (gmime_special_table[i] & bitcopy) - gmime_special_table[i] |= bit; - } - } - } else { - for (i = 0; i < 256; i++) - gmime_special_table[i] |= bit; - for (i = 0; i < len; i++) { - gmime_special_table[vals[i]] &= ~bit; - } - if (bitcopy) { - for (i = 0; i < 256; i++) { - if (gmime_special_table[i] & bitcopy) - gmime_special_table[i] &= ~bit; - } - } - } -} - -static void -header_decode_init (void) -{ - gint i; - - for (i = 0; i < 256; i++) { - gmime_special_table[i] = 0; - if (i < 32) - gmime_special_table[i] |= IS_CTRL; - if ((i >= 33 && i <= 60) || (i >= 62 && i <= 126) || i == 32 || i == 9) - gmime_special_table[i] |= (IS_QPSAFE | IS_ESAFE); - if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')) - gmime_special_table[i] |= IS_PSAFE; - } - - gmime_special_table[127] |= IS_CTRL; - gmime_special_table[' '] |= IS_SPACE; - header_init_bits (IS_LWSP, 0, FALSE, CHARS_LWSP); - header_init_bits (IS_TSPECIAL, IS_CTRL, FALSE, CHARS_TSPECIAL); - header_init_bits (IS_SPECIAL, 0, FALSE, CHARS_SPECIAL); - header_init_bits (IS_DSPECIAL, 0, FALSE, CHARS_DSPECIAL); - header_remove_bits (IS_ESAFE, CHARS_ESPECIAL); - header_init_bits (IS_PSAFE, 0, FALSE, CHARS_PSPECIAL); -} -#endif /* BUILD_TABLE */ - /* hrm, is there a library for this shit? */ static struct { char *name; @@ -654,6 +541,28 @@ g_mime_utils_header_printf (const gchar *format, ...) return ret; } +static gboolean +need_quotes (const char *string) +{ + gboolean quoted = FALSE; + const char *inptr; + + inptr = string; + + while (*inptr) { + if (*inptr == '\\') + inptr++; + else if (*inptr == '"') + quoted = !quoted; + else if (!quoted && is_tspecial (*inptr)) + return TRUE; + + if (*inptr) + inptr++; + } + + return FALSE; +} /** * g_mime_utils_quote_string: Quote a string. @@ -667,18 +576,16 @@ g_mime_utils_header_printf (const gchar *format, ...) gchar * g_mime_utils_quote_string (const gchar *string) { - GString *out; + gboolean quote; + const gchar *c; gchar *qstring; - guchar *c; - gboolean quote = FALSE; + GString *out; out = g_string_new (""); + quote = need_quotes (string); - for (c = (guchar *) string; *c; c++) { - if (is_tspecial (*c)) - quote = TRUE; - - if (*c == '"' || *c == '\\') + for (c = string; *c; c++) { + if ((*c == '"' && quote) || *c == '\\') g_string_append_c (out, '\\'); g_string_append_c (out, *c); diff --git a/gmime/gen-table.c b/gmime/gen-table.c new file mode 100644 index 00000000..672ef9a9 --- /dev/null +++ b/gmime/gen-table.c @@ -0,0 +1,183 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 Ximain, 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. + * + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +enum { + FALSE, + TRUE +}; + +#define CHARS_LWSP " \t\n\r" /* linear whitespace chars */ +#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" +#define CHARS_SPECIAL "()<>@,;:\\\".[]" +#define CHARS_CSPECIAL "()\\\r" /* not in comments */ +#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) */ + +static unsigned char gmime_special_table[256]; + +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, /* encoded word in phrase safe */ +}; + +#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0) +#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0) +#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0) +#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0) +#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0) +#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0) +#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0) +#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0) +#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0) +#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0) +#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0) + +/* code to rebuild the gmime_special_table */ +static void +header_remove_bits (unsigned short bit, unsigned char *vals) +{ + int i; + + for (i = 0; vals[i]; i++) + gmime_special_table[vals[i]] &= ~bit; +} + +static void +header_init_bits (unsigned short bit, unsigned short bitcopy, int remove, unsigned char *vals) +{ + int i, len = strlen (vals); + + if (!remove) { + for (i = 0; i < len; i++) { + gmime_special_table[vals[i]] |= bit; + } + if (bitcopy) { + for (i = 0; i < 256; i++) { + if (gmime_special_table[i] & bitcopy) + gmime_special_table[i] |= bit; + } + } + } else { + for (i = 0; i < 256; i++) + gmime_special_table[i] |= bit; + for (i = 0; i < len; i++) { + gmime_special_table[vals[i]] &= ~bit; + } + if (bitcopy) { + for (i = 0; i < 256; i++) { + if (gmime_special_table[i] & bitcopy) + gmime_special_table[i] &= ~bit; + } + } + } +} + +static void +header_decode_init (void) +{ + int i; + + for (i = 0; i < 256; i++) { + gmime_special_table[i] = 0; + if (i < 32) + gmime_special_table[i] |= IS_CTRL; + if ((i >= 33 && i <= 60) || (i >= 62 && i <= 126) || i == 32 || i == 9) + gmime_special_table[i] |= (IS_QPSAFE | IS_ESAFE); + if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')) + gmime_special_table[i] |= IS_PSAFE; + } + + gmime_special_table[127] |= IS_CTRL; + gmime_special_table[' '] |= IS_SPACE; + header_init_bits (IS_LWSP, 0, FALSE, CHARS_LWSP); + header_init_bits (IS_TSPECIAL, IS_CTRL, FALSE, CHARS_TSPECIAL); + header_init_bits (IS_SPECIAL, 0, FALSE, CHARS_SPECIAL); + header_init_bits (IS_DSPECIAL, 0, FALSE, CHARS_DSPECIAL); + header_remove_bits (IS_ESAFE, CHARS_ESPECIAL); + header_init_bits (IS_PSAFE, 0, FALSE, CHARS_PSPECIAL); +} + +int main (int argc, char **argv) +{ + int i; + + header_decode_init (); + + printf ("/* THIS FILE IS AUTOGENERATED: DO NOT EDIT! */\n\n"); + printf ("/**\n * To regenerate:\n * make gen-table\n"); + printf (" * ./gen-table > gmime-table-private.h\n **/\n\n"); + + /* print out the table */ + printf ("static unsigned short gmime_special_table[256] = {"); + for (i = 0; i < 256; i++) { + printf ("%s%3d%s", (i % 16) ? "" : "\n\t", + gmime_special_table[i], i != 255 ? "," : "\n"); + } + printf ("};\n\n"); + + /* print out the enum */ + printf ("enum {\n"); + printf ("\tIS_CTRL \t= 1 << 0,\n"); + printf ("\tIS_LWSP \t= 1 << 1,\n"); + printf ("\tIS_TSPECIAL\t= 1 << 2,\n"); + printf ("\tIS_SPECIAL \t= 1 << 3,\n"); + printf ("\tIS_SPACE \t= 1 << 4,\n"); + printf ("\tIS_DSPECIAL\t= 1 << 5,\n"); + printf ("\tIS_QPSAFE \t= 1 << 6,\n"); + printf ("\tIS_ESAFE \t= 1 << 7, /* encoded word safe */\n"); + printf ("\tIS_PPSAFE \t= 1 << 8 /* encode word in phrase safe */\n"); + printf ("};\n\n"); + + printf ("#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0)\n"); + printf ("#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0)\n"); + printf ("#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0)\n"); + printf ("#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0)\n"); + printf ("#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0)\n"); + printf ("#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0)\n"); + printf ("#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0)\n"); + printf ("#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0)\n"); + printf ("#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0)\n"); + printf ("#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0)\n"); + printf ("#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0)\n\n"); + + printf ("#define CHARS_LWSP \" \\t\\n\\r\" /* linear whitespace chars */\n"); + printf ("#define CHARS_TSPECIAL \"%s\"\n", CHARS_TSPECIAL); + printf ("#define CHARS_SPECIAL \"%s\"\n", CHARS_SPECIAL); + printf ("#define CHARS_CSPECIAL \"()\\\\\\r\" /* not in comments */\n"); + printf ("#define CHARS_DSPECIAL \"[]\\\\\\r \\t\" /* not in domains */\n"); + printf ("#define CHARS_ESPECIAL \"()<>@,;:\\\"/[]?.=\" /* encoded word specials (rfc2047 5.1) */\n"); + printf ("#define CHARS_PSPECIAL \"!*+-/\" /* encoded phrase specials (rfc2047 5.3) */\n"); + + return 0; +} diff --git a/gmime/gmime-content-type.c b/gmime/gmime-content-type.c index de4d76d2..a37b0974 100644 --- a/gmime/gmime-content-type.c +++ b/gmime/gmime-content-type.c @@ -49,10 +49,15 @@ g_mime_content_type_new (const gchar *type, const gchar *subtype) } else { if (type && *type) { mime_type->type = g_strdup (type); - if (!g_strcasecmp (type, "text")) + if (!g_strcasecmp (type, "text")) { mime_type->subtype = g_strdup ("plain"); - else + } else if (!g_strcasecmp (type, "multipart")) { + mime_type->subtype = g_strdup ("mixed"); + } else { + g_free (mime_type->type); + mime_type->type = g_strdup ("application"); mime_type->subtype = g_strdup ("octet-stream"); + } } else { mime_type->type = g_strdup ("application"); mime_type->subtype = g_strdup ("octet-stream"); diff --git a/gmime/gmime-message.c b/gmime/gmime-message.c index 726fdeeb..02f74eef 100644 --- a/gmime/gmime-message.c +++ b/gmime/gmime-message.c @@ -20,10 +20,14 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "gmime-message.h" #include "gmime-utils.h" #include "internet-address.h" -#include <config.h> + #include <string.h> #include <ctype.h> #include <locale.h> @@ -202,7 +206,7 @@ g_mime_message_add_recipient (GMimeMessage *message, gchar *type, const gchar *n InternetAddress *ia; GList *recipients; - ia = internet_address_new (name, address); + ia = internet_address_new_name (name, address); recipients = g_hash_table_lookup (message->header->recipients, type); g_hash_table_remove (message->header->recipients, type); @@ -227,9 +231,7 @@ g_mime_message_add_recipient (GMimeMessage *message, gchar *type, const gchar *n void g_mime_message_add_recipients_from_string (GMimeMessage *message, gchar *type, const gchar *string) { - InternetAddress *ia; - GList *recipients; - gchar *ptr, *eptr, *recipient; + GList *recipients, *addrlist; g_return_if_fail (message != NULL); g_return_if_fail (string != NULL); @@ -237,38 +239,9 @@ g_mime_message_add_recipients_from_string (GMimeMessage *message, gchar *type, c recipients = g_hash_table_lookup (message->header->recipients, type); g_hash_table_remove (message->header->recipients, type); - ptr = (gchar *) string; - - while (*ptr) { - gboolean in_quotes = FALSE; - - /* skip through leading whitespace */ - for ( ; *ptr && isspace (*ptr); ptr++); - - if (*ptr == '"') - in_quotes = TRUE; - - /* find the end of this address */ - eptr = ptr + 1; - while (*eptr) { - if (*eptr == '"' && *(eptr - 1) != '\\') - in_quotes = !in_quotes; - else if (*eptr == ',' && !in_quotes) - break; - - eptr++; - } - - recipient = g_strndup (ptr, (gint) (eptr - ptr)); - ia = internet_address_new_from_string (recipient); - g_free (recipient); - recipients = g_list_append (recipients, ia); - - if (*eptr) - ptr = eptr + 1; - else - break; - } + addrlist = internet_address_parse_string (string); + if (addrlist) + recipients = g_list_concat (recipients, addrlist); g_hash_table_insert (message->header->recipients, type, recipients); } @@ -523,7 +496,7 @@ create_header (GMimeMessage *message) GString *recip; recip = g_string_new ("To: "); - while (TRUE) { + while (recipients) { InternetAddress *ia; gchar *address; @@ -535,8 +508,6 @@ create_header (GMimeMessage *message) recipients = recipients->next; if (recipients) g_string_append (recip, ", "); - else - break; } g_string_append (recip, "\n"); buf = g_mime_utils_header_fold (recip->str); @@ -551,7 +522,7 @@ create_header (GMimeMessage *message) GString *recip; recip = g_string_new ("Cc: "); - while (TRUE) { + while (recipients) { InternetAddress *ia; gchar *address; @@ -563,8 +534,6 @@ create_header (GMimeMessage *message) recipients = recipients->next; if (recipients) g_string_append (recip, ", "); - else - break; } g_string_append (recip, "\n"); buf = g_mime_utils_header_fold (recip->str); diff --git a/gmime/gmime-parser.c b/gmime/gmime-parser.c index 98326ac0..f2c69a79 100644 --- a/gmime/gmime-parser.c +++ b/gmime/gmime-parser.c @@ -21,9 +21,13 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "gmime-parser.h" #include "gmime-utils.h" -#include <config.h> + #include <string.h> #include <ctype.h> @@ -114,51 +118,56 @@ g_strstrbound (const gchar *haystack, const gchar *needle, const gchar *end) * leave fp_in pointing at the message body that comes after the * headers. **/ -static GArray* +static GArray * get_header_block_from_file (FILE *fp) { - GArray * a; + GArray *a; gchar buf [GMIME_PARSER_MAX_LINE_WIDTH]; - - g_return_val_if_fail (fp!=NULL, NULL); - + + g_return_val_if_fail (fp != NULL, NULL); + a = g_array_new (TRUE, FALSE, 1); for (;;) { - gchar * pch = fgets (buf, sizeof(buf), fp); - if (pch == NULL) { /* eof reached before end-of-headers! */ + gchar *pch; + + pch = fgets (buf, sizeof (buf), fp); + if (pch == NULL) { + /* eof reached before end-of-headers! */ g_array_free (a, TRUE); a = NULL; break; } - if (!strcmp(buf,"\n")) /* empty line -- end of headers */ + + /* empty line -- end of headers */ + if (!strcmp (buf,"\n")) break; - + g_array_append_vals (a, buf, strlen(buf)); } - + return a; } /** * get_header_block: Get the header block from a message. - * @message the message text; that is, header + body. + * @message: the message text; that is, header + body. * * This will read all of the headers into an unparsed GArray. **/ -static GArray* +static GArray * get_header_block (const gchar *pch) { - GArray * a = NULL; - const gchar * header_end; - - g_return_val_if_fail (pch!=NULL, NULL); - + GArray *a = NULL; + const gchar *header_end; + + g_return_val_if_fail (pch != NULL, NULL); + header_end = strstr (pch, "\n\n"); if (header_end != NULL) { a = g_array_new (TRUE, FALSE, 1); - g_array_append_vals (a, pch, header_end-pch); + g_array_append_vals (a, pch, header_end - pch); } - + return a; } @@ -170,235 +179,234 @@ get_header_block (const gchar *pch) * * Returns a pointer to the end of a header. **/ -static const gchar* +static const gchar * find_header_end (const gchar *header, const gchar *boundary) { const gchar * eptr; - - g_return_val_if_fail (header!=NULL, NULL); - g_return_val_if_fail (boundary!=NULL, NULL); - - for (eptr=header; eptr<boundary; ++eptr) - if (*eptr=='\n' && !isblank(eptr[1])) + + g_return_val_if_fail (header != NULL, NULL); + g_return_val_if_fail (boundary != NULL, NULL); + + for (eptr = header; eptr < boundary; eptr++) + if (*eptr == '\n' && !isblank (eptr[1])) break; - - return (gchar*)eptr; + + return eptr; } /** - * parse_content_heaaders: picks the Content-* headers from a header block and extracts info from them. + * parse_content_heaaders: picks the Content-* headers from a header block + * and extracts info from them. * @headers: string containing the zero-terminated header block. * @headers_len: length of the header block. - * @mime_part: mime_part to populate with the information we get from the Content-* headers. - * @is_multipart: set to TRUE if the part is a multipart, FALSE otherwise - * @boundary: if this is a multipart, *boundary is set with the boundary string, which must be g_free()d by the caller. - * @end_boundary: if this is a multipart, *boundary is set with the end boundary string, which must be g_free()d by the caller. + * @mime_part: mime part to populate with the information we get from the Content-* headers. + * @is_multipart: set to TRUE if the part is a multipart, FALSE otherwise (to be set by function) + * @boundary: multipart boundary string (to be set by function) + * @end_boundary: multipart end boundary string (to be set by function) * * Parse a header block for content information. */ static void -parse_content_headers (const gchar *headers, - gint headers_len, - GMimePart *mime_part, - gboolean *is_multipart, - gchar **boundary, - gchar **end_boundary) +parse_content_headers (const gchar *headers, gint headers_len, + GMimePart *mime_part, gboolean *is_multipart, + gchar **boundary, gchar **end_boundary) { - const gchar * headers_ptr = headers; - const gchar * headers_end = headers + headers_len; - + const gchar *headers_ptr = headers; + const gchar *headers_end = headers + headers_len; + while (headers_ptr < headers_end) { const gint type = content_header (headers_ptr); - + switch (type) { - case CONTENT_DESCRIPTION: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *desc_enc = g_strndup (pch, (gint)(end - pch)); - gchar *desc_dec = g_mime_utils_8bit_header_decode (desc_enc); - g_strstrip (desc_dec); - g_mime_part_set_content_description (mime_part, desc_dec); - g_free (desc_enc); - g_free (desc_dec); - - headers_ptr = end + 1; - break; - } - case CONTENT_LOCATION: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar * loc = g_strndup (pch, (gint)(end - pch)); - g_strstrip (loc); - g_mime_part_set_content_location (mime_part, loc); - g_free (loc); - - headers_ptr = end + 1; - break; - } - case CONTENT_MD5: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *md5 = g_strndup (pch, (gint) (end - pch)); - g_strstrip (md5); - g_mime_part_set_content_md5 (mime_part, md5); - g_free (md5); - - headers_ptr = end + 1; - break; - } - case CONTENT_ID: { - const gchar* pch = headers_ptr + strlen(content_headers[type]); - const gchar* end = find_header_end (pch, headers_end); - gchar *id = g_strndup (pch, (gint) (end - pch)); - g_strstrip (id); - g_mime_part_set_content_id (mime_part, id); - g_free (id); - - headers_ptr = end + 1; - break; - } - case CONTENT_TRANSFER_ENCODING: { - const gchar* pch = headers_ptr + strlen(content_headers[type]); - const gchar* end = find_header_end (pch, headers_end); - gchar *encoding = g_strndup (pch, (gint) (end - pch)); - g_strstrip (encoding); - g_mime_part_set_encoding (mime_part, g_mime_part_encoding_from_string(encoding)); - g_free (encoding); + case CONTENT_DESCRIPTION: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *desc_enc = g_strndup (pch, (gint)(end - pch)); + gchar *desc_dec = g_mime_utils_8bit_header_decode (desc_enc); - headers_ptr = end + 1; - break; - } - case CONTENT_TYPE: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *type = g_strndup (pch, (gint) (end - pch)); - GMimeContentType *mime_type; - g_strstrip (type); - mime_type = g_mime_content_type_new_from_string (type); - g_free (type); - - *is_multipart = g_mime_content_type_is_type (mime_type, "multipart", "*"); - if (*is_multipart) { - const gchar *b; - b = g_mime_content_type_get_parameter (mime_type, "boundary"); - if (b != NULL) { - /* create our temp boundary vars */ - *boundary = g_strdup_printf ("--%s\n", b); - *end_boundary = g_strdup_printf ("--%s--\n", b); - } else { - g_warning ("Invalid MIME structure: boundary not found for multipart" - " - defaulting to text/plain."); - - /* let's continue onward as if this was not a multipart */ - g_mime_content_type_destroy (mime_type); - mime_type = g_mime_content_type_new ("text", "plain"); - is_multipart = FALSE; - } + g_strstrip (desc_dec); + g_mime_part_set_content_description (mime_part, desc_dec); + g_free (desc_enc); + g_free (desc_dec); + + headers_ptr = end + 1; + break; + } + case CONTENT_LOCATION: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar * loc = g_strndup (pch, (gint)(end - pch)); + + g_strstrip (loc); + g_mime_part_set_content_location (mime_part, loc); + g_free (loc); + + headers_ptr = end + 1; + break; + } + case CONTENT_MD5: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *md5 = g_strndup (pch, (gint) (end - pch)); + + g_strstrip (md5); + g_mime_part_set_content_md5 (mime_part, md5); + g_free (md5); + + headers_ptr = end + 1; + break; + } + case CONTENT_ID: { + const gchar* pch = headers_ptr + strlen (content_headers[type]); + const gchar* end = find_header_end (pch, headers_end); + gchar *id = g_strndup (pch, (gint) (end - pch)); + + g_strstrip (id); + g_mime_part_set_content_id (mime_part, id); + g_free (id); + + headers_ptr = end + 1; + break; + } + case CONTENT_TRANSFER_ENCODING: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *encoding = g_strndup (pch, (gint) (end - pch)); + + g_strstrip (encoding); + g_mime_part_set_encoding (mime_part, g_mime_part_encoding_from_string (encoding)); + g_free (encoding); + + headers_ptr = end + 1; + break; + } + case CONTENT_TYPE: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *type = g_strndup (pch, (gint) (end - pch)); + GMimeContentType *mime_type; + + g_strstrip (type); + mime_type = g_mime_content_type_new_from_string (type); + g_free (type); + + *is_multipart = g_mime_content_type_is_type (mime_type, "multipart", "*"); + if (*is_multipart) { + const gchar *b; + + b = g_mime_content_type_get_parameter (mime_type, "boundary"); + if (b != NULL) { + /* create our temp boundary vars */ + *boundary = g_strdup_printf ("--%s\n", b); + *end_boundary = g_strdup_printf ("--%s--\n", b); + } else { + g_warning ("Invalid MIME structure: boundary not found for multipart" + " - defaulting to text/plain."); + + /* let's continue onward as if this was not a multipart */ + g_mime_content_type_destroy (mime_type); + mime_type = g_mime_content_type_new ("text", "plain"); + is_multipart = FALSE; } - g_mime_part_set_content_type (mime_part, mime_type); - - headers_ptr = end + 1; - break; } - case CONTENT_DISPOSITION: { - const gchar *pch = headers_ptr + strlen(content_headers[type]); - const gchar *end = find_header_end (pch, headers_end); - gchar *disposition = g_strndup (pch, (gint) (end - pch)); - gchar *ptr; - gchar *disp; - - /* get content disposition part */ - for (ptr=disposition; *ptr && *ptr!=';'; ++ptr); /* find ; or \0 */ - disp = g_strndup (disposition, (gint)(ptr - disposition)); - g_strstrip (disp); - g_mime_part_set_content_disposition (mime_part, disp); - g_free (disp); - - /* parse the parameters, if any */ - while (*ptr==';') { - gchar *name; - gchar *value; - - /* get the param name */ - for (name = ptr + 1; *name && !isspace((int)*name); ++name); - for (ptr = name; *ptr && *ptr != '='; ++ptr); - name = g_strndup (name, (gint) (ptr - name)); - g_strstrip (name); - - /* convert param name to lowercase */ - g_strdown (name); + g_mime_part_set_content_type (mime_part, mime_type); + + headers_ptr = end + 1; + break; + } + case CONTENT_DISPOSITION: { + const gchar *pch = headers_ptr + strlen (content_headers[type]); + const gchar *end = find_header_end (pch, headers_end); + gchar *disposition = g_strndup (pch, (gint) (end - pch)); + gchar *ptr; + gchar *disp; + + /* get content disposition part */ + for (ptr = disposition; *ptr && *ptr != ';'; ptr++); /* find ; or \0 */ + disp = g_strndup (disposition, (gint)(ptr - disposition)); + g_strstrip (disp); + g_mime_part_set_content_disposition (mime_part, disp); + g_free (disp); + + /* parse the parameters, if any */ + while (*ptr == ';') { + gchar *name; + gchar *value; + + /* get the param name */ + for (name = ptr + 1; *name && !isspace ((int)*name); name++); + for (ptr = name; *ptr && *ptr != '='; ptr++); + name = g_strndup (name, (gint) (ptr - name)); + g_strstrip (name); + + /* convert param name to lowercase */ + g_strdown (name); - /* skip any whitespace */ - for (value = ptr + 1; *value && isspace((int)*value); value++); + /* skip any whitespace */ + for (value = ptr + 1; *value && isspace ((int)*value); value++); - if (*value == '"') /* value is in quotes */ - { - value++; - for (ptr = value; *ptr; ptr++) - if (*ptr == '"' && *(ptr - 1) != '\\') - break; - value = g_strndup (value, (gint) (ptr - value)); - g_strstrip (value); - g_mime_utils_unquote_string (value); + if (*value == '"') { + /* value is in quotes */ + value++; + for (ptr = value; *ptr; ptr++) + if (*ptr == '"' && *(ptr - 1) != '\\') + break; + value = g_strndup (value, (gint) (ptr - value)); + g_strstrip (value); + g_mime_utils_unquote_string (value); - for ( ; *ptr && *ptr != ';'; ptr++); - } - else /* value is not in quotes */ - { - for (ptr = value; *ptr && *ptr != ';'; ptr++); - value = g_strndup (value, (gint) (ptr - value)); - g_strstrip (value); - } - - g_mime_part_add_content_disposition_parameter (mime_part, name, value); - - g_free (name); - g_free (value); + for ( ; *ptr && *ptr != ';'; ptr++); + } else { + /* value is not in quotes */ + for (ptr = value; *ptr && *ptr != ';'; ptr++); + value = g_strndup (value, (gint) (ptr - value)); + g_strstrip (value); } - - g_free (disposition); - headers_ptr = end + 1; - break; - } - default: { /* ignore this header */ - const gchar *pch = headers_ptr; - const gchar *end = find_header_end (pch, headers_end); - headers_ptr = end + 1; - break; + g_mime_part_add_content_disposition_parameter (mime_part, name, value); + + g_free (name); + g_free (value); } + + g_free (disposition); + + headers_ptr = end + 1; + break; + } + default: { /* ignore this header */ + const gchar *pch = headers_ptr; + const gchar *end = find_header_end (pch, headers_end); + + headers_ptr = end + 1; + break; + } } } } -typedef enum -{ +typedef enum { PARSER_EOF, PARSER_BOUNDARY, PARSER_END_BOUNDARY, PARSER_LINE -} -ParserState; +} ParserState; static ParserState -get_next_line (gchar *buf, - guint buf_len, - FILE *fp, - const gchar *boundary, - const gchar *end_boundary) +get_next_line (gchar *buf, guint buf_len, FILE *fp, const gchar *boundary, const gchar *end_boundary) { ParserState state; - + *buf = '\0'; - if (fgets(buf, buf_len, fp) == NULL) + if (fgets (buf, buf_len, fp) == NULL) state = PARSER_EOF; - else if (boundary!=NULL && !strcmp (buf, boundary)) + else if (boundary != NULL && !strcmp (buf, boundary)) state = PARSER_BOUNDARY; - else if (end_boundary!=NULL && !strcmp (buf, end_boundary)) + else if (end_boundary != NULL && !strcmp (buf, end_boundary)) state = PARSER_END_BOUNDARY; else state = PARSER_LINE; - + return state; } @@ -420,49 +428,47 @@ g_mime_parser_construct_part_from_file (const gchar *headers, gchar *end_boundary; gboolean is_multipart; GMimePart *mime_part; - - g_return_val_if_fail (headers!=NULL, NULL); - g_return_val_if_fail (headers_len>0, NULL); - g_return_val_if_fail (fp!=NULL, NULL); - g_return_val_if_fail (setme_state!=NULL, NULL); - - /** - *** Headers - **/ - + + g_return_val_if_fail (headers != NULL, NULL); + g_return_val_if_fail (headers_len > 0, NULL); + g_return_val_if_fail (fp != NULL, NULL); + g_return_val_if_fail (setme_state != NULL, NULL); + + /* Headers */ boundary = NULL; end_boundary = NULL; is_multipart = FALSE; mime_part = g_mime_part_new (); parse_content_headers (headers, headers_len, mime_part, &is_multipart, &boundary, &end_boundary); - - /** - *** Body - **/ - + + /* Body */ if (is_multipart && boundary!=NULL && end_boundary!=NULL) { /* is a multipart */ /* get all the subparts */ gchar buf[GMIME_PARSER_MAX_LINE_WIDTH]; - + for (;;) { /* get the next line, we're looking for the beginning of a subpart */ - ParserState ps = get_next_line (buf, sizeof(buf), fp, parent_boundary, parent_end_boundary); + ParserState ps = get_next_line (buf, sizeof (buf), fp, parent_boundary, + parent_end_boundary); if (ps != PARSER_LINE) { *setme_state = ps; break; } - + /* is the beginning of a subpart? */ if (strcmp (buf, boundary)) continue; - + /* add subparts as long as we keep getting boundaries */ for (;;) { GArray *h = get_header_block_from_file (fp); if (h != NULL) { ParserState ps = 0; - GMimePart * part = g_mime_parser_construct_part_from_file ( - h->data, h->len, fp, boundary, end_boundary, &ps); + GMimePart *part; + + part = g_mime_parser_construct_part_from_file (h->data, h->len, + fp, boundary, + end_boundary, &ps); g_array_free (h, TRUE); if (part != NULL) g_mime_part_add_subpart (mime_part, part); @@ -471,36 +477,38 @@ g_mime_parser_construct_part_from_file (const gchar *headers, } } } - } - else { /* not a multipart */ + } else { + /* not a multipart */ GMimePartEncodingType encoding = g_mime_part_get_encoding (mime_part); - GArray * a = g_array_new (TRUE, FALSE, 1); + GArray *a = g_array_new (TRUE, FALSE, 1); gchar buf [GMIME_PARSER_MAX_LINE_WIDTH]; - + /* keep reading lines until we reach a boundary or EOF, we're populating a part */ for (;;) { - ParserState ps = get_next_line (buf, sizeof(buf), fp, parent_boundary, parent_end_boundary); - if (ps == PARSER_LINE) - g_array_append_vals (a, buf, strlen(buf)); - else { + ParserState ps = get_next_line (buf, sizeof (buf), fp, + parent_boundary, + parent_end_boundary); + if (ps == PARSER_LINE) { + g_array_append_vals (a, buf, strlen (buf)); + } else { *setme_state = ps; break; } - } - + /* trim off excess trailing \n's */ - while (a->len>2 && a->data[a->len-1]=='\n' && a->data[a->len-2]=='\n') - g_array_set_size (a, a->len-1); - + while (a->len > 2 && a->data[a->len-1] == '\n' && a->data[a->len - 2] == '\n') + g_array_set_size (a, a->len - 1); + if (a->len > 0) g_mime_part_set_pre_encoded_content (mime_part, a->data, a->len, encoding); - + g_array_free (a, TRUE); } - + g_free (boundary); g_free (end_boundary); + return mime_part; } @@ -523,33 +531,27 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) gboolean is_multipart; const gchar *inptr; const gchar *inend = in + inlen; - - g_return_val_if_fail (in!=NULL, NULL); - g_return_val_if_fail (inlen!=0, NULL); - /** - *** Headers - **/ - + g_return_val_if_fail (in != NULL, NULL); + g_return_val_if_fail (inlen != 0, NULL); + + /* Headers */ headers = get_header_block (in); mime_part = g_mime_part_new (); is_multipart = FALSE; boundary = NULL; end_boundary = NULL; - parse_content_headers (headers->data, headers->len, mime_part, &is_multipart, &boundary, &end_boundary); - - /** - *** Body - **/ - + parse_content_headers (headers->data, headers->len, mime_part, + &is_multipart, &boundary, &end_boundary); + + /* Body */ inptr = in + headers->len; - - if (is_multipart && boundary && end_boundary) - { + + if (is_multipart && boundary && end_boundary) { /* get all the subparts */ - GMimePart * subpart; - const gchar * part_begin; - const gchar * part_end; + GMimePart *subpart; + const gchar *part_begin; + const gchar *part_end; part_begin = g_strstrbound (inptr, boundary, inend); while (part_begin && part_begin < inend) { @@ -560,7 +562,8 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) /* find the end of this part */ part_end = g_strstrbound (part_begin + strlen (boundary), boundary, inend); if (!part_end || part_end >= inend) { - part_end = g_strstrbound (part_begin + strlen (boundary), end_boundary, inend); + part_end = g_strstrbound (part_begin + strlen (boundary), + end_boundary, inend); if (!part_end || part_end >= inend) part_end = inend; } @@ -578,12 +581,12 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) g_free (end_boundary); } else { GMimePartEncodingType encoding; - const gchar * content = NULL; + const gchar *content = NULL; guint len = 0; /* from here to the end is the content */ if (inptr < inend) { - for (inptr++; inptr < inend && isspace((int)*inptr); inptr++); + for (inptr++; inptr < inend && isspace ((int)*inptr); inptr++); len = inend - inptr; content = inptr; @@ -600,8 +603,9 @@ g_mime_parser_construct_part (const gchar *in, guint inlen) if (len > 0) g_mime_part_set_pre_encoded_content (mime_part, content, len, encoding); } - + g_array_free (headers, TRUE); + return mime_part; } @@ -734,21 +738,20 @@ construct_headers (GMimeMessage *message, const gchar *headers, gint inlen, gboo GMimeMessage * g_mime_parser_construct_message (const gchar *data, gboolean save_extra_headers) { - GMimeMessage * message = NULL; - GArray * headers; + GMimeMessage *message = NULL; + GArray *headers; g_return_val_if_fail (data != NULL, NULL); - + headers = get_header_block (data); - if (headers != NULL) - { + if (headers != NULL) { GMimePart * part; - + message = g_mime_message_new (); construct_headers (message, headers->data, headers->len, save_extra_headers); part = g_mime_parser_construct_part (data, strlen(data)); g_mime_message_set_mime_part (message, part); - + g_array_free (headers, TRUE); } @@ -765,24 +768,24 @@ g_mime_parser_construct_message (const gchar *data, gboolean save_extra_headers) GMimeMessage * g_mime_parser_construct_message_from_file (FILE *fp, gboolean save_extra_headers) { - GMimeMessage * message = NULL; - GArray * headers; - - g_return_val_if_fail (fp!=NULL, NULL); - + GMimeMessage *message = NULL; + GArray *headers; + + g_return_val_if_fail (fp != NULL, NULL); + headers = get_header_block_from_file (fp); - if (headers != NULL) - { - GMimePart * part; + if (headers != NULL) { + GMimePart *part; ParserState state = -1; - + message = g_mime_message_new (); construct_headers (message, headers->data, headers->len, save_extra_headers); - part = g_mime_parser_construct_part_from_file (headers->data, headers->len, fp, NULL, NULL, &state); + part = g_mime_parser_construct_part_from_file (headers->data, headers->len, fp, + NULL, NULL, &state); g_mime_message_set_mime_part (message, part); if (state != PARSER_EOF) g_warning ("Didn't reach end of file - parser error?"); - + g_array_free (headers, TRUE); } diff --git a/gmime/gmime-table-private.h b/gmime/gmime-table-private.h new file mode 100644 index 00000000..7350960a --- /dev/null +++ b/gmime/gmime-table-private.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 Ximain, 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. + * + */ + +static unsigned short gmime_special_table[256] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5,231, 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, 4, 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, 64, + 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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +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, /* encoded word in phrase safe */ +}; + +#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0) +#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0) +#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0) +#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0) +#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0) +#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0) +#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0) +#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0) +#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0) +#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0) +#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0) + +#ifndef HAVE_ISBLANK +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif /* HAVE_ISBLANK */ + +#define CHARS_LWSP " \t\n\r" /* linear whitespace chars */ +#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" +#define CHARS_SPECIAL "()<>@,;:\\\".[]" +#define CHARS_CSPECIAL "()\\\r" /* not in comments */ +#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) */ diff --git a/gmime/gmime-utils.c b/gmime/gmime-utils.c index 540ff670..f0c062bd 100644 --- a/gmime/gmime-utils.c +++ b/gmime/gmime-utils.c @@ -21,9 +21,14 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "gmime-utils.h" +#include "gmime-table-private.h" #include "gmime-part.h" -#include <config.h> + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -35,6 +40,10 @@ #define GMIME_FOLD_LEN 76 +#ifndef HAVE_ISBLANK +#define isblank(c) (c == ' ' || c == '\t') +#endif + static char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -43,25 +52,6 @@ static unsigned char tohex[16] = { '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; -static unsigned short gmime_special_table[256] = { - 5, 5, 5, 5, 5, 5, 5, 5, 5,231, 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, 4, 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, 64, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - static unsigned char gmime_base64_rank[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, @@ -101,109 +91,6 @@ static unsigned char gmime_uu_rank[256] = { }; -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, /* encoded word in phrase safe */ -}; - -#define is_ctrl(x) ((gmime_special_table[(unsigned char)(x)] & IS_CTRL) != 0) -#define is_lwsp(x) ((gmime_special_table[(unsigned char)(x)] & IS_LWSP) != 0) -#define is_tspecial(x) ((gmime_special_table[(unsigned char)(x)] & IS_TSPECIAL) != 0) -#define is_type(x, t) ((gmime_special_table[(unsigned char)(x)] & (t)) != 0) -#define is_ttoken(x) ((gmime_special_table[(unsigned char)(x)] & (IS_TSPECIAL|IS_LWSP|IS_CTRL)) == 0) -#define is_atom(x) ((gmime_special_table[(unsigned char)(x)] & (IS_SPECIAL|IS_SPACE|IS_CTRL)) == 0) -#define is_dtext(x) ((gmime_special_table[(unsigned char)(x)] & IS_DSPECIAL) == 0) -#define is_fieldname(x) ((gmime_special_table[(unsigned char)(x)] & (IS_CTRL|IS_SPACE)) == 0) -#define is_qpsafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_QPSAFE) != 0) -#define is_especial(x) ((gmime_special_table[(unsigned char)(x)] & IS_ESPECIAL) != 0) -#define is_psafe(x) ((gmime_special_table[(unsigned char)(x)] & IS_PSAFE) != 0) - -#ifndef HAVE_ISBLANK -#define isblank(c) ((c) == ' ' || (c) == '\t') -#endif /* HAVE_ISBLANK */ - -#define CHARS_LWSP " \t\n\r" /* linear whitespace chars */ -#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" -#define CHARS_SPECIAL "()<>@,;:\\\".[]" -#define CHARS_CSPECIAL "()\\\r" /* not in comments */ -#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) */ - -#ifdef BUILD_TABLE -/* code to rebuild the gmime_special_table */ -static void -header_remove_bits (gushort bit, guchar *vals) -{ - gint i; - - for (i = 0; vals[i]; i++) - gmime_special_table[vals[i]] &= ~bit; -} - -static void -header_init_bits (gushort bit, gushort bitcopy, gboolean remove, guchar *vals) -{ - gint i, len = strlen (vals); - - if (!remove) { - for (i = 0; i < len; i++) { - gmime_special_table[vals[i]] |= bit; - } - if (bitcopy) { - for (i = 0; i < 256; i++) { - if (gmime_special_table[i] & bitcopy) - gmime_special_table[i] |= bit; - } - } - } else { - for (i = 0; i < 256; i++) - gmime_special_table[i] |= bit; - for (i = 0; i < len; i++) { - gmime_special_table[vals[i]] &= ~bit; - } - if (bitcopy) { - for (i = 0; i < 256; i++) { - if (gmime_special_table[i] & bitcopy) - gmime_special_table[i] &= ~bit; - } - } - } -} - -static void -header_decode_init (void) -{ - gint i; - - for (i = 0; i < 256; i++) { - gmime_special_table[i] = 0; - if (i < 32) - gmime_special_table[i] |= IS_CTRL; - if ((i >= 33 && i <= 60) || (i >= 62 && i <= 126) || i == 32 || i == 9) - gmime_special_table[i] |= (IS_QPSAFE | IS_ESAFE); - if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')) - gmime_special_table[i] |= IS_PSAFE; - } - - gmime_special_table[127] |= IS_CTRL; - gmime_special_table[' '] |= IS_SPACE; - header_init_bits (IS_LWSP, 0, FALSE, CHARS_LWSP); - header_init_bits (IS_TSPECIAL, IS_CTRL, FALSE, CHARS_TSPECIAL); - header_init_bits (IS_SPECIAL, 0, FALSE, CHARS_SPECIAL); - header_init_bits (IS_DSPECIAL, 0, FALSE, CHARS_DSPECIAL); - header_remove_bits (IS_ESAFE, CHARS_ESPECIAL); - header_init_bits (IS_PSAFE, 0, FALSE, CHARS_PSPECIAL); -} -#endif /* BUILD_TABLE */ - /* hrm, is there a library for this shit? */ static struct { char *name; @@ -654,6 +541,28 @@ g_mime_utils_header_printf (const gchar *format, ...) return ret; } +static gboolean +need_quotes (const char *string) +{ + gboolean quoted = FALSE; + const char *inptr; + + inptr = string; + + while (*inptr) { + if (*inptr == '\\') + inptr++; + else if (*inptr == '"') + quoted = !quoted; + else if (!quoted && is_tspecial (*inptr)) + return TRUE; + + if (*inptr) + inptr++; + } + + return FALSE; +} /** * g_mime_utils_quote_string: Quote a string. @@ -667,18 +576,16 @@ g_mime_utils_header_printf (const gchar *format, ...) gchar * g_mime_utils_quote_string (const gchar *string) { - GString *out; + gboolean quote; + const gchar *c; gchar *qstring; - guchar *c; - gboolean quote = FALSE; + GString *out; out = g_string_new (""); + quote = need_quotes (string); - for (c = (guchar *) string; *c; c++) { - if (is_tspecial (*c)) - quote = TRUE; - - if (*c == '"' || *c == '\\') + for (c = string; *c; c++) { + if ((*c == '"' && quote) || *c == '\\') g_string_append_c (out, '\\'); g_string_append_c (out, *c); diff --git a/gmime/internet-address.c b/gmime/internet-address.c index 308327c5..f4a90493 100644 --- a/gmime/internet-address.c +++ b/gmime/internet-address.c @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast <fejj@helixcode.com> + * Authors: Jeffrey Stedfast <fejj@ximian.com> * - * Copyright 2000 Helix Code, Inc. (www.helixcode.com) + * Copyright 2001 Ximain, 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,235 +20,731 @@ * */ +#ifdef HAVE_CONFIG_H #include <config.h> +#endif #include "internet-address.h" +#include "gmime-table-private.h" #include "gmime-utils.h" -#include <stdio.h> + #include <string.h> #include <ctype.h> /** * internet_address_new: Create a new Internet Address object + * + * Returns a new Internet Address object. + **/ +InternetAddress * +internet_address_new () +{ + return g_new0 (InternetAddress, 1); +} + + +/** + * internet_address_destroy: Destroy an Internet Address object + * @ia: Internet Address object to destroy + * + * Destroy the InternetAddress object pointed to by @ia. + **/ +void +internet_address_destroy (InternetAddress *ia) +{ + if (ia) { + g_free (ia->name); + + if (ia->type == INTERNET_ADDRESS_GROUP) { + GList *members; + + members = ia->value.members; + while (members) { + InternetAddress *member; + + member = members->data; + internet_address_destroy (member); + members = members->next; + } + + g_list_free (ia->value.members); + } else { + g_free (ia->value.addr); + } + + g_free (ia); + } +} + + +/** + * internet_address_new_name: Create a new Internet Address object * @name: person's name * @address: person's address * * Returns a new Internet Address object. **/ InternetAddress * -internet_address_new (const gchar *name, const gchar *address) +internet_address_new_name (const char *name, const char *addr) { InternetAddress *ia; - g_return_val_if_fail (address != NULL, NULL); - - ia = g_new (InternetAddress, 1); + g_return_val_if_fail (addr != NULL, NULL); + ia = internet_address_new (); + ia->type = INTERNET_ADDRESS_NAME; if (name) { ia->name = g_mime_utils_8bit_header_decode (name); g_mime_utils_unquote_string (ia->name); - } else { - ia->name = NULL; } + ia->value.addr = g_strdup (addr); + + return ia; +} + + +/** + * internet_address_new_group: Create a new Internet Address object + * @name: group name + * + * Returns a new Internet Address object. + **/ +InternetAddress * +internet_address_new_group (const char *name) +{ + InternetAddress *ia; - ia->address = g_strdup (address); + ia = internet_address_new (); + ia->type = INTERNET_ADDRESS_GROUP; + if (name) { + ia->name = g_mime_utils_8bit_header_decode (name); + g_mime_utils_unquote_string (ia->name); + } return ia; } -static gchar * -get_next_token (const gchar *in, guint inlen, guint *len) + +/** + * internet_address_set_name: + * @ia: internet address + * @name: group or contact's name + * + * Set the name of the internet address. + **/ +void +internet_address_set_name (InternetAddress *ia, const char *name) { - gchar *token; - gchar *inptr, *inend, *end; - gint depth = 0; - gchar dp = '\0', dm = '\0'; + g_return_if_fail (ia != NULL); - inptr = (gchar *) in; - inend = inptr + inlen; + g_free (ia->name); + if (name) { + ia->name = g_mime_utils_8bit_header_decode (name); + g_mime_utils_unquote_string (ia->name); + } else + ia->name = NULL; +} + + +/** + * internet_address_set_addr: + * @ia: internet address + * @addr: contact's email address + * + * Set the intenet address's address. + **/ +void +internet_address_set_addr (InternetAddress *ia, const char *addr) +{ + g_return_if_fail (ia != NULL); + g_return_if_fail (ia->type != INTERNET_ADDRESS_GROUP); - while (isspace (*inptr) && inptr < inend) - inptr++; + ia->type = INTERNET_ADDRESS_NAME; + g_free (ia->value.addr); + ia->value.addr = g_strdup (addr); +} + + +/** + * internet_address_set_group: + * @ia: internet address + * @group: a list of internet addresses + * + * Set the members of the internet address group. + **/ +void +internet_address_set_group (InternetAddress *ia, GList *group) +{ + GList *members; - if (*inptr == '"') { - dm = '"'; - depth = 1; - } else if (*inptr == '(') { - dp = '('; - dm = ')'; - depth = 1; - } - - end = inptr; - while (end < inend) { - end++; - - if (*end == dp) - depth++; - else if (*end == dm) - depth--; - else if (!depth && isspace (*end)) - break; - } + g_return_if_fail (ia != NULL); + g_return_if_fail (ia->type != INTERNET_ADDRESS_NAME); - token = g_strndup (inptr, end - inptr); - *len = end - in; + ia->type = INTERNET_ADDRESS_GROUP; + members = ia->value.members; + while (members) { + InternetAddress *member; + + member = members->data; + internet_address_destroy (member); + members = members->next; + } - return token; + g_list_free (ia->value.members); + ia->value.members = group; } -static GPtrArray * -rfc822_tokenize (const gchar *in, guint inlen) + +/** + * internet_address_add_member: + * @ia: internet address + * @member: group member's internet address + * + * Add a contact to the internet address group. + **/ +void +internet_address_add_member (InternetAddress *ia, InternetAddress *member) { - GPtrArray *tokens; - gchar *token, *inptr, *inend; - guint len; + g_return_if_fail (ia != NULL); + g_return_if_fail (ia->type != INTERNET_ADDRESS_NAME); - inptr = (gchar *) in; - inend = inptr + inlen; + ia->type = INTERNET_ADDRESS_GROUP; + ia->value.members = g_list_append (ia->value.members, member); +} + +static gchar * +encoded_name (const gchar *raw, gboolean rfc2047_encode) +{ + gchar *name; - tokens = g_ptr_array_new (); + g_return_val_if_fail (raw != NULL, NULL); - while (inptr < inend) { - token = get_next_token (inptr, inend - inptr, &len); - inptr += len; - g_ptr_array_add (tokens, token); + if (rfc2047_encode && g_mime_utils_text_is_8bit (raw, strlen (raw))) { + name = g_mime_utils_8bit_header_encode_phrase (raw); + } else { + name = g_mime_utils_quote_string (raw); } - return tokens; + return name; } /** - * internet_address_new_from_string: Create a new Internet Address object - * @string: rfc822 internet address string + * internet_address_to_string: Write the InternetAddress object to a string + * @ia: Internet Address object + * @encode: TRUE if the address should be rfc2047 encoded * - * Returns a new Internet Address object based upon the rfc822 address - * string. + * Returns the InternetAddress object as an allocated string in rfc822 + * format. **/ -InternetAddress * -internet_address_new_from_string (const gchar *string) +gchar * +internet_address_to_string (InternetAddress *ia, gboolean encode) { - InternetAddress *ia; - GPtrArray *tokens; - gchar *name = NULL, *address = NULL; - int i; - - g_return_val_if_fail (string != NULL, NULL); - g_return_val_if_fail (*string != '\0', NULL); + char *string = NULL; - tokens = rfc822_tokenize (string, strlen (string)); - if (!tokens->len) { - g_ptr_array_free (tokens, TRUE); - return NULL; + if (ia->type == INTERNET_ADDRESS_NAME) { + if (ia->name) { + char *name; + + name = encoded_name (ia->name, encode); + string = g_strdup_printf ("%s <%s>", name, + ia->value.addr); + g_free (name); + } else { + string = g_strdup (ia->value.addr); + } + } else if (ia->type == INTERNET_ADDRESS_GROUP) { + GList *members; + GString *gstr; + + gstr = g_string_new (ia->name); + g_string_append (gstr, ": "); + + members = ia->value.members; + while (members) { + InternetAddress *member; + char *addr; + + member = members->data; + members = members->next; + + addr = internet_address_to_string (member, encode); + if (addr) { + g_string_append (gstr, addr); + g_free (addr); + if (members) + g_string_append (gstr, ", "); + } + } + + g_string_append (gstr, ";"); + + string = gstr->str; + g_string_free (gstr, FALSE); } - /* find the address */ - for (i = 0; i < tokens->len; i++) { - gchar *token = tokens->pdata[i]; + return string; +} + +static void +decode_lwsp (const char **in) +{ + const char *inptr = *in; + + while (*inptr && (*inptr == '(' || is_lwsp (*inptr))) { + while (*inptr && is_lwsp (*inptr)) + inptr++; - if (*token == '<' && *(token + strlen (token) - 1) == '>') { - address = token; + /* skip over any comments */ + if (*inptr == '(') { + int depth = 1; - /* strip the <>'s */ - memmove (address, address + 1, strlen (address)); - *(address + strlen (address) - 1) = '\0'; - - /* remove the address from the list of tokens */ - g_ptr_array_remove_index (tokens, i); - break; + inptr++; + while (*inptr && depth) { + if (*inptr == '\\' && *(inptr + 1)) + inptr++; + else if (*inptr == '(') + depth++; + else if (*inptr == ')') + depth--; + + inptr++; + } } } - if (!address) { - /* the first token should be the address if it wasn't surrounded in <>'s */ - address = tokens->pdata[0]; - g_ptr_array_remove_index (tokens, 0); + *in = inptr; +} + +static char * +decode_quoted_string (const char **in) +{ + const char *inptr = *in; + GString *string = NULL; + char *out = NULL; + + decode_lwsp (&inptr); + if (*inptr == '"') { + out = (char *) inptr; + + inptr++; + while (*inptr && *inptr != '"') { + if (*inptr == '\\') + inptr++; + + if (*inptr) + inptr++; + } + + if (*inptr == '"') + inptr++; + + out = g_strndup (out, inptr - out); } - /* recreate the name from the tokens */ - if (tokens->len) { - char *token = tokens->pdata[0]; - char *end = token + strlen (token) - 1; + *in = inptr; + + return out; +} + +#if 0 +static char * +decode_quoted_string (const char **in) +{ + const char *inptr = *in; + char *out = NULL, *outptr; + int outlen; + int c; + + decode_lwsp (&inptr); + if (*inptr == '"') { + const char *intmp; + int skip = 0; - if (*token == '(' && *end == ')') { - token++; - *end = '\0'; + /* first, calc length */ + inptr++; + intmp = inptr + 1; + while ((c = *intmp++) && c != '"') { + if (c == '\\' && *intmp) { + intmp++; + skip++; + } + } + + outlen = intmp - inptr - skip; + out = outptr = g_malloc (outlen + 1); + + while ((c = *inptr++) && c != '"') { + if (c == '\\' && *inptr) { + c = *inptr++; + } + *outptr++ = c; + } + *outptr = '\0'; + } + + *in = inptr; + + return out; +} +#endif + +static char * +decode_atom (const char **in) +{ + const char *inptr = *in, *start; + + decode_lwsp (&inptr); + start = inptr; + while (is_atom (*inptr)) + inptr++; + *in = inptr; + if (inptr > start) + return g_strndup (start, inptr - start); + else + return NULL; +} + +static char * +decode_word (const char **in) +{ + const char *inptr = *in; + + decode_lwsp (&inptr); + if (*inptr == '"') { + *in = inptr; + return decode_quoted_string (in); + } else { + *in = inptr; + return decode_atom (in); + } +} + +static gboolean +decode_subliteral (const char **in, GString *domain) +{ + const char *inptr = *in; + gboolean got = FALSE; + + while (*inptr && *inptr != '.' && *inptr != ']') { + if (is_dtext (*inptr)) { + g_string_append_c (domain, *inptr); + inptr++; + got = TRUE; + } else if (is_lwsp (*inptr)) + decode_lwsp (&inptr); + else + break; + } + + *in = inptr; + + return got; +} + +static void +decode_domain_literal (const char **in, GString *domain) +{ + const char *inptr = *in; + + decode_lwsp (&inptr); + while (*inptr && *inptr != ']') { + if (decode_subliteral (&inptr, domain) && *inptr == '.') { + g_string_append_c (domain, *inptr); + inptr++; + } else if (*inptr != ']') { + g_warning ("Malformed domain-literal, " + "unexpected char (%c): %s", *inptr, *in); + + /* try and skip to the next char ?? */ + inptr++; + } + } + + *in = inptr; +} + +static char * +decode_domain (const char **in) +{ + const char *inptr, *save; + GString *domain; + char *dom, *atom; + + domain = g_string_new (""); + + inptr = *in; + while (TRUE) { + decode_lwsp (&inptr); + if (*inptr == '[') { + /* domain literal */ + g_string_append_c (domain, '['); + inptr++; - name = g_mime_utils_8bit_header_decode (token); + decode_domain_literal (&inptr, domain); + + if (*inptr == ']') { + g_string_append_c (domain, ']'); + inptr++; + } else + g_warning ("Missing ']' in domain-literal: %s", *in); } else { - name = g_strjoinv (" ", (gchar **) tokens->pdata); + atom = decode_atom (&inptr); + if (atom) + g_string_append (domain, atom); + g_free (atom); + } + + save = inptr; + decode_lwsp (&inptr); + if (*inptr != '.') { + inptr = save; + break; } + + g_string_append_c (domain, '.'); + inptr++; } - for (i = 0; i < tokens->len; i++) - g_free (tokens->pdata[i]); - g_ptr_array_free (tokens, TRUE); + dom = domain->str; + g_string_free (domain, FALSE); - ia = internet_address_new (name, address); - g_free (name); - g_free (address); + *in = inptr; - return ia; + return dom; } - -/** - * internet_address_destroy: Destroy an Internet Address object - * @ia: Internet Address object to destroy - * - * Destroy the InternetAddress object pointed to by #ia. - **/ -void -internet_address_destroy (InternetAddress *ia) +static InternetAddress * +decode_mailbox (const char **in) { - g_return_if_fail (ia != NULL); + InternetAddress *mailbox = NULL; + const char *inptr; + gboolean bracket = FALSE; + GString *name = NULL; + GString *addr; + char *pre; - g_free (ia->name); - g_free (ia->address); - g_free (ia); + addr = g_string_new (""); + + decode_lwsp (in); + inptr = *in; + + pre = decode_word (&inptr); + decode_lwsp (&inptr); + if (*inptr && !strchr (",.@", *inptr)) { + /* this mailbox has a name part, so get the name */ + name = g_string_new (""); + while (pre) { + g_string_append (name, pre); + g_free (pre); + pre = decode_word (&inptr); + if (pre) + g_string_append_c (name, ' '); + } + + decode_lwsp (&inptr); + if (*inptr == '<') { + inptr++; + bracket = TRUE; + pre = decode_word (&inptr); + } else { + g_string_free (name, TRUE); + g_string_free (addr, TRUE); + *in = inptr; + + return NULL; + } + } + + if (pre) { + g_string_append (addr, pre); + } else { + g_warning ("No local part for email address: %s", *in); + if (name) + g_string_free (name, TRUE); + g_string_free (addr, TRUE); + return NULL; + } + + /* get the rest of the local-part */ + decode_lwsp (&inptr); + while (*inptr == '.' && pre) { + inptr++; + g_free (pre); + pre = decode_word (&inptr); + if (pre) { + g_string_append_c (addr, '.'); + g_string_append (addr, pre); + } + decode_lwsp (&inptr); + } + g_free (pre); + + /* we should be at the '@' now... */ + if (*inptr == '@') { + char *domain; + + g_string_append_c (addr, '@'); + inptr++; + + domain = decode_domain (&inptr); + g_string_append (addr, domain); + g_free (domain); + } else { + g_warning ("No domain in email address: %s", *in); + } + + if (bracket) { + decode_lwsp (&inptr); + if (*inptr == '>') + inptr++; + else + g_warning ("Missing closing '>' bracket for email address: %s", *in); + } + + if (!name) { + /* look for a trailing comment to use as the name? */ + char *comment; + + comment = (char *) inptr; + decode_lwsp (&inptr); + if (inptr > comment) { + comment = memchr (comment, '(', inptr - comment); + if (comment) { + const char *cend; + + /* find the end of the comment */ + for (cend = inptr - 1; cend > comment && is_lwsp (*cend); cend--); + if (*cend == ')') + cend--; + comment = g_strndup (comment + 1, cend - comment); + + name = g_string_new (comment); + g_free (comment); + } + } + } + + *in = inptr; + + if (addr->len) + mailbox = internet_address_new_name (name ? name->str : NULL, addr->str); + + g_string_free (addr, TRUE); + if (name) + g_string_free (name, TRUE); + + return mailbox; } -static gchar * -encoded_name (const gchar *raw, gboolean rfc2047_encode) +static InternetAddress * +decode_address (const char **in) { - gchar *name; + InternetAddress *addr = NULL, *member; + const char *inptr, *start; + GString *name; + char *pre; - g_return_val_if_fail (raw != NULL, NULL); + decode_lwsp (in); + start = inptr = *in; - if (rfc2047_encode && g_mime_utils_text_is_8bit (raw, strlen (raw))) { - name = g_mime_utils_8bit_header_encode_phrase (raw); + /* pre-scan */ + name = g_string_new (""); + pre = decode_word (&inptr); + while (pre) { + g_string_append (name, pre); + g_free (pre); + + pre = decode_word (&inptr); + if (pre) + g_string_append_c (name, ' '); + } + + decode_lwsp (&inptr); + if (*inptr == ':') { + /* this is a group */ + inptr++; + addr = internet_address_new_group (name->str); + + decode_lwsp (&inptr); + while (*inptr && *inptr != ';') { + InternetAddress *member; + + member = decode_mailbox (&inptr); + if (member) + internet_address_add_member (addr, member); + + decode_lwsp (&inptr); + while (*inptr == ',') { + inptr++; + decode_lwsp (&inptr); + member = decode_mailbox (&inptr); + if (member) + internet_address_add_member (addr, member); + + decode_lwsp (&inptr); + } + } + + if (*inptr == ';') + inptr++; + else + g_warning ("Invalid group spec, missing closing ';': %.*s", + inptr - start, start); + + *in = inptr; } else { - name = g_mime_utils_quote_string (raw); + /* this is a mailbox */ + addr = decode_mailbox (in); } - return name; + g_string_free (name, TRUE); + + return addr; } /** - * internet_address_to_string: Write the InternetAddress object to a string - * @ia: Internet Address object - * @rfc2047_encode: TRUE if the address should be encoded - * - * Returns the InternetAddress object as an allocated string in rfc822 - * format. + * internet_address_paarse_string: + * @string: a string containing internet addresses + * + * Construct a list of internet addresses from the given string. + * + * Returns a linked list of internet addresses. **/ -gchar * -internet_address_to_string (InternetAddress *ia, gboolean rfc2047_encode) +GList * +internet_address_parse_string (const char *string) { - gchar *name, *string; + GList *addrlist = NULL; + const char *inptr; - g_return_val_if_fail (ia != NULL, NULL); + inptr = string; - if (ia->name) { - name = encoded_name (ia->name, rfc2047_encode); - string = g_strdup_printf ("%s <%s>", name, ia->address); - g_free (name); - } else { - string = g_strdup (ia->address); + while (inptr && *inptr) { + InternetAddress *addr; + const char *start; + + start = inptr; + + addr = decode_address (&inptr); + + if (addr) + addrlist = g_list_append (addrlist, addr); + else + g_warning ("Invalid or incomplete address: %.*s", inptr - start, start); + + decode_lwsp (&inptr); + if (*inptr == ',') + inptr++; + else if (*inptr) { + g_warning ("Parse error at '%s': expected ','", inptr); + /* try skipping to the next address */ + inptr = strchr (inptr, ','); + if (inptr) + inptr++; + } } - return string; + return addrlist; } diff --git a/gmime/internet-address.h b/gmime/internet-address.h index 629ae11a..003f04da 100644 --- a/gmime/internet-address.h +++ b/gmime/internet-address.h @@ -30,18 +30,36 @@ extern "C" { #include <glib.h> +typedef enum { + INTERNET_ADDRESS_NONE, + INTERNET_ADDRESS_NAME, + INTERNET_ADDRESS_GROUP +} InternetAddressType; + struct _InternetAddress { + InternetAddressType type; gchar *name; - gchar *address; + union { + gchar *addr; + GList *members; + } value; }; typedef struct _InternetAddress InternetAddress; -InternetAddress *internet_address_new (const gchar *name, const gchar *address); -InternetAddress *internet_address_new_from_string (const gchar *string); +InternetAddress *internet_address_new (void); +InternetAddress *internet_address_new_name (const gchar *name, const gchar *addr); +InternetAddress *internet_address_new_group (const gchar *name); void internet_address_destroy (InternetAddress *ia); +void internet_address_set_name (InternetAddress *ia, const gchar *name); +void internet_address_set_addr (InternetAddress *ia, const gchar *addr); +void internet_address_set_group (InternetAddress *ia, GList *group); +void internet_address_add_member (InternetAddress *ia, InternetAddress *member); + +GList *internet_address_parse_string (const gchar *string); + gchar *internet_address_to_string (InternetAddress *ia, gboolean rfc2047_encode); #ifdef __cplusplus diff --git a/internet-address.c b/internet-address.c index 308327c5..f4a90493 100644 --- a/internet-address.c +++ b/internet-address.c @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * Authors: Jeffrey Stedfast <fejj@helixcode.com> + * Authors: Jeffrey Stedfast <fejj@ximian.com> * - * Copyright 2000 Helix Code, Inc. (www.helixcode.com) + * Copyright 2001 Ximain, 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,235 +20,731 @@ * */ +#ifdef HAVE_CONFIG_H #include <config.h> +#endif #include "internet-address.h" +#include "gmime-table-private.h" #include "gmime-utils.h" -#include <stdio.h> + #include <string.h> #include <ctype.h> /** * internet_address_new: Create a new Internet Address object + * + * Returns a new Internet Address object. + **/ +InternetAddress * +internet_address_new () +{ + return g_new0 (InternetAddress, 1); +} + + +/** + * internet_address_destroy: Destroy an Internet Address object + * @ia: Internet Address object to destroy + * + * Destroy the InternetAddress object pointed to by @ia. + **/ +void +internet_address_destroy (InternetAddress *ia) +{ + if (ia) { + g_free (ia->name); + + if (ia->type == INTERNET_ADDRESS_GROUP) { + GList *members; + + members = ia->value.members; + while (members) { + InternetAddress *member; + + member = members->data; + internet_address_destroy (member); + members = members->next; + } + + g_list_free (ia->value.members); + } else { + g_free (ia->value.addr); + } + + g_free (ia); + } +} + + +/** + * internet_address_new_name: Create a new Internet Address object * @name: person's name * @address: person's address * * Returns a new Internet Address object. **/ InternetAddress * -internet_address_new (const gchar *name, const gchar *address) +internet_address_new_name (const char *name, const char *addr) { InternetAddress *ia; - g_return_val_if_fail (address != NULL, NULL); - - ia = g_new (InternetAddress, 1); + g_return_val_if_fail (addr != NULL, NULL); + ia = internet_address_new (); + ia->type = INTERNET_ADDRESS_NAME; if (name) { ia->name = g_mime_utils_8bit_header_decode (name); g_mime_utils_unquote_string (ia->name); - } else { - ia->name = NULL; } + ia->value.addr = g_strdup (addr); + + return ia; +} + + +/** + * internet_address_new_group: Create a new Internet Address object + * @name: group name + * + * Returns a new Internet Address object. + **/ +InternetAddress * +internet_address_new_group (const char *name) +{ + InternetAddress *ia; - ia->address = g_strdup (address); + ia = internet_address_new (); + ia->type = INTERNET_ADDRESS_GROUP; + if (name) { + ia->name = g_mime_utils_8bit_header_decode (name); + g_mime_utils_unquote_string (ia->name); + } return ia; } -static gchar * -get_next_token (const gchar *in, guint inlen, guint *len) + +/** + * internet_address_set_name: + * @ia: internet address + * @name: group or contact's name + * + * Set the name of the internet address. + **/ +void +internet_address_set_name (InternetAddress *ia, const char *name) { - gchar *token; - gchar *inptr, *inend, *end; - gint depth = 0; - gchar dp = '\0', dm = '\0'; + g_return_if_fail (ia != NULL); - inptr = (gchar *) in; - inend = inptr + inlen; + g_free (ia->name); + if (name) { + ia->name = g_mime_utils_8bit_header_decode (name); + g_mime_utils_unquote_string (ia->name); + } else + ia->name = NULL; +} + + +/** + * internet_address_set_addr: + * @ia: internet address + * @addr: contact's email address + * + * Set the intenet address's address. + **/ +void +internet_address_set_addr (InternetAddress *ia, const char *addr) +{ + g_return_if_fail (ia != NULL); + g_return_if_fail (ia->type != INTERNET_ADDRESS_GROUP); - while (isspace (*inptr) && inptr < inend) - inptr++; + ia->type = INTERNET_ADDRESS_NAME; + g_free (ia->value.addr); + ia->value.addr = g_strdup (addr); +} + + +/** + * internet_address_set_group: + * @ia: internet address + * @group: a list of internet addresses + * + * Set the members of the internet address group. + **/ +void +internet_address_set_group (InternetAddress *ia, GList *group) +{ + GList *members; - if (*inptr == '"') { - dm = '"'; - depth = 1; - } else if (*inptr == '(') { - dp = '('; - dm = ')'; - depth = 1; - } - - end = inptr; - while (end < inend) { - end++; - - if (*end == dp) - depth++; - else if (*end == dm) - depth--; - else if (!depth && isspace (*end)) - break; - } + g_return_if_fail (ia != NULL); + g_return_if_fail (ia->type != INTERNET_ADDRESS_NAME); - token = g_strndup (inptr, end - inptr); - *len = end - in; + ia->type = INTERNET_ADDRESS_GROUP; + members = ia->value.members; + while (members) { + InternetAddress *member; + + member = members->data; + internet_address_destroy (member); + members = members->next; + } - return token; + g_list_free (ia->value.members); + ia->value.members = group; } -static GPtrArray * -rfc822_tokenize (const gchar *in, guint inlen) + +/** + * internet_address_add_member: + * @ia: internet address + * @member: group member's internet address + * + * Add a contact to the internet address group. + **/ +void +internet_address_add_member (InternetAddress *ia, InternetAddress *member) { - GPtrArray *tokens; - gchar *token, *inptr, *inend; - guint len; + g_return_if_fail (ia != NULL); + g_return_if_fail (ia->type != INTERNET_ADDRESS_NAME); - inptr = (gchar *) in; - inend = inptr + inlen; + ia->type = INTERNET_ADDRESS_GROUP; + ia->value.members = g_list_append (ia->value.members, member); +} + +static gchar * +encoded_name (const gchar *raw, gboolean rfc2047_encode) +{ + gchar *name; - tokens = g_ptr_array_new (); + g_return_val_if_fail (raw != NULL, NULL); - while (inptr < inend) { - token = get_next_token (inptr, inend - inptr, &len); - inptr += len; - g_ptr_array_add (tokens, token); + if (rfc2047_encode && g_mime_utils_text_is_8bit (raw, strlen (raw))) { + name = g_mime_utils_8bit_header_encode_phrase (raw); + } else { + name = g_mime_utils_quote_string (raw); } - return tokens; + return name; } /** - * internet_address_new_from_string: Create a new Internet Address object - * @string: rfc822 internet address string + * internet_address_to_string: Write the InternetAddress object to a string + * @ia: Internet Address object + * @encode: TRUE if the address should be rfc2047 encoded * - * Returns a new Internet Address object based upon the rfc822 address - * string. + * Returns the InternetAddress object as an allocated string in rfc822 + * format. **/ -InternetAddress * -internet_address_new_from_string (const gchar *string) +gchar * +internet_address_to_string (InternetAddress *ia, gboolean encode) { - InternetAddress *ia; - GPtrArray *tokens; - gchar *name = NULL, *address = NULL; - int i; - - g_return_val_if_fail (string != NULL, NULL); - g_return_val_if_fail (*string != '\0', NULL); + char *string = NULL; - tokens = rfc822_tokenize (string, strlen (string)); - if (!tokens->len) { - g_ptr_array_free (tokens, TRUE); - return NULL; + if (ia->type == INTERNET_ADDRESS_NAME) { + if (ia->name) { + char *name; + + name = encoded_name (ia->name, encode); + string = g_strdup_printf ("%s <%s>", name, + ia->value.addr); + g_free (name); + } else { + string = g_strdup (ia->value.addr); + } + } else if (ia->type == INTERNET_ADDRESS_GROUP) { + GList *members; + GString *gstr; + + gstr = g_string_new (ia->name); + g_string_append (gstr, ": "); + + members = ia->value.members; + while (members) { + InternetAddress *member; + char *addr; + + member = members->data; + members = members->next; + + addr = internet_address_to_string (member, encode); + if (addr) { + g_string_append (gstr, addr); + g_free (addr); + if (members) + g_string_append (gstr, ", "); + } + } + + g_string_append (gstr, ";"); + + string = gstr->str; + g_string_free (gstr, FALSE); } - /* find the address */ - for (i = 0; i < tokens->len; i++) { - gchar *token = tokens->pdata[i]; + return string; +} + +static void +decode_lwsp (const char **in) +{ + const char *inptr = *in; + + while (*inptr && (*inptr == '(' || is_lwsp (*inptr))) { + while (*inptr && is_lwsp (*inptr)) + inptr++; - if (*token == '<' && *(token + strlen (token) - 1) == '>') { - address = token; + /* skip over any comments */ + if (*inptr == '(') { + int depth = 1; - /* strip the <>'s */ - memmove (address, address + 1, strlen (address)); - *(address + strlen (address) - 1) = '\0'; - - /* remove the address from the list of tokens */ - g_ptr_array_remove_index (tokens, i); - break; + inptr++; + while (*inptr && depth) { + if (*inptr == '\\' && *(inptr + 1)) + inptr++; + else if (*inptr == '(') + depth++; + else if (*inptr == ')') + depth--; + + inptr++; + } } } - if (!address) { - /* the first token should be the address if it wasn't surrounded in <>'s */ - address = tokens->pdata[0]; - g_ptr_array_remove_index (tokens, 0); + *in = inptr; +} + +static char * +decode_quoted_string (const char **in) +{ + const char *inptr = *in; + GString *string = NULL; + char *out = NULL; + + decode_lwsp (&inptr); + if (*inptr == '"') { + out = (char *) inptr; + + inptr++; + while (*inptr && *inptr != '"') { + if (*inptr == '\\') + inptr++; + + if (*inptr) + inptr++; + } + + if (*inptr == '"') + inptr++; + + out = g_strndup (out, inptr - out); } - /* recreate the name from the tokens */ - if (tokens->len) { - char *token = tokens->pdata[0]; - char *end = token + strlen (token) - 1; + *in = inptr; + + return out; +} + +#if 0 +static char * +decode_quoted_string (const char **in) +{ + const char *inptr = *in; + char *out = NULL, *outptr; + int outlen; + int c; + + decode_lwsp (&inptr); + if (*inptr == '"') { + const char *intmp; + int skip = 0; - if (*token == '(' && *end == ')') { - token++; - *end = '\0'; + /* first, calc length */ + inptr++; + intmp = inptr + 1; + while ((c = *intmp++) && c != '"') { + if (c == '\\' && *intmp) { + intmp++; + skip++; + } + } + + outlen = intmp - inptr - skip; + out = outptr = g_malloc (outlen + 1); + + while ((c = *inptr++) && c != '"') { + if (c == '\\' && *inptr) { + c = *inptr++; + } + *outptr++ = c; + } + *outptr = '\0'; + } + + *in = inptr; + + return out; +} +#endif + +static char * +decode_atom (const char **in) +{ + const char *inptr = *in, *start; + + decode_lwsp (&inptr); + start = inptr; + while (is_atom (*inptr)) + inptr++; + *in = inptr; + if (inptr > start) + return g_strndup (start, inptr - start); + else + return NULL; +} + +static char * +decode_word (const char **in) +{ + const char *inptr = *in; + + decode_lwsp (&inptr); + if (*inptr == '"') { + *in = inptr; + return decode_quoted_string (in); + } else { + *in = inptr; + return decode_atom (in); + } +} + +static gboolean +decode_subliteral (const char **in, GString *domain) +{ + const char *inptr = *in; + gboolean got = FALSE; + + while (*inptr && *inptr != '.' && *inptr != ']') { + if (is_dtext (*inptr)) { + g_string_append_c (domain, *inptr); + inptr++; + got = TRUE; + } else if (is_lwsp (*inptr)) + decode_lwsp (&inptr); + else + break; + } + + *in = inptr; + + return got; +} + +static void +decode_domain_literal (const char **in, GString *domain) +{ + const char *inptr = *in; + + decode_lwsp (&inptr); + while (*inptr && *inptr != ']') { + if (decode_subliteral (&inptr, domain) && *inptr == '.') { + g_string_append_c (domain, *inptr); + inptr++; + } else if (*inptr != ']') { + g_warning ("Malformed domain-literal, " + "unexpected char (%c): %s", *inptr, *in); + + /* try and skip to the next char ?? */ + inptr++; + } + } + + *in = inptr; +} + +static char * +decode_domain (const char **in) +{ + const char *inptr, *save; + GString *domain; + char *dom, *atom; + + domain = g_string_new (""); + + inptr = *in; + while (TRUE) { + decode_lwsp (&inptr); + if (*inptr == '[') { + /* domain literal */ + g_string_append_c (domain, '['); + inptr++; - name = g_mime_utils_8bit_header_decode (token); + decode_domain_literal (&inptr, domain); + + if (*inptr == ']') { + g_string_append_c (domain, ']'); + inptr++; + } else + g_warning ("Missing ']' in domain-literal: %s", *in); } else { - name = g_strjoinv (" ", (gchar **) tokens->pdata); + atom = decode_atom (&inptr); + if (atom) + g_string_append (domain, atom); + g_free (atom); + } + + save = inptr; + decode_lwsp (&inptr); + if (*inptr != '.') { + inptr = save; + break; } + + g_string_append_c (domain, '.'); + inptr++; } - for (i = 0; i < tokens->len; i++) - g_free (tokens->pdata[i]); - g_ptr_array_free (tokens, TRUE); + dom = domain->str; + g_string_free (domain, FALSE); - ia = internet_address_new (name, address); - g_free (name); - g_free (address); + *in = inptr; - return ia; + return dom; } - -/** - * internet_address_destroy: Destroy an Internet Address object - * @ia: Internet Address object to destroy - * - * Destroy the InternetAddress object pointed to by #ia. - **/ -void -internet_address_destroy (InternetAddress *ia) +static InternetAddress * +decode_mailbox (const char **in) { - g_return_if_fail (ia != NULL); + InternetAddress *mailbox = NULL; + const char *inptr; + gboolean bracket = FALSE; + GString *name = NULL; + GString *addr; + char *pre; - g_free (ia->name); - g_free (ia->address); - g_free (ia); + addr = g_string_new (""); + + decode_lwsp (in); + inptr = *in; + + pre = decode_word (&inptr); + decode_lwsp (&inptr); + if (*inptr && !strchr (",.@", *inptr)) { + /* this mailbox has a name part, so get the name */ + name = g_string_new (""); + while (pre) { + g_string_append (name, pre); + g_free (pre); + pre = decode_word (&inptr); + if (pre) + g_string_append_c (name, ' '); + } + + decode_lwsp (&inptr); + if (*inptr == '<') { + inptr++; + bracket = TRUE; + pre = decode_word (&inptr); + } else { + g_string_free (name, TRUE); + g_string_free (addr, TRUE); + *in = inptr; + + return NULL; + } + } + + if (pre) { + g_string_append (addr, pre); + } else { + g_warning ("No local part for email address: %s", *in); + if (name) + g_string_free (name, TRUE); + g_string_free (addr, TRUE); + return NULL; + } + + /* get the rest of the local-part */ + decode_lwsp (&inptr); + while (*inptr == '.' && pre) { + inptr++; + g_free (pre); + pre = decode_word (&inptr); + if (pre) { + g_string_append_c (addr, '.'); + g_string_append (addr, pre); + } + decode_lwsp (&inptr); + } + g_free (pre); + + /* we should be at the '@' now... */ + if (*inptr == '@') { + char *domain; + + g_string_append_c (addr, '@'); + inptr++; + + domain = decode_domain (&inptr); + g_string_append (addr, domain); + g_free (domain); + } else { + g_warning ("No domain in email address: %s", *in); + } + + if (bracket) { + decode_lwsp (&inptr); + if (*inptr == '>') + inptr++; + else + g_warning ("Missing closing '>' bracket for email address: %s", *in); + } + + if (!name) { + /* look for a trailing comment to use as the name? */ + char *comment; + + comment = (char *) inptr; + decode_lwsp (&inptr); + if (inptr > comment) { + comment = memchr (comment, '(', inptr - comment); + if (comment) { + const char *cend; + + /* find the end of the comment */ + for (cend = inptr - 1; cend > comment && is_lwsp (*cend); cend--); + if (*cend == ')') + cend--; + comment = g_strndup (comment + 1, cend - comment); + + name = g_string_new (comment); + g_free (comment); + } + } + } + + *in = inptr; + + if (addr->len) + mailbox = internet_address_new_name (name ? name->str : NULL, addr->str); + + g_string_free (addr, TRUE); + if (name) + g_string_free (name, TRUE); + + return mailbox; } -static gchar * -encoded_name (const gchar *raw, gboolean rfc2047_encode) +static InternetAddress * +decode_address (const char **in) { - gchar *name; + InternetAddress *addr = NULL, *member; + const char *inptr, *start; + GString *name; + char *pre; - g_return_val_if_fail (raw != NULL, NULL); + decode_lwsp (in); + start = inptr = *in; - if (rfc2047_encode && g_mime_utils_text_is_8bit (raw, strlen (raw))) { - name = g_mime_utils_8bit_header_encode_phrase (raw); + /* pre-scan */ + name = g_string_new (""); + pre = decode_word (&inptr); + while (pre) { + g_string_append (name, pre); + g_free (pre); + + pre = decode_word (&inptr); + if (pre) + g_string_append_c (name, ' '); + } + + decode_lwsp (&inptr); + if (*inptr == ':') { + /* this is a group */ + inptr++; + addr = internet_address_new_group (name->str); + + decode_lwsp (&inptr); + while (*inptr && *inptr != ';') { + InternetAddress *member; + + member = decode_mailbox (&inptr); + if (member) + internet_address_add_member (addr, member); + + decode_lwsp (&inptr); + while (*inptr == ',') { + inptr++; + decode_lwsp (&inptr); + member = decode_mailbox (&inptr); + if (member) + internet_address_add_member (addr, member); + + decode_lwsp (&inptr); + } + } + + if (*inptr == ';') + inptr++; + else + g_warning ("Invalid group spec, missing closing ';': %.*s", + inptr - start, start); + + *in = inptr; } else { - name = g_mime_utils_quote_string (raw); + /* this is a mailbox */ + addr = decode_mailbox (in); } - return name; + g_string_free (name, TRUE); + + return addr; } /** - * internet_address_to_string: Write the InternetAddress object to a string - * @ia: Internet Address object - * @rfc2047_encode: TRUE if the address should be encoded - * - * Returns the InternetAddress object as an allocated string in rfc822 - * format. + * internet_address_paarse_string: + * @string: a string containing internet addresses + * + * Construct a list of internet addresses from the given string. + * + * Returns a linked list of internet addresses. **/ -gchar * -internet_address_to_string (InternetAddress *ia, gboolean rfc2047_encode) +GList * +internet_address_parse_string (const char *string) { - gchar *name, *string; + GList *addrlist = NULL; + const char *inptr; - g_return_val_if_fail (ia != NULL, NULL); + inptr = string; - if (ia->name) { - name = encoded_name (ia->name, rfc2047_encode); - string = g_strdup_printf ("%s <%s>", name, ia->address); - g_free (name); - } else { - string = g_strdup (ia->address); + while (inptr && *inptr) { + InternetAddress *addr; + const char *start; + + start = inptr; + + addr = decode_address (&inptr); + + if (addr) + addrlist = g_list_append (addrlist, addr); + else + g_warning ("Invalid or incomplete address: %.*s", inptr - start, start); + + decode_lwsp (&inptr); + if (*inptr == ',') + inptr++; + else if (*inptr) { + g_warning ("Parse error at '%s': expected ','", inptr); + /* try skipping to the next address */ + inptr = strchr (inptr, ','); + if (inptr) + inptr++; + } } - return string; + return addrlist; } diff --git a/internet-address.h b/internet-address.h index 629ae11a..003f04da 100644 --- a/internet-address.h +++ b/internet-address.h @@ -30,18 +30,36 @@ extern "C" { #include <glib.h> +typedef enum { + INTERNET_ADDRESS_NONE, + INTERNET_ADDRESS_NAME, + INTERNET_ADDRESS_GROUP +} InternetAddressType; + struct _InternetAddress { + InternetAddressType type; gchar *name; - gchar *address; + union { + gchar *addr; + GList *members; + } value; }; typedef struct _InternetAddress InternetAddress; -InternetAddress *internet_address_new (const gchar *name, const gchar *address); -InternetAddress *internet_address_new_from_string (const gchar *string); +InternetAddress *internet_address_new (void); +InternetAddress *internet_address_new_name (const gchar *name, const gchar *addr); +InternetAddress *internet_address_new_group (const gchar *name); void internet_address_destroy (InternetAddress *ia); +void internet_address_set_name (InternetAddress *ia, const gchar *name); +void internet_address_set_addr (InternetAddress *ia, const gchar *addr); +void internet_address_set_group (InternetAddress *ia, GList *group); +void internet_address_add_member (InternetAddress *ia, InternetAddress *member); + +GList *internet_address_parse_string (const gchar *string); + gchar *internet_address_to_string (InternetAddress *ia, gboolean rfc2047_encode); #ifdef __cplusplus diff --git a/test-mime.c b/test-mime.c index d00e4b20..5ec2b94b 100644 --- a/test-mime.c +++ b/test-mime.c @@ -157,23 +157,14 @@ test_encodings (void) { char *enc, *dec; int pos, state = -1, save = 0; - - fprintf (stderr, "hello\n"); - - enc = g_strdup ("fpons@mandrakesoft.com (=?iso-8859-1?q?Fran=E7ois?= Pons)"); - fprintf (stderr, "encoded: %s\n", enc); - dec = g_mime_utils_8bit_header_decode (enc); - fprintf (stderr, "decoded: %s\n", dec); - g_free (enc); - g_free (dec); - + enc = g_strdup ("=?iso-8859-1?q?blablah?="); fprintf (stderr, "encoded: %s\n", enc); dec = g_mime_utils_8bit_header_decode (enc); fprintf (stderr, "decoded: %s\n", dec); g_free (enc); g_free (dec); - + enc = g_strdup ("=?iso-8859-1?Q?blablah?="); fprintf (stderr, "encoded: %s\n", enc); dec = g_mime_utils_8bit_header_decode (enc); @@ -250,32 +241,80 @@ static gchar *addresses[] = { "Jeffrey \"fejj\" Stedfast <fejj@helixcode.com>", "\"Stedfast, Jeffrey\" <fejj@helixcode.com>", "fejj@helixcode.com (Jeffrey Stedfast)", - "<fejj@helixcode.com> (Jeff)", + "Jeff <fejj(recursive (comment) block)@helixcode.(and a comment here)com>", "=?iso-8859-1?q?Kristoffer=20Br=E5nemyr?= <ztion@swipenet.se>", "fpons@mandrakesoft.com (=?iso-8859-1?q?Fran=E7ois?= Pons)", + "GNOME Hackers: miguel@gnome.org (Miguel de Icaza), Havoc Pennington <hp@redhat.com>;, fejj@helixcode.com", + "Local recipients: phil, joe, alex, bob", + "@develop:sblab!att!thumper.bellcore.com!nsb", + "\":sysmail\"@ Some-Group. Some-Org,\n Muhammed.(I am the greatest) Ali @(the)Vegas.WBA", + "Charles S. Kerr <charles@foo.com>", + "Charles \"Likes, to, put, commas, in, quoted, strings\" Kerr <charles@foo.com>", + "Charles Kerr, Pan Programmer <charles@superpimp.org>", + "Charles Kerr <charles@[127.0.0.1]>", + "Charles <charles@[127..0.1]>", + "<charles@>", NULL }; +static void +dump_addrlist (GList *addrlist, int i, gboolean group, gboolean destroy) +{ + InternetAddress *ia; + GList *addr; + + addr = addrlist; + while (addr) { + char *str; + + ia = addr->data; + addr = addr->next; + + if (i != -1) + fprintf (stderr, "Original: %s\n", addresses[i]); + if (ia->type == INTERNET_ADDRESS_GROUP) { + fprintf (stderr, "Address is a group:\n"); + fprintf (stderr, "Name: %s\n", ia->name ? ia->name : ""); + dump_addrlist (ia->value.members, -1, TRUE, FALSE); + fprintf (stderr, "End of group.\n"); + } else if (ia->type == INTERNET_ADDRESS_NAME) { + fprintf (stderr, "%sName: %s\n", group ? "\t" : "", + ia->name ? ia->name : ""); + fprintf (stderr, "%sEMail: %s\n", group ? "\t" : "", + ia->value.addr ? ia->value.addr : ""); + } + + str = internet_address_to_string (ia, FALSE); + fprintf (stderr, "%sRewritten (display): %s\n", group ? "\t" : "", + str ? str : ""); + g_free (str); + + str = internet_address_to_string (ia, TRUE); + fprintf (stderr, "%sRewritten (encoded): %s\n\n", group ? "\t" : "", + str ? str : ""); + g_free (str); + + if (destroy) + internet_address_destroy (ia); + } +} + void test_addresses (void) { - InternetAddress *ia; - gchar *str; int i; for (i = 0; addresses[i]; i++) { - ia = internet_address_new_from_string (addresses[i]); - if (!ia) { + InternetAddress *ia; + GList *addrlist, *l; + + addrlist = internet_address_parse_string (addresses[i]); + if (!addrlist) { fprintf (stderr, "failed to parse '%s'.\n", addresses[i]); continue; } - fprintf (stderr, "Original: %s\n", addresses[i]); - fprintf (stderr, "Name: %s\n", ia->name ? ia->name : ""); - fprintf (stderr, "EMail: %s\n", ia->address ? ia->address : ""); - str = internet_address_to_string (ia, TRUE); - internet_address_destroy (ia); - fprintf (stderr, "Rewritten: %s\n\n", str ? str : "(null)"); - g_free (str); + + dump_addrlist (addrlist, i, FALSE, TRUE); } } diff --git a/tests/test-mime.c b/tests/test-mime.c index d00e4b20..5ec2b94b 100644 --- a/tests/test-mime.c +++ b/tests/test-mime.c @@ -157,23 +157,14 @@ test_encodings (void) { char *enc, *dec; int pos, state = -1, save = 0; - - fprintf (stderr, "hello\n"); - - enc = g_strdup ("fpons@mandrakesoft.com (=?iso-8859-1?q?Fran=E7ois?= Pons)"); - fprintf (stderr, "encoded: %s\n", enc); - dec = g_mime_utils_8bit_header_decode (enc); - fprintf (stderr, "decoded: %s\n", dec); - g_free (enc); - g_free (dec); - + enc = g_strdup ("=?iso-8859-1?q?blablah?="); fprintf (stderr, "encoded: %s\n", enc); dec = g_mime_utils_8bit_header_decode (enc); fprintf (stderr, "decoded: %s\n", dec); g_free (enc); g_free (dec); - + enc = g_strdup ("=?iso-8859-1?Q?blablah?="); fprintf (stderr, "encoded: %s\n", enc); dec = g_mime_utils_8bit_header_decode (enc); @@ -250,32 +241,80 @@ static gchar *addresses[] = { "Jeffrey \"fejj\" Stedfast <fejj@helixcode.com>", "\"Stedfast, Jeffrey\" <fejj@helixcode.com>", "fejj@helixcode.com (Jeffrey Stedfast)", - "<fejj@helixcode.com> (Jeff)", + "Jeff <fejj(recursive (comment) block)@helixcode.(and a comment here)com>", "=?iso-8859-1?q?Kristoffer=20Br=E5nemyr?= <ztion@swipenet.se>", "fpons@mandrakesoft.com (=?iso-8859-1?q?Fran=E7ois?= Pons)", + "GNOME Hackers: miguel@gnome.org (Miguel de Icaza), Havoc Pennington <hp@redhat.com>;, fejj@helixcode.com", + "Local recipients: phil, joe, alex, bob", + "@develop:sblab!att!thumper.bellcore.com!nsb", + "\":sysmail\"@ Some-Group. Some-Org,\n Muhammed.(I am the greatest) Ali @(the)Vegas.WBA", + "Charles S. Kerr <charles@foo.com>", + "Charles \"Likes, to, put, commas, in, quoted, strings\" Kerr <charles@foo.com>", + "Charles Kerr, Pan Programmer <charles@superpimp.org>", + "Charles Kerr <charles@[127.0.0.1]>", + "Charles <charles@[127..0.1]>", + "<charles@>", NULL }; +static void +dump_addrlist (GList *addrlist, int i, gboolean group, gboolean destroy) +{ + InternetAddress *ia; + GList *addr; + + addr = addrlist; + while (addr) { + char *str; + + ia = addr->data; + addr = addr->next; + + if (i != -1) + fprintf (stderr, "Original: %s\n", addresses[i]); + if (ia->type == INTERNET_ADDRESS_GROUP) { + fprintf (stderr, "Address is a group:\n"); + fprintf (stderr, "Name: %s\n", ia->name ? ia->name : ""); + dump_addrlist (ia->value.members, -1, TRUE, FALSE); + fprintf (stderr, "End of group.\n"); + } else if (ia->type == INTERNET_ADDRESS_NAME) { + fprintf (stderr, "%sName: %s\n", group ? "\t" : "", + ia->name ? ia->name : ""); + fprintf (stderr, "%sEMail: %s\n", group ? "\t" : "", + ia->value.addr ? ia->value.addr : ""); + } + + str = internet_address_to_string (ia, FALSE); + fprintf (stderr, "%sRewritten (display): %s\n", group ? "\t" : "", + str ? str : ""); + g_free (str); + + str = internet_address_to_string (ia, TRUE); + fprintf (stderr, "%sRewritten (encoded): %s\n\n", group ? "\t" : "", + str ? str : ""); + g_free (str); + + if (destroy) + internet_address_destroy (ia); + } +} + void test_addresses (void) { - InternetAddress *ia; - gchar *str; int i; for (i = 0; addresses[i]; i++) { - ia = internet_address_new_from_string (addresses[i]); - if (!ia) { + InternetAddress *ia; + GList *addrlist, *l; + + addrlist = internet_address_parse_string (addresses[i]); + if (!addrlist) { fprintf (stderr, "failed to parse '%s'.\n", addresses[i]); continue; } - fprintf (stderr, "Original: %s\n", addresses[i]); - fprintf (stderr, "Name: %s\n", ia->name ? ia->name : ""); - fprintf (stderr, "EMail: %s\n", ia->address ? ia->address : ""); - str = internet_address_to_string (ia, TRUE); - internet_address_destroy (ia); - fprintf (stderr, "Rewritten: %s\n\n", str ? str : "(null)"); - g_free (str); + + dump_addrlist (addrlist, i, FALSE, TRUE); } } |