diff options
author | Thomas Roessler <roessler@does-not-exist.org> | 1998-06-08 09:16:03 +0000 |
---|---|---|
committer | Thomas Roessler <roessler@does-not-exist.org> | 1998-06-08 09:16:03 +0000 |
commit | 1a5381e07e97fe482c2b3a7c75f99938f0b105d4 (patch) | |
tree | b4fa4088bbbf5fc9217ee6f87ab60034175e6899 /rfc2047.c |
Initial revision
Diffstat (limited to 'rfc2047.c')
-rw-r--r-- | rfc2047.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/rfc2047.c b/rfc2047.c new file mode 100644 index 00000000..733a8d25 --- /dev/null +++ b/rfc2047.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "mutt.h" +#include "mime.h" +#include "rfc2047.h" + +#include <ctype.h> +#include <string.h> + +typedef void encode_t (char *, size_t, const unsigned char *); + +extern char MimeSpecials[]; +extern char B64Chars[]; + +static void q_encode_string (char *d, size_t dlen, const unsigned char *s) +{ + char charset[SHORT_STRING]; + size_t cslen, wordlen; + char *wptr = d; + + snprintf (charset, sizeof (charset), "=?%s?Q?", + strcasecmp ("us-ascii", charset) == 0 ? "unknown-8bit" : Charset); + cslen = strlen (charset); + + strcpy (wptr, charset); + wptr += cslen; + wordlen = cslen; + dlen -= cslen; + + dlen -= 3; /* save room for the word terminator */ + + while (*s && dlen > 0) + { + if (wordlen >= 72) + { + if (dlen < 4 + cslen) + break; + + strcpy (wptr, "?=\n "); + wptr += 4; + dlen -= 4; + strcpy (wptr, charset); + wptr += cslen; + wordlen = cslen; + dlen -= cslen; + } + + if (*s == ' ') + { + *wptr++ = '_'; + wordlen++; + dlen--; + } + else if ((*s & 0x80) || *s == '\t' || strchr (MimeSpecials, *s)) + { + if (wordlen >= 70) + { + if (dlen < 4 + cslen) + break; + + strcpy (wptr, "?=\n "); + wptr += 4; + dlen -= 4; + + strcpy (wptr, charset); + wptr += cslen; + wordlen = cslen; + dlen -= cslen; + } + + if (dlen < 3) + break; + sprintf (wptr, "=%02X", *s); + wptr += 3; + wordlen += 3; + dlen -= 3; + } + else + { + *wptr++ = *s; + wordlen++; + dlen--; + } + s++; + } + + strcpy (wptr, "?="); +} + +static void b_encode_string (char *d, size_t dlen, const unsigned char *s) +{ + char charset[SHORT_STRING]; + char *wptr = d; + int cslen; + int wordlen; + + snprintf (charset, sizeof (charset), "=?%s?B?", Charset); + cslen = strlen (charset); + strcpy (wptr, charset); + wptr += cslen; + wordlen = cslen; + dlen -= cslen; + + dlen -= 3; /* save room for the word terminator */ + + while (*s && dlen >= 4) + { + if (wordlen >= 71) + { + if (dlen < 4 + cslen) + break; + + strcpy (wptr, "?=\n "); + wptr += 4; + dlen -= 4; + + strcpy (wptr, charset); + wptr += cslen; + wordlen = cslen; + dlen -= cslen; + } + + *wptr++ = B64Chars[ (*s >> 2) & 0x3f ]; + *wptr++ = B64Chars[ ((*s & 0x3) << 4) | ((*(s+1) >> 4) & 0xf) ]; + s++; + if (*s) + { + *wptr++ = B64Chars[ ((*s & 0xf) << 2) | ((*(s+1) >> 6) & 0x3) ]; + s++; + if (*s) + { + *wptr++ = B64Chars[ *s & 0x3f ]; + s++; + } + else + *wptr++ = '='; + } + else + { + *wptr++ = '='; + *wptr++ = '='; + } + + wordlen += 4; + dlen -= 4; + } + + strcpy (wptr, "?="); +} + +void rfc2047_encode_string (char *d, size_t dlen, const unsigned char *s) +{ + int count = 0; + int len; + const unsigned char *p = s; + encode_t *encoder; + + /* First check to see if there are any 8-bit characters */ + for (; *p; p++) + { + if (*p & 0x80) + count++; + else if (*p == '=' && *p == '?') + { + count += 2; + p++; + } + } + if (!count) + { + strfcpy (d, (const char *)s, dlen); + return; + } + + if (strcasecmp("us-ascii", Charset) == 0 || + strncasecmp("iso-8859", Charset, 8) == 0) + encoder = q_encode_string; + else + { + /* figure out which encoding generates the most compact representation */ + len = strlen ((char *) s); + if ((count * 2) + len <= (4 * len) / 3) + encoder = q_encode_string; + else + encoder = b_encode_string; + } + + /* Hack to pull the Re: and Fwd: out of the encoded word for better + handling by agents which do not support RFC2047. */ + if (!strncasecmp ("re: ", (char *) s, 4)) + { + strncpy (d, (char *) s, 4); + d += 4; + dlen -= 4; + s += 4; + } + else if (!strncasecmp ("fwd: ", (char *) s, 5)) + { + strncpy (d, (char *) s, 5); + d += 5; + dlen -= 5; + s += 5; + } + + (*encoder) (d, dlen, s); +} + +void rfc2047_encode_adrlist (ADDRESS *addr) +{ + ADDRESS *ptr = addr; + char buffer[STRING]; + + while (ptr) + { + if (ptr->personal) + { + rfc2047_encode_string (buffer, sizeof (buffer), (const unsigned char *)ptr->personal); + safe_free ((void **) &ptr->personal); + ptr->personal = safe_strdup (buffer); + } + ptr = ptr->next; + } +} + +static int rfc2047_decode_word (char *d, const char *s, size_t len) +{ + char *p = safe_strdup (s); + char *pp = p; + char *pd = d; + int enc = 0, filter = 0, count = 0, c1, c2, c3, c4; + + while ((pp = strtok (pp, "?")) != NULL) + { + count++; + switch (count) + { + case 2: + if (strcasecmp (pp, Charset) != 0) + filter = 1; + break; + case 3: + if (toupper (*pp) == 'Q') + enc = ENCQUOTEDPRINTABLE; + else if (toupper (*pp) == 'B') + enc = ENCBASE64; + else + return (-1); + break; + case 4: + if (enc == ENCQUOTEDPRINTABLE) + { + while (*pp && len > 0) + { + if (*pp == '_') + { + *pd++ = ' '; + len--; + } + else if (*pp == '=') + { + *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]); + len--; + pp += 2; + } + else + { + *pd++ = *pp; + len--; + } + pp++; + } + *pd = 0; + } + else if (enc == ENCBASE64) + { + while (*pp && len > 0) + { + c1 = Index_64[(int) pp[0]]; + c2 = Index_64[(int) pp[1]]; + *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3); + if (--len == 0) break; + + if (pp[2] == '=') break; + + c3 = Index_64[(int) pp[2]]; + *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf); + if (--len == 0) + break; + + if (pp[3] == '=') + break; + + c4 = Index_64[(int) pp[3]]; + *pd++ = ((c3 & 0x3) << 6) | c4; + if (--len == 0) + break; + + pp += 4; + } + *pd = 0; + } + break; + } + pp = 0; + } + safe_free ((void **) &p); + if (filter) + { + pd = d; + while (*pd) + { + if (!IsPrint ((unsigned char) *pd)) + *pd = '?'; + pd++; + } + } + return (0); +} + +/* try to decode anything that looks like a valid RFC2047 encoded + * header field, ignoring RFC822 parsing rules + */ +void rfc2047_decode (char *d, const char *s, size_t dlen) +{ + const char *p, *q; + size_t n; + int found_encoded = 0; + + dlen--; /* save room for the terminal nul */ + + while (*s && dlen > 0) + { + if ((p = strstr (s, "=?")) == NULL || + (q = strchr (p + 2, '?')) == NULL || + (q = strchr (q + 1, '?')) == NULL || + (q = strstr (q + 1, "?=")) == NULL) + { + /* no encoded words */ + if (d != s) + strfcpy (d, s, dlen + 1); + return; + } + + if (p != s) + { + n = (size_t) (p - s); + /* ignore spaces between encoded words */ + if (!found_encoded || strspn (s, " \t\r\n") != n) + { + if (n > dlen) + n = dlen; + if (d != s) + memcpy (d, s, n); + d += n; + dlen -= n; + } + } + + rfc2047_decode_word (d, p, dlen); + found_encoded = 1; + s = q + 2; + n = strlen (d); + dlen -= n; + d += n; + } + *d = 0; +} + +void rfc2047_decode_adrlist (ADDRESS *a) +{ + while (a) + { + if (a->personal && strstr (a->personal, "=?") != NULL) + rfc2047_decode (a->personal, a->personal, strlen (a->personal) + 1); + a = a->next; + } +} |