/*
**
** Copyright (C) 1993 Swedish University Network (SUNET)
**
**
** This program is developed by UDAC, Uppsala University by commission
** of the Swedish University Network (SUNET).
**
** 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 FITTNESS 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.
**
**
** Martin.Wendel@its.uu.se
** Torbjorn.Wictorin@its.uu.se
**
** ITS
** P.O. Box 887
** S-751 08 Uppsala
** Sweden
**
*/
#include "emil.h"
#include "charset/charset.h"
int strptrncnv(CHARSET *, CHARSET *, struct data *, struct data *, int);
static char * out_data_part(struct data *);
static char delim[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
struct resheader {
char *field;
long types;
};
static struct resheader resh[] = {
"Return-Path", 0,
"Received", 0,
"Reply-To", RADDR | ATOM | RFC1522,
"From", RADDR | ATOM | RFC1522,
"Sender", RADDR | ATOM | RFC1522,
"Resent-Reply-To", RADDR | ATOM | RFC1522,
"Resent-From", RADDR | ATOM | RFC1522,
"Resent-Sender", RADDR | ATOM | RFC1522,
"Date", 0,
"Resent-Date", 0,
"To", RADDR | ATOM | RFC1522,
"Resent-To", RADDR | ATOM | RFC1522,
"cc", RADDR | ATOM | RFC1522,
"Resent-cc", RADDR | ATOM | RFC1522,
"bcc", RADDR | ATOM | RFC1522,
"Resent-bcc", RADDR | ATOM | RFC1522,
"Message-ID", 0,
"Resent-Message-ID", 0,
"In-Reply-To", RADDR | ATOM | RFC1522,
"References", RADDR | ATOM | RFC1522,
"Keywords", ATOM | RFC1522,
"Subject", TEXT,
"Comments", TEXT,
"Encrypted", ATOM,
"X-Sun-Encoding-Info", ATOM,
NULL, TEXT};
struct hprs *
copy_hprs(struct hprs *h)
{
struct hprs *th;
struct data *tbuf, *inbuf;
inbuf = h->td;
tbuf = (struct data *)Yalloc(sizeof(struct data ));
tbuf->contents = inbuf->contents;
tbuf->offset = inbuf->offset;
tbuf->end = inbuf->end;
tbuf->size = inbuf->size;
th = (struct hprs *)Yalloc(sizeof(struct hprs));
th->sd = tbuf;
th->td = tbuf;
return(th);
}
int
where(char *s, char c)
{
char *t;
if ((t = index(s, c)) == NULL)
return(0);
else
return(t - s);
}
int parse_rfc1522(struct header *th)
{
struct hprs *h;
char *field;
int i;
if (th->value == NULL)
return(OK);
#ifdef DEBUG
if (edebug)
fprintf(stderr, "+ Parse header\n");
#endif
if ((field = th->field->contents) == NULL)
return(OK);
/* Return if header is reserved, otherwise get types */
for (i = 0; resh[i].field != NULL; i++)
if (cmatch(field, resh[i].field) == TRUE)
break;
if (resh[i].types == 0)
return(OK);
/* Insert discrimination of headers here */
if (th->hvalue == NULL)
{
h = (struct hprs *)Yalloc(sizeof(struct hprs));
h->types = resh[i].types;
h->sd = th->value;
h->td = h->sd;
if (parse_hline(h) != OK)
h = NULL;
}
else
h = th->hvalue;
if (h != NULL)
{
if (convert_hlines(h) == OK)
th->hvalue = h;
return(OK);
}
else
{
logger(LOG_WARNING, "parse_rfc1522: Error parsing hline");
return(NOK);
}
}
int
parse_hline(struct hprs *h)
{
struct data *inbuf;
struct hprs *tmpd;
int offset;
int type = UNKNOWN;
unsigned char *inb;
int len;
inbuf = h->sd;
inb = inbuf->contents + inbuf->offset;
offset = inbuf->offset;
while (inbuf->offset < inbuf->end)
{
switch (type & h->types)
{
case UNKNOWN:
/* Loop through the available types */
if (type == 0)
type = 1;
else
type = type <<1; /* Shift */
if (type == TOP)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "+ No applicable type (failed).\n");
#endif
return(NOK);
}
break;
case COMMENT:
offset = inbuf->offset;
if (*inb != '(')
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inb++;
inbuf->offset += 1;
if ((len = where(inb, ')')) == 0)
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inbuf->bodystart = inbuf->offset;
inbuf->bodyend = inbuf->bodystart + len;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found comment '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->sd->end = inbuf->bodyend ? inbuf->bodyend : inbuf->end;
tmpd->types = ATOM | RFC1522 | COMMENT;
inbuf->offset += len + 1;
inb += len + 1;
if (parse_hline(tmpd) == OK)
{
h->type = COMMENT;
h->child = tmpd;
}
else
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
break;
case HQSTRING:
offset = inbuf->offset;
if (*inb != '"')
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inb++;
inbuf->offset += 1;
if ((len = where(inb, '"')) == 0)
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inbuf->bodystart = inbuf->offset;
inbuf->bodyend = inbuf->bodystart + len;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found qstring '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->types = ATOM;
tmpd->sd->end = inbuf->bodyend ? inbuf->bodyend : inbuf->end;
inbuf->offset += len + 1;
inb += len + 1;
if (parse_hline(tmpd) == OK)
{
h->type = HQSTRING;
h->child = tmpd;
}
else
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
break;
case DLITERAL:
offset = inbuf->offset;
if (*inb != '[')
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inb++;
inbuf->offset += 1;
if ((len = where(inb, ']')) == 0)
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inbuf->bodystart = inbuf->offset;
inbuf->bodyend = inbuf->bodystart + len;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found domain literal '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->types = ATOM;
tmpd->sd->end = inbuf->bodyend ? inbuf->bodyend : inbuf->end;
inbuf->offset += len + 1;
inb += len + 1;
if (parse_hline(tmpd) == OK)
{
h->type = DLITERAL;
h->child = tmpd;
}
else
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
break;
case RADDR:
offset = inbuf->offset;
if (*inb != '<')
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inb++;
inbuf->offset += 1;
if ((len = where(inb, '>')) == 0)
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
inbuf->bodystart = inbuf->offset;
inbuf->bodyend = inbuf->bodystart + len;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found address '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->types = ATOM;
tmpd->sd->end = inbuf->bodyend ? inbuf->bodyend : inbuf->end;
inbuf->offset += len + 1;
inb += len + 1;
if (parse_hline(tmpd) == OK)
{
h->type = RADDR;
h->child = tmpd;
}
else
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
break;
case TEXT:
offset = inbuf->offset;
inbuf->bodystart = inbuf->offset;
inbuf->bodyend = inbuf->end;
tmpd = copy_hprs(h);
tmpd->types = ATOM | RFC1522 | COMMENT;
tmpd->sd->end = inbuf->bodyend ? inbuf->bodyend : inbuf->end;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found text.\n");
#endif
if (parse_hline(tmpd) == OK)
{
h->type = TEXT;
h->child = tmpd;
return(OK);
}
else
{
type = type <<1; /* Shift */
inbuf->offset = offset;
break;
}
break;
case RFC1522:
offset = inbuf->offset;
if (*inb != '=')
{
type = type <<1;
inbuf->offset = offset;
break;
}
inbuf->offset += 1;
inb++;
if (*inb != '?')
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on starting '=?'\n");
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
inbuf->offset += 1;
inb++;
/* Get charset */
if ((len = where(inb, '?')) == 0)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on second '='\n");
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
inbuf->charset = (char *)Yalloc(len + 1);
strncpy(inbuf->charset, inb, len);
inb += len + 1;
inbuf->offset += len + 1;
/* Get encoding */
inbuf->encoding = 0;
switch(*inb)
{
case 'q':
case 'Q':
inbuf->encoding = EQP;
break;
case 'b':
case 'B':
inbuf->encoding = EBASE64;
break;
default:
break;
}
if (inbuf->encoding == 0)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on encoding '%c'\n", *inb);
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
inb++;
inbuf->offset += 1;
if (*inb != '?')
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on third '?'\n");
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
inb++;
inbuf->offset += 1;
/* Get string */
if ((len = where(inb, '?')) == 0)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on string\n");
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
if (*(inbuf->contents + len + inbuf->offset) != '?')
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on closing '?'\n");
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
inb += len + 1;
if (*inb != '=')
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* rfc1522 decoding failed on closing '='\n");
#endif
type = type <<1;
inbuf->offset = offset;
break;
}
h->pstart = inbuf->offset;
h->pend = inbuf->offset + len;
inbuf->bodystart = offset;
inbuf->offset += len + 2;
inbuf->bodyend = inbuf->offset;
inb++;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found rfc1522 encoded string '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->type = RFC1522;
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
break;
case ATOM:
offset = inbuf->offset;
h->types = h->types | DELIMITER;
if (delim[*inb] != '\0')
{
type = type <<1;
inbuf->offset = offset;
break;
}
else
{
inbuf->bodystart = inbuf->offset;
while(inbuf->offset < inbuf->end)
{
if (delim[*inb] != '\0')
break;
else
{
inb++;
inbuf->offset += 1;
}
}
inbuf->bodyend = inbuf->offset;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found atom '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->type = ATOM;
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
}
break;
case DELIMITER:
offset = inbuf->offset;
if (delim[*inb] == '\0')
{
type = type <<1;
inbuf->offset = offset;
break;
}
inbuf->bodystart = inbuf->offset;
inb++;
inbuf->offset += 1;
inbuf->bodyend = inbuf->offset;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- Found delimiter '%s'.\n", out_data_part(inbuf));
#endif
tmpd = copy_hprs(h);
tmpd->types = h->types;
if (parse_hline(tmpd) == OK)
{
h->type = DELIMITER;
h->sibling = tmpd;
return(OK);
}
else
{
type = type <<1;
inbuf->offset = offset;
break;
}
break;
default:
#ifdef DEBUG
if (edebug)
fprintf(stderr, "*** Unknown type (failed).\n");
#endif
return(NOK);
break;
}
}
inbuf->bodyend = inbuf->offset;
return(OK);
}
int
convert_hlines(struct hprs *h)
{
int status;
if (target == NULL || source == NULL)
return(NOK);
h->td->offset = h->td->bodystart;
/* Check 8bit data */
check_bits(h->td);
while ((status = recode_hline(h)) != OK)
h->td->offset = h->td->bodystart;
if (h->child != NULL)
(void)convert_hlines(h->child);
if (h->sibling != NULL)
(void)convert_hlines(h->sibling);
return(OK);
}
int
recode_hline(struct hprs *h)
{
switch (h->td->encoding)
{
case E7BIT:
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- is 7bit\n");
#endif
if (h->td->charset == NULL)
h->td->charset = source->charset == NULL ? NEWSTR("ISO-8859-1"):
source->charset;
if (h_tocharset(h) == OK)
check_bits(h->td);
if (h->td->encoding != E7BIT)
return(NOK);
return(OK);
break;
case E8BIT:
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- is 8bit\n");
#endif
if (h->td->charset == NULL)
h->td->charset = source->charset == NULL ? NEWSTR("ISO-8859-1"):
source->charset;
if (h_tocharset(h) == OK)
check_bits(h->td);
if (h->td->encoding != E8BIT)
return(NOK);
/* Convert if allowed */
if (process)
{
if (h->types & RFC1522 && h->type == ATOM)
switch(target->htext)
{
case EQP:
h_to_quoted_printable(h);
break;
case EBASE64:
h_to_base64(h);
break;
}
}
return(OK);
break;
case EQP:
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- is qp\n");
#endif
if (process == 0 || target->htext != EQP ||
((target->charset != NULL) &&
(cmatch(target->charset, h->td->charset) == FALSE)))
{
if (h_from_quoted_printable(h) == OK)
{
check_bits(h->td);
return(NOK);
}
return(OK);
}
return(OK);
break;
case EBASE64:
#ifdef DEBUG
if (edebug)
fprintf(stderr, "- is base64\n");
#endif
if (process == 0 || target->htext != EBASE64 ||
((target->charset != NULL) &&
(cmatch(target->charset, h->td->charset) == FALSE)))
{
if (h_from_base64(h) == OK)
{
check_bits(h->td);
return(NOK);
}
return(OK);
}
else
return(OK);
break;
default:
return(OK);
break;
}
return(NOK);
}
int
h_tocharset(struct hprs *h)
{
CHARSET *schar, *tchar;
struct data *inbuf, *outbuf;
char *sset, *tset;
int copy = 0;
inbuf = h->td;
sset = h->td->charset;
if (target->htext == E7BIT || target->htext == ESE)
tset = NEWSTR("ISO-8859-1");
else
tset = target->charset;
#ifdef DEBUG
if (edebug)
fprintf(stderr, "* Header tocharset");
#endif
if (inbuf->size == 0)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, "--- Empty input (failed).\n");
#endif
return(NOK);
}
if (sset != NULL)
if ((strcasecmp(sset, "DK-US") == 0) ||
(strcasecmp(sset, "US-DK") == 0))
{
h->td->charset = NEWSTR("US-ASCII");
sset = h->td->charset;
}
if (sset == NULL || tset == NULL ||
(schar = (CHARSET *)getchset(sset, 29)) == NULL ||
(tchar = (CHARSET *)getchset(tset, 29)) == NULL)
copy = 1;
if (copy)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, ", copying.\n");
#endif
return(NOK);
}
else
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, ", from %s to %s", sset, tset);
#endif
outbuf = (struct data *) Yalloc(sizeof(struct data ));
strptrncnv(tchar, schar, outbuf, inbuf, HEAD_BUF);
outbuf->bodyend = outbuf->end;
outbuf->charset = NEWSTR(tset);
h->td = outbuf;
}
if (target->htext == E7BIT)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, ", to 7bit");
#endif
h->td->offset = h->td->bodystart;
to_hstripped(h);
}
if (target->htext == ESE)
{
#ifdef DEBUG
if (edebug)
fprintf(stderr, ", to se");
#endif
h->td->offset = h->td->bodystart;
to_h7bit(h);
}
#ifdef DEBUG
if (edebug)
fprintf(stderr, ", done.\n");
#endif
return(OK);
}
char *
out_data_part(struct data *d)
{
char *c;
c = (char *)Yalloc(2 + d->bodyend - d->bodystart);
strncpy(c, d->contents + d->bodystart, d->bodyend - d->bodystart);
return(c);
}
syntax highlighted by Code2HTML, v. 0.9.1