dma: add recipient parsing from headers
authorSimon Schubert <corecode@dragonflybsd.org>
Thu, 27 Aug 2009 20:20:38 +0000 (22:20 +0200)
committerSimon Schubert <corecode@dragonflybsd.org>
Thu, 27 Aug 2009 21:12:57 +0000 (23:12 +0200)
libexec/dma/dma.8
libexec/dma/dma.c
libexec/dma/dma.h
libexec/dma/mail.c

index 49c4f5e..54272a6 100644 (file)
@@ -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
index 61abe22..a77fb8c 100644 (file)
@@ -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");
 
index 17b9437..5e4e13e 100644 (file)
@@ -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);
index 547990b..0058e89 100644 (file)
@@ -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);