#ifdef MIME_SUPPORT
#include <sys/cdefs.h>
#ifndef __lint__
__RCSID("$NetBSD: mime_header.c,v 1.9 2013/02/14 18:23:45 christos Exp $");
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "def.h"
#include "extern.h"
#include "mime.h"
#include "mime_header.h"
#include "mime_codecs.h"
static const char *
grab_charset(char *from_cs, size_t from_cs_len, const char *p)
{
char *q;
q = from_cs;
for (; *p != '?'; p++) {
if (*p == '\0' || q >= from_cs + from_cs_len - 1)
return NULL;
*q++ = *p;
}
*q = '\0';
return ++p;
}
static int
decode_word(const char **ibuf, char **obuf, char *oend, const char *to_cs)
{
ssize_t declen;
size_t enclen, dstlen;
char decword[LINESIZE];
char from_cs[LINESIZE];
const char *encword, *iend, *p;
char *dstend;
char enctype;
p = *ibuf;
if (p[0] != '=' && p[1] != '?')
return -1;
if (strlen(p) < 2 + 1 + 3 + 1 + 2)
return -1;
p = grab_charset(from_cs, sizeof(from_cs), p + 2);
if (p == NULL)
return -1;
enctype = *p++;
if (*p++ != '?')
return -1;
encword = p;
p = strchr(p, '?');
if (p == NULL || p[1] != '=')
return -1;
enclen = p - encword;
iend = p + 2;
if (iend > *ibuf + 75)
return -1;
if (oend < *obuf + 1) {
assert( 0);
return -1;
}
dstend = to_cs ? decword : *obuf;
dstlen = (to_cs ? sizeof(decword) : (size_t)(oend - *obuf)) - 1;
declen = mime_rfc2047_decode(enctype, dstend, dstlen, encword, enclen);
if (declen == -1)
return -1;
dstend += declen;
#ifdef CHARSET_SUPPORT
if (to_cs != NULL) {
iconv_t cd;
const char *src;
size_t srclen;
size_t cnt;
cd = iconv_open(to_cs, from_cs);
if (cd == (iconv_t)-1)
return -1;
src = decword;
srclen = declen;
dstend = *obuf;
dstlen = oend - *obuf - 1;
cnt = mime_iconv(cd, &src, &srclen, &dstend, &dstlen);
(void)iconv_close(cd);
if (cnt == (size_t)-1)
return -1;
}
#endif
*dstend = '\0';
*ibuf = iend;
*obuf = dstend;
return 0;
}
static inline int
is_FWS(int c)
{
return c == ' ' || c == '\t' || c == '\n';
}
static inline const char *
skip_FWS(const char *p)
{
while (is_FWS(*p))
p++;
return p;
}
static inline void
copy_skipped_FWS(char **dst, char *dstend, const char **src, const char *srcend)
{
const char *p, *pend;
char *q, *qend;
p = *src;
q = *dst;
pend = srcend;
qend = dstend;
if (p) {
while (p < pend && q < qend)
*q++ = *p++;
*dst = q;
*src = NULL;
}
}
static void
mime_decode_usfield(char *outbuf, size_t outsize, const char *hstring)
{
const char *p, *p0;
char *q, *qend;
int lastc;
const char *charset;
charset = value(ENAME_MIME_CHARSET);
qend = outbuf + outsize - 1;
q = outbuf;
p = hstring;
p0 = NULL;
lastc = (unsigned char)' ';
while (*p && q < qend) {
const char *p1;
char *q1;
if (is_FWS(lastc) && p[0] == '=' && p[1] == '?' &&
decode_word((p1 = p, &p1), (q1 = q, &q1), qend, charset) == 0 &&
(*p1 == '\0' || is_FWS(*p1))) {
p0 = p1;
q = q1;
p = skip_FWS(p1);
lastc = (unsigned char)*p0;
}
else {
copy_skipped_FWS(&q, qend, &p0, p);
lastc = (unsigned char)*p;
if (q < qend)
*q++ = *p++;
}
}
copy_skipped_FWS(&q, qend, &p0, p);
*q = '\0';
}
static int
decode_comment(char **obuf, char *oend, const char **ibuf, const char *iend, const char *charset)
{
const char *p, *pend, *p0;
char *q, *qend;
int lastc;
p = *ibuf;
q = *obuf;
pend = iend;
qend = oend;
lastc = ' ';
p0 = NULL;
while (p < pend && q < qend) {
const char *p1;
char *q1;
if (is_FWS(lastc) && p[0] == '=' && p[1] == '?' &&
decode_word((p1 = p, &p1), (q1 = q, &q1), qend, charset) == 0 &&
(*p1 == ')' || is_FWS(*p1))) {
lastc = (unsigned char)*p1;
p0 = p1;
q = q1;
p = skip_FWS(p1);
if (p > pend)
p = pend;
}
else {
copy_skipped_FWS(&q, qend, &p0, p);
if (q >= qend)
break;
if (*p == ')') {
*q++ = *p++;
break;
}
if (*p == '(') {
*q++ = *p++;
if (decode_comment(&q, qend, &p, pend, charset) == -1)
return -1;
lastc = ')';
}
else if (*p == '\\' && p + 1 < pend) {
if (p[1] == '(' || p[1] == ')' || p[1] == '\\')
*q++ = *p;
p++;
lastc = (unsigned char)*p;
if (q < qend)
*q++ = *p++;
}
else {
lastc = (unsigned char)*p;
*q++ = *p++;
}
}
}
*ibuf = p;
*obuf = q;
return 0;
}
static void
decode_quoted_string(char **obuf, char *oend, const char **ibuf, const char *iend)
{
const char *p, *pend;
char *q, *qend;
qend = oend;
pend = iend;
p = *ibuf;
q = *obuf;
while (p < pend && q < qend) {
if (*p == '"') {
*q++ = *p++;
break;
}
if (*p == '\\' && p + 1 < pend) {
if (p[1] == '"' || p[1] == '\\') {
*q++ = *p;
if (q >= qend)
break;
}
p++;
}
*q++ = *p++;
}
*ibuf = p;
*obuf = q;
}
static void
decode_domain_literal(char **obuf, char *oend, const char **ibuf, const char *iend)
{
const char *p, *pend;
char *q, *qend;
qend = oend;
pend = iend;
p = *ibuf;
q = *obuf;
while (p < pend && q < qend) {
if (*p == ']') {
*q++ = *p++;
break;
}
if (*p == '\\' && p + 1 < pend) {
if (p[1] == '[' || p[1] == ']' || p[1] == '\\') {
*q++ = *p;
if (q >= qend)
break;
}
p++;
}
*q++ = *p++;
}
*ibuf = p;
*obuf = q;
}
static inline int
is_specials(int c)
{
static const char specialtab[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
return !(c & ~0x7f) ? specialtab[c] : 0;
}
static void
mime_decode_sfield(char *linebuf, size_t bufsize, const char *hstring)
{
const char *p, *pend, *p0;
char *q, *qend;
const char *charset;
int lastc;
charset = value(ENAME_MIME_CHARSET);
p = hstring;
q = linebuf;
pend = hstring + strlen(hstring);
qend = linebuf + bufsize - 1;
lastc = (unsigned char)' ';
p0 = NULL;
while (p < pend && q < qend) {
const char *p1;
char *q1;
if (*p != '=') {
copy_skipped_FWS(&q, qend, &p0, p);
if (q >= qend)
break;
}
switch (*p) {
case '(':
*q++ = *p++;
(void)decode_comment(&q, qend, &p, pend, charset);
lastc = (unsigned char)p[-1];
break;
case '"':
*q++ = *p++;
decode_quoted_string(&q, qend, &p, pend);
lastc = (unsigned char)p[-1];
break;
case '[':
*q++ = *p++;
decode_domain_literal(&q, qend, &p, pend);
lastc = (unsigned char)p[-1];
break;
case '\\':
if (p + 1 < pend) {
if (is_specials(p[1])) {
*q++ = *p;
if (q >= qend)
break;
}
p++;
}
goto copy_char;
case '=':
if ((lastc == ',' || is_FWS(lastc)) && p[1] == '?' &&
decode_word((p1 = p, &p1), (q1 = q, &q1), qend, charset) == 0 &&
(*p1 == '\0' || *p1 == ',' || is_FWS(*p1))) {
lastc = (unsigned char)*p1;
p0 = p1;
q = q1;
p = skip_FWS(p1);
if (p > pend)
p = pend;
break;
}
else {
copy_skipped_FWS(&q, qend, &p0, p);
if (q >= qend)
break;
goto copy_char;
}
case '<':
default:
copy_char:
lastc = (unsigned char)*p;
*q++ = *p++;
break;
}
}
copy_skipped_FWS(&q, qend, &p0, p);
*q = '\0';
}
PUBLIC hfield_decoder_t
mime_hfield_decoder(const char *name)
{
static const struct field_decoder_tbl_s {
const char *field_name;
size_t field_len;
hfield_decoder_t decoder;
} field_decoder_tbl[] = {
#define X(s) s, sizeof(s) - 1
{ X("Received:"), NULL },
{ X("Content-Type:"), NULL },
{ X("Content-Disposition:"), NULL },
{ X("Content-Transfer-Encoding:"), NULL },
{ X("Content-Description:"), mime_decode_sfield },
{ X("Content-ID:"), mime_decode_sfield },
{ X("MIME-Version:"), mime_decode_sfield },
{ X("Bcc:"), mime_decode_sfield },
{ X("Cc:"), mime_decode_sfield },
{ X("Date:"), mime_decode_sfield },
{ X("From:"), mime_decode_sfield },
{ X("In-Reply-To:"), mime_decode_sfield },
{ X("Keywords:"), mime_decode_sfield },
{ X("Message-ID:"), mime_decode_sfield },
{ X("References:"), mime_decode_sfield },
{ X("Reply-To:"), mime_decode_sfield },
{ X("Return-Path:"), mime_decode_sfield },
{ X("Sender:"), mime_decode_sfield },
{ X("To:"), mime_decode_sfield },
{ X("Subject:"), mime_decode_usfield },
{ X("Comments:"), mime_decode_usfield },
{ X("X-"), mime_decode_usfield },
{ NULL, 0, mime_decode_usfield },
#undef X
};
const struct field_decoder_tbl_s *fp;
for (fp = field_decoder_tbl; fp->field_name; fp++)
if (strncasecmp(name, fp->field_name, fp->field_len) == 0)
break;
return fp->decoder;
}
#endif