From ff48fce6e064249f6be5c86778c52e72fb826100 Mon Sep 17 00:00:00 2001 From: Simon Schubert Date: Thu, 27 Aug 2009 22:20:38 +0200 Subject: [PATCH] dma: add recipient parsing from headers --- libexec/dma/dma.8 | 15 ++- libexec/dma/dma.c | 24 +++-- libexec/dma/dma.h | 2 +- libexec/dma/mail.c | 232 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 260 insertions(+), 13 deletions(-) diff --git a/libexec/dma/dma.8 b/libexec/dma/dma.8 index 49c4f5ef0a..54272a62c3 100644 --- a/libexec/dma/dma.8 +++ b/libexec/dma/dma.8 @@ -39,7 +39,7 @@ .Nd DragonFly Mail Agent .Sh SYNOPSIS .Nm -.Op Fl DiO +.Op Fl DiOt .Op Fl A Ar mode .Op Fl b Ar mode .Op Fl f Ar sender @@ -111,6 +111,19 @@ The argument is required for compatibility with sendmail, and ignored. .It Fl r Ar sender Same as .Fl f . +.It Fl t +Obtain recipient addresses from the message header. +.Nm will parse the +.Li To: , +.Li Cc: , +and +.Li Bcc: +headers. +The +.Li Bcc: +header will be removed independent of whether +.Fl t +is specified or not. .El .Sh CONFIGURATION .Nm diff --git a/libexec/dma/dma.c b/libexec/dma/dma.c index 61abe22c19..a77fb8c52a 100644 --- a/libexec/dma/dma.c +++ b/libexec/dma/dma.c @@ -350,6 +350,7 @@ main(int argc, char **argv) struct queue queue; int i, ch; int nodot = 0, doqueue = 0, showq = 0, queue_only = 0; + int recp_from_header = 0; atexit(deltmp); @@ -365,7 +366,7 @@ main(int argc, char **argv) } opterr = 0; - while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:UV:vX:")) != -1) { + while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) { switch (ch) { case 'A': /* -AX is being ignored, except for -A{c,m} */ @@ -394,6 +395,10 @@ main(int argc, char **argv) sender = optarg; break; + case 't': + recp_from_header = 1; + break; + case 'o': /* -oX is being ignored, except for -oi */ if (optarg[0] != 'i') @@ -491,22 +496,25 @@ skipopts: if ((sender = set_from(&queue, sender)) == NULL) errlog(1, NULL); + if (newspoolf(&queue) != 0) + errlog(1, "can not create temp file"); + + setlogident("%s", queue.id); + for (i = 0; i < argc; i++) { if (add_recp(&queue, argv[i], 1) != 0) errlogx(1, "invalid recipient `%s'", argv[i]); } - if (LIST_EMPTY(&queue.queue)) + if (LIST_EMPTY(&queue.queue) && !recp_from_header) errlogx(1, "no recipients"); - if (newspoolf(&queue) != 0) - errlog(1, "can not create temp file"); - - setlogident("%s", queue.id); - - if (readmail(&queue, nodot) != 0) + if (readmail(&queue, nodot, recp_from_header) != 0) errlog(1, "can not read mail"); + if (LIST_EMPTY(&queue.queue)) + errlogx(1, "no recipients"); + if (linkspool(&queue) != 0) errlog(1, "can not create spools"); diff --git a/libexec/dma/dma.h b/libexec/dma/dma.h index 17b9437f43..5e4e13e1b8 100644 --- a/libexec/dma/dma.h +++ b/libexec/dma/dma.h @@ -190,7 +190,7 @@ int deliver_local(struct qitem *, const char **errmsg); /* mail.c */ void bounce(struct qitem *, const char *); -int readmail(struct queue *, int); +int readmail(struct queue *, int, int); /* util.c */ const char *hostname(void); diff --git a/libexec/dma/mail.c b/libexec/dma/mail.c index 547990b171..0058e89b71 100644 --- a/libexec/dma/mail.c +++ b/libexec/dma/mail.c @@ -133,9 +133,202 @@ fail: exit(1); } +struct parse_state { + char addr[1000]; + int pos; + + enum { + NONE = 0, + START, + MAIN, + EOL, + QUIT + } state; + int comment; + int quote; + int brackets; + int esc; +}; + +/* + * Simplified RFC2822 header/address parsing. + * We copy escapes and quoted strings directly, since + * we have to pass them like this to the mail server anyways. + * XXX local addresses will need treatment + */ +static int +parse_addrs(struct parse_state *ps, char *s, struct queue *queue) +{ + char *addr; + +again: + switch (ps->state) { + case NONE: + return (-1); + + case START: + /* init our data */ + bzero(ps, sizeof(*ps)); + + /* skip over header name */ + while (*s != ':') + s++; + s++; + ps->state = MAIN; + break; + + case MAIN: + /* all fine */ + break; + + case EOL: + switch (*s) { + case ' ': + case '\t': + s++; + /* continue */ + break; + + default: + ps->state = QUIT; + if (ps->pos != 0) + goto newaddr; + return (0); + } + + case QUIT: + return (0); + } + + for (; *s != 0; s++) { + if (ps->esc) { + ps->esc = 0; + + switch (*s) { + case '\r': + case '\n': + goto err; + + default: + goto copy; + } + } + + if (ps->quote) { + switch (*s) { + case '"': + ps->quote = 0; + goto copy; + + case '\\': + ps->esc = 1; + goto copy; + + case '\r': + case '\n': + goto eol; + + default: + goto copy; + } + } + + switch (*s) { + case '(': + ps->comment++; + break; + + case ')': + if (ps->comment) + ps->comment--; + else + goto err; + goto skip; + + case '"': + ps->quote = 1; + goto copy; + + case '\\': + ps->esc = 1; + goto copy; + + case '\r': + case '\n': + goto eol; + } + + if (ps->comment) + goto skip; + + switch (*s) { + case ' ': + case '\t': + /* ignore whitespace */ + goto skip; + + case '<': + ps->brackets = 1; + goto skip; + + case '>': + if (!ps->brackets) + goto err; + ps->brackets = 0; + + s++; + goto newaddr; + + case ':': + /* group - ignore */ + ps->pos = 0; + goto skip; + + case ',': + case ';': + s++; + goto newaddr; + + default: + goto copy; + } + +copy: + if (ps->comment) + goto skip; + + if (ps->pos + 1 == sizeof(ps->addr)) + goto err; + ps->addr[ps->pos++] = *s; + +skip: + ; + } + +eol: + ps->state = EOL; + return (0); + +err: + ps->state = QUIT; + return (-1); + +newaddr: + ps->addr[ps->pos] = 0; + ps->pos = 0; + addr = strdup(ps->addr); + if (addr == NULL) + errlog(1, NULL); + + add_recp(queue, addr, 1); + fprintf(stderr, "parsed `%s'\n", addr); + goto again; +} + int -readmail(struct queue *queue, int nodot) +readmail(struct queue *queue, int nodot, int recp_from_header) { + struct parse_state parse_state; char line[1000]; /* by RFC2822 */ size_t linelen; size_t error; @@ -143,6 +336,9 @@ readmail(struct queue *queue, int nodot) int had_from = 0; int had_messagid = 0; int had_date = 0; + int nocopy = 0; + + parse_state.state = NONE; error = fprintf(queue->mailf, "Received: from %s (uid %d)\n" @@ -167,13 +363,41 @@ readmail(struct queue *queue, int nodot) return (-1); } if (!had_headers) { + /* + * Unless this is a continuation, switch of + * the Bcc: nocopy flag. + */ + if (!(line[0] == ' ' || line[0] == '\t')) + nocopy = 0; + if (strprefixcmp(line, "Date:") == 0) had_date = 1; else if (strprefixcmp(line, "Message-Id:") == 0) had_messagid = 1; else if (strprefixcmp(line, "From:") == 0) had_from = 1; + else if (strprefixcmp(line, "Bcc:") == 0) + nocopy = 1; + + if (parse_state.state != NONE) { + if (parse_addrs(&parse_state, line, queue) < 0) { + errlogx(1, "invalid address in header\n"); + /* NOTREACHED */ + } + } + + if (recp_from_header && ( + strprefixcmp(line, "To:") == 0 || + strprefixcmp(line, "Cc:") == 0 || + strprefixcmp(line, "Bcc:") == 0)) { + parse_state.state = START; + if (parse_addrs(&parse_state, line, queue) < 0) { + errlogx(1, "invalid address in header\n"); + /* NOTREACHED */ + } + } } + if (strcmp(line, "\n") == 0 && !had_headers) { had_headers = 1; while (!had_date || !had_messagid || !had_from) { @@ -196,8 +420,10 @@ readmail(struct queue *queue, int nodot) } if (!nodot && linelen == 2 && line[0] == '.') break; - if (fwrite(line, strlen(line), 1, queue->mailf) != 1) - return (-1); + if (!nocopy) { + if (fwrite(line, strlen(line), 1, queue->mailf) != 1) + return (-1); + } } return (0); -- 2.41.0