/* ** Copyright 1998 - 2000 Double Precision, Inc. See COPYING for ** distribution information. */ #include #include #include #include #include "rfc822.h" #include "rfc2047.h" static const char rcsid[]="$Id$"; static const char xdigit[]="0123456789ABCDEF"; static char *rfc2047_search_quote(const char **ptr) { const char *p= *ptr; char *s; while (**ptr && **ptr != '?') ++ *ptr; if ((s=malloc( *ptr - p + 1)) == 0) return (0); memcpy(s, p, *ptr-p); s[*ptr - p]=0; return (s); } static int nyb(int c) { const char *p; c=toupper( (int)(unsigned char)c ); p=strchr(xdigit, c); return (p ? p-xdigit:0); } static unsigned char decode64tab[256]; static int decode64tab_init=0; static size_t decodebase64(char *ptr, size_t cnt) { size_t i, j; char a,b,c; size_t k; if (!decode64tab_init) { for (i=0; i<256; i++) decode64tab[i]=0; for (i=0; i<64; i++) decode64tab[ (int) ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i])]=i; decode64tab[ (int)'=' ] = 99; } i=cnt / 4; i=i*4; k=0; for (j=0; j> 4); b= (x << 4) | (y >> 2); c= (y << 6) | z; ptr[k++]=a; if ( ptr[j+2] != '=') ptr[k++]=b; if ( ptr[j+3] != '=') ptr[k++]=c; } return (k); } /* ** This is the main rfc2047 decoding function. It receives rfc2047-encoded ** text, and a callback function. The callback function is repeatedly ** called, each time receiving a piece of decoded text. The decoded ** info includes a text fragment - string, string length arg - followed ** by the character set, followed by a context pointer that is received ** from the caller. If the callback function returns non-zero, rfc2047 ** decoding terminates, returning the result code. Otherwise, ** rfc2047_decode returns 0 after a successfull decoding (-1 if malloc ** failed). */ int rfc2047_decode(const char *text, int (*func)(const char *, int, const char *, void *), void *arg) { int rc; int had_last_word=0; const char *p; char *chset; char *encoding; char *enctext; while (text && *text) { if (text[0] != '=' || text[1] != '?') { p=text; while (*text) { if (text[0] == '=' && text[1] == '?') break; if (!isspace((int)(unsigned char)*text)) had_last_word=0; ++text; } if (text > p && !had_last_word) { rc=(*func)(p, text-p, 0, arg); if (rc) return (rc); } continue; } text += 2; if ((chset=rfc2047_search_quote( &text )) == 0) return (-1); if (*text) ++text; if ((encoding=rfc2047_search_quote( &text )) == 0) { free(chset); return (-1); } if (*text) ++text; if ((enctext=rfc2047_search_quote( &text )) == 0) { free(encoding); free(chset); return (-1); } if (*text == '?' && text[1] == '=') text += 2; if (strcmp(encoding, "Q") == 0 || strcmp(encoding, "q") == 0) { char *q, *r; for (q=r=enctext; *q; ) { int c; if (*q == '=' && q[1] && q[2]) { *r++ = (char)( nyb(q[1])*16+nyb(q[2])); q += 3; continue; } c=*q++; if (c == '_') c=' '; *r++ = c ; } *r=0; } else if (strcmp(encoding, "B") == 0 || strcmp(encoding, "b")==0) { enctext[decodebase64(enctext, strlen(enctext))]=0; } rc=(*func)(enctext, strlen(enctext), chset, arg); free(enctext); free(chset); free(encoding); if (rc) return (rc); had_last_word=1; /* Ignore blanks between enc words */ } return (0); } /* ** rfc2047_decode_simple just strips out the rfc2047 decoding, throwing away ** the character set. This is done by calling rfc2047_decode twice, once ** to count the number of characters in the decoded text, the second time to ** actually do it. */ struct simple_info { char *string; int index; const char *mychset; } ; static int count_simple(const char *txt, int len, const char *chset, void *arg) { struct simple_info *iarg= (struct simple_info *)arg; iarg->index += len; return (0); } static int save_simple(const char *txt, int len, const char *chset, void *arg) { struct simple_info *iarg= (struct simple_info *)arg; memcpy(iarg->string+iarg->index, txt, len); iarg->index += len; return (0); } char *rfc2047_decode_simple(const char *text) { struct simple_info info; info.index=1; if (rfc2047_decode(text, &count_simple, &info)) return (0); if ((info.string=malloc(info.index)) == 0) return (0); info.index=0; if (rfc2047_decode(text, &save_simple, &info)) { free(info.string); return (0); } info.string[info.index]=0; return (info.string); } /* ** rfc2047_decode_enhanced is like simply, but prefixes the character set ** name before the text, in brackets. */ static int do_enhanced(const char *txt, int len, const char *chset, void *arg, int (*func)(const char *, int, const char *, void *) ) { int rc=0; struct simple_info *info=(struct simple_info *)arg; if (chset && info->mychset && strcmp(chset, info->mychset) == 0) chset=0; if (chset) { rc= (*func)(" [", 2, 0, arg); if (rc == 0) rc= (*func)(chset, strlen(chset), 0, arg); if (rc == 0) rc= (*func)("] ", 2, 0, arg); } if (rc == 0) rc= (*func)(txt, len, 0, arg); return (rc); } static int count_enhanced(const char *txt, int len, const char *chset, void *arg) { return (do_enhanced(txt, len, chset, arg, &count_simple)); } static int save_enhanced(const char *txt, int len, const char *chset, void *arg) { return (do_enhanced(txt, len, chset, arg, &save_simple)); } char *rfc2047_decode_enhanced(const char *text, const char *mychset) { struct simple_info info; info.mychset=mychset; info.index=1; if (rfc2047_decode(text, &count_enhanced, &info)) return (0); if ((info.string=malloc(info.index)) == 0) return (0); info.index=0; if (rfc2047_decode(text, &save_enhanced, &info)) { free(info.string); return (0); } info.string[info.index]=0; return (info.string); } void rfc2047_print(const struct rfc822a *a, const char *charset, void (*print_func)(char, void *), void (*print_separator)(const char *, void *), void *ptr) { rfc822_print_common(a, &rfc2047_decode_enhanced, charset, print_func, print_separator, ptr); } static char *a_rfc2047_encode_str(const char *str, const char *charset); static void rfc2047_encode_header_do(const struct rfc822a *a, const char *charset, void (*print_func)(char, void *), void (*print_separator)(const char *, void *), void *ptr) { rfc822_print_common(a, &a_rfc2047_encode_str, charset, print_func, print_separator, ptr); } /* ** When MIMEifying names from an RFC822 list of addresses, strip quotes ** before MIMEifying them, and add them afterwards. */ static char *a_rfc2047_encode_str(const char *str, const char *charset) { size_t l=strlen(str); char *p, *s; if (*str != '"' || str[l-1] != '"') return (rfc2047_encode_str(str, charset)); p=malloc(l); if (!p) return (0); memcpy(p, str+1, l-2); p[l-2]=0; s=rfc2047_encode_str(p, charset); free(p); if (!s) return (0); p=malloc(strlen(s)+3); if (!p) { free(s); return (0); } p[0]='"'; strcpy(p+1, s); strcat(p, "\""); free(s); return (p); } static void count(char c, void *p); static void counts(const char *c, void *p); static void save(char c, void *p); static void saves(const char *c, void *p); char *rfc2047_encode_header(const struct rfc822a *a, const char *charset) { size_t l; char *s, *p; l=1; rfc2047_encode_header_do(a, charset, &count, &counts, &l); if ((s=malloc(l)) == 0) return (0); p=s; rfc2047_encode_header_do(a, charset, &save, &saves, &p); *p=0; return (s); } static void count(char c, void *p) { ++*(size_t *)p; } static void counts(const char *c, void *p) { while (*c) count(*c++, p); } static void save(char c, void *p) { **(char **)p=c; ++*(char **)p; } static void saves(const char *c, void *p) { while (*c) save(*c++, p); } int rfc2047_encode_callback(const char *str, const char *charset, int (*func)(const char *, size_t, void *), void *arg) { int rc; while (*str) { size_t i, c; for (i=0; str[i]; i++) if ((str[i] & 0x80) || str[i] == '"') break; if (str[i] == 0) return ( i ? (*func)(str, i, arg):0); /* Find start of word */ while (i) { --i; if (isspace((int)(unsigned char)str[i])) { ++i; break; } } if (i) { rc= (*func)(str, i, arg); if (rc) return (rc); str += i; } /* ** Figure out when to stop MIME decoding. Consecutive ** MIME-encoded words are MIME-encoded together. */ i=0; for (;;) { for ( ; str[i]; i++) if (isspace((int)(unsigned char)str[i])) break; if (str[i] == 0) break; for (c=i; str[c] && isspace((int)(unsigned char)str[c]); ++c) ; for (; str[c]; c++) if (isspace((int)(unsigned char)str[c]) || (str[c] & 0x80) || str[c] == '"') break; if (str[c] == 0 || isspace((int)(unsigned char)str[c])) break; i=c; } /* Output mimeified text, insert spaces at 70+ character ** boundaries for line wrapping. */ c=0; while (i) { if (c == 0) { if ( (rc=(*func)("=?", 2, arg)) != 0 || (rc=(*func)(charset, strlen(charset), arg)) != 0 || (rc=(*func)("?Q?", 3, arg)) != 0) return (rc); c += strlen(charset)+5; } if ((*str & 0x80) || *str == '"') { char buf[3]; buf[0]='='; buf[1]=xdigit[ ( *str >> 4) & 0x0F ]; buf[2]=xdigit[ *str & 0x0F ]; if ( (rc=(*func)(buf, 3, arg)) != 0) return (rc); c += 3; ++str; --i; } else { size_t j; for (j=0; j < i && !(str[j] & 0x80) && str[j] != '"'; j++) if (j + c >= 70) break; if ( (rc=(*func)(str, j, arg)) != 0) return (rc); c += j; str += j; i -= j; } if (i == 0 || c >= 70) { if ( (rc=(*func)("?= ", i ? 3:2, arg)) != 0) return (rc); c=0; } } } return (0); } static int count_char(const char *c, size_t l, void *p) { size_t *i=(size_t *)p; *i += l; return (0); } static int save_char(const char *c, size_t l, void *p) { char **s=(char **)p; memcpy(*s, c, l); *s += l; return (0); } char *rfc2047_encode_str(const char *str, const char *charset) { size_t i=1; char *s, *p; (void)rfc2047_encode_callback(str, charset, &count_char, &i); if ((s=malloc(i)) == 0) return (0); p=s; (void)rfc2047_encode_callback(str, charset, &save_char, &p); *p=0; return (s); }