diff options
Diffstat (limited to 'camel/camel-mime-utils.c')
-rw-r--r-- | camel/camel-mime-utils.c | 4318 |
1 files changed, 0 insertions, 4318 deletions
diff --git a/camel/camel-mime-utils.c b/camel/camel-mime-utils.c deleted file mode 100644 index 510d07f019..0000000000 --- a/camel/camel-mime-utils.c +++ /dev/null @@ -1,4318 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2000-2003 Ximian Inc. - * - * Authors: Michael Zucchi <notzed@ximian.com> - * Jeffrey Stedfast <fejj@ximian.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * 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 Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* dont touch this file without my permission - Michael */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/param.h> /* for MAXHOSTNAMELEN */ -#include <sys/stat.h> -#include <pthread.h> -#include <unistd.h> -#include <regex.h> -#include <fcntl.h> -#include <errno.h> -#include <ctype.h> -#include <time.h> - -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 1024 -#endif - -#include <glib.h> -#include <libedataserver/e-iconv.h> -#include <libedataserver/e-time-utils.h> - -#include "camel-mime-utils.h" -#include "camel-charset-map.h" -#include "camel-net-utils.h" -#include "camel-utf8.h" - -#ifndef CLEAN_DATE -#include "broken-date-parser.h" -#endif - -#if 0 -int strdup_count = 0; -int malloc_count = 0; -int free_count = 0; - -#define g_strdup(x) (strdup_count++, g_strdup(x)) -#define g_malloc(x) (malloc_count++, g_malloc(x)) -#define g_free(x) (free_count++, g_free(x)) -#endif - -/* for all non-essential warnings ... */ -#define w(x) - -#define d(x) -#define d2(x) - -#define CAMEL_UUENCODE_CHAR(c) ((c) ? (c) + ' ' : '`') -#define CAMEL_UUDECODE_CHAR(c) (((c) - ' ') & 077) - -static char *base64_alphabet = -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static unsigned char tohex[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' -}; - -unsigned short camel_mime_special_table[256]; -static unsigned char camel_mime_base64_rank[256]; - -/* Used by table initialisation code for special characters */ -#define CHARS_LWSP " \t\n\r" -#define CHARS_TSPECIAL "()<>@,;:\\\"/[]?=" -#define CHARS_SPECIAL "()<>@,;:\\\".[]" -#define CHARS_CSPECIAL "()\\\r" /* not in comments */ -#define CHARS_DSPECIAL "[]\\\r \t" /* not in domains */ -#define CHARS_ESPECIAL "()<>@,;:\"/[]?.=_" /* list of characters that must be encoded. - encoded word in text specials: rfc 2047 5(1)*/ -#define CHARS_PSPECIAL "!*+-/" /* list of additional characters that can be left unencoded. - encoded word in phrase specials: rfc 2047 5(3) */ -#define CHARS_ATTRCHAR "*\'% " /* extra non-included attribute-chars */ - -static void -header_remove_bits(unsigned short bit, unsigned char *vals) -{ - int i; - - for (i=0;vals[i];i++) - camel_mime_special_table[vals[i]] &= ~ bit; -} - -static void -header_init_bits(unsigned short bit, unsigned short bitcopy, int remove, unsigned char *vals) -{ - int i; - int len = strlen(vals); - - if (!remove) { - for (i=0;i<len;i++) { - camel_mime_special_table[vals[i]] |= bit; - } - if (bitcopy) { - for (i=0;i<256;i++) { - if (camel_mime_special_table[i] & bitcopy) - camel_mime_special_table[i] |= bit; - } - } - } else { - for (i=0;i<256;i++) - camel_mime_special_table[i] |= bit; - for (i=0;i<len;i++) { - camel_mime_special_table[vals[i]] &= ~bit; - } - if (bitcopy) { - for (i=0;i<256;i++) { - if (camel_mime_special_table[i] & bitcopy) - camel_mime_special_table[i] &= ~bit; - } - } - } -} - -static void -header_decode_init(void) -{ - int i; - - for (i=0;i<256;i++) { - camel_mime_special_table[i] = 0; - if (i<32 || i==127) - camel_mime_special_table[i] |= CAMEL_MIME_IS_CTRL; - else if (i < 127) - camel_mime_special_table[i] |= CAMEL_MIME_IS_ATTRCHAR; - if ((i>=32 && i<=60) || (i>=62 && i<=126) || i==9) - camel_mime_special_table[i] |= (CAMEL_MIME_IS_QPSAFE|CAMEL_MIME_IS_ESAFE); - if ((i>='0' && i<='9') || (i>='a' && i<='z') || (i>='A' && i<= 'Z')) - camel_mime_special_table[i] |= CAMEL_MIME_IS_PSAFE; - } - camel_mime_special_table[' '] |= CAMEL_MIME_IS_SPACE; - header_init_bits(CAMEL_MIME_IS_LWSP, 0, 0, CHARS_LWSP); - header_init_bits(CAMEL_MIME_IS_TSPECIAL, CAMEL_MIME_IS_CTRL, 0, CHARS_TSPECIAL); - header_init_bits(CAMEL_MIME_IS_SPECIAL, 0, 0, CHARS_SPECIAL); - header_init_bits(CAMEL_MIME_IS_DSPECIAL, 0, FALSE, CHARS_DSPECIAL); - header_remove_bits(CAMEL_MIME_IS_ESAFE, CHARS_ESPECIAL); - header_remove_bits(CAMEL_MIME_IS_ATTRCHAR, CHARS_TSPECIAL CHARS_ATTRCHAR); - header_init_bits(CAMEL_MIME_IS_PSAFE, 0, 0, CHARS_PSPECIAL); -} - -static void -base64_init(void) -{ - int i; - - memset(camel_mime_base64_rank, 0xff, sizeof(camel_mime_base64_rank)); - for (i=0;i<64;i++) { - camel_mime_base64_rank[(unsigned int)base64_alphabet[i]] = i; - } - camel_mime_base64_rank['='] = 0; -} - -/* call this when finished encoding everything, to - flush off the last little bit */ -size_t -camel_base64_encode_close(unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save) -{ - int c1, c2; - unsigned char *outptr = out; - - if (inlen>0) - outptr += camel_base64_encode_step(in, inlen, break_lines, outptr, state, save); - - c1 = ((unsigned char *)save)[1]; - c2 = ((unsigned char *)save)[2]; - - d(printf("mode = %d\nc1 = %c\nc2 = %c\n", - (int)((char *)save)[0], - (int)((char *)save)[1], - (int)((char *)save)[2])); - - switch (((char *)save)[0]) { - case 2: - outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; - g_assert(outptr[2] != 0); - goto skip; - case 1: - outptr[2] = '='; - skip: - outptr[0] = base64_alphabet[ c1 >> 2 ]; - outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )]; - outptr[3] = '='; - outptr += 4; - break; - } - if (break_lines) - *outptr++ = '\n'; - - *save = 0; - *state = 0; - - return outptr-out; -} - -/* - performs an 'encode step', only encodes blocks of 3 characters to the - output at a time, saves left-over state in state and save (initialise to - 0 on first invocation). -*/ -size_t -camel_base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save) -{ - register unsigned char *inptr, *outptr; - - if (len<=0) - return 0; - - inptr = in; - outptr = out; - - d(printf("we have %d chars, and %d saved chars\n", len, ((char *)save)[0])); - - if (len + ((char *)save)[0] > 2) { - unsigned char *inend = in+len-2; - register int c1, c2, c3; - register int already; - - already = *state; - - switch (((char *)save)[0]) { - case 1: c1 = ((unsigned char *)save)[1]; goto skip1; - case 2: c1 = ((unsigned char *)save)[1]; - c2 = ((unsigned char *)save)[2]; goto skip2; - } - - /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */ - while (inptr < inend) { - c1 = *inptr++; - skip1: - c2 = *inptr++; - skip2: - c3 = *inptr++; - *outptr++ = base64_alphabet[ c1 >> 2 ]; - *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ]; - *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ]; - *outptr++ = base64_alphabet[ c3 & 0x3f ]; - /* this is a bit ugly ... */ - if (break_lines && (++already)>=19) { - *outptr++='\n'; - already = 0; - } - } - - ((char *)save)[0] = 0; - len = 2-(inptr-inend); - *state = already; - } - - d(printf("state = %d, len = %d\n", - (int)((char *)save)[0], - len)); - - if (len>0) { - register char *saveout; - - /* points to the slot for the next char to save */ - saveout = & (((char *)save)[1]) + ((char *)save)[0]; - - /* len can only be 0 1 or 2 */ - switch(len) { - case 2: *saveout++ = *inptr++; - case 1: *saveout++ = *inptr++; - } - ((char *)save)[0]+=len; - } - - d(printf("mode = %d\nc1 = %c\nc2 = %c\n", - (int)((char *)save)[0], - (int)((char *)save)[1], - (int)((char *)save)[2])); - - return outptr-out; -} - - -/** - * camel_base64_decode_step: decode a chunk of base64 encoded data - * @in: input stream - * @len: max length of data to decode - * @out: output stream - * @state: holds the number of bits that are stored in @save - * @save: leftover bits that have not yet been decoded - * - * Decodes a chunk of base64 encoded data - **/ -size_t -camel_base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save) -{ - register unsigned char *inptr, *outptr; - unsigned char *inend, c; - register unsigned int v; - int i; - - inend = in+len; - outptr = out; - - /* convert 4 base64 bytes to 3 normal bytes */ - v=*save; - i=*state; - inptr = in; - while (inptr<inend) { - c = camel_mime_base64_rank[*inptr++]; - if (c != 0xff) { - v = (v<<6) | c; - i++; - if (i==4) { - *outptr++ = v>>16; - *outptr++ = v>>8; - *outptr++ = v; - i=0; - } - } - } - - *save = v; - *state = i; - - /* quick scan back for '=' on the end somewhere */ - /* fortunately we can drop 1 output char for each trailing = (upto 2) */ - i=2; - while (inptr>in && i) { - inptr--; - if (camel_mime_base64_rank[*inptr] != 0xff) { - if (*inptr == '=' && outptr>out) - outptr--; - i--; - } - } - - /* if i!= 0 then there is a truncation error! */ - return outptr-out; -} - -char * -camel_base64_encode_simple (const char *data, size_t len) -{ - unsigned char *out; - int state = 0, outlen; - unsigned int save = 0; - - out = g_malloc (len * 4 / 3 + 5); - outlen = camel_base64_encode_close ((unsigned char *)data, len, FALSE, - out, &state, &save); - out[outlen] = '\0'; - return (char *)out; -} - -size_t -camel_base64_decode_simple (char *data, size_t len) -{ - int state = 0; - unsigned int save = 0; - - return camel_base64_decode_step ((unsigned char *)data, len, - (unsigned char *)data, &state, &save); -} - -/** - * camel_uuencode_close: uuencode a chunk of data - * @in: input stream - * @len: input stream length - * @out: output stream - * @uubuf: temporary buffer of 60 bytes - * @state: holds the number of bits that are stored in @save - * @save: leftover bits that have not yet been encoded - * - * Returns the number of bytes encoded. Call this when finished - * encoding data with camel_uuencode_step to flush off the last little - * bit. - **/ -size_t -camel_uuencode_close (unsigned char *in, size_t len, unsigned char *out, unsigned char *uubuf, int *state, guint32 *save) -{ - register unsigned char *outptr, *bufptr; - register guint32 saved; - int uulen, uufill, i; - - outptr = out; - - if (len > 0) - outptr += camel_uuencode_step (in, len, out, uubuf, state, save); - - uufill = 0; - - saved = *save; - i = *state & 0xff; - uulen = (*state >> 8) & 0xff; - - bufptr = uubuf + ((uulen / 3) * 4); - - if (i > 0) { - while (i < 3) { - saved <<= 8 | 0; - uufill++; - i++; - } - - if (i == 3) { - /* convert 3 normal bytes into 4 uuencoded bytes */ - unsigned char b0, b1, b2; - - b0 = saved >> 16; - b1 = saved >> 8 & 0xff; - b2 = saved & 0xff; - - *bufptr++ = CAMEL_UUENCODE_CHAR ((b0 >> 2) & 0x3f); - *bufptr++ = CAMEL_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f); - *bufptr++ = CAMEL_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f); - *bufptr++ = CAMEL_UUENCODE_CHAR (b2 & 0x3f); - - i = 0; - saved = 0; - uulen += 3; - } - } - - if (uulen > 0) { - int cplen = ((uulen / 3) * 4); - - *outptr++ = CAMEL_UUENCODE_CHAR ((uulen - uufill) & 0xff); - memcpy (outptr, uubuf, cplen); - outptr += cplen; - *outptr++ = '\n'; - uulen = 0; - } - - *outptr++ = CAMEL_UUENCODE_CHAR (uulen & 0xff); - *outptr++ = '\n'; - - *save = 0; - *state = 0; - - return outptr - out; -} - - -/** - * camel_uuencode_step: uuencode a chunk of data - * @in: input stream - * @len: input stream length - * @out: output stream - * @uubuf: temporary buffer of 60 bytes - * @state: holds the number of bits that are stored in @save - * @save: leftover bits that have not yet been encoded - * - * Returns the number of bytes encoded. Performs an 'encode step', - * only encodes blocks of 45 characters to the output at a time, saves - * left-over state in @uubuf, @state and @save (initialize to 0 on first - * invocation). - **/ -size_t -camel_uuencode_step (unsigned char *in, size_t len, unsigned char *out, unsigned char *uubuf, int *state, guint32 *save) -{ - register unsigned char *inptr, *outptr, *bufptr; - unsigned char *inend; - register guint32 saved; - int uulen, i; - - saved = *save; - i = *state & 0xff; - uulen = (*state >> 8) & 0xff; - - inptr = in; - inend = in + len; - - outptr = out; - - bufptr = uubuf + ((uulen / 3) * 4); - - while (inptr < inend) { - while (uulen < 45 && inptr < inend) { - while (i < 3 && inptr < inend) { - saved = (saved << 8) | *inptr++; - i++; - } - - if (i == 3) { - /* convert 3 normal bytes into 4 uuencoded bytes */ - unsigned char b0, b1, b2; - - b0 = saved >> 16; - b1 = saved >> 8 & 0xff; - b2 = saved & 0xff; - - *bufptr++ = CAMEL_UUENCODE_CHAR ((b0 >> 2) & 0x3f); - *bufptr++ = CAMEL_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f); - *bufptr++ = CAMEL_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f); - *bufptr++ = CAMEL_UUENCODE_CHAR (b2 & 0x3f); - - i = 0; - saved = 0; - uulen += 3; - } - } - - if (uulen >= 45) { - *outptr++ = CAMEL_UUENCODE_CHAR (uulen & 0xff); - memcpy (outptr, uubuf, ((uulen / 3) * 4)); - outptr += ((uulen / 3) * 4); - *outptr++ = '\n'; - uulen = 0; - bufptr = uubuf; - } - } - - *save = saved; - *state = ((uulen & 0xff) << 8) | (i & 0xff); - - return outptr - out; -} - - -/** - * camel_uudecode_step: uudecode a chunk of data - * @in: input stream - * @inlen: max length of data to decode ( normally strlen(in) ??) - * @out: output stream - * @state: holds the number of bits that are stored in @save - * @save: leftover bits that have not yet been decoded - * - * Returns the number of bytes decoded. Performs a 'decode step' on - * a chunk of uuencoded data. Assumes the "begin <mode> <file name>" - * line has been stripped off. - **/ -size_t -camel_uudecode_step (unsigned char *in, size_t len, unsigned char *out, int *state, guint32 *save) -{ - register unsigned char *inptr, *outptr; - unsigned char *inend, ch; - register guint32 saved; - gboolean last_was_eoln; - int uulen, i; - - if (*state & CAMEL_UUDECODE_STATE_END) - return 0; - - saved = *save; - i = *state & 0xff; - uulen = (*state >> 8) & 0xff; - if (uulen == 0) - last_was_eoln = TRUE; - else - last_was_eoln = FALSE; - - inend = in + len; - outptr = out; - - inptr = in; - while (inptr < inend) { - if (*inptr == '\n' || last_was_eoln) { - if (last_was_eoln && *inptr != '\n') { - uulen = CAMEL_UUDECODE_CHAR (*inptr); - last_was_eoln = FALSE; - if (uulen == 0) { - *state |= CAMEL_UUDECODE_STATE_END; - break; - } - } else { - last_was_eoln = TRUE; - } - - inptr++; - continue; - } - - ch = *inptr++; - - if (uulen > 0) { - /* save the byte */ - saved = (saved << 8) | ch; - i++; - if (i == 4) { - /* convert 4 uuencoded bytes to 3 normal bytes */ - unsigned char b0, b1, b2, b3; - - b0 = saved >> 24; - b1 = saved >> 16 & 0xff; - b2 = saved >> 8 & 0xff; - b3 = saved & 0xff; - - if (uulen >= 3) { - *outptr++ = CAMEL_UUDECODE_CHAR (b0) << 2 | CAMEL_UUDECODE_CHAR (b1) >> 4; - *outptr++ = CAMEL_UUDECODE_CHAR (b1) << 4 | CAMEL_UUDECODE_CHAR (b2) >> 2; - *outptr++ = CAMEL_UUDECODE_CHAR (b2) << 6 | CAMEL_UUDECODE_CHAR (b3); - } else { - if (uulen >= 1) { - *outptr++ = CAMEL_UUDECODE_CHAR (b0) << 2 | CAMEL_UUDECODE_CHAR (b1) >> 4; - } - if (uulen >= 2) { - *outptr++ = CAMEL_UUDECODE_CHAR (b1) << 4 | CAMEL_UUDECODE_CHAR (b2) >> 2; - } - } - - i = 0; - saved = 0; - uulen -= 3; - } - } else { - break; - } - } - - *save = saved; - *state = (*state & CAMEL_UUDECODE_STATE_MASK) | ((uulen & 0xff) << 8) | (i & 0xff); - - return outptr - out; -} - - -/* complete qp encoding */ -size_t -camel_quoted_decode_close(unsigned char *in, size_t len, unsigned char *out, int *state, int *save) -{ - register unsigned char *outptr = out; - int last; - - if (len>0) - outptr += camel_quoted_encode_step(in, len, outptr, state, save); - - last = *state; - if (last != -1) { - /* space/tab must be encoded if it's the last character on - the line */ - if (camel_mime_is_qpsafe(last) && last!=' ' && last!=9) { - *outptr++ = last; - } else { - *outptr++ = '='; - *outptr++ = tohex[(last>>4) & 0xf]; - *outptr++ = tohex[last & 0xf]; - } - } - - *save = 0; - *state = -1; - - return outptr-out; -} - -/* perform qp encoding, initialise state to -1 and save to 0 on first invocation */ -size_t -camel_quoted_encode_step (unsigned char *in, size_t len, unsigned char *out, int *statep, int *save) -{ - register guchar *inptr, *outptr, *inend; - unsigned char c; - register int sofar = *save; /* keeps track of how many chars on a line */ - register int last = *statep; /* keeps track if last char to end was a space cr etc */ - - inptr = in; - inend = in + len; - outptr = out; - while (inptr < inend) { - c = *inptr++; - if (c == '\r') { - if (last != -1) { - *outptr++ = '='; - *outptr++ = tohex[(last >> 4) & 0xf]; - *outptr++ = tohex[last & 0xf]; - sofar += 3; - } - last = c; - } else if (c == '\n') { - if (last != -1 && last != '\r') { - *outptr++ = '='; - *outptr++ = tohex[(last >> 4) & 0xf]; - *outptr++ = tohex[last & 0xf]; - } - *outptr++ = '\n'; - sofar = 0; - last = -1; - } else { - if (last != -1) { - if (camel_mime_is_qpsafe(last)) { - *outptr++ = last; - sofar++; - } else { - *outptr++ = '='; - *outptr++ = tohex[(last >> 4) & 0xf]; - *outptr++ = tohex[last & 0xf]; - sofar += 3; - } - } - - if (camel_mime_is_qpsafe(c)) { - if (sofar > 74) { - *outptr++ = '='; - *outptr++ = '\n'; - sofar = 0; - } - - /* delay output of space char */ - if (c==' ' || c=='\t') { - last = c; - } else { - *outptr++ = c; - sofar++; - last = -1; - } - } else { - if (sofar > 72) { - *outptr++ = '='; - *outptr++ = '\n'; - sofar = 3; - } else - sofar += 3; - - *outptr++ = '='; - *outptr++ = tohex[(c >> 4) & 0xf]; - *outptr++ = tohex[c & 0xf]; - last = -1; - } - } - } - *save = sofar; - *statep = last; - - return (outptr - out); -} - -/* - FIXME: this does not strip trailing spaces from lines (as it should, rfc 2045, section 6.7) - Should it also canonicalise the end of line to CR LF?? - - Note: Trailing rubbish (at the end of input), like = or =x or =\r will be lost. -*/ - -size_t -camel_quoted_decode_step(unsigned char *in, size_t len, unsigned char *out, int *savestate, int *saveme) -{ - register unsigned char *inptr, *outptr; - unsigned char *inend, c; - int state, save; - - inend = in+len; - outptr = out; - - d(printf("quoted-printable, decoding text '%.*s'\n", len, in)); - - state = *savestate; - save = *saveme; - inptr = in; - while (inptr<inend) { - switch (state) { - case 0: - while (inptr<inend) { - c = *inptr++; - if (c=='=') { - state = 1; - break; - } -#ifdef CANONICALISE_EOL - /*else if (c=='\r') { - state = 3; - } else if (c=='\n') { - *outptr++ = '\r'; - *outptr++ = c; - } */ -#endif - else { - *outptr++ = c; - } - } - break; - case 1: - c = *inptr++; - if (c=='\n') { - /* soft break ... unix end of line */ - state = 0; - } else { - save = c; - state = 2; - } - break; - case 2: - c = *inptr++; - if (isxdigit(c) && isxdigit(save)) { - c = toupper(c); - save = toupper(save); - *outptr++ = (((save>='A'?save-'A'+10:save-'0')&0x0f) << 4) - | ((c>='A'?c-'A'+10:c-'0')&0x0f); - } else if (c=='\n' && save == '\r') { - /* soft break ... canonical end of line */ - } else { - /* just output the data */ - *outptr++ = '='; - *outptr++ = save; - *outptr++ = c; - } - state = 0; - break; -#ifdef CANONICALISE_EOL - case 3: - /* convert \r -> to \r\n, leaves \r\n alone */ - c = *inptr++; - if (c=='\n') { - *outptr++ = '\r'; - *outptr++ = c; - } else { - *outptr++ = '\r'; - *outptr++ = '\n'; - *outptr++ = c; - } - state = 0; - break; -#endif - } - } - - *savestate = state; - *saveme = save; - - return outptr-out; -} - -/* - this is for the "Q" encoding of international words, - which is slightly different than plain quoted-printable (mainly by allowing 0x20 <> _) -*/ -static size_t -quoted_decode(const unsigned char *in, size_t len, unsigned char *out) -{ - register const unsigned char *inptr; - register unsigned char *outptr; - unsigned const char *inend; - unsigned char c, c1; - int ret = 0; - - inend = in+len; - outptr = out; - - d(printf("decoding text '%.*s'\n", len, in)); - - inptr = in; - while (inptr<inend) { - c = *inptr++; - if (c=='=') { - /* silently ignore truncated data? */ - if (inend-in>=2) { - c = toupper(*inptr++); - c1 = toupper(*inptr++); - *outptr++ = (((c>='A'?c-'A'+10:c-'0')&0x0f) << 4) - | ((c1>='A'?c1-'A'+10:c1-'0')&0x0f); - } else { - ret = -1; - break; - } - } else if (c=='_') { - *outptr++ = 0x20; - } else if (c==' ' || c==0x09) { - /* FIXME: this is an error! ignore for now ... */ - ret = -1; - break; - } else { - *outptr++ = c; - } - } - if (ret==0) { - return outptr-out; - } - return 0; -} - -/* rfc2047 version of quoted-printable */ -/* safemask is the mask to apply to the camel_mime_special_table to determine what - characters can safely be included without encoding */ -static size_t -quoted_encode (const unsigned char *in, size_t len, unsigned char *out, unsigned short safemask) -{ - register const unsigned char *inptr, *inend; - unsigned char *outptr; - unsigned char c; - - inptr = in; - inend = in + len; - outptr = out; - while (inptr < inend) { - c = *inptr++; - if (c==' ') { - *outptr++ = '_'; - } else if (camel_mime_special_table[c] & safemask) { - *outptr++ = c; - } else { - *outptr++ = '='; - *outptr++ = tohex[(c >> 4) & 0xf]; - *outptr++ = tohex[c & 0xf]; - } - } - - d(printf("encoding '%.*s' = '%.*s'\n", len, in, outptr-out, out)); - - return (outptr - out); -} - - -static void -header_decode_lwsp(const char **in) -{ - const char *inptr = *in; - char c; - - d2(printf("is ws: '%s'\n", *in)); - - while (camel_mime_is_lwsp(*inptr) || (*inptr =='(' && *inptr != '\0')) { - while (camel_mime_is_lwsp(*inptr) && inptr != '\0') { - d2(printf("(%c)", *inptr)); - inptr++; - } - d2(printf("\n")); - - /* check for comments */ - if (*inptr == '(') { - int depth = 1; - inptr++; - while (depth && (c=*inptr) && *inptr != '\0') { - if (c=='\\' && inptr[1]) { - inptr++; - } else if (c=='(') { - depth++; - } else if (c==')') { - depth--; - } - inptr++; - } - } - } - *in = inptr; -} - -/* decode rfc 2047 encoded string segment */ -static char * -rfc2047_decode_word(const char *in, size_t len) -{ - const char *inptr = in+2; - const char *inend = in+len-2; - const char *inbuf; - const char *charset; - char *encname, *p; - int tmplen; - size_t ret; - char *decword = NULL; - char *decoded = NULL; - char *outbase = NULL; - char *outbuf; - size_t inlen, outlen; - gboolean retried = FALSE; - iconv_t ic; - - d(printf("rfc2047: decoding '%.*s'\n", len, in)); - - /* quick check to see if this could possibly be a real encoded word */ - if (len < 8 || !(in[0] == '=' && in[1] == '?' && in[len-1] == '=' && in[len-2] == '?')) { - d(printf("invalid\n")); - return NULL; - } - - /* skip past the charset to the encoding type */ - inptr = memchr (inptr, '?', inend-inptr); - if (inptr != NULL && inptr < inend + 2 && inptr[2] == '?') { - d(printf("found ?, encoding is '%c'\n", inptr[0])); - inptr++; - tmplen = inend-inptr-2; - decword = g_alloca (tmplen); /* this will always be more-than-enough room */ - switch(toupper(inptr[0])) { - case 'Q': - inlen = quoted_decode(inptr+2, tmplen, decword); - break; - case 'B': { - int state = 0; - unsigned int save = 0; - - inlen = camel_base64_decode_step((char *)inptr+2, tmplen, decword, &state, &save); - /* if state != 0 then error? */ - break; - } - default: - /* uhhh, unknown encoding type - probably an invalid encoded word string */ - return NULL; - } - d(printf("The encoded length = %d\n", inlen)); - if (inlen > 0) { - /* yuck, all this snot is to setup iconv! */ - tmplen = inptr - in - 3; - encname = g_alloca (tmplen + 1); - memcpy (encname, in + 2, tmplen); - encname[tmplen] = '\0'; - - /* rfc2231 updates rfc2047 encoded words... - * The ABNF given in RFC 2047 for encoded-words is: - * encoded-word := "=?" charset "?" encoding "?" encoded-text "?=" - * This specification changes this ABNF to: - * encoded-word := "=?" charset ["*" language] "?" encoding "?" encoded-text "?=" - */ - - /* trim off the 'language' part if it's there... */ - p = strchr (encname, '*'); - if (p) - *p = '\0'; - - charset = e_iconv_charset_name (encname); - - inbuf = decword; - - outlen = inlen * 6 + 16; - outbase = g_alloca (outlen); - outbuf = outbase; - - retry: - ic = e_iconv_open ("UTF-8", charset); - if (ic != (iconv_t) -1) { - ret = e_iconv (ic, &inbuf, &inlen, &outbuf, &outlen); - if (ret != (size_t) -1) { - e_iconv (ic, NULL, 0, &outbuf, &outlen); - *outbuf = 0; - decoded = g_strdup (outbase); - } - e_iconv_close (ic); - } else { - w(g_warning ("Cannot decode charset, header display may be corrupt: %s: %s", - charset, strerror (errno))); - - if (!retried) { - charset = e_iconv_locale_charset (); - if (!charset) - charset = "iso-8859-1"; - - retried = TRUE; - goto retry; - } - - /* we return the encoded word here because we've got to return valid utf8 */ - decoded = g_strndup (in, inlen); - } - } - } - - d(printf("decoded '%s'\n", decoded)); - - return decoded; -} - -/* ok, a lot of mailers are BROKEN, and send iso-latin1 encoded - headers, when they should just be sticking to US-ASCII - according to the rfc's. Anyway, since the conversion to utf-8 - is trivial, just do it here without iconv */ -static GString * -append_latin1 (GString *out, const char *in, size_t len) -{ - unsigned int c; - - while (len) { - c = (unsigned int)*in++; - len--; - if (c & 0x80) { - out = g_string_append_c (out, 0xc0 | ((c >> 6) & 0x3)); /* 110000xx */ - out = g_string_append_c (out, 0x80 | (c & 0x3f)); /* 10xxxxxx */ - } else { - out = g_string_append_c (out, c); - } - } - return out; -} - -static int -append_8bit (GString *out, const char *inbuf, size_t inlen, const char *charset) -{ - char *outbase, *outbuf; - size_t outlen; - iconv_t ic; - - ic = e_iconv_open ("UTF-8", charset); - if (ic == (iconv_t) -1) - return FALSE; - - outlen = inlen * 6 + 16; - outbuf = outbase = g_malloc(outlen); - - if (e_iconv (ic, &inbuf, &inlen, &outbuf, &outlen) == (size_t) -1) { - w(g_warning("Conversion to '%s' failed: %s", charset, strerror (errno))); - g_free(outbase); - e_iconv_close (ic); - return FALSE; - } - - e_iconv (ic, NULL, NULL, &outbuf, &outlen); - - *outbuf = 0; - g_string_append(out, outbase); - g_free(outbase); - e_iconv_close (ic); - - return TRUE; - -} - -static void -append_quoted_pair (GString *str, const char *in, int inlen) -{ - register const char *inptr = in; - const char *inend = in + inlen; - char c; - - while (inptr < inend) { - c = *inptr++; - if (c == '\\' && inptr < inend) - g_string_append_c (str, *inptr++); - else - g_string_append_c (str, c); - } -} - -/* decodes a simple text, rfc822 + rfc2047 */ -static char * -header_decode_text (const char *in, size_t inlen, int ctext, const char *default_charset) -{ - GString *out; - const char *inptr, *inend, *start, *chunk, *locale_charset; - void (* append) (GString *, const char *, int); - char *dword = NULL; - guint32 mask; - - locale_charset = e_iconv_locale_charset (); - - if (ctext) { - mask = (CAMEL_MIME_IS_SPECIAL | CAMEL_MIME_IS_SPACE | CAMEL_MIME_IS_CTRL); - append = append_quoted_pair; - } else { - mask = (CAMEL_MIME_IS_LWSP); - append = g_string_append_len; - } - - out = g_string_new (""); - inptr = in; - inend = inptr + inlen; - chunk = NULL; - - while (inptr < inend) { - start = inptr; - while (inptr < inend && camel_mime_is_type (*inptr, mask)) - inptr++; - - if (inptr == inend) { - append (out, start, inptr - start); - break; - } else if (dword == NULL) { - append (out, start, inptr - start); - } else { - chunk = start; - } - - start = inptr; - while (inptr < inend && !camel_mime_is_type (*inptr, mask)) - inptr++; - - dword = rfc2047_decode_word(start, inptr-start); - if (dword) { - g_string_append(out, dword); - g_free(dword); - } else { - if (!chunk) - chunk = start; - - if ((default_charset == NULL || !append_8bit (out, chunk, inptr-chunk, default_charset)) - && (locale_charset == NULL || !append_8bit(out, chunk, inptr-chunk, locale_charset))) - append_latin1(out, chunk, inptr-chunk); - } - - chunk = NULL; - } - - dword = out->str; - g_string_free (out, FALSE); - - return dword; -} - -char * -camel_header_decode_string (const char *in, const char *default_charset) -{ - if (in == NULL) - return NULL; - return header_decode_text (in, strlen (in), FALSE, default_charset); -} - -char * -camel_header_format_ctext (const char *in, const char *default_charset) -{ - if (in == NULL) - return NULL; - return header_decode_text (in, strlen (in), TRUE, default_charset); -} - -/* how long a sequence of pre-encoded words should be less than, to attempt to - fit into a properly folded word. Only a guide. */ -#define CAMEL_FOLD_PREENCODED (24) - -/* FIXME: needs a way to cache iconv opens for different charsets? */ -static void -rfc2047_encode_word(GString *outstring, const char *in, size_t len, const char *type, unsigned short safemask) -{ - iconv_t ic = (iconv_t) -1; - char *buffer, *out, *ascii; - size_t inlen, outlen, enclen, bufflen; - const char *inptr, *p; - int first = 1; - - d(printf("Converting [%d] '%.*s' to %s\n", len, len, in, type)); - - /* convert utf8->encoding */ - bufflen = len * 6 + 16; - buffer = g_alloca (bufflen); - inlen = len; - inptr = in; - - ascii = g_alloca (bufflen); - - if (strcasecmp (type, "UTF-8") != 0) - ic = e_iconv_open (type, "UTF-8"); - - while (inlen) { - size_t convlen, proclen; - int i; - - /* break up words into smaller bits, what we really want is encoded + overhead < 75, - but we'll just guess what that means in terms of input chars, and assume its good enough */ - - out = buffer; - outlen = bufflen; - - if (ic == (iconv_t) -1) { - /* native encoding case, the easy one (?) */ - /* we work out how much we can convert, and still be in length */ - /* proclen will be the result of input characters that we can convert, to the nearest - (approximated) valid utf8 char */ - convlen = 0; - proclen = 0; - p = inptr; - i = 0; - while (p < (in+len) && convlen < (75 - strlen("=?utf-8?q\?\?="))) { - unsigned char c = *p++; - - if (c >= 0xc0) - proclen = i; - i++; - if (c < 0x80) - proclen = i; - if (camel_mime_special_table[c] & safemask) - convlen += 1; - else - convlen += 3; - } - /* well, we probably have broken utf8, just copy it anyway what the heck */ - if (proclen == 0) { - w(g_warning("Appear to have truncated utf8 sequence")); - proclen = inlen; - } - memcpy(out, inptr, proclen); - inptr += proclen; - inlen -= proclen; - out += proclen; - } else { - /* well we could do similar, but we can't (without undue effort), we'll just break it up into - hopefully-small-enough chunks, and leave it at that */ - convlen = MIN(inlen, CAMEL_FOLD_PREENCODED); - p = inptr; - if (e_iconv (ic, &inptr, &convlen, &out, &outlen) == (size_t) -1 && errno != EINVAL) { - w(g_warning("Conversion problem: conversion truncated: %s", strerror (errno))); - /* blah, we include it anyway, better than infinite loop ... */ - inptr = p + convlen; - } else { - /* make sure we flush out any shift state */ - e_iconv (ic, NULL, 0, &out, &outlen); - } - inlen -= (inptr - p); - } - - enclen = out-buffer; - - if (enclen) { - /* create token */ - out = ascii; - if (first) - first = 0; - else - *out++ = ' '; - out += sprintf (out, "=?%s?Q?", type); - out += quoted_encode (buffer, enclen, out, safemask); - sprintf (out, "?="); - - d(printf("converted part = %s\n", ascii)); - - g_string_append (outstring, ascii); - } - } - - if (ic != (iconv_t) -1) - e_iconv_close (ic); -} - - -/* TODO: Should this worry about quotes?? */ -char * -camel_header_encode_string (const unsigned char *in) -{ - const unsigned char *inptr = in, *start, *word; - gboolean last_was_encoded = FALSE; - gboolean last_was_space = FALSE; - int encoding; - GString *out; - char *outstr; - - g_return_val_if_fail (g_utf8_validate (in, -1, NULL), NULL); - - if (in == NULL) - return NULL; - - /* do a quick us-ascii check (the common case?) */ - while (*inptr) { - if (*inptr > 127) - break; - inptr++; - } - if (*inptr == '\0') - return g_strdup (in); - - /* This gets each word out of the input, and checks to see what charset - can be used to encode it. */ - /* TODO: Work out when to merge subsequent words, or across word-parts */ - out = g_string_new (""); - inptr = in; - encoding = 0; - word = NULL; - start = inptr; - while (inptr && *inptr) { - gunichar c; - const char *newinptr; - - newinptr = g_utf8_next_char (inptr); - c = g_utf8_get_char (inptr); - if (newinptr == NULL || !g_unichar_validate (c)) { - w(g_warning ("Invalid UTF-8 sequence encountered (pos %d, char '%c'): %s", - (inptr-in), inptr[0], in)); - inptr++; - continue; - } - - if (c < 256 && camel_mime_is_lwsp (c) && !last_was_space) { - /* we've reached the end of a 'word' */ - if (word && !(last_was_encoded && encoding)) { - /* output lwsp between non-encoded words */ - g_string_append_len (out, start, word - start); - start = word; - } - - switch (encoding) { - case 0: - g_string_append_len (out, start, inptr - start); - last_was_encoded = FALSE; - break; - case 1: - if (last_was_encoded) - g_string_append_c (out, ' '); - - rfc2047_encode_word (out, start, inptr - start, "ISO-8859-1", CAMEL_MIME_IS_ESAFE); - last_was_encoded = TRUE; - break; - case 2: - if (last_was_encoded) - g_string_append_c (out, ' '); - - rfc2047_encode_word (out, start, inptr - start, - camel_charset_best (start, inptr - start), CAMEL_MIME_IS_ESAFE); - last_was_encoded = TRUE; - break; - } - - last_was_space = TRUE; - start = inptr; - word = NULL; - encoding = 0; - } else if (c > 127 && c < 256) { - encoding = MAX (encoding, 1); - last_was_space = FALSE; - } else if (c >= 256) { - encoding = MAX (encoding, 2); - last_was_space = FALSE; - } else if (!camel_mime_is_lwsp (c)) { - last_was_space = FALSE; - } - - if (!(c < 256 && camel_mime_is_lwsp (c)) && !word) - word = inptr; - - inptr = newinptr; - } - - if (inptr - start) { - if (word && !(last_was_encoded && encoding)) { - g_string_append_len (out, start, word - start); - start = word; - } - - switch (encoding) { - case 0: - g_string_append_len (out, start, inptr - start); - break; - case 1: - if (last_was_encoded) - g_string_append_c (out, ' '); - - rfc2047_encode_word (out, start, inptr - start, "ISO-8859-1", CAMEL_MIME_IS_ESAFE); - break; - case 2: - if (last_was_encoded) - g_string_append_c (out, ' '); - - rfc2047_encode_word (out, start, inptr - start, - camel_charset_best (start, inptr - start - 1), CAMEL_MIME_IS_ESAFE); - break; - } - } - - outstr = out->str; - g_string_free (out, FALSE); - - return outstr; -} - -/* apply quoted-string rules to a string */ -static void -quote_word(GString *out, gboolean do_quotes, const char *start, size_t len) -{ - int i, c; - - /* TODO: What about folding on long lines? */ - if (do_quotes) - g_string_append_c(out, '"'); - for (i=0;i<len;i++) { - c = *start++; - if (c == '\"' || c=='\\' || c=='\r') - g_string_append_c(out, '\\'); - g_string_append_c(out, c); - } - if (do_quotes) - g_string_append_c(out, '"'); -} - -/* incrementing possibility for the word type */ -enum _phrase_word_t { - WORD_ATOM, - WORD_QSTRING, - WORD_2047 -}; - -struct _phrase_word { - const unsigned char *start, *end; - enum _phrase_word_t type; - int encoding; -}; - -static gboolean -word_types_compatable (enum _phrase_word_t type1, enum _phrase_word_t type2) -{ - switch (type1) { - case WORD_ATOM: - return type2 == WORD_QSTRING; - case WORD_QSTRING: - return type2 != WORD_2047; - case WORD_2047: - return type2 == WORD_2047; - default: - return FALSE; - } -} - -/* split the input into words with info about each word - * merge common word types clean up */ -static GList * -header_encode_phrase_get_words (const unsigned char *in) -{ - const unsigned char *inptr = in, *start, *last; - struct _phrase_word *word; - enum _phrase_word_t type; - int encoding, count = 0; - GList *words = NULL; - - /* break the input into words */ - type = WORD_ATOM; - last = inptr; - start = inptr; - encoding = 0; - while (inptr && *inptr) { - gunichar c; - const char *newinptr; - - newinptr = g_utf8_next_char (inptr); - c = g_utf8_get_char (inptr); - - if (!g_unichar_validate (c)) { - w(g_warning ("Invalid UTF-8 sequence encountered (pos %d, char '%c'): %s", - (inptr - in), inptr[0], in)); - inptr++; - continue; - } - - inptr = newinptr; - if (g_unichar_isspace (c)) { - if (count > 0) { - word = g_new0 (struct _phrase_word, 1); - word->start = start; - word->end = last; - word->type = type; - word->encoding = encoding; - words = g_list_append (words, word); - count = 0; - } - - start = inptr; - type = WORD_ATOM; - encoding = 0; - } else { - count++; - if (c < 128) { - if (!camel_mime_is_atom (c)) - type = MAX (type, WORD_QSTRING); - } else if (c > 127 && c < 256) { - type = WORD_2047; - encoding = MAX (encoding, 1); - } else if (c >= 256) { - type = WORD_2047; - encoding = MAX (encoding, 2); - } - } - - last = inptr; - } - - if (count > 0) { - word = g_new0 (struct _phrase_word, 1); - word->start = start; - word->end = last; - word->type = type; - word->encoding = encoding; - words = g_list_append (words, word); - } - - return words; -} - -#define MERGED_WORD_LT_FOLDLEN(wordlen, type) ((type) == WORD_2047 ? (wordlen) < CAMEL_FOLD_PREENCODED : (wordlen) < (CAMEL_FOLD_SIZE - 8)) - -static gboolean -header_encode_phrase_merge_words (GList **wordsp) -{ - GList *wordl, *nextl, *words = *wordsp; - struct _phrase_word *word, *next; - gboolean merged = FALSE; - - /* scan the list, checking for words of similar types that can be merged */ - wordl = words; - while (wordl) { - word = wordl->data; - nextl = g_list_next (wordl); - - while (nextl) { - next = nextl->data; - /* merge nodes of the same type AND we are not creating too long a string */ - if (word_types_compatable (word->type, next->type)) { - if (MERGED_WORD_LT_FOLDLEN (next->end - word->start, MAX (word->type, next->type))) { - /* the resulting word type is the MAX of the 2 types */ - word->type = MAX(word->type, next->type); - - word->end = next->end; - words = g_list_remove_link (words, nextl); - g_list_free_1 (nextl); - g_free (next); - - nextl = g_list_next (wordl); - - merged = TRUE; - } else { - /* if it is going to be too long, make sure we include the - separating whitespace */ - word->end = next->start; - break; - } - } else { - break; - } - } - - wordl = g_list_next (wordl); - } - - *wordsp = words; - - return merged; -} - -/* encodes a phrase sequence (different quoting/encoding rules to strings) */ -char * -camel_header_encode_phrase (const unsigned char *in) -{ - struct _phrase_word *word = NULL, *last_word = NULL; - GList *words, *wordl; - GString *out; - char *outstr; - - if (in == NULL) - return NULL; - - words = header_encode_phrase_get_words (in); - if (!words) - return NULL; - - while (header_encode_phrase_merge_words (&words)) - ; - - out = g_string_new (""); - - /* output words now with spaces between them */ - wordl = words; - while (wordl) { - const char *start; - size_t len; - - word = wordl->data; - - /* append correct number of spaces between words */ - if (last_word && !(last_word->type == WORD_2047 && word->type == WORD_2047)) { - /* one or both of the words are not encoded so we write the spaces out untouched */ - len = word->start - last_word->end; - out = g_string_append_len (out, last_word->end, len); - } - - switch (word->type) { - case WORD_ATOM: - out = g_string_append_len (out, word->start, word->end - word->start); - break; - case WORD_QSTRING: - quote_word (out, TRUE, word->start, word->end - word->start); - break; - case WORD_2047: - if (last_word && last_word->type == WORD_2047) { - /* include the whitespace chars between these 2 words in the - resulting rfc2047 encoded word. */ - len = word->end - last_word->end; - start = last_word->end; - - /* encoded words need to be separated by linear whitespace */ - g_string_append_c (out, ' '); - } else { - len = word->end - word->start; - start = word->start; - } - - if (word->encoding == 1) - rfc2047_encode_word (out, start, len, "ISO-8859-1", CAMEL_MIME_IS_PSAFE); - else - rfc2047_encode_word (out, start, len, - camel_charset_best (start, len), CAMEL_MIME_IS_PSAFE); - break; - } - - g_free (last_word); - wordl = g_list_next (wordl); - - last_word = word; - } - - /* and we no longer need the list */ - g_free (word); - g_list_free (words); - - outstr = out->str; - g_string_free (out, FALSE); - - return outstr; -} - - -/* these are all internal parser functions */ - -static char * -decode_token (const char **in) -{ - const char *inptr = *in; - const char *start; - - header_decode_lwsp (&inptr); - start = inptr; - while (camel_mime_is_ttoken (*inptr)) - inptr++; - if (inptr > start) { - *in = inptr; - return g_strndup (start, inptr - start); - } else { - return NULL; - } -} - -char * -camel_header_token_decode(const char *in) -{ - if (in == NULL) - return NULL; - - return decode_token(&in); -} - -/* - <"> * ( <any char except <"> \, cr / \ <any char> ) <"> -*/ -static char * -header_decode_quoted_string(const char **in) -{ - const char *inptr = *in; - char *out = NULL, *outptr; - size_t outlen; - int c; - - header_decode_lwsp(&inptr); - if (*inptr == '"') { - const char *intmp; - int skip = 0; - - /* first, calc length */ - inptr++; - intmp = inptr; - 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; -} - -static char * -header_decode_atom(const char **in) -{ - const char *inptr = *in, *start; - - header_decode_lwsp(&inptr); - start = inptr; - while (camel_mime_is_atom(*inptr)) - inptr++; - *in = inptr; - if (inptr > start) - return g_strndup(start, inptr-start); - else - return NULL; -} - -static char * -header_decode_word (const char **in) -{ - const char *inptr = *in; - - header_decode_lwsp (&inptr); - if (*inptr == '"') { - *in = inptr; - return header_decode_quoted_string (in); - } else { - *in = inptr; - return header_decode_atom (in); - } -} - -static char * -header_decode_value(const char **in) -{ - const char *inptr = *in; - - header_decode_lwsp(&inptr); - if (*inptr == '"') { - d(printf("decoding quoted string\n")); - return header_decode_quoted_string(in); - } else if (camel_mime_is_ttoken(*inptr)) { - d(printf("decoding token\n")); - /* this may not have the right specials for all params? */ - return decode_token(in); - } - return NULL; -} - -/* should this return -1 for no int? */ -int -camel_header_decode_int(const char **in) -{ - const char *inptr = *in; - int c, v=0; - - header_decode_lwsp(&inptr); - while ( (c=*inptr++ & 0xff) - && isdigit(c) ) { - v = v*10+(c-'0'); - } - *in = inptr-1; - return v; -} - -#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10) - -static char * -hex_decode (const char *in, size_t len) -{ - const unsigned char *inend = in + len; - unsigned char *inptr, *outptr; - char *outbuf; - - outptr = outbuf = g_malloc (len + 1); - - inptr = (unsigned char *) in; - while (inptr < inend) { - if (*inptr == '%') { - if (isxdigit (inptr[1]) && isxdigit (inptr[2])) { - *outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]); - inptr += 3; - } else - *outptr++ = *inptr++; - } else - *outptr++ = *inptr++; - } - - *outptr = '\0'; - - return outbuf; -} - -/* Tries to convert @in @from charset @to charset. Any failure, we get no data out rather than partial conversion */ -static char * -header_convert(const char *to, const char *from, const char *in, size_t inlen) -{ - iconv_t ic; - size_t outlen, ret; - char *outbuf, *outbase, *result = NULL; - - ic = e_iconv_open(to, from); - if (ic == (iconv_t) -1) - return NULL; - - outlen = inlen * 6 + 16; - outbuf = outbase = g_malloc(outlen); - - ret = e_iconv(ic, &in, &inlen, &outbuf, &outlen); - if (ret != (size_t) -1) { - e_iconv(ic, NULL, 0, &outbuf, &outlen); - *outbuf = '\0'; - result = g_strdup(outbase); - } - e_iconv_close(ic); - g_free(outbase); - - return result; -} - -/* an rfc2184 encoded string looks something like: - * us-ascii'en'This%20is%20even%20more%20 - */ - -static char * -rfc2184_decode (const char *in, size_t len) -{ - const char *inptr = in; - const char *inend = in + len; - const char *charset; - char *decoded, *decword, *encoding; - - inptr = memchr (inptr, '\'', len); - if (!inptr) - return NULL; - - encoding = g_alloca(inptr-in+1); - memcpy(encoding, in, inptr-in); - encoding[inptr-in] = 0; - charset = e_iconv_charset_name (encoding); - - inptr = memchr (inptr + 1, '\'', inend - inptr - 1); - if (!inptr) - return NULL; - inptr++; - if (inptr >= inend) - return NULL; - - decword = hex_decode (inptr, inend - inptr); - decoded = header_convert("UTF-8", charset, decword, strlen(decword)); - g_free(decword); - - return decoded; -} - -char * -camel_header_param (struct _camel_header_param *p, const char *name) -{ - while (p && g_ascii_strcasecmp (p->name, name) != 0) - p = p->next; - if (p) - return p->value; - return NULL; -} - -struct _camel_header_param * -camel_header_set_param (struct _camel_header_param **l, const char *name, const char *value) -{ - struct _camel_header_param *p = (struct _camel_header_param *)l, *pn; - - if (name == NULL) - return NULL; - - while (p->next) { - pn = p->next; - if (!g_ascii_strcasecmp (pn->name, name)) { - g_free (pn->value); - if (value) { - pn->value = g_strdup (value); - return pn; - } else { - p->next = pn->next; - g_free (pn->name); - g_free (pn); - return NULL; - } - } - p = pn; - } - - if (value == NULL) - return NULL; - - pn = g_malloc (sizeof (*pn)); - pn->next = 0; - pn->name = g_strdup (name); - pn->value = g_strdup (value); - p->next = pn; - - return pn; -} - -const char * -camel_content_type_param (CamelContentType *t, const char *name) -{ - if (t==NULL) - return NULL; - return camel_header_param (t->params, name); -} - -void -camel_content_type_set_param (CamelContentType *t, const char *name, const char *value) -{ - camel_header_set_param (&t->params, name, value); -} - -/** - * camel_content_type_is: - * @ct: A content type specifier, or #NULL. - * @type: A type to check against. - * @subtype: A subtype to check against, or "*" to match any subtype. - * - * Returns #TRUE if the content type @ct is of type @type/@subtype. - * The subtype of "*" will match any subtype. If @ct is #NULL, then - * it will match the type "text/plain". - * - * Return value: #TRUE or #FALSE depending on the matching of the type. - **/ -int -camel_content_type_is(CamelContentType *ct, const char *type, const char *subtype) -{ - /* no type == text/plain or text/"*" */ - if (ct==NULL || (ct->type == NULL && ct->subtype == NULL)) { - return (!strcasecmp(type, "text") - && (!g_ascii_strcasecmp(subtype, "plain") - || !strcasecmp(subtype, "*"))); - } - - return (ct->type != NULL - && (!g_ascii_strcasecmp(ct->type, type) - && ((ct->subtype != NULL - && !g_ascii_strcasecmp(ct->subtype, subtype)) - || !strcasecmp("*", subtype)))); -} - -void -camel_header_param_list_free(struct _camel_header_param *p) -{ - struct _camel_header_param *n; - - while (p) { - n = p->next; - g_free(p->name); - g_free(p->value); - g_free(p); - p = n; - } -} - -CamelContentType * -camel_content_type_new(const char *type, const char *subtype) -{ - CamelContentType *t = g_malloc(sizeof(*t)); - - t->type = g_strdup(type); - t->subtype = g_strdup(subtype); - t->params = NULL; - t->refcount = 1; - return t; -} - -void -camel_content_type_ref(CamelContentType *ct) -{ - if (ct) - ct->refcount++; -} - - -void -camel_content_type_unref(CamelContentType *ct) -{ - if (ct) { - if (ct->refcount <= 1) { - camel_header_param_list_free(ct->params); - g_free(ct->type); - g_free(ct->subtype); - g_free(ct); - } else { - ct->refcount--; - } - } -} - -/* for decoding email addresses, canonically */ -static char * -header_decode_domain(const char **in) -{ - const char *inptr = *in, *start; - int go = TRUE; - char *ret; - GString *domain = g_string_new(""); - - /* domain ref | domain literal */ - header_decode_lwsp(&inptr); - while (go) { - if (*inptr == '[') { /* domain literal */ - domain = g_string_append_c(domain, '['); - inptr++; - header_decode_lwsp(&inptr); - start = inptr; - while (camel_mime_is_dtext(*inptr)) { - domain = g_string_append_c(domain, *inptr); - inptr++; - } - if (*inptr == ']') { - domain = g_string_append_c(domain, ']'); - inptr++; - } else { - w(g_warning("closing ']' not found in domain: %s", *in)); - } - } else { - char *a = header_decode_atom(&inptr); - if (a) { - domain = g_string_append(domain, a); - g_free(a); - } else { - w(g_warning("missing atom from domain-ref")); - break; - } - } - header_decode_lwsp(&inptr); - if (*inptr == '.') { /* next sub-domain? */ - domain = g_string_append_c(domain, '.'); - inptr++; - header_decode_lwsp(&inptr); - } else - go = FALSE; - } - - *in = inptr; - - ret = domain->str; - g_string_free(domain, FALSE); - return ret; -} - -static char * -header_decode_addrspec(const char **in) -{ - const char *inptr = *in; - char *word; - GString *addr = g_string_new(""); - - header_decode_lwsp(&inptr); - - /* addr-spec */ - word = header_decode_word (&inptr); - if (word) { - addr = g_string_append(addr, word); - header_decode_lwsp(&inptr); - g_free(word); - while (*inptr == '.' && word) { - inptr++; - addr = g_string_append_c(addr, '.'); - word = header_decode_word (&inptr); - if (word) { - addr = g_string_append(addr, word); - header_decode_lwsp(&inptr); - g_free(word); - } else { - w(g_warning("Invalid address spec: %s", *in)); - } - } - if (*inptr == '@') { - inptr++; - addr = g_string_append_c(addr, '@'); - word = header_decode_domain(&inptr); - if (word) { - addr = g_string_append(addr, word); - g_free(word); - } else { - w(g_warning("Invalid address, missing domain: %s", *in)); - } - } else { - w(g_warning("Invalid addr-spec, missing @: %s", *in)); - } - } else { - w(g_warning("invalid addr-spec, no local part")); - } - - /* FIXME: return null on error? */ - - *in = inptr; - word = addr->str; - g_string_free(addr, FALSE); - return word; -} - -/* - address: - word *('.' word) @ domain | - *(word) '<' [ *('@' domain ) ':' ] word *( '.' word) @ domain | - - 1*word ':' [ word ... etc (mailbox, as above) ] ';' - */ - -/* mailbox: - word *( '.' word ) '@' domain - *(word) '<' [ *('@' domain ) ':' ] word *( '.' word) @ domain - */ - -static struct _camel_header_address * -header_decode_mailbox(const char **in, const char *charset) -{ - const char *inptr = *in; - char *pre; - int closeme = FALSE; - GString *addr; - GString *name = NULL; - struct _camel_header_address *address = NULL; - const char *comment = NULL; - - addr = g_string_new(""); - - /* for each address */ - pre = header_decode_word (&inptr); - header_decode_lwsp(&inptr); - if (!(*inptr == '.' || *inptr == '@' || *inptr==',' || *inptr=='\0')) { - /* ',' and '\0' required incase it is a simple address, no @ domain part (buggy writer) */ - name = g_string_new (""); - while (pre) { - char *text, *last; - - /* perform internationalised decoding, and append */ - text = camel_header_decode_string (pre, charset); - g_string_append (name, text); - last = pre; - g_free(text); - - pre = header_decode_word (&inptr); - if (pre) { - size_t l = strlen (last); - size_t p = strlen (pre); - - /* dont append ' ' between sucsessive encoded words */ - if ((l>6 && last[l-2] == '?' && last[l-1] == '=') - && (p>6 && pre[0] == '=' && pre[1] == '?')) { - /* dont append ' ' */ - } else { - name = g_string_append_c(name, ' '); - } - } else { - /* Fix for stupidly-broken-mailers that like to put '.''s in names unquoted */ - /* see bug #8147 */ - while (!pre && *inptr && *inptr != '<') { - w(g_warning("Working around stupid mailer bug #5: unescaped characters in names")); - name = g_string_append_c(name, *inptr++); - pre = header_decode_word (&inptr); - } - } - g_free(last); - } - header_decode_lwsp(&inptr); - if (*inptr == '<') { - closeme = TRUE; - try_address_again: - inptr++; - header_decode_lwsp(&inptr); - if (*inptr == '@') { - while (*inptr == '@') { - inptr++; - header_decode_domain(&inptr); - header_decode_lwsp(&inptr); - if (*inptr == ',') { - inptr++; - header_decode_lwsp(&inptr); - } - } - if (*inptr == ':') { - inptr++; - } else { - w(g_warning("broken route-address, missing ':': %s", *in)); - } - } - pre = header_decode_word (&inptr); - header_decode_lwsp(&inptr); - } else { - w(g_warning("broken address? %s", *in)); - } - } - - if (pre) { - addr = g_string_append(addr, pre); - } else { - w(g_warning("No local-part for email address: %s", *in)); - } - - /* should be at word '.' localpart */ - while (*inptr == '.' && pre) { - inptr++; - g_free(pre); - pre = header_decode_word (&inptr); - addr = g_string_append_c(addr, '.'); - if (pre) - addr = g_string_append(addr, pre); - comment = inptr; - header_decode_lwsp(&inptr); - } - g_free(pre); - - /* now at '@' domain part */ - if (*inptr == '@') { - char *dom; - - inptr++; - addr = g_string_append_c(addr, '@'); - comment = inptr; - dom = header_decode_domain(&inptr); - addr = g_string_append(addr, dom); - g_free(dom); - } else if (*inptr != '>' || !closeme) { - /* If we get a <, the address was probably a name part, lets try again shall we? */ - /* Another fix for seriously-broken-mailers */ - if (*inptr && *inptr != ',') { - char *text; - - w(g_warning("We didn't get an '@' where we expected in '%s', trying again", *in)); - w(g_warning("Name is '%s', Addr is '%s' we're at '%s'\n", name?name->str:"<UNSET>", addr->str, inptr)); - - /* need to keep *inptr, as try_address_again will drop the current character */ - if (*inptr == '<') - closeme = TRUE; - else - g_string_append_c(addr, *inptr); - - /* check for address is encoded word ... */ - text = camel_header_decode_string(addr->str, charset); - if (name == NULL) { - name = addr; - addr = g_string_new(""); - if (text) { - g_string_truncate(name, 0); - g_string_append(name, text); - } - } else { - g_string_append(name, text?text:addr->str); - g_string_truncate(addr, 0); - } - g_free(text); - - /* or maybe that we've added up a bunch of broken bits to make an encoded word */ - text = rfc2047_decode_word(name->str, name->len); - if (text) { - g_string_truncate(name, 0); - g_string_append(name, text); - g_free(text); - } - - goto try_address_again; - } - w(g_warning("invalid address, no '@' domain part at %c: %s", *inptr, *in)); - } - - if (closeme) { - header_decode_lwsp(&inptr); - if (*inptr == '>') { - inptr++; - } else { - w(g_warning("invalid route address, no closing '>': %s", *in)); - } - } else if (name == NULL && comment != NULL && inptr>comment) { /* check for comment after address */ - char *text, *tmp; - const char *comstart, *comend; - - /* this is a bit messy, we go from the last known position, because - decode_domain/etc skip over any comments on the way */ - /* FIXME: This wont detect comments inside the domain itself, - but nobody seems to use that feature anyway ... */ - - d(printf("checking for comment from '%s'\n", comment)); - - comstart = strchr(comment, '('); - if (comstart) { - comstart++; - header_decode_lwsp(&inptr); - comend = inptr-1; - while (comend > comstart && comend[0] != ')') - comend--; - - if (comend > comstart) { - d(printf(" looking at subset '%.*s'\n", comend-comstart, comstart)); - tmp = g_strndup (comstart, comend-comstart); - text = camel_header_decode_string (tmp, charset); - name = g_string_new (text); - g_free (tmp); - g_free (text); - } - } - } - - *in = inptr; - - if (addr->len > 0) { - if (!g_utf8_validate (addr->str, addr->len, NULL)) { - /* workaround for invalid addr-specs containing 8bit chars (see bug #42170 for details) */ - const char *locale_charset; - GString *out; - - locale_charset = e_iconv_locale_charset (); - - out = g_string_new (""); - - if ((charset == NULL || !append_8bit (out, addr->str, addr->len, charset)) - && (locale_charset == NULL || !append_8bit (out, addr->str, addr->len, locale_charset))) - append_latin1 (out, addr->str, addr->len); - - g_string_free (addr, TRUE); - addr = out; - } - - address = camel_header_address_new_name(name ? name->str : "", addr->str); - } - - d(printf("got mailbox: %s\n", addr->str)); - - g_string_free(addr, TRUE); - if (name) - g_string_free(name, TRUE); - - return address; -} - -static struct _camel_header_address * -header_decode_address(const char **in, const char *charset) -{ - const char *inptr = *in; - char *pre; - GString *group = g_string_new(""); - struct _camel_header_address *addr = NULL, *member; - - /* pre-scan, trying to work out format, discard results */ - header_decode_lwsp(&inptr); - while ((pre = header_decode_word (&inptr))) { - group = g_string_append(group, pre); - group = g_string_append(group, " "); - g_free(pre); - } - header_decode_lwsp(&inptr); - if (*inptr == ':') { - d(printf("group detected: %s\n", group->str)); - addr = camel_header_address_new_group(group->str); - /* that was a group spec, scan mailbox's */ - inptr++; - /* FIXME: check rfc 2047 encodings of words, here or above in the loop */ - header_decode_lwsp(&inptr); - if (*inptr != ';') { - int go = TRUE; - do { - member = header_decode_mailbox(&inptr, charset); - if (member) - camel_header_address_add_member(addr, member); - header_decode_lwsp(&inptr); - if (*inptr == ',') - inptr++; - else - go = FALSE; - } while (go); - if (*inptr == ';') { - inptr++; - } else { - w(g_warning("Invalid group spec, missing closing ';': %s", *in)); - } - } else { - inptr++; - } - *in = inptr; - } else { - addr = header_decode_mailbox(in, charset); - } - - g_string_free(group, TRUE); - - return addr; -} - -static char * -header_msgid_decode_internal(const char **in) -{ - const char *inptr = *in; - char *msgid = NULL; - - d(printf("decoding Message-ID: '%s'\n", *in)); - - header_decode_lwsp(&inptr); - if (*inptr == '<') { - inptr++; - header_decode_lwsp(&inptr); - msgid = header_decode_addrspec(&inptr); - if (msgid) { - header_decode_lwsp(&inptr); - if (*inptr == '>') { - inptr++; - } else { - w(g_warning("Missing closing '>' on message id: %s", *in)); - } - } else { - w(g_warning("Cannot find message id in: %s", *in)); - } - } else { - w(g_warning("missing opening '<' on message id: %s", *in)); - } - *in = inptr; - - return msgid; -} - -char * -camel_header_msgid_decode(const char *in) -{ - if (in == NULL) - return NULL; - - return header_msgid_decode_internal(&in); -} - -char * -camel_header_contentid_decode (const char *in) -{ - const char *inptr = in; - gboolean at = FALSE; - GString *addr; - char *buf; - - d(printf("decoding Content-ID: '%s'\n", in)); - - header_decode_lwsp (&inptr); - - /* some lame mailers quote the Content-Id */ - if (*inptr == '"') - inptr++; - - /* make sure the content-id is not "" which can happen if we get a - * content-id such as <.@> (which Eudora likes to use...) */ - if ((buf = camel_header_msgid_decode (inptr)) != NULL && *buf) - return buf; - - g_free (buf); - - /* ugh, not a valid msg-id - try to get something useful out of it then? */ - inptr = in; - header_decode_lwsp (&inptr); - if (*inptr == '<') { - inptr++; - header_decode_lwsp (&inptr); - } - - /* Eudora has been known to use <.@> as a content-id */ - if (!(buf = header_decode_word (&inptr)) && !strchr (".@", *inptr)) - return NULL; - - addr = g_string_new (""); - header_decode_lwsp (&inptr); - while (buf != NULL || *inptr == '.' || (*inptr == '@' && !at)) { - if (buf != NULL) { - g_string_append (addr, buf); - g_free (buf); - buf = NULL; - } - - if (!at) { - if (*inptr == '.') { - g_string_append_c (addr, *inptr++); - buf = header_decode_word (&inptr); - } else if (*inptr == '@') { - g_string_append_c (addr, *inptr++); - buf = header_decode_word (&inptr); - at = TRUE; - } - } else if (strchr (".[]", *inptr)) { - g_string_append_c (addr, *inptr++); - buf = header_decode_atom (&inptr); - } - - header_decode_lwsp (&inptr); - } - - buf = addr->str; - g_string_free (addr, FALSE); - - return buf; -} - -void -camel_header_references_list_append_asis(struct _camel_header_references **list, char *ref) -{ - struct _camel_header_references *w = (struct _camel_header_references *)list, *n; - while (w->next) - w = w->next; - n = g_malloc(sizeof(*n)); - n->id = ref; - n->next = 0; - w->next = n; -} - -int -camel_header_references_list_size(struct _camel_header_references **list) -{ - int count = 0; - struct _camel_header_references *w = *list; - while (w) { - count++; - w = w->next; - } - return count; -} - -void -camel_header_references_list_clear(struct _camel_header_references **list) -{ - struct _camel_header_references *w = *list, *n; - while (w) { - n = w->next; - g_free(w->id); - g_free(w); - w = n; - } - *list = NULL; -} - -static void -header_references_decode_single (const char **in, struct _camel_header_references **head) -{ - struct _camel_header_references *ref; - const char *inptr = *in; - char *id, *word; - - while (*inptr) { - header_decode_lwsp (&inptr); - if (*inptr == '<') { - id = header_msgid_decode_internal (&inptr); - if (id) { - ref = g_malloc (sizeof (struct _camel_header_references)); - ref->next = *head; - ref->id = id; - *head = ref; - break; - } - } else { - word = header_decode_word (&inptr); - if (word) - g_free (word); - else if (*inptr != '\0') - inptr++; /* Stupid mailer tricks */ - } - } - - *in = inptr; -} - -/* TODO: why is this needed? Can't the other interface also work? */ -struct _camel_header_references * -camel_header_references_inreplyto_decode (const char *in) -{ - struct _camel_header_references *ref = NULL; - - if (in == NULL || in[0] == '\0') - return NULL; - - header_references_decode_single (&in, &ref); - - return ref; -} - -/* generate a list of references, from most recent up */ -struct _camel_header_references * -camel_header_references_decode (const char *in) -{ - struct _camel_header_references *refs = NULL; - - if (in == NULL || in[0] == '\0') - return NULL; - - while (*in) - header_references_decode_single (&in, &refs); - - return refs; -} - -struct _camel_header_references * -camel_header_references_dup(const struct _camel_header_references *list) -{ - struct _camel_header_references *new = NULL, *tmp; - - while (list) { - tmp = g_new(struct _camel_header_references, 1); - tmp->next = new; - tmp->id = g_strdup(list->id); - new = tmp; - list = list->next; - } - return new; -} - -struct _camel_header_address * -camel_header_mailbox_decode(const char *in, const char *charset) -{ - if (in == NULL) - return NULL; - - return header_decode_mailbox(&in, charset); -} - -struct _camel_header_address * -camel_header_address_decode(const char *in, const char *charset) -{ - const char *inptr = in, *last; - struct _camel_header_address *list = NULL, *addr; - - d(printf("decoding To: '%s'\n", in)); - - if (in == NULL) - return NULL; - - header_decode_lwsp(&inptr); - if (*inptr == 0) - return NULL; - - do { - last = inptr; - addr = header_decode_address(&inptr, charset); - if (addr) - camel_header_address_list_append(&list, addr); - header_decode_lwsp(&inptr); - if (*inptr == ',') - inptr++; - else - break; - } while (inptr != last); - - if (*inptr) { - w(g_warning("Invalid input detected at %c (%d): %s\n or at: %s", *inptr, inptr-in, in, inptr)); - } - - if (inptr == last) { - w(g_warning("detected invalid input loop at : %s", last)); - } - - return list; -} - -struct _camel_header_newsgroup * -camel_header_newsgroups_decode(const char *in) -{ - const char *inptr = in; - register char c; - struct _camel_header_newsgroup *head, *last, *ng; - const char *start; - - head = NULL; - last = (struct _camel_header_newsgroup *)&head; - - do { - header_decode_lwsp(&inptr); - start = inptr; - while ((c = *inptr++) && !camel_mime_is_lwsp(c) && c != ',') - ; - if (start != inptr-1) { - ng = g_malloc(sizeof(*ng)); - ng->newsgroup = g_strndup(start, inptr-start-1); - ng->next = NULL; - last->next = ng; - last = ng; - } - } while (c); - - return head; -} - -void -camel_header_newsgroups_free(struct _camel_header_newsgroup *ng) -{ - while (ng) { - struct _camel_header_newsgroup *nng = ng->next; - - g_free(ng->newsgroup); - g_free(ng); - ng = nng; - } -} - -/* this must be kept in sync with the header */ -static const char *encodings[] = { - "", - "7bit", - "8bit", - "base64", - "quoted-printable", - "binary", - "x-uuencode", -}; - -const char * -camel_transfer_encoding_to_string (CamelTransferEncoding encoding) -{ - if (encoding >= sizeof (encodings) / sizeof (encodings[0])) - encoding = 0; - - return encodings[encoding]; -} - -CamelTransferEncoding -camel_transfer_encoding_from_string (const char *string) -{ - int i; - - if (string != NULL) { - for (i = 0; i < sizeof (encodings) / sizeof (encodings[0]); i++) - if (!g_ascii_strcasecmp (string, encodings[i])) - return i; - } - - return CAMEL_TRANSFER_ENCODING_DEFAULT; -} - -void -camel_header_mime_decode(const char *in, int *maj, int *min) -{ - const char *inptr = in; - int major=-1, minor=-1; - - d(printf("decoding MIME-Version: '%s'\n", in)); - - if (in != NULL) { - header_decode_lwsp(&inptr); - if (isdigit(*inptr)) { - major = camel_header_decode_int(&inptr); - header_decode_lwsp(&inptr); - if (*inptr == '.') { - inptr++; - header_decode_lwsp(&inptr); - if (isdigit(*inptr)) - minor = camel_header_decode_int(&inptr); - } - } - } - - if (maj) - *maj = major; - if (min) - *min = minor; - - d(printf("major = %d, minor = %d\n", major, minor)); -} - -struct _rfc2184_param { - struct _camel_header_param param; - int index; -}; - -static int -rfc2184_param_cmp(const void *ap, const void *bp) -{ - const struct _rfc2184_param *a = *(void **)ap; - const struct _rfc2184_param *b = *(void **)bp; - int res; - - res = strcmp(a->param.name, b->param.name); - if (res == 0) { - if (a->index > b->index) - res = 1; - else if (a->index < b->index) - res = -1; - } - - return res; -} - -/* NB: Steals name and value */ -static struct _camel_header_param * -header_append_param(struct _camel_header_param *last, char *name, char *value) -{ - struct _camel_header_param *node; - - /* This handles - - 8 bit data in parameters, illegal, tries to convert using locale, or just safens it up. - rfc2047 ecoded parameters, illegal, decodes them anyway. Some Outlook & Mozilla do this? - */ - node = g_malloc(sizeof(*node)); - last->next = node; - node->next = NULL; - node->name = name; - if (strncmp(value, "=?", 2) == 0 - && (node->value = header_decode_text(value, strlen(value), FALSE, NULL))) { - g_free(value); - } else if (!g_utf8_validate(value, -1, NULL)) { - const char * charset = e_iconv_locale_charset(); - - if ((node->value = header_convert("UTF-8", charset?charset:"ISO-8859-1", value, strlen(value)))) { - g_free(value); - } else { - node->value = value; - for (;*value;value++) - if (!isascii((unsigned char)*value)) - *value = '_'; - } - } else - node->value = value; - - return node; -} - -static struct _camel_header_param * -header_decode_param_list (const char **in) -{ - struct _camel_header_param *head = NULL, *last = (struct _camel_header_param *)&head; - GPtrArray *split = NULL; - const char *inptr = *in; - struct _rfc2184_param *work; - char *tmp; - - /* Dump parameters into the output list, in the order found. RFC 2184 split parameters are kept in an array */ - header_decode_lwsp(&inptr); - while (*inptr == ';') { - char *name; - char *value = NULL; - - inptr++; - name = decode_token(&inptr); - header_decode_lwsp(&inptr); - if (*inptr == '=') { - inptr++; - value = header_decode_value(&inptr); - } - - if (name && value) { - char *index = strchr(name, '*'); - - if (index) { - if (index[1] == 0) { - /* VAL*="foo", decode immediately and append */ - *index = 0; - tmp = rfc2184_decode(value, strlen(value)); - if (tmp) { - g_free(value); - value = tmp; - } - last = header_append_param(last, name, value); - } else { - /* VAL*1="foo", save for later */ - *index++ = 0; - work = g_malloc(sizeof(*work)); - work->param.name = name; - work->param.value = value; - work->index = atoi(index); - if (split == NULL) - split = g_ptr_array_new(); - g_ptr_array_add(split, work); - } - } else { - last = header_append_param(last, name, value); - } - } else { - g_free(name); - g_free(value); - } - - header_decode_lwsp(&inptr); - } - - /* Rejoin any RFC 2184 split parameters in the proper order */ - /* Parameters with the same index will be concatenated in undefined order */ - if (split) { - GString *value = g_string_new(""); - struct _rfc2184_param *first; - int i; - - qsort(split->pdata, split->len, sizeof(split->pdata[0]), rfc2184_param_cmp); - first = split->pdata[0]; - for (i=0;i<split->len;i++) { - work = split->pdata[i]; - if (split->len-1 == i) - g_string_append(value, work->param.value); - if (split->len-1 == i || strcmp(work->param.name, first->param.name) != 0) { - tmp = rfc2184_decode(value->str, value->len); - if (tmp == NULL) - tmp = g_strdup(value->str); - - last = header_append_param(last, g_strdup(first->param.name), tmp); - g_string_truncate(value, 0); - first = work; - } - if (split->len-1 != i) - g_string_append(value, work->param.value); - } - g_string_free(value, TRUE); - for (i=0;i<split->len;i++) { - work = split->pdata[i]; - g_free(work->param.name); - g_free(work->param.value); - g_free(work); - } - g_ptr_array_free(split, TRUE); - } - - *in = inptr; - - return head; -} - -struct _camel_header_param * -camel_header_param_list_decode(const char *in) -{ - if (in == NULL) - return NULL; - - return header_decode_param_list(&in); -} - -static char * -header_encode_param (const unsigned char *in, gboolean *encoded) -{ - const unsigned char *inptr = in; - unsigned char *outbuf = NULL; - const char *charset; - int encoding; - GString *out; - guint32 c; - - *encoded = FALSE; - - g_return_val_if_fail (in != NULL, NULL); - - /* do a quick us-ascii check (the common case?) */ - while (*inptr) { - if (*inptr > 127) - break; - inptr++; - } - - if (*inptr == '\0') - return g_strdup (in); - - inptr = in; - encoding = 0; - while ( encoding !=2 && (c = camel_utf8_getc(&inptr)) ) { - if (c > 127 && c < 256) - encoding = MAX (encoding, 1); - else if (c >= 256) - encoding = MAX (encoding, 2); - } - - if (encoding == 2) - charset = camel_charset_best(in, strlen(in)); - else - charset = "iso-8859-1"; - - if (strcasecmp(charset, "UTF-8") != 0 - && (outbuf = header_convert(charset, "UTF-8", in, strlen(in)))) { - inptr = outbuf; - } else { - charset = "UTF-8"; - inptr = in; - } - - /* FIXME: set the 'language' as well, assuming we can get that info...? */ - out = g_string_new (charset); - g_string_append(out, "''"); - - while ( (c = *inptr++) ) { - if (camel_mime_is_attrchar(c)) - g_string_append_c (out, c); - else - g_string_append_printf (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]); - } - g_free (outbuf); - - outbuf = out->str; - g_string_free (out, FALSE); - *encoded = TRUE; - - return outbuf; -} - -void -camel_header_param_list_format_append (GString *out, struct _camel_header_param *p) -{ - int used = out->len; - - while (p) { - gboolean encoded = FALSE; - gboolean quote = FALSE; - int here = out->len; - size_t nlen, vlen; - char *value; - - if (!p->value) { - p = p->next; - continue; - } - - value = header_encode_param (p->value, &encoded); - if (!value) { - w(g_warning ("appending parameter %s=%s violates rfc2184", p->name, p->value)); - value = g_strdup (p->value); - } - - if (!encoded) { - char *ch; - - for (ch = value; *ch; ch++) { - if (camel_mime_is_tspecial (*ch) || camel_mime_is_lwsp (*ch)) - break; - } - - quote = ch && *ch; - } - - nlen = strlen (p->name); - vlen = strlen (value); - - if (used + nlen + vlen > CAMEL_FOLD_SIZE - 8) { - out = g_string_append (out, ";\n\t"); - here = out->len; - used = 0; - } else - out = g_string_append (out, "; "); - - if (nlen + vlen > CAMEL_FOLD_SIZE - 8) { - /* we need to do special rfc2184 parameter wrapping */ - int maxlen = CAMEL_FOLD_SIZE - (nlen + 8); - char *inptr, *inend; - int i = 0; - - inptr = value; - inend = value + vlen; - - while (inptr < inend) { - char *ptr = inptr + MIN (inend - inptr, maxlen); - - if (encoded && ptr < inend) { - /* be careful not to break an encoded char (ie %20) */ - char *q = ptr; - int j = 2; - - for ( ; j > 0 && q > inptr && *q != '%'; j--, q--); - if (*q == '%') - ptr = q; - } - - if (i != 0) { - g_string_append (out, ";\n\t"); - here = out->len; - used = 0; - } - - g_string_append_printf (out, "%s*%d%s=", p->name, i++, encoded ? "*" : ""); - if (encoded || !quote) - g_string_append_len (out, inptr, ptr - inptr); - else - quote_word (out, TRUE, inptr, ptr - inptr); - - d(printf ("wrote: %s\n", out->str + here)); - - used += (out->len - here); - - inptr = ptr; - } - } else { - g_string_append_printf (out, "%s%s=", p->name, encoded ? "*" : ""); - - if (encoded || !quote) - g_string_append (out, value); - else - quote_word (out, TRUE, value, vlen); - - used += (out->len - here); - } - - g_free (value); - - p = p->next; - } -} - -char * -camel_header_param_list_format(struct _camel_header_param *p) -{ - GString *out = g_string_new(""); - char *ret; - - camel_header_param_list_format_append(out, p); - ret = out->str; - g_string_free(out, FALSE); - return ret; -} - -CamelContentType * -camel_content_type_decode(const char *in) -{ - const char *inptr = in; - char *type, *subtype = NULL; - CamelContentType *t = NULL; - - if (in==NULL) - return NULL; - - type = decode_token(&inptr); - header_decode_lwsp(&inptr); - if (type) { - if (*inptr == '/') { - inptr++; - subtype = decode_token(&inptr); - } - if (subtype == NULL && (!strcasecmp(type, "text"))) { - w(g_warning("text type with no subtype, resorting to text/plain: %s", in)); - subtype = g_strdup("plain"); - } - if (subtype == NULL) { - w(g_warning("MIME type with no subtype: %s", in)); - } - - t = camel_content_type_new(type, subtype); - t->params = header_decode_param_list(&inptr); - g_free(type); - g_free(subtype); - } else { - g_free(type); - d(printf("cannot find MIME type in header (2) '%s'", in)); - } - return t; -} - -void -camel_content_type_dump(CamelContentType *ct) -{ - struct _camel_header_param *p; - - printf("Content-Type: "); - if (ct==NULL) { - printf("<NULL>\n"); - return; - } - printf("%s / %s", ct->type, ct->subtype); - p = ct->params; - if (p) { - while (p) { - printf(";\n\t%s=\"%s\"", p->name, p->value); - p = p->next; - } - } - printf("\n"); -} - -char * -camel_content_type_format (CamelContentType *ct) -{ - GString *out; - char *ret; - - if (ct == NULL) - return NULL; - - out = g_string_new (""); - if (ct->type == NULL) { - g_string_append_printf (out, "text/plain"); - w(g_warning ("Content-Type with no main type")); - } else if (ct->subtype == NULL) { - w(g_warning ("Content-Type with no sub type: %s", ct->type)); - if (!g_ascii_strcasecmp (ct->type, "multipart")) - g_string_append_printf (out, "%s/mixed", ct->type); - else - g_string_append_printf (out, "%s", ct->type); - } else { - g_string_append_printf (out, "%s/%s", ct->type, ct->subtype); - } - camel_header_param_list_format_append (out, ct->params); - - ret = out->str; - g_string_free (out, FALSE); - - return ret; -} - -char * -camel_content_type_simple (CamelContentType *ct) -{ - if (ct->type == NULL) { - w(g_warning ("Content-Type with no main type")); - return g_strdup ("text/plain"); - } else if (ct->subtype == NULL) { - w(g_warning ("Content-Type with no sub type: %s", ct->type)); - if (!g_ascii_strcasecmp (ct->type, "multipart")) - return g_strdup_printf ("%s/mixed", ct->type); - else - return g_strdup (ct->type); - } else - return g_strdup_printf ("%s/%s", ct->type, ct->subtype); -} - -char * -camel_content_transfer_encoding_decode (const char *in) -{ - if (in) - return decode_token (&in); - - return NULL; -} - -CamelContentDisposition * -camel_content_disposition_decode(const char *in) -{ - CamelContentDisposition *d = NULL; - const char *inptr = in; - - if (in == NULL) - return NULL; - - d = g_malloc(sizeof(*d)); - d->refcount = 1; - d->disposition = decode_token(&inptr); - if (d->disposition == NULL) - w(g_warning("Empty disposition type")); - d->params = header_decode_param_list(&inptr); - return d; -} - -void -camel_content_disposition_ref(CamelContentDisposition *d) -{ - if (d) - d->refcount++; -} - -void -camel_content_disposition_unref(CamelContentDisposition *d) -{ - if (d) { - if (d->refcount<=1) { - camel_header_param_list_free(d->params); - g_free(d->disposition); - g_free(d); - } else { - d->refcount--; - } - } -} - -char * -camel_content_disposition_format(CamelContentDisposition *d) -{ - GString *out; - char *ret; - - if (d==NULL) - return NULL; - - out = g_string_new(""); - if (d->disposition) - out = g_string_append(out, d->disposition); - else - out = g_string_append(out, "attachment"); - camel_header_param_list_format_append(out, d->params); - - ret = out->str; - g_string_free(out, FALSE); - return ret; -} - -/* hrm, is there a library for this shit? */ -static struct { - char *name; - int offset; -} tz_offsets [] = { - { "UT", 0 }, - { "GMT", 0 }, - { "EST", -500 }, /* these are all US timezones. bloody yanks */ - { "EDT", -400 }, - { "CST", -600 }, - { "CDT", -500 }, - { "MST", -700 }, - { "MDT", -600 }, - { "PST", -800 }, - { "PDT", -700 }, - { "Z", 0 }, - { "A", -100 }, - { "M", -1200 }, - { "N", 100 }, - { "Y", 1200 }, -}; - -static char *tz_months [] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static char *tz_days [] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - -char * -camel_header_format_date(time_t time, int offset) -{ - struct tm tm; - - d(printf("offset = %d\n", offset)); - - d(printf("converting date %s", ctime(&time))); - - time += ((offset / 100) * (60*60)) + (offset % 100)*60; - - d(printf("converting date %s", ctime(&time))); - - gmtime_r (&time, &tm); - - return g_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d %+05d", - tz_days[tm.tm_wday], - tm.tm_mday, tz_months[tm.tm_mon], - tm.tm_year + 1900, - tm.tm_hour, tm.tm_min, tm.tm_sec, - offset); -} - -/* convert a date to time_t representation */ -/* this is an awful mess oh well */ -time_t -camel_header_decode_date(const char *in, int *saveoffset) -{ - const char *inptr = in; - char *monthname; - gboolean foundmonth; - int year, offset = 0; - struct tm tm; - int i; - time_t t; - - if (in == NULL) { - if (saveoffset) - *saveoffset = 0; - return 0; - } - - d(printf ("\ndecoding date '%s'\n", inptr)); - - memset (&tm, 0, sizeof(tm)); - - header_decode_lwsp (&inptr); - if (!isdigit (*inptr)) { - char *day = decode_token (&inptr); - /* we dont really care about the day, it's only for display */ - if (day) { - d(printf ("got day: %s\n", day)); - g_free (day); - header_decode_lwsp (&inptr); - if (*inptr == ',') { - inptr++; - } else { -#ifndef CLEAN_DATE - return parse_broken_date (in, saveoffset); -#else - if (saveoffset) - *saveoffset = 0; - return 0; -#endif /* ! CLEAN_DATE */ - } - } - } - tm.tm_mday = camel_header_decode_int(&inptr); -#ifndef CLEAN_DATE - if (tm.tm_mday == 0) { - return parse_broken_date (in, saveoffset); - } -#endif /* ! CLEAN_DATE */ - - monthname = decode_token(&inptr); - foundmonth = FALSE; - if (monthname) { - for (i=0;i<sizeof(tz_months)/sizeof(tz_months[0]);i++) { - if (!g_ascii_strcasecmp(tz_months[i], monthname)) { - tm.tm_mon = i; - foundmonth = TRUE; - break; - } - } - g_free(monthname); - } -#ifndef CLEAN_DATE - if (!foundmonth) { - return parse_broken_date (in, saveoffset); - } -#endif /* ! CLEAN_DATE */ - - year = camel_header_decode_int(&inptr); - if (year < 69) { - tm.tm_year = 100 + year; - } else if (year < 100) { - tm.tm_year = year; - } else if (year >= 100 && year < 1900) { - tm.tm_year = year; - } else { - tm.tm_year = year - 1900; - } - /* get the time ... yurck */ - tm.tm_hour = camel_header_decode_int(&inptr); - header_decode_lwsp(&inptr); - if (*inptr == ':') - inptr++; - tm.tm_min = camel_header_decode_int(&inptr); - header_decode_lwsp(&inptr); - if (*inptr == ':') - inptr++; - tm.tm_sec = camel_header_decode_int(&inptr); - header_decode_lwsp(&inptr); - if (*inptr == '+' - || *inptr == '-') { - offset = (*inptr++)=='-'?-1:1; - offset = offset * camel_header_decode_int(&inptr); - d(printf("abs signed offset = %d\n", offset)); - if (offset < -1200 || offset > 1400) - offset = 0; - } else if (isdigit(*inptr)) { - offset = camel_header_decode_int(&inptr); - d(printf("abs offset = %d\n", offset)); - if (offset < -1200 || offset > 1400) - offset = 0; - } else { - char *tz = decode_token(&inptr); - - if (tz) { - for (i=0;i<sizeof(tz_offsets)/sizeof(tz_offsets[0]);i++) { - if (!g_ascii_strcasecmp(tz_offsets[i].name, tz)) { - offset = tz_offsets[i].offset; - break; - } - } - g_free(tz); - } - /* some broken mailers seem to put in things like GMT+1030 instead of just +1030 */ - header_decode_lwsp(&inptr); - if (*inptr == '+' || *inptr == '-') { - int sign = (*inptr++)=='-'?-1:1; - offset = offset + (camel_header_decode_int(&inptr)*sign); - } - d(printf("named offset = %d\n", offset)); - } - - t = e_mktime_utc(&tm); - - /* t is now GMT of the time we want, but not offset by the timezone ... */ - - d(printf(" gmt normalized? = %s\n", ctime(&t))); - - /* this should convert the time to the GMT equiv time */ - t -= ( (offset/100) * 60*60) + (offset % 100)*60; - - d(printf(" gmt normalized for timezone? = %s\n", ctime(&t))); - - d({ - char *tmp; - tmp = camel_header_format_date(t, offset); - printf(" encoded again: %s\n", tmp); - g_free(tmp); - }); - - if (saveoffset) - *saveoffset = offset; - - return t; -} - -char * -camel_header_location_decode(const char *in) -{ - int quote = 0; - GString *out = g_string_new(""); - char c, *res; - - /* Sigh. RFC2557 says: - * content-location = "Content-Location:" [CFWS] URI [CFWS] - * where URI is restricted to the syntax for URLs as - * defined in Uniform Resource Locators [URL] until - * IETF specifies other kinds of URIs. - * - * But Netscape puts quotes around the URI when sending web - * pages. - * - * Which is required as defined in rfc2017 [3.1]. Although - * outlook doesn't do this. - * - * Since we get headers already unfolded, we need just drop - * all whitespace. URL's cannot contain whitespace or quoted - * characters, even when included in quotes. - */ - - header_decode_lwsp(&in); - if (*in == '"') { - in++; - quote = 1; - } - - while ( (c = *in++) ) { - if (quote && c=='"') - break; - if (!camel_mime_is_lwsp(c)) - g_string_append_c(out, c); - } - - res = g_strdup(out->str); - g_string_free(out, TRUE); - - return res; -} - -/* extra rfc checks */ -#define CHECKS - -#ifdef CHECKS -static void -check_header(struct _camel_header_raw *h) -{ - unsigned char *p; - - p = h->value; - while (p && *p) { - if (!isascii(*p)) { - w(g_warning("Appending header violates rfc: %s: %s", h->name, h->value)); - return; - } - p++; - } -} -#endif - -void -camel_header_raw_append_parse(struct _camel_header_raw **list, const char *header, int offset) -{ - register const char *in; - size_t fieldlen; - char *name; - - in = header; - while (camel_mime_is_fieldname(*in) || *in==':') - in++; - fieldlen = in-header-1; - while (camel_mime_is_lwsp(*in)) - in++; - if (fieldlen == 0 || header[fieldlen] != ':') { - printf("Invalid header line: '%s'\n", header); - return; - } - name = g_alloca (fieldlen + 1); - memcpy(name, header, fieldlen); - name[fieldlen] = 0; - - camel_header_raw_append(list, name, in, offset); -} - -void -camel_header_raw_append(struct _camel_header_raw **list, const char *name, const char *value, int offset) -{ - struct _camel_header_raw *l, *n; - - d(printf("Header: %s: %s\n", name, value)); - - n = g_malloc(sizeof(*n)); - n->next = NULL; - n->name = g_strdup(name); - n->value = g_strdup(value); - n->offset = offset; -#ifdef CHECKS - check_header(n); -#endif - l = (struct _camel_header_raw *)list; - while (l->next) { - l = l->next; - } - l->next = n; - - /* debug */ -#if 0 - if (!strcasecmp(name, "To")) { - printf("- Decoding To\n"); - camel_header_to_decode(value); - } else if (!strcasecmp(name, "Content-type")) { - printf("- Decoding content-type\n"); - camel_content_type_dump(camel_content_type_decode(value)); - } else if (!strcasecmp(name, "MIME-Version")) { - printf("- Decoding mime version\n"); - camel_header_mime_decode(value); - } -#endif -} - -static struct _camel_header_raw * -header_raw_find_node(struct _camel_header_raw **list, const char *name) -{ - struct _camel_header_raw *l; - - l = *list; - while (l) { - if (!g_ascii_strcasecmp(l->name, name)) - break; - l = l->next; - } - return l; -} - -const char * -camel_header_raw_find(struct _camel_header_raw **list, const char *name, int *offset) -{ - struct _camel_header_raw *l; - - l = header_raw_find_node(list, name); - if (l) { - if (offset) - *offset = l->offset; - return l->value; - } else - return NULL; -} - -const char * -camel_header_raw_find_next(struct _camel_header_raw **list, const char *name, int *offset, const char *last) -{ - struct _camel_header_raw *l; - - if (last == NULL || name == NULL) - return NULL; - - l = *list; - while (l && l->value != last) - l = l->next; - return camel_header_raw_find(&l, name, offset); -} - -static void -header_raw_free(struct _camel_header_raw *l) -{ - g_free(l->name); - g_free(l->value); - g_free(l); -} - -void -camel_header_raw_remove(struct _camel_header_raw **list, const char *name) -{ - struct _camel_header_raw *l, *p; - - /* the next pointer is at the head of the structure, so this is safe */ - p = (struct _camel_header_raw *)list; - l = *list; - while (l) { - if (!g_ascii_strcasecmp(l->name, name)) { - p->next = l->next; - header_raw_free(l); - l = p->next; - } else { - p = l; - l = l->next; - } - } -} - -void -camel_header_raw_replace(struct _camel_header_raw **list, const char *name, const char *value, int offset) -{ - camel_header_raw_remove(list, name); - camel_header_raw_append(list, name, value, offset); -} - -void -camel_header_raw_clear(struct _camel_header_raw **list) -{ - struct _camel_header_raw *l, *n; - l = *list; - while (l) { - n = l->next; - header_raw_free(l); - l = n; - } - *list = NULL; -} - -char * -camel_header_msgid_generate (void) -{ - static pthread_mutex_t count_lock = PTHREAD_MUTEX_INITIALIZER; -#define COUNT_LOCK() pthread_mutex_lock (&count_lock) -#define COUNT_UNLOCK() pthread_mutex_unlock (&count_lock) - char host[MAXHOSTNAMELEN]; - char *name; - static int count = 0; - char *msgid; - int retval; - struct addrinfo *ai = NULL, hints = { 0 }; - - retval = gethostname (host, sizeof (host)); - if (retval == 0 && *host) { - hints.ai_flags = AI_CANONNAME; - ai = camel_getaddrinfo(host, NULL, &hints, NULL); - if (ai && ai->ai_canonname) - name = ai->ai_canonname; - else - name = host; - } else - name = "localhost.localdomain"; - - COUNT_LOCK (); - msgid = g_strdup_printf ("%d.%d.%d.camel@%s", (int) time (NULL), getpid (), count++, name); - COUNT_UNLOCK (); - - if (ai) - camel_freeaddrinfo(ai); - - return msgid; -} - - -static struct { - char *name; - char *pattern; - regex_t regex; -} mail_list_magic[] = { - /* List-Post: <mailto:gnome-hackers@gnome.org> */ - /* List-Post: <mailto:gnome-hackers> */ - { "List-Post", "[ \t]*<mailto:([^@>]+)@?([^ \n\t\r>]*)" }, - /* List-Id: GNOME stuff <gnome-hackers.gnome.org> */ - /* List-Id: <gnome-hackers.gnome.org> */ - /* List-Id: <gnome-hackers> */ - /* This old one wasn't very useful: { "List-Id", " *([^<]+)" },*/ - { "List-Id", "[^<]*<([^\\.>]+)\\.?([^ \n\t\r>]*)" }, - /* Mailing-List: list gnome-hackers@gnome.org; contact gnome-hackers-owner@gnome.org */ - { "Mailing-List", "[ \t]*list ([^@]+)@?([^ \n\t\r>;]*)" }, - /* Originator: gnome-hackers@gnome.org */ - { "Originator", "[ \t]*([^@]+)@?([^ \n\t\r>]*)" }, - /* X-Mailing-List: <gnome-hackers@gnome.org> arcive/latest/100 */ - /* X-Mailing-List: gnome-hackers@gnome.org */ - /* X-Mailing-List: gnome-hackers */ - /* X-Mailing-List: <gnome-hackers> */ - { "X-Mailing-List", "[ \t]*<?([^@>]+)@?([^ \n\t\r>]*)" }, - /* X-Loop: gnome-hackers@gnome.org */ - { "X-Loop", "[ \t]*([^@]+)@?([^ \n\t\r>]*)" }, - /* X-List: gnome-hackers */ - /* X-List: gnome-hackers@gnome.org */ - { "X-List", "[ \t]*([^@]+)@?([^ \n\t\r>]*)" }, - /* Sender: owner-gnome-hackers@gnome.org */ - /* Sender: owner-gnome-hacekrs */ - { "Sender", "[ \t]*owner-([^@]+)@?([^ @\n\t\r>]*)" }, - /* Sender: gnome-hackers-owner@gnome.org */ - /* Sender: gnome-hackers-owner */ - { "Sender", "[ \t]*([^@]+)-owner@?([^ @\n\t\r>]*)" }, - /* Delivered-To: mailing list gnome-hackers@gnome.org */ - /* Delivered-To: mailing list gnome-hackers */ - { "Delivered-To", "[ \t]*mailing list ([^@]+)@?([^ \n\t\r>]*)" }, - /* Sender: owner-gnome-hackers@gnome.org */ - /* Sender: <owner-gnome-hackers@gnome.org> */ - /* Sender: owner-gnome-hackers */ - /* Sender: <owner-gnome-hackers> */ - { "Return-Path", "[ \t]*<?owner-([^@>]+)@?([^ \n\t\r>]*)" }, - /* X-BeenThere: gnome-hackers@gnome.org */ - /* X-BeenThere: gnome-hackers */ - { "X-BeenThere", "[ \t]*([^@]+)@?([^ \n\t\r>]*)" }, - /* List-Unsubscribe: <mailto:gnome-hackers-unsubscribe@gnome.org> */ - { "List-Unsubscribe", "<mailto:(.+)-unsubscribe@([^ \n\t\r>]*)" }, -}; - -char * -camel_header_raw_check_mailing_list(struct _camel_header_raw **list) -{ - const char *v; - regmatch_t match[3]; - int i, j; - - for (i = 0; i < sizeof (mail_list_magic) / sizeof (mail_list_magic[0]); i++) { - v = camel_header_raw_find (list, mail_list_magic[i].name, NULL); - for (j=0;j<3;j++) { - match[j].rm_so = -1; - match[j].rm_eo = -1; - } - if (v != NULL && regexec (&mail_list_magic[i].regex, v, 3, match, 0) == 0 && match[1].rm_so != -1) { - char *list; - int len1, len2; - - len1 = match[1].rm_eo - match[1].rm_so; - len2 = match[2].rm_eo - match[2].rm_so; - - list = g_malloc(len1+len2+2); - memcpy(list, v + match[1].rm_so, len1); - if (len2) { - list[len1] = '@'; - memcpy(list+len1+1, v+match[2].rm_so, len2); - list[len1+len2+1]=0; - } else { - list[len1] = 0; - } - - return list; - } - } - - return NULL; -} - -/* ok, here's the address stuff, what a mess ... */ -struct _camel_header_address * -camel_header_address_new (void) -{ - struct _camel_header_address *h; - h = g_malloc0(sizeof(*h)); - h->type = CAMEL_HEADER_ADDRESS_NONE; - h->refcount = 1; - return h; -} - -struct _camel_header_address * -camel_header_address_new_name(const char *name, const char *addr) -{ - struct _camel_header_address *h; - h = camel_header_address_new(); - h->type = CAMEL_HEADER_ADDRESS_NAME; - h->name = g_strdup(name); - h->v.addr = g_strdup(addr); - return h; -} - -struct _camel_header_address * -camel_header_address_new_group (const char *name) -{ - struct _camel_header_address *h; - - h = camel_header_address_new(); - h->type = CAMEL_HEADER_ADDRESS_GROUP; - h->name = g_strdup(name); - return h; -} - -void -camel_header_address_ref(struct _camel_header_address *h) -{ - if (h) - h->refcount++; -} - -void -camel_header_address_unref(struct _camel_header_address *h) -{ - if (h) { - if (h->refcount <= 1) { - if (h->type == CAMEL_HEADER_ADDRESS_GROUP) { - camel_header_address_list_clear(&h->v.members); - } else if (h->type == CAMEL_HEADER_ADDRESS_NAME) { - g_free(h->v.addr); - } - g_free(h->name); - g_free(h); - } else { - h->refcount--; - } - } -} - -void -camel_header_address_set_name(struct _camel_header_address *h, const char *name) -{ - if (h) { - g_free(h->name); - h->name = g_strdup(name); - } -} - -void -camel_header_address_set_addr(struct _camel_header_address *h, const char *addr) -{ - if (h) { - if (h->type == CAMEL_HEADER_ADDRESS_NAME - || h->type == CAMEL_HEADER_ADDRESS_NONE) { - h->type = CAMEL_HEADER_ADDRESS_NAME; - g_free(h->v.addr); - h->v.addr = g_strdup(addr); - } else { - g_warning("Trying to set the address on a group"); - } - } -} - -void -camel_header_address_set_members(struct _camel_header_address *h, struct _camel_header_address *group) -{ - if (h) { - if (h->type == CAMEL_HEADER_ADDRESS_GROUP - || h->type == CAMEL_HEADER_ADDRESS_NONE) { - h->type = CAMEL_HEADER_ADDRESS_GROUP; - camel_header_address_list_clear(&h->v.members); - /* should this ref them? */ - h->v.members = group; - } else { - g_warning("Trying to set the members on a name, not group"); - } - } -} - -void -camel_header_address_add_member(struct _camel_header_address *h, struct _camel_header_address *member) -{ - if (h) { - if (h->type == CAMEL_HEADER_ADDRESS_GROUP - || h->type == CAMEL_HEADER_ADDRESS_NONE) { - h->type = CAMEL_HEADER_ADDRESS_GROUP; - camel_header_address_list_append(&h->v.members, member); - } - } -} - -void -camel_header_address_list_append_list(struct _camel_header_address **l, struct _camel_header_address **h) -{ - if (l) { - struct _camel_header_address *n = (struct _camel_header_address *)l; - - while (n->next) - n = n->next; - n->next = *h; - } -} - - -void -camel_header_address_list_append(struct _camel_header_address **l, struct _camel_header_address *h) -{ - if (h) { - camel_header_address_list_append_list(l, &h); - h->next = NULL; - } -} - -void -camel_header_address_list_clear(struct _camel_header_address **l) -{ - struct _camel_header_address *a, *n; - a = *l; - while (a) { - n = a->next; - camel_header_address_unref(a); - a = n; - } - *l = NULL; -} - -/* if encode is true, then the result is suitable for mailing, otherwise - the result is suitable for display only (and may not even be re-parsable) */ -static void -header_address_list_encode_append (GString *out, int encode, struct _camel_header_address *a) -{ - char *text; - - while (a) { - switch (a->type) { - case CAMEL_HEADER_ADDRESS_NAME: - if (encode) - text = camel_header_encode_phrase (a->name); - else - text = a->name; - if (text && *text) - g_string_append_printf (out, "%s <%s>", text, a->v.addr); - else - g_string_append (out, a->v.addr); - if (encode) - g_free (text); - break; - case CAMEL_HEADER_ADDRESS_GROUP: - if (encode) - text = camel_header_encode_phrase (a->name); - else - text = a->name; - g_string_append_printf (out, "%s: ", text); - header_address_list_encode_append (out, encode, a->v.members); - g_string_append_printf (out, ";"); - if (encode) - g_free (text); - break; - default: - g_warning ("Invalid address type"); - break; - } - a = a->next; - if (a) - g_string_append (out, ", "); - } -} - -char * -camel_header_address_list_encode (struct _camel_header_address *a) -{ - GString *out; - char *ret; - - if (a == NULL) - return NULL; - - out = g_string_new (""); - header_address_list_encode_append (out, TRUE, a); - ret = out->str; - g_string_free (out, FALSE); - - return ret; -} - -char * -camel_header_address_list_format (struct _camel_header_address *a) -{ - GString *out; - char *ret; - - if (a == NULL) - return NULL; - - out = g_string_new (""); - - header_address_list_encode_append (out, FALSE, a); - ret = out->str; - g_string_free (out, FALSE); - - return ret; -} - -char * -camel_header_address_fold (const char *in, size_t headerlen) -{ - size_t len, outlen; - const char *inptr = in, *space, *p, *n; - GString *out; - char *ret; - int i, needunfold = FALSE; - - if (in == NULL) - return NULL; - - /* first, check to see if we even need to fold */ - len = headerlen + 2; - p = in; - while (*p) { - n = strchr (p, '\n'); - if (n == NULL) { - len += strlen (p); - break; - } - - needunfold = TRUE; - len += n-p; - - if (len >= CAMEL_FOLD_SIZE) - break; - len = 0; - p = n + 1; - } - if (len < CAMEL_FOLD_SIZE) - return g_strdup (in); - - /* we need to fold, so first unfold (if we need to), then process */ - if (needunfold) - inptr = in = camel_header_unfold (in); - - out = g_string_new (""); - outlen = headerlen + 2; - while (*inptr) { - space = strchr (inptr, ' '); - if (space) { - len = space - inptr + 1; - } else { - len = strlen (inptr); - } - - d(printf("next word '%.*s'\n", len, inptr)); - - if (outlen + len > CAMEL_FOLD_SIZE) { - d(printf("outlen = %d wordlen = %d\n", outlen, len)); - /* strip trailing space */ - if (out->len > 0 && out->str[out->len-1] == ' ') - g_string_truncate (out, out->len-1); - g_string_append (out, "\n\t"); - outlen = 1; - } - - outlen += len; - for (i = 0; i < len; i++) { - g_string_append_c (out, inptr[i]); - } - - inptr += len; - } - ret = out->str; - g_string_free (out, FALSE); - - if (needunfold) - g_free ((char *)in); - - return ret; -} - -/* simple header folding */ -/* will work even if the header is already folded */ -char * -camel_header_fold(const char *in, size_t headerlen) -{ - size_t len, outlen, i; - const char *inptr = in, *space, *p, *n; - GString *out; - char *ret; - int needunfold = FALSE; - - if (in == NULL) - return NULL; - - /* first, check to see if we even need to fold */ - len = headerlen + 2; - p = in; - while (*p) { - n = strchr(p, '\n'); - if (n == NULL) { - len += strlen (p); - break; - } - - needunfold = TRUE; - len += n-p; - - if (len >= CAMEL_FOLD_SIZE) - break; - len = 0; - p = n + 1; - } - if (len < CAMEL_FOLD_SIZE) - return g_strdup(in); - - /* we need to fold, so first unfold (if we need to), then process */ - if (needunfold) - inptr = in = camel_header_unfold(in); - - out = g_string_new(""); - outlen = headerlen+2; - while (*inptr) { - space = strchr(inptr, ' '); - if (space) { - len = space-inptr+1; - } else { - len = strlen(inptr); - } - d(printf("next word '%.*s'\n", len, inptr)); - if (outlen + len > CAMEL_FOLD_SIZE) { - d(printf("outlen = %d wordlen = %d\n", outlen, len)); - /* strip trailing space */ - if (out->len > 0 && out->str[out->len-1] == ' ') - g_string_truncate(out, out->len-1); - g_string_append(out, "\n\t"); - outlen = 1; - /* check for very long words, just cut them up */ - while (outlen+len > CAMEL_FOLD_MAX_SIZE) { - for (i=0;i<CAMEL_FOLD_MAX_SIZE-outlen;i++) - g_string_append_c(out, inptr[i]); - inptr += CAMEL_FOLD_MAX_SIZE-outlen; - len -= CAMEL_FOLD_MAX_SIZE-outlen; - g_string_append(out, "\n\t"); - outlen = 1; - } - } - outlen += len; - for (i=0;i<len;i++) { - g_string_append_c(out, inptr[i]); - } - inptr += len; - } - ret = out->str; - g_string_free(out, FALSE); - - if (needunfold) - g_free((char *)in); - - return ret; -} - -char * -camel_header_unfold(const char *in) -{ - char *out = g_malloc(strlen(in)+1); - const char *inptr = in; - char c, *o = out; - - o = out; - while ((c = *inptr++)) { - if (c == '\n') { - if (camel_mime_is_lwsp(*inptr)) { - do { - inptr++; - } while (camel_mime_is_lwsp(*inptr)); - *o++ = ' '; - } else { - *o++ = c; - } - } else { - *o++ = c; - } - } - *o = 0; - - return out; -} - -void -camel_mime_utils_init(void) -{ - int i, errcode, regex_compilation_failed=0; - - /* Init tables */ - header_decode_init(); - base64_init(); - - /* precompile regex's for speed at runtime */ - for (i = 0; i < G_N_ELEMENTS (mail_list_magic); i++) { - errcode = regcomp(&mail_list_magic[i].regex, mail_list_magic[i].pattern, REG_EXTENDED|REG_ICASE); - if (errcode != 0) { - char *errstr; - size_t len; - - len = regerror(errcode, &mail_list_magic[i].regex, NULL, 0); - errstr = g_malloc0(len + 1); - regerror(errcode, &mail_list_magic[i].regex, errstr, len); - - g_warning("Internal error, compiling regex failed: %s: %s", mail_list_magic[i].pattern, errstr); - g_free(errstr); - regex_compilation_failed++; - } - } - - g_assert(regex_compilation_failed == 0); -} - - -void -camel_mime_utils_shutdown (void) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (mail_list_magic); i++) - regfree (&mail_list_magic[i].regex); -} |