/*
* Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
*/
/*
* This file implements most of an RFC822/976 scanner/parser.
*/
#include "mailer.h"
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "io.h"
#include "prototypes.h"
#ifndef _IOFBF
#define _IOFBF 0
#endif /* !_IOFBF */
struct headerinfo nullhdr = { 0, nilHeaderSemantics, nilUserType, normal };
int D_rfc822 = 0; /* Debug traceing */
extern int do_hdr_warning; /* If set, headers with errors in them are
printed "as is" -- h_line */
/*
* Apply RFC822 scanner and parser to the list of Line tokens passed as args.
*
* The return value in the appropriate form for the type of header line.
* This function is the primary interface to the scanner/parser routines here.
*/
union misc
hdr_scanparse(e, h, commentflag, no_line_crossing)
struct envelope *e;
struct header *h;
int commentflag, no_line_crossing;
{
register token822 *t;
long len;
const char *cp, *ocp;
char c1, c2;
union misc retval;
token822 *tlist, **prev_tp, *scan_t, *nt, tt;
struct headerinfo *hd;
/* If we don't recognize the header, we can't parse it
properly, but we can always do unstructured tokenization */
if ((hd = h->h_descriptor) == NULL || hd->hdr_name == NULL
|| hd->semantics == nilHeaderSemantics) {
#if 1
tlist = NULL;
prev_tp = &tlist;
for (t = h->h_lines; t != NULL; t = t->t_next) {
if (t->t_type != Line) /* sanity check */
continue;
cp = t->t_pname;
len = TOKENLEN(t);
while (len > 0) {
ocp = cp;
nt = t;
scan_t = scan822utext(&cp, len, &nt);
len -= cp - ocp;
/* Append the scanner tokens to the list of tokens */
*prev_tp = scan_t;
while (scan_t != NULL) {
/*
* Doing it in a loop allows the scanner to
* return a token list instead of one token.
*/
prev_tp = &(scan_t->t_next);
scan_t = scan_t->t_next;
}
}
tt.t_len = 1;
tt.t_pname = "\n";
tt.t_type = Space;
tt.t_next = NULL;
*prev_tp = copyToken(&tt);
prev_tp = &((*prev_tp)->t_next);
}
*prev_tp = NULL;
retval.t = tlist;
#else
retval.t = h->h_lines;
#endif
return retval;
}
/*
* The list of special characters in RFC822 isn't always enough
* to properly interpret certain header fields, for example date
* strings in strange formats, or %-@ addresses. Here we determine
* which extra characters should be considered specials, so we can
* inform the scanner of it.
*/
switch (hd->semantics) {
case DateTime:
c1 = '/'; c2 = '-'; break;
case Received:
/* I wish we didn't have to do it for the whole line */
/* There's a definite BUG possibility here -- what if one
* sees 'from mitre-bedford.arpa', how will it be treated?
* The -bedford.arpa part might get lost, thats what!
*/
c1 = '/'; c2 = '-'; break;
#ifdef RFC976
case Address: case Addresses: case AddressList:
case RouteAddressInAngles: case RouteAddress:
case Mailbox: case Mailboxes: case MailboxList: case AMailboxList:
c1 = '!'; c2 = '%'; break; /* violates RFC822! */
#endif /* RFC976 */
default:
c1 = '\0'; c2 = '\0'; break;
}
tlist = NULL;
prev_tp = &tlist;
for (t = h->h_lines; t != NULL; t = t->t_next) {
if (t->t_type != Line) /* sanity check */
continue;
/*
* Scan the entire line at a time, instead of having the
* parser call the scanner when a token is needed. This avoids
* nontrivial function-call overhead, but does decrease
* flexibility slightly.
*/
cp = t->t_pname;
len = TOKENLEN(t);
while (len > 0) {
ocp = cp;
nt = t;
scan_t = scan822(&cp, len, c1, c2, commentflag, &nt);
if (nt != t && !no_line_crossing) {
/* compound token across line */
while (t != nt)
t = t->t_next;
/* len should be 0 */
len = t->t_pname + TOKENLEN(t) - cp;
} else
len -= cp - ocp;
/* Append the scanner tokens to the list of tokens */
*prev_tp = scan_t;
while (scan_t != NULL) {
/*
* Doing it in a loop allows the scanner to
* return a token list instead of one token.
*/
prev_tp = &(scan_t->t_next);
scan_t = scan_t->t_next;
}
}
}
*prev_tp = NULL;
/* parse it */
if (hd->semantics == nilHeaderSemantics)
retval.t = tlist;
else
retval = parse822(hd->semantics,
&tlist, &(e->e_localtime),
D_rfc822 ? stderr : NULL);
return retval;
}
/*
* If s points to a known header field name,
* return the description associated with that field.
*/
struct header *
makeHeader(hdb, s, len)
struct sptree *hdb;
register const char *s;
int len;
{
struct headerinfo *hlp;
struct header *h;
char c;
const char *p = s;
int testlen = len;
h = (struct header *)tmalloc(sizeof (struct header));
h->h_pname = strnsave(s, len);
/* Using strlen() may go beyond the original buffer,
as is the case with partial reading of the email
headers to a limited size buffer.. */
while (*p != 0 && --testlen >= 0) ++p;
if (testlen > 0 || *p != 0) {
c = *(s+len);
*(char *)(s+len) = '\0';
hlp = find_header(hdb, s);
*(char *)(s+len) = c;
} else
hlp = find_header(hdb, s);
if (hlp == NULL)
hlp = &nullhdr;
h->h_contents.a = NULL;
h->h_descriptor = hlp;
h->h_stamp = newHeader;
h->h_next = NULL;
return h;
}
struct headerinfo *
senderDesc()
{
static struct headerinfo *sd = NULL;
if (sd != NULL)
return sd;
for (sd = &envelope_hdrs[0]; sd->hdr_name != NULL; ++sd)
if (sd->class == eFrom)
break;
return sd;
}
void
set_pname(e, h, s)
struct envelope *e;
struct header *h;
const char *s;
{
struct headerinfo *sd;
char buf[BUFSIZ];
if (e->e_resent) {
strcpy(buf, "Resent-");
strncpy(buf+7, s, sizeof(buf)-8);
buf[sizeof(buf)-1] = 0;
s = strsave(buf);
}
if (!CISTREQ(h->h_descriptor->hdr_name, s)) {
sd = (struct headerinfo *)tmalloc(sizeof (struct headerinfo));
*sd = *(h->h_descriptor);
sd->hdr_name = s;
h->h_descriptor = sd;
}
h->h_pname = s;
}
/*
* Copy the preferred value for the message sender to a new header struct.
*/
struct header *
copySender(e)
struct envelope *e;
{
register struct header *h;
register char **cpp;
const char *cp;
struct header *best;
int minval;
best = NULL;
minval = 10000;
for (h = e->e_headers; h != NULL; h = h->h_next) {
if (h->h_descriptor->user_type != Sender
|| h->h_stamp == BadHeader
|| (e->e_resent != 0 && h->h_descriptor->class != Resent))
continue;
cp = h->h_descriptor->hdr_name + e->e_resent;
for (cpp = prio_list; *cpp != NULL; ++cpp) {
/* printf("comparing '%s' and '%s'\n", *cpp, cp); */
if (**cpp == *cp && STREQ(*cpp, cp) &&
(cpp - prio_list) < minval) {
best = h;
minval = cpp - prio_list;
}
}
}
if (best == NULL)
return NULL;
h = (struct header *)tmalloc(sizeof (struct header));
*h = *best;
h->h_pname = e->e_resent ? "Resent-From" : "From";
h->h_descriptor = senderDesc();
h->h_next = NULL;
h->h_lines = NULL;
/* should check that we don't have a MailboxList or AddressList here */
return h;
}
/*
* Copy recipient headers to fill out the envelope information.
*/
struct header *
copyRecipient(h)
register struct header *h;
{
register struct header *nh;
static struct headerinfo *recipientDesc;
if (recipientDesc == NULL) {
for (recipientDesc = &envelope_hdrs[0];
recipientDesc->hdr_name != NULL; ++recipientDesc) {
if (recipientDesc->class == eTo)
break;
}
}
nh = (struct header *)tmalloc(sizeof (struct header));
*nh = *h;
nh->h_pname = "to";
nh->h_descriptor = recipientDesc;
/* we know nh->h_next is going to be overwritten immediately */
return nh;
}
/*
* Construct a header line containing a message id.
*/
struct header *
mkMessageId(e, unixtime)
struct envelope *e;
time_t unixtime;
{
register struct header *h;
static struct headerinfo *msgidDesc;
static int genseq = 0;
char buf[200]; /* XX: tsk tsk */
struct tm *ts;
if (msgidDesc == NULL) {
for (msgidDesc = &mandatory_hdrs[0];
msgidDesc->hdr_name != NULL; ++msgidDesc) {
if (msgidDesc->semantics == MessageID)
break;
}
}
h = (struct header *)tmalloc(sizeof (struct header));
h->h_pname = e->e_resent ? "Resent-Message-Id" : "Message-Id";
h->h_descriptor = msgidDesc;
h->h_stamp = newHeader;
h->h_next = 0;
#if 0
{
char *cp, *s, *p, tzbuf[20];
cp = rfc822tz(&unixtime, &ts, 1);
for (s = cp, p = tzbuf; *s != '\0'; ++s) {
if (isascii(*s) && isupper(*s))
*p++ = tolower(*s);
else if (*s == '(' || *s == ')')
continue; /* Skip them.. */
else if (isascii(*s) && isspace(*s))
*p++ = '_';
else
*p++ = *s;
}
*p = '\0';
cp = tzbuf;
sprintf(buf, "<%s/%02d%s%d.%02d%02d%02d%.20s.%.20s+%d@%.90s>",
e->e_spoolid,
ts->tm_year % 100, monthname[ts->tm_mon], ts->tm_mday,
ts->tm_hour, ts->tm_min, ts->tm_sec,
cp, e->e_file, ++genseq, myhostname);
}
#else /* New way to do this .. more compressed one.. */
ts = gmtime(&unixtime);
sprintf(buf, "<%s/%04d%02d%02d%02d%02d%02dZ+%d@%.90s>",
e->e_spoolid,
ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday,
ts->tm_hour, ts->tm_min, ts->tm_sec,
++genseq, myhostname);
#endif
h->h_lines = makeToken(buf, strlen(buf));
h->h_lines->t_type = Line;
h->h_contents = hdr_scanparse(e, h, 0, 0);
/* OPTIMIZE: save this in a hashtable somewhere */
return h;
}
struct header *
mkToHeader(e, buf)
struct envelope *e;
const char *buf;
{
register struct header *h;
static struct headerinfo *toDesc;
if (toDesc == NULL) {
for (toDesc = &mandatory_hdrs[0];
toDesc->hdr_name != NULL; ++toDesc) {
if (toDesc->semantics == AddressList)
break;
}
}
h = (struct header *)tmalloc(sizeof (struct header));
h->h_pname = e->e_resent ? "Resent-To" : "To";
h->h_descriptor = toDesc;
h->h_stamp = newHeader;
h->h_next = 0;
h->h_lines = makeToken(buf, strlen(buf));
h->h_lines->t_type = Line;
h->h_contents = hdr_scanparse(e, h, 0, 0);
/* OPTIMIZE: save this in a hashtable somewhere */
return h;
}
/*
* Construct an RFC822 date header.
*/
struct header *
mkDate(isresent, unixtime)
int isresent;
time_t unixtime;
{
register struct header *h;
char *cp, *s;
/* There is really only one kind of dateDesc.. */
static struct headerinfo dateDesc[] = {
{ "date", DateTime, nilUserType, normal }
};
h = (struct header *)tmalloc(sizeof (struct header));
h->h_pname = isresent ? "Resent-Date" : "Date";
h->h_descriptor = dateDesc;
h->h_stamp = newHeader;
h->h_next = 0;
h->h_contents.d = unixtime;
cp = rfc822date(&(h->h_contents.d));
s = cp + strlen(cp) -1;
if (*cp != 0 && *s == '\n') *s = 0;
h->h_lines = makeToken(cp, strlen(cp));
h->h_lines->t_type = Line;
/* OPTIMIZE: save this in a hashtable somewhere */
return h;
}
/*
* Print an entire header entry onto fp, possibly multiple lines.
*/
int hdr_width = 78;
void
hdr_print(h, fp)
struct header *h;
FILE *fp;
{
int col, cl, addrlen, newline;
token822 *t;
struct address *ap;
struct addr *pp;
HeaderSemantics sem;
int foldcol, hadspc;
int canprefold = 0;
if (h == NULL)
return;
sem = h->h_descriptor->semantics;
if (h->h_stamp == BadHeader)
sem = nilHeaderSemantics;
foldcol = col = 1 + strlen(h->h_pname);
fprintf(fp, "%s:", h->h_pname);
/* If it was pre-existing header, output all its pre-existing
white-spaces now */
for (t = h->h_lines; t != NULL; t = t->t_next) {
const char *p = t->t_pname;
while (*p == ' ' || *p == '\t' ||
*p == '\n' || *p == '\r') {
/* putc(*p, fp); */
if (*p == ' ')
++col;
else if (*p == '\t')
col += 8 - (col % 8);
else
col = 0;
++p;
}
if (*p != 0)
break;
if (!t->t_next) /* Don't fold here ! */
break;
col = 0;
/* putc('\n', fp); */
}
/* Fill to column 8 in all cases.. */
if (col < 8) col = 8;
/* always produces TABFULLY INDENTED headers, TA's
may fold them to SPACEFULL if so desired.. */
hadspc = 0;
while (foldcol < col) {
int c2 = foldcol + 8 - (foldcol & 7);
if (c2 <= col) {
putc('\t', fp);
foldcol = c2;
} else {
putc(' ', fp);
foldcol += 1;
}
hadspc = 1;
}
/* Always at least one space! */
if (!hadspc) {
putc(' ', fp);
++col;
++foldcol;
}
foldcol = col;
switch (sem) {
case Received:
if (h->h_lines != NULL) {
/* Write out the original lines, if possible */
int first = 1;
for (t = h->h_lines; t != NULL; t = t->t_next) {
const char *p = t->t_pname;
int len = (int)(TOKENLEN(t));
if (first) {
while ((len > 0) &&
(*p == ' '||*p == '\t'||
*p == '\n'||*p == '\r'))
++p, --len;
if (!len) continue;
first = 0;
}
fwrite(p, sizeof (char), len, fp);
putc('\n', fp);
}
break;
}
hadspc = 1;
if ((ap = h->h_contents.r->r_from) != NULL) {
if (ap->a_tokens == NULL) {
if (ap->a_pname[0] != '(') {
fprintf(fp, "from");
col += 5;
}
fprintf(fp, " %s", ap->a_pname);
col += strlen(ap->a_pname)+1;
} else {
pp = ap->a_tokens;
t = pp ? pp->p_tokens : NULL;
if (t && t->t_type == Atom && t->t_len == 5 &&
t->t_pname && memcmp(t->t_pname, "STDIN", 5)==0)
pp = pp->p_next;
/* PURE comment */
else {
fprintf(fp, "from ");
col += 5;
}
if (pp)
col = printLAddress(fp, pp, col, 8, 0);
else
fprintf(fp, "(nil?\?)"), col += 7;
}
hadspc = 0;
}
if ((ap = h->h_contents.r->r_by) != NULL) {
cl = printAddress(NULL, ap->a_tokens, col+3);
if (cl > hdr_width) {
fprintf(fp, "\n\t");
col = 8;
} else if (!hadspc)
putc(' ',fp), ++col;
fprintf(fp, "by ");
col = printLAddress(fp, ap->a_tokens, col+3, 8, 0);
}
if ((t = h->h_contents.r->r_via) != NULL) {
cl = fprintToken(NULL, t, col+4);
if (cl > hdr_width) {
fprintf(fp, "\n\t");
col = 8;
} else
putc(' ',fp), ++col;
fprintf(fp, "via ");
col = fprintToken(fp, t, col+4);
}
for (t = h->h_contents.r->r_with; t != NULL; t = t->t_next) {
cl = fprintToken(NULL, t, col+5);
if (cl > hdr_width) {
fprintf(fp, "\n\t");
col = 8;
} else
putc(' ',fp), ++col;
fprintf(fp, "with ");
col = fprintToken(fp, t, col+5);
}
if ((ap = h->h_contents.r->r_id) != NULL) {
cl = printAddress(NULL, ap->a_tokens, col+3);
if (cl > hdr_width) {
fprintf(fp, "\n\t");
col = 8;
} else
putc(' ',fp), ++col;
fprintf(fp, "id ");
col = printLAddress(fp, ap->a_tokens, col+5, 8, 0);
}
if ((ap = h->h_contents.r->r_for) != NULL) {
cl = printAddress(NULL, ap->a_tokens, col+4);
if (cl > hdr_width) {
fprintf(fp, "\n\t");
col = 8;
} else
putc(' ',fp), ++col;
fprintf(fp, "for ");
col = printLAddress(fp, ap->a_tokens, col+4, 8, 0);
}
putc(';', fp);
++col;
{
char *cp, *s;
int len;
cp = rfc822date(&(h->h_contents.r->r_time));
s = cp + (len = strlen(cp)) -1;
if (*cp != 0 && *s == '\n') *s = 0;
if ((col+len+1) > hdr_width)
fprintf(fp, "\n\t%s\n", cp);
else
fprintf(fp, " %s\n", cp);
}
break;
case Address:
case Addresses:
case AddressList:
case DomainName:
case Mailbox:
case Mailboxes:
case MailboxList:
case AMailboxList:
case MessageID:
case RouteAddressInAngles:
case RouteAddress:
case UserAtDomain:
newline = 1;
for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next) {
addrlen = printAddress(NULL, ap->a_tokens, col);
for (pp = ap->a_tokens; pp != NULL; pp = pp->p_next)
if (pp->p_type == anAddress)
break;
/* pp == NULL means mail group phrase */
if (ap->a_next != NULL)
++addrlen;
if (newline || ((addrlen+3 > hdr_width) && !canprefold)) {
if (!newline)
++col, putc(' ',fp);
if (addrlen + 3 >= hdr_width) {
/* Too close to the edge, (or over it) do folding */
col = printLAddress(fp, ap->a_tokens, col, foldcol,
canprefold);
} else {
/* Within right margin, no fold needed */
col = printAddress(fp, ap->a_tokens, col);
}
newline = 0;
} else if (addrlen +3 < hdr_width /* columns */) {
/* Within margins, plain simple insert, no folding */
putc(' ', fp);
col = printAddress(fp, ap->a_tokens, col+1);
} else {
/* Explicite prefold */
putc('\n', fp);
for (col = 0; col + 8 <= foldcol; col += 8)
putc('\t', fp);
for (;col < foldcol; ++col)
putc(' ', fp);
if (col < 1)
putc(' ', fp), ++col;
col = printLAddress(fp, ap->a_tokens, col, foldcol, 0);
}
canprefold = 1;
if (pp != NULL && ap->a_next != NULL)
++col, putc(',', fp);
}
putc('\n', fp);
break;
case DateTime:
/* Use this ONLY if no original lines are available! */
if (h->h_contents.d > 0L && h->h_lines == NULL) {
char *cp, *s;
cp = rfc822date(&(h->h_contents.d));
s = cp + strlen(cp) -1;
if (*cp != 0 && *s == '\n') *s = 0;
fputs(cp, fp);
break;
} /* else we have the original line ... */
/* Fall thru ... */
case nilHeaderSemantics:
default:
/* Write out the original lines, if possible */
#if 0
/* Damn... This doesn't work all the time.. */
if (h->h_contents.t != NULL && sem == nilHeaderSemantics) {
int first = 1;
int newcol;
for (t = h->h_contents.t; t; t = t->t_next) {
const char *p = t->t_pname;
if (t->t_type == Line) {
int len = (int)(TOKENLEN(t));
if (first) {
while ((len > 0) &&
(*p == ' '||*p == '\t'||*p == '\n'||*p == '\r'))
++p, --len;
if (!len) continue;
first = 0;
}
fwrite(p, sizeof (char), len, fp);
putc('\n', fp);
continue;
}
if (first) {
if (t->t_type == Space && *p != '\n')
continue;
first = 0;
}
newcol = fprintToken(NULL, t, 0);
if ((t->t_type == Space && *p == '\n' && t->t_next) ||
((newcol + col > hdr_width) &&
(newcol + foldcol <= hdr_width))) {
putc('\n', fp);
for (col = 0; col+8 <= foldcol; col += 8)
putc('\t', fp);
for (;col < foldcol; ++col)
putc(' ', fp);
if (col < 1)
putc(' ', fp), ++col;
first = 1;
if (t->t_type == Space)
continue;
}
first = 0;
col = fprintToken(fp, t, col);
}
if (first) /* totally empty header! */
putc('\n', fp);
}
else
#endif
{
int first = 1;
for (t = h->h_lines; t != NULL; t = t->t_next) {
const char *p = t->t_pname;
int len = (int)(TOKENLEN(t));
if (first) {
while ((len > 0) &&
(*p == ' '||*p == '\t'||*p == '\n'||*p == '\r'))
++p, --len;
if (!len) continue;
first = 0;
}
fwrite(p, sizeof (char), len, fp);
putc('\n', fp);
}
if (first) /* totally empty header! */
putc('\n', fp);
}
break;
}
}
/* is there a header-value ? */
int
hdr_nilp(h)
register struct header *h;
{
if (h == NULL)
return 1;
if (h->h_stamp != BadHeader) {
switch (h->h_descriptor->semantics) {
case DateTime:
return h->h_contents.d == 0;
case Received:
return h->h_contents.r == NULL;
case Address:
case Addresses:
case AddressList:
case DomainName:
case Mailbox:
case Mailboxes:
case MailboxList:
case AMailboxList:
case MessageID:
case RouteAddressInAngles:
case RouteAddress:
case UserAtDomain:
return h->h_contents.a == NULL;
default:
break;
}
}
return h->h_lines == NULL;
}
void
pureAddress(fp, pp)
FILE *fp;
register struct addr *pp;
{
register token822 *t;
for (; pp != NULL ; pp = pp->p_next) {
if (pp->p_type != anAddress)
continue;
for (t = pp->p_tokens ; t != NULL ; t = t->t_next)
fprintToken(fp, t, 0);
}
}
int
pureAddressBuf(buf, len, pp)
char *buf;
int len;
register struct addr *pp;
{
int tlen;
for (; pp != NULL ; pp = pp->p_next) {
if (pp->p_type != anAddress)
continue;
tlen = printToken(buf, buf+len, pp->p_tokens, NULL, 0);
buf += tlen;
len -= tlen;
}
return len;
}
/*
* Print the address pointed to by pp onto fp. If the onlylength flag
* is set, no printing is done, but the number of characters that would
* have been printed is returned instead.
*/
/* If 'fp' is non-null, retuns column where the output is,
* for null 'fp', returns the number of output characters.
*/
int
printAddress(fp, pp, col)
FILE *fp;
register struct addr *pp;
int col;
{
int inAddress;
token822 *t;
struct addr *lastp, *tpp;
inAddress = 0;
for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
if (tpp->p_type == anAddress)
lastp = tpp;
for (; pp != NULL; pp = pp->p_next) {
if (pp->p_type == aComment || pp->p_type == anError) {
if (fp) putc('(', fp);
++col;
} else if (pp->p_type == anAddress)
inAddress = 1;
for (t = pp->p_tokens; t != NULL; t = t->t_next) {
switch (pp->p_type) {
case aPhrase:
case aComment:
case aGroup:
case aWord:
if (t != pp->p_tokens) {
if (fp) putc(' ', fp);
++col;
}
/* fall through */
case anAddress:
case aDomain:
case anError:
case reSync:
col = fprintToken(fp, t, col);
/* Per fp != NULL state this 'col' is now
either total length, or current column */
if (pp->p_type == reSync && t->t_next != NULL
&& (t->t_next->t_type == t->t_type
|| *(t->t_next->t_pname) == '<')) {
if (fp) putc(' ', fp);
++col;
}
break;
case aSpecial:
if (t != pp->p_tokens && *(t->t_pname) == '<'){
if (fp) putc(' ', fp);
++col;
}
if (fp) putc((*t->t_pname), fp);
++col;
break;
}
}
if (pp->p_type == aComment || pp->p_type == anError) {
if (fp) putc(')', fp);
++col;
} else if (lastp == pp)
inAddress = 0;
if (!inAddress && pp->p_next != NULL
&& !(pp->p_next->p_type == anAddress
&& pp->p_type == aSpecial
&& *pp->p_tokens->t_pname == '<')
&& !(pp->p_next->p_type == aSpecial
&& pp->p_type == anAddress
&& *pp->p_next->p_tokens->t_pname == '>')
&& !(pp->p_next->p_type == aSpecial
&& *pp->p_next->p_tokens->t_pname == ':')
&& pp->p_next->p_type != anError) {
if (fp) putc(' ', fp);
++col;
}
}
return col;
}
static int ThisAddressLen(pp)
register struct addr *pp;
{
int len = 0;
token822 *t;
int hadSpecial = (pp->p_type == aSpecial);
if (hadSpecial)
/* Ok, this one is aSpecial '<', next one is anAddress.
Scan as long as there are Addresses. */
++len;
for (pp = pp->p_next; pp && pp->p_type == anAddress; pp = pp->p_next)
for (t = pp->p_tokens; t != NULL; t = t->t_next)
len = fprintToken(NULL, t, len);
if (hadSpecial) /* Just imply it.. */
len += 2;
return len;
}
/* Return the column where the cursor is */
int
printLAddress(fp, pp, col, foldcol, canprefold)
FILE *fp;
register struct addr *pp;
int col, foldcol, canprefold;
{
int inAddress;
token822 *t;
struct addr *lastp, *tpp;
inAddress = 0;
for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
if (tpp->p_type == anAddress)
lastp = tpp;
for (; pp != NULL; pp = pp->p_next) {
if (pp->p_type == aComment || pp->p_type == anError) {
++col;
putc('(', fp);
} else if (pp->p_type == anAddress)
inAddress = 1;
if (canprefold)
if ((!inAddress &&
((pp->p_type == aSpecial) && (t = pp->p_tokens) &&
(*t->t_pname == '<') && (pp->p_next) &&
((pp->p_next->p_type == anAddress) ||
(pp->p_next->p_type == aDomain))))
|| (inAddress == 1)) {
/* If *first* of them, see how long is the address
sequence ? Do have have a need to fold NOW ? */
int alen = ThisAddressLen(pp);
if (alen + col > hdr_width) {
/* Ok, fold now */
putc('\n', fp);
for (col = 0; col+8 <= foldcol; col += 8)
putc('\t', fp);
for (;col < foldcol; ++col)
putc(' ', fp);
if (col < 1)
putc(' ', fp), ++col;
}
}
canprefold = 1;
for (t = pp->p_tokens; t != NULL; t = t->t_next) {
int special = 0;
int add_space = 0;
int canfold = 0;
switch (pp->p_type) {
case aPhrase:
case aComment:
case aGroup:
case aWord:
if (t != pp->p_tokens) {
++col;
putc(' ', fp);
}
canfold = 1;
/* fall through */
case anAddress:
case aDomain:
case anError:
case reSync:
#if 1
if (canfold)
col = fprintFold(fp, t, col, foldcol);
else
col = fprintToken(fp, t, col);
#else
fputs("(<)",fp);
if (pp->p_type == aComment) {
col = fprintFold(fp, t, col, foldcol);
} else {
col = fprintToken(fp, t, col);
}
fputs("(>)",fp);
#endif
if (pp->p_type == reSync && t->t_next != NULL
&& (t->t_next->t_type == t->t_type
|| *(t->t_next->t_pname) == '<'))
add_space = 1;
break;
case aSpecial:
if (t != pp->p_tokens && *(t->t_pname)=='<') {
add_space = 1;
special = *t->t_pname;
} else {
putc(*t->t_pname, fp);
++col;
}
break;
}
if (inAddress) ++inAddress;
if (canfold &&
((special && (col + 10) >= hdr_width) ||
(t->t_next != NULL && (TOKENLEN(t->t_next) > 1)
&& (fprintToken(NULL, t->t_next, col+2)
>= hdr_width)))) {
putc('\n', fp);
for (col = 0; col+8 <= foldcol; col += 8)
putc('\t', fp);
for (;col < foldcol; ++col)
putc(' ', fp);
if (col < 1)
putc(' ', fp), ++col;
} else if (add_space)
putc(' ', fp), ++col;
if (special)
putc(special, fp), ++col;
}
if (pp->p_type == aComment || pp->p_type == anError) {
++col;
putc(')', fp);
} else if (lastp == pp)
inAddress = 0;
if (!inAddress && pp->p_next != NULL
&& !(pp->p_next->p_type == anAddress
&& pp->p_type == aSpecial
&& *pp->p_tokens->t_pname == '<')
&& !(pp->p_next->p_type == aSpecial
&& pp->p_type == anAddress
&& *pp->p_next->p_tokens->t_pname == '>')
&& !(pp->p_next->p_type == aSpecial
&& *pp->p_next->p_tokens->t_pname == ':')
&& pp->p_next->p_type != anError) {
++col;
putc(' ', fp);
}
}
return col;
}
/*
* More or less the same as printAddress (they must be kept in sync!!),
* but returns a string instead of printing it somewhere. The two routines
* should really be merged, when one can do stdio to a string.
*/
char *
saveAddress(pp)
register struct addr *pp;
{
int len, totallen, inAddress;
char *cp, *buf;
token822 *t;
struct addr *lastp, *tpp;
totallen = printAddress((FILE *)NULL, pp, 1);
cp = buf = tmalloc((u_int)(totallen+1));
inAddress = 0;
for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
if (tpp->p_type == anAddress)
lastp = tpp;
for (; pp != NULL; pp = pp->p_next) {
if (pp->p_type == aComment || pp->p_type == anError)
*cp++ = '(';
else if (pp->p_type == anAddress)
inAddress = 1;
for (t = pp->p_tokens; t != NULL; t = t->t_next) {
len = TOKENLEN(t);
switch (pp->p_type) {
case aPhrase:
case aComment:
case aGroup:
case aWord:
if (t != pp->p_tokens)
*cp++ = ' ';
/* fall through */
case anAddress:
case aDomain:
case anError:
case reSync:
if (t->t_type == DomainLiteral)
*cp++ = '[';
else if (t->t_type == String)
*cp++ = '"';
memcpy(cp, t->t_pname, len);
cp += len;
if (t->t_type == DomainLiteral)
*cp++ = ']';
else if (t->t_type == String)
*cp++ = '"';
if (pp->p_type == reSync && t->t_next != NULL
&& (t->t_next->t_type == t->t_type
|| *(t->t_next->t_pname) == '<'))
*cp++ = ' ';
break;
case aSpecial:
if (t != pp->p_tokens && *(t->t_pname) == '<')
*cp++ = ' ';
*cp++ = *t->t_pname;
break;
}
}
if (pp->p_type == aComment || pp->p_type == anError)
*cp++ = ')';
else if (lastp == pp)
inAddress = 0;
if (!inAddress && pp->p_next != NULL
&& !(pp->p_next->p_type == anAddress
&& pp->p_type == aSpecial
&& *pp->p_tokens->t_pname == '<')
&& !(pp->p_next->p_type == aSpecial
&& pp->p_type == anAddress
&& *pp->p_next->p_tokens->t_pname == '>')
&& !(pp->p_next->p_type == aSpecial
&& *pp->p_next->p_tokens->t_pname == ':')
&& pp->p_next->p_type != anError)
*cp++ = ' ';
}
*cp = '\0';
return buf;
}
/*
* This function does the real work of hdr_errprint(). It is here
* because it needs to be kept in sync with the functions above.
*/
struct errmsgpos { int pos; token822 *tokens; };
void
errprint(fp, pp, hdrlen)
FILE *fp;
register struct addr *pp;
int hdrlen;
{
int inAddress, n, i, j, len;
token822 *t;
struct addr *lastp, *tpp;
static struct errmsgpos * errmsg = NULL;
static int errmsgposspace = 0;
inAddress = 0;
for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
if (tpp->p_type == anAddress)
lastp = tpp;
putc('\t', fp);
len = 1;
n = 0;
for (; pp != NULL; pp = pp->p_next) {
if (pp->p_type == aComment)
putc('(', fp), ++len;
else if (pp->p_type == anAddress)
inAddress = 1;
else if (pp->p_type == anError) {
if (n >= errmsgposspace) {
/* Must expand the space */
if (errmsgposspace == 0) {
errmsgposspace = 100;
errmsg = (void*) emalloc(sizeof(*errmsg) *
errmsgposspace);
} else {
errmsgposspace <<= 1;
errmsg = (void*) erealloc(errmsg,
sizeof(*errmsg) *
errmsgposspace);
}
}
errmsg[n].pos = len - 1;
errmsg[n++].tokens = pp->p_tokens;
continue;
}
for (t = pp->p_tokens; t != NULL; t = t->t_next) {
switch (pp->p_type) {
case aPhrase:
case aComment:
case aGroup:
case aWord:
if (t != pp->p_tokens)
putc(' ', fp), ++len;
/* fall through */
case anAddress:
case aDomain:
case reSync:
len = fprintToken(fp, t, len);
if (pp->p_type == reSync && t->t_next != NULL
&& (t->t_next->t_type == t->t_type
|| *(t->t_next->t_pname) == '<'))
putc(' ', fp), ++len;
break;
case aSpecial:
if (t != pp->p_tokens && *(t->t_pname) == '<')
putc(' ', fp), ++len;
putc((*t->t_pname), fp), ++len;
break;
case anError:
break;
}
}
if (pp->p_type == aComment) {
putc(')', fp), ++len;
} else if (lastp == pp)
inAddress = 0;
if (!inAddress && pp->p_next != NULL
&& pp->p_next->p_type != anError
&& !(pp->p_next->p_type == anAddress
&& pp->p_type == aSpecial
&& *pp->p_tokens->t_pname == '<')
&& !(pp->p_next->p_type == aSpecial
&& pp->p_type == anAddress
&& *pp->p_next->p_tokens->t_pname == '>')
&& !(pp->p_next->p_type == aSpecial
&& *pp->p_next->p_tokens->t_pname == ':')) {
putc(' ', fp), ++len;
}
}
putc('\n', fp);
for (i = 0; i < hdrlen; i += 8)
putc('\t', fp);
putc('\t', fp);
for (i = 0, len = 0; i < n;) {
for (; (len+1)/8 < errmsg[i].pos/8; len = ((len/8)+1)*8)
putc('\t', fp);
for (; len < errmsg[i].pos; ++len)
putc(' ', fp);
while (i < n && len == errmsg[i].pos)
putc('^', fp), ++i;
++len;
}
--n;
fprintf(fp, "-%s", errmsg[n].tokens->t_pname);
for (t = errmsg[n].tokens->t_next; t != NULL; t = t->t_next)
fprintf(fp, ", %s", t->t_pname);
putc('\n', fp);
for (j = 0; j < n; ++j) {
for (i = 0; i < (hdrlen+1)/8 ; ++i)
putc('\t', fp);
putc('\t', fp);
for (i = 0, len = 0; i < n-j;) {
for (; (len+1)/8 < errmsg[i].pos/8; len = ((len/8)+1)*8)
putc('\t', fp);
for (; len < errmsg[i].pos; ++len)
putc(' ', fp);
while (i < n-j && len == errmsg[i].pos) {
if (i == n-j-1) {
if (j == 0)
putc(' ', fp), ++i;
putc('\\', fp), ++i;
} else
putc('|', fp), ++i;
}
++len;
}
fprintf(fp, "-%s", errmsg[n-j-1].tokens->t_pname);
for (t = errmsg[n-j-1].tokens->t_next; t != NULL; t = t->t_next)
fprintf(fp, ", %s", t->t_pname);
putc('\n', fp);
}
}
HeaderStamp
hdr_type(h)
struct header *h;
{
register struct address *ap;
switch (h->h_descriptor->semantics) {
case DateTime:
case Received:
case nilHeaderSemantics:
break;
default:
for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
if (ap->a_stamp == BadAddress)
return BadHeader;
}
return newHeader;
}
/*
* Construct a warning header loosely following RFC886.
*
* The erroneous header *must* be parsed to address form,
* i.e. h->h_contents.a is the correct union member.
*/
struct header *
hdr_warning(h)
struct header *h;
{
struct address *ap, *pwap, *hwap, **papp;
struct header *wh;
int len;
char *cp;
if (h->h_stamp != BadHeader)
return h;
/*
* Splice out the bad addresses, put them in a separate header.
*/
pwap = hwap = NULL;
papp = &h->h_contents.a;
for (ap = h->h_contents.a; ap != NULL;) {
if (ap->a_stamp == BadAddress) { /* splice it out */
if (pwap != NULL)
pwap->a_next = ap;
pwap = ap;
if (hwap == NULL)
hwap = ap;
ap = ap->a_next;
pwap->a_next = NULL;
} else { /* leave it where it is */
*papp = ap;
papp = &ap->a_next;
ap = ap->a_next;
}
}
*papp = NULL;
if (hwap == NULL)
return h;
/*
* Now hwap contains list of addresses that need warnings,
* and the header itself has got all those bad addresses spliced out.
* If the header doesn't have any addresses left, it is replaced
* by the new synthesized Mailer-Warning header. Otherwise the
* new warning header is linked into the message header right after
* this header.
*/
wh = (struct header *)tmalloc(sizeof (struct header));
wh->h_next = h->h_next;
h->h_next = wh;
wh->h_pname = "Illegal-Object";
wh->h_descriptor = &nullhdr;
wh->h_stamp = newHeader;
wh->h_contents.a = NULL;
cp = tmalloc(strlen(h->h_pname) + strlen(myhostname) + 50);
if (h->h_descriptor->user_type == nilUserType) {
sprintf(cp,
" Syntax error in %s: value%s found on %s:",
h->h_pname, hwap->a_next == NULL ? "" : "s",
myhostname);
} else {
sprintf(cp,
" Syntax error in %s: address%s found on %s:",
h->h_pname, hwap->a_next == NULL ? "" : "es",
myhostname);
}
wh->h_lines = makeToken(cp, strlen(cp));
sb_external(FILENO(stdout));
printf("\t%s:", h->h_pname);
errprint(stdout, hwap->a_tokens, strlen(h->h_pname)+8);
cp = sb_retrieve(FILENO(stdout));
len = strlen(cp);
if (*(cp + len - 1) == '\n')
--len;
wh->h_lines->t_next = makeToken(cp, len);
if (h->h_contents.a == NULL)
return wh;
return h;
}
/*
* Print a useful error message to fp, describing the erroneous addresses
* in the header, using nice diagrams etc. etc. Non-cryptic output desired.
*/
void
hdr_errprint(e, h, fp, msg)
struct envelope *e;
struct header *h;
FILE *fp;
const char *msg;
{
int i;
struct address *ap;
if (h->h_stamp != BadHeader)
return;
for (i = 0, ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
i += (ap->a_stamp == BadAddress);
fprintf(fp, "Error%s in \"%s\" %s address%s:\n",
i > 1 ? "s" : "", h->h_pname, msg, i > 1 ? "es" : "");
for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
if (ap->a_stamp == BadAddress && ap->a_tokens != NULL) {
putc('\n', fp);
errprint(fp, ap->a_tokens, 0);
}
putc('\n', fp);
if (erraddrlog != NULL) {
FILE *ealfp;
if ((ealfp = fopen(erraddrlog, "a")) != NULL) {
struct siobuf *osiop = siofds[FILENO(ealfp)];
char vbuf[BUFSIZ];
setvbuf(ealfp, vbuf, _IOFBF, sizeof vbuf);
siofds[FILENO(ealfp)] = NULL;
if (e != NULL)
fprintf(ealfp, "%s: %s\n",
e->e_file, rfc822date(&(e->e_nowtime)));
fprintf(ealfp, "Error%s in \"%s\" %s address%s:\n",
i > 1 ? "s" : "", h->h_pname, msg,
i > 1 ? "es" : "");
for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
if (ap->a_stamp == BadAddress
&& ap->a_tokens != NULL) {
putc('\n', ealfp);
errprint(ealfp, ap->a_tokens, 0);
}
putc('\n', ealfp);
fclose(ealfp);
siofds[FILENO(ealfp)] = osiop;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1