X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=rfc822.c;h=5315f1b63e934295a016470714837fff0ca64faa;hb=4e1db9d3b89d27acf1b80c51c02e73cc7ad69bc1;hp=3e7a96b11b7007e4a4cc96043253e41c6b4e9069;hpb=e13db1a773fa9e6836b2a1db497c077253bb34eb;p=~andy%2Ffetchmail diff --git a/rfc822.c b/rfc822.c index 3e7a96b1..5315f1b6 100644 --- a/rfc822.c +++ b/rfc822.c @@ -1,8 +1,7 @@ /* * rfc822.c -- code for slicing and dicing RFC822 mail headers * - * Copyright 1996 by Eric S. Raymond - * All rights reserved. + * Copyright 1997 by Eric S. Raymond * For license terms, see the file COPYING in this directory. */ @@ -13,89 +12,192 @@ #include #endif -#include "fetchmail.h" +#include "config.h" +#include "fetchmail.h" +#include "i18n.h" -void reply_hack(buf, host) +#define HEADER_END(p) ((p)[0] == '\n' && ((p)[1] != ' ' && (p)[1] != '\t')) + +#ifdef TESTMAIN +static int verbose; +char *program_name = "rfc822"; +#endif /* TESTMAIN */ + +unsigned char *reply_hack(buf, host) /* hack message headers so replies will work properly */ -char *buf; /* header to be hacked */ -const char *host; /* server hostname */ +unsigned char *buf; /* header to be hacked */ +const unsigned char *host; /* server hostname */ { - const char *from; - int parendepth, oldstate, state = 0, has_host_part = FALSE; - char mycopy[MSGBUFSIZE+1]; - - if (strncmp("From: ", buf, 6) - && strncmp("To: ", buf, 4) - && strncmp("Reply-", buf, 6) - && strncmp("Cc: ", buf, 4) - && strncmp("Bcc: ", buf, 5)) { - return; + unsigned char *from, *cp, last_nws = '\0', *parens_from = NULL; + int parendepth, state, has_bare_name_part, has_host_part; +#ifndef TESTMAIN + int addresscount = 1; +#endif /* TESTMAIN */ + + if (strncasecmp("From:", buf, 5) + && strncasecmp("To:", buf, 3) + && strncasecmp("Reply-To:", buf, 9) + && strncasecmp("Return-Path:", buf, 12) + && strncasecmp("Cc:", buf, 3) + && strncasecmp("Bcc:", buf, 4) + && strncasecmp("Resent-From:", buf, 12) + && strncasecmp("Resent-To:", buf, 10) + && strncasecmp("Resent-Cc:", buf, 10) + && strncasecmp("Resent-Bcc:", buf, 11) + && strncasecmp("Apparently-From:", buf, 16) + && strncasecmp("Apparently-To:", buf, 14) + && strncasecmp("Sender:", buf, 7) + && strncasecmp("Resent-Sender:", buf, 14) + ) { + return(buf); } - strcpy(mycopy, buf); - for (from = mycopy; *from; from++) +#ifndef TESTMAIN + if (outlevel >= O_DEBUG) + report_build(stdout, _("About to rewrite %s"), buf); + + /* make room to hack the address; buf must be malloced */ + for (cp = buf; *cp; cp++) + if (*cp == ',' || isspace(*cp)) + addresscount++; + buf = (unsigned char *)xrealloc(buf, strlen(buf) + addresscount * strlen(host) + 1); +#endif /* TESTMAIN */ + + /* + * This is going to foo up on some ill-formed addresses. + * Note that we don't rewrite the fake address <> in order to + * avoid screwing up bounce suppression with a null Return-Path. + */ + + parendepth = state = 0; + has_host_part = has_bare_name_part = FALSE; + for (from = buf; *from; from++) { -#define INSERT_HOSTNAME \ - strcpy(buf, "@"); \ - strcat(buf, host); \ - buf += strlen(buf); \ - has_host_part = TRUE; - - if (*from == '(') - ++parendepth; - else if (*from == ')') - --parendepth; - - if (!parendepth) +#ifdef TESTMAIN + if (verbose) + { + printf("state %d: %s", state, buf); + printf("%*s^\n", from - buf + 10, " "); + } +#endif /* TESTMAIN */ + if (state != 2) + { + if (*from == '(') + ++parendepth; + else if (*from == ')') + --parendepth; + } + + if (!parendepth && !has_host_part) switch (state) { - case 0: /* before header colon */ + case 0: /* before header colon */ if (*from == ':') state = 1; break; - case 1: /* we've seen the colon, we're looking for addresses */ + case 1: /* we've seen the colon, we're looking for addresses */ + if (!isspace(*from)) + last_nws = *from; if (*from == '<') - state = 2; + state = 3; else if (*from == '@') has_host_part = TRUE; - else if ((*from == ',' || *from == '\n') && !has_host_part) + else if (*from == '"') + state = 2; + /* + * Not expanding on last non-WS == ';' deals with groupnames, + * an obscure misfeature described in sections + * 6.1, 6.2.6, and A.1.5 of the RFC822 standard. + */ + else if ((*from == ',' || HEADER_END(from)) + && has_bare_name_part + && !has_host_part + && last_nws != ';') { - INSERT_HOSTNAME - } + int hostlen; + unsigned char *p; + + p = from; + if (parens_from) + from = parens_from; + while (isspace(*from) || (*from == ',')) + --from; + from++; + hostlen = strlen(host); + for (cp = from + strlen(from); cp >= from; --cp) + cp[hostlen+1] = *cp; + *from++ = '@'; + memcpy(from, host, hostlen); + from = p + hostlen + 1; + has_host_part = TRUE; + } + else if (from[1] == '(' + && has_bare_name_part + && !has_host_part + && last_nws != ';' && last_nws != ')') + { + parens_from = from; + } + else if (!isspace(*from)) + has_bare_name_part = TRUE; + break; + + case 2: /* we're in a string */ + if (*from == '"') + state = 1; break; - case 2: /* we're in a <>-enclosed address */ + case 3: /* we're in a <>-enclosed address */ if (*from == '@') has_host_part = TRUE; - else if (*from == '>' && !has_host_part) + else if (*from == '>' && from[-1] != '<') { - INSERT_HOSTNAME + state = 1; + if (!has_host_part) + { + int hostlen; + + hostlen = strlen(host); + for (cp = from + strlen(from); cp >= from; --cp) + cp[hostlen+1] = *cp; + *from++ = '@'; + memcpy(from, host, hostlen); + from += hostlen; + has_host_part = TRUE; + } } break; } - /* all characters from the old buffer get copied to the new one */ - *buf++ = *from; -#undef INSERT_HOSTNAME + /* + * If we passed a comma, reset everything. + */ + if (from[-1] == ',' && !parendepth) { + has_host_part = has_bare_name_part = FALSE; + parens_from = NULL; + } } - *buf = '\0'; +#ifndef TESTMAIN + if (outlevel >= O_DEBUG) + report_complete(stdout, _("Rewritten version is %s\n"), buf); +#endif /* TESTMAIN */ + return(buf); } -char *nxtaddr(hdr) +unsigned char *nxtaddr(hdr) /* parse addresses in succession out of a specified RFC822 header */ -const char *hdr; /* header to be parsed, NUL to continue previous hdr */ +const unsigned char *hdr; /* header to be parsed, NUL to continue previous hdr */ { - static char *tp, address[POPBUFSIZE+1]; - static const char *hp; + static unsigned char *tp, address[POPBUFSIZE+1]; + static const unsigned char *hp; static int state, oldstate; - int parendepth; +#ifdef TESTMAIN + static const unsigned char *orighdr; +#endif /* TESTMAIN */ + int parendepth = 0; - /* - * Note: it is important that this routine not stop on \r, since - * we use \r as a marker for RFC822 continuations elsewhere. - */ #define START_HDR 0 /* before header colon */ #define SKIP_JUNK 1 /* skip whitespace, \n, and junk */ #define BARE_ADDRESS 2 /* collecting address without delimiters */ @@ -108,37 +210,52 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ { hp = hdr; state = START_HDR; +#ifdef TESTMAIN + orighdr = hdr; +#endif /* TESTMAIN */ + tp = address; } for (; *hp; hp++) { - switch (state) +#ifdef TESTMAIN + if (verbose) { - case START_HDR: /* before header colon */ - if (*hp == '\n') + printf("state %d: %s", state, orighdr); + printf("%*s^\n", hp - orighdr + 10, " "); + } +#endif /* TESTMAIN */ + + if (state == ENDIT_ALL) /* after last address */ + return(NULL); + else if (HEADER_END(hp)) + { + state = ENDIT_ALL; + if (tp > address) { - state = ENDIT_ALL; - return(NULL); + while (isspace(*--tp)) + continue; + *++tp = '\0'; } - else if (*hp == ':') + return(tp > address ? (tp = address) : (unsigned char *)NULL); + } + else if (*hp == '\\') /* handle RFC822 escaping */ + { + if (state != INSIDE_PARENS) { - state = SKIP_JUNK; - tp = address; + *tp++ = *hp++; /* take the escape */ + *tp++ = *hp; /* take following unsigned char */ } + } + else switch (state) + { + case START_HDR: /* before header colon */ + if (*hp == ':') + state = SKIP_JUNK; break; case SKIP_JUNK: /* looking for address start */ - if (*hp == '\n') /* no more addresses */ - { - state = ENDIT_ALL; - return(NULL); - } - else if (*hp == '\\') /* handle RFC822 escaping */ - { - *tp++ = *hp++; /* take the escape */ - *tp++ = *hp; /* take following char */ - } - else if (*hp == '"') /* quoted string */ + if (*hp == '"') /* quoted string */ { oldstate = SKIP_JUNK; state = INSIDE_DQUOTE; @@ -147,6 +264,7 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ else if (*hp == '(') /* address comment -- ignore */ { parendepth = 1; + oldstate = SKIP_JUNK; state = INSIDE_PARENS; } else if (*hp == '<') /* begin
*/ @@ -154,7 +272,7 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ state = INSIDE_BRACKETS; tp = address; } - else if (!isspace(*hp)) /* ignore space */ + else if (*hp != ',' && !isspace(*hp)) { --hp; state = BARE_ADDRESS; @@ -162,50 +280,32 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ break; case BARE_ADDRESS: /* collecting address without delimiters */ - if (*hp == '\n') /* end of bare address */ + if (*hp == ',') /* end of address */ { if (tp > address) { *tp++ = '\0'; - state = ENDIT_ALL; + state = SKIP_JUNK; return(tp = address); } } - else if (*hp == '\\') /* handle RFC822 escaping */ - { - *tp++ = *hp++; /* take the escape */ - *tp++ = *hp; /* take following char */ - } - else if (*hp == ',') /* end of address */ + else if (*hp == '(') /* beginning of comment */ { - if (tp > address) - { - *tp++ = '\0'; - state = SKIP_JUNK; - return(tp = address); - } + parendepth = 1; + oldstate = BARE_ADDRESS; + state = INSIDE_PARENS; } else if (*hp == '<') /* beginning of real address */ { state = INSIDE_BRACKETS; tp = address; } - else /* just take it */ + else if (!isspace(*hp)) /* just take it, ignoring whitespace */ *tp++ = *hp; break; case INSIDE_DQUOTE: /* we're in a quoted string, copy verbatim */ - if (*hp == '\n') /* premature end of string */ - { - state = ENDIT_ALL; - return(NULL); - } - else if (*hp == '\\') /* handle RFC822 escaping */ - { - *tp++ = *hp++; /* take the escape */ - *tp++ = *hp; /* take following char */ - } - else if (*hp != '"') + if (*hp != '"') *tp++ = *hp; else { @@ -215,28 +315,16 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ break; case INSIDE_PARENS: /* we're in a parenthesized comment, ignore */ - if (*hp == '\n') /* end of line, just bomb out */ - return(NULL); - else if (*hp == '\\') /* handle RFC822 escaping */ - { - *tp++ = *hp++; /* take the escape */ - *tp++ = *hp; /* take following char */ - } - else if (*hp == '(') + if (*hp == '(') ++parendepth; else if (*hp == ')') --parendepth; if (parendepth == 0) - state = SKIP_JUNK; + state = oldstate; break; case INSIDE_BRACKETS: /* possible <>-enclosed address */ - if (*hp == '\\') /* handle RFC822 escaping */ - { - *tp++ = *hp++; /* take the escape */ - *tp++ = *hp; /* take following char */ - } - else if (*hp == '>') /* end of address */ + if (*hp == '>') /* end of address */ { *tp++ = '\0'; state = SKIP_JUNK; @@ -254,10 +342,6 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ else /* just copy address */ *tp++ = *hp; break; - - case ENDIT_ALL: /* after last address */ - return(NULL); - break; } } @@ -265,35 +349,66 @@ const char *hdr; /* header to be parsed, NUL to continue previous hdr */ } #ifdef TESTMAIN +static void parsebuf(unsigned char *longbuf, int reply) +{ + unsigned char *cp; + + if (reply) + { + reply_hack(longbuf, "HOSTNAME.NET"); + printf("Rewritten buffer: %s", longbuf); + } + else + if ((cp = nxtaddr(longbuf)) != (unsigned char *)NULL) + do { + printf("\t-> \"%s\"\n", cp); + } while + ((cp = nxtaddr((unsigned char *)NULL)) != (unsigned char *)NULL); +} + + + main(int argc, char *argv[]) { - char buf[POPBUFSIZE], *cp; - int reply = (argc > 1 && !strcmp(argv[1], "-r")); + unsigned char buf[MSGBUFSIZE], longbuf[BUFSIZ]; + int ch, reply; + + verbose = reply = FALSE; + while ((ch = getopt(argc, argv, "rv")) != EOF) + switch(ch) + { + case 'r': + reply = TRUE; + break; + + case 'v': + verbose = TRUE; + break; + } while (fgets(buf, sizeof(buf)-1, stdin)) { - if (strncmp("From: ", buf, 6) - && strncmp("To: ", buf, 4) - && strncmp("Reply-", buf, 6) - && strncmp("Cc: ", buf, 4) - && strncmp("Bcc: ", buf, 5)) - continue; - else + if (buf[0] == ' ' || buf[0] == '\t') + strcat(longbuf, buf); + else if (!strncasecmp("From: ", buf, 6) + || !strncasecmp("To: ", buf, 4) + || !strncasecmp("Reply-", buf, 6) + || !strncasecmp("Cc: ", buf, 4) + || !strncasecmp("Bcc: ", buf, 5)) + strcpy(longbuf, buf); + else if (longbuf[0]) { - fputs(buf, stdout); - if (reply) - { - reply_hack(buf, "HOSTNAME.NET"); - printf("Rewritten buffer: %s", buf); - } - else - if ((cp = nxtaddr(buf)) != (char *)NULL) - do { - printf("\t%s\n", cp); - } while - ((cp = nxtaddr((char *)NULL)) != (char *)NULL); + if (verbose) + fputs(longbuf, stdout); + parsebuf(longbuf, reply); + longbuf[0] = '\0'; } - + } + if (longbuf[0]) + { + if (verbose) + fputs(longbuf, stdout); + parsebuf(longbuf, reply); } } #endif /* TESTMAIN */