Hi,

This diff implements Delivery Status Notification as specified in
RFC3461 for NOTIFY=SUCCESS case. OpenSMTPD can now relay DSN related
parameters to DSN capable server as well as advertises 250-DSN in
banner. It's now possible to mail -s subject [email protected] -N success
-R hdrs and get a notification from the relaying server. If OpenSMTPD
recieves a mail for which DSN is requested it now generates a DSN
to the sender.  Note though that this diff currently only works for
NOTIFY=success case, however, OpenSMTPD already handles "delay" and
"failure" as part of bounce handling.  The bounce generation is not
bound by DSN parameters yet. I tested it with postfix server and
would appreciate test reports against other DSN capable servers.

Comments?

diff --git a/smtpd/bounce.c b/smtpd/bounce.c
index ef3e345..7791f1b 100644
--- a/smtpd/bounce.c
+++ b/smtpd/bounce.c
@@ -93,6 +93,7 @@ static void bounce_status(struct bounce_session *, const char 
*, ...);
 static void bounce_io(struct io *, int);
 static void bounce_timeout(int, short, void *);
 static void bounce_free(struct bounce_session *);
+static const char *bounce_strtype(enum bounce_type);
 
 static struct tree                     wait_fd;
 static struct bounce_message_tree      messages;
@@ -317,6 +318,9 @@ const char *notice_warning2 =
     "    The message is kept in the queue for up to %s.\n"
     "    You DO NOT NEED to re-send the message to these recipients.\n\n";
 
+const char *notice_success =
+    "    Your message was successfully delivered to these recipients.\n\n";
+
 static int
 bounce_next_message(struct bounce_session *s)
 {
@@ -400,18 +404,28 @@ bounce_next(struct bounce_session *s)
                    "\n"
                    NOTICE_INTRO
                    "\n",
-                   (s->msg->bounce.type == B_ERROR) ? "error" : "warning",
+                   bounce_strtype(s->msg->bounce.type),
                    env->sc_hostname,
                    s->msg->to,
                    time_to_text(time(NULL)));
 
-               if (s->msg->bounce.type == B_ERROR)
+               switch (s->msg->bounce.type) {
+               case B_ERROR:
                        iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
                            notice_error);
-               else
+                       break;
+               case B_WARNING:
                        iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
                            notice_warning,
                            bounce_duration(s->msg->bounce.delay));
+                       break;
+               case B_DSN:
+                       iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
+                           notice_success);
+                       break;
+               default:
+                       log_warn("warn: bounce: unknown bounce_type");
+               }
 
                TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
                        iobuf_xfqueue(&s->iobuf,
@@ -655,5 +669,21 @@ bounce_message_cmp(const struct bounce_message *a,
        return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
 }
 
+static const char *
+bounce_strtype(enum bounce_type t)
+{
+       switch (t) {
+       case B_ERROR:
+               return ("error");
+       case B_WARNING:
+               return ("warning");
+       case B_DSN:
+               return ("dsn");
+       default:
+               log_warn("warn: bounce: unknown bounce_type");
+               return ("");
+       }
+}
+
 SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
     bounce_message_cmp);
diff --git a/smtpd/enqueue.c b/smtpd/enqueue.c
index b74efd8..4b6914c 100644
--- a/smtpd/enqueue.c
+++ b/smtpd/enqueue.c
@@ -105,6 +105,9 @@ struct {
        char     *from;
        char     *fromname;
        char    **rcpts;
+       char     *dsn_notify;
+       char     *dsn_ret;
+       char     *dsn_envid;
        int       rcpt_cnt;
        int       need_linesplit;
        int       saw_date;
@@ -161,7 +164,7 @@ enqueue(int argc, char *argv[])
        char                    *fake_from = NULL, *buf;
        struct passwd           *pw;
        FILE                    *fp, *fout;
-       size_t                   len;
+       size_t                   len, envid_sz;
        char                    *line;
        int                      dotted;
        int                      inheaders = 0;
@@ -175,7 +178,7 @@ enqueue(int argc, char *argv[])
        save_argv = argv;
 
        while ((ch = getopt(argc, argv,
-           "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvx")) != -1) {
+           "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvV:x")) != -1) {
                switch (ch) {
                case 'f':
                        fake_from = optarg;
@@ -183,12 +186,21 @@ enqueue(int argc, char *argv[])
                case 'F':
                        msg.fromname = optarg;
                        break;
+               case 'N':
+                       msg.dsn_notify = optarg;
+                       break;
+               case 'R':
+                       msg.dsn_ret = optarg;
+                       break;
                case 't':
                        tflag = 1;
                        break;
                case 'v':
                        verbose = 1;
                        break;
+               case 'V':
+                       msg.dsn_envid = optarg;
+                       break;
                /* all remaining: ignored, sendmail compat */
                case 'A':
                case 'B':
@@ -198,10 +210,8 @@ enqueue(int argc, char *argv[])
                case 'i':
                case 'L':
                case 'm':
-               case 'N': /* XXX: DSN */
                case 'o':
                case 'p':
-               case 'R':
                case 'x':
                        break;
                case 'q':
@@ -265,11 +275,20 @@ enqueue(int argc, char *argv[])
        send_line(fout, verbose, "EHLO localhost\n");
        get_responses(fout, 1);
 
-       send_line(fout, verbose, "MAIL FROM:<%s>\n", msg.from);
+       envid_sz = strlen(msg.dsn_envid);
+       send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n",
+           msg.from,
+           msg.dsn_ret ? "RET=" : "",
+           msg.dsn_ret ? msg.dsn_ret : "",
+           envid_sz ? "ENVID=" : "",
+           envid_sz ? msg.dsn_envid : "");
        get_responses(fout, 1);
 
        for (i = 0; i < msg.rcpt_cnt; i++) {
-               send_line(fout, verbose, "RCPT TO:<%s>\n", msg.rcpts[i]);
+               send_line(fout, verbose, "RCPT TO:<%s> %s%s\n",
+                   msg.rcpts[i],
+                   msg.dsn_notify ? "NOTIFY=" : "",
+                   msg.dsn_notify ? msg.dsn_notify : "");
                get_responses(fout, 1);
        }
 
diff --git a/smtpd/envelope.c b/smtpd/envelope.c
index 146d737..4c7da61 100644
--- a/smtpd/envelope.c
+++ b/smtpd/envelope.c
@@ -43,6 +43,7 @@
 #include "smtpd.h"
 #include "log.h"
 
+static int ascii_load_uint8(uint8_t *, char *);
 static int ascii_load_uint16(uint16_t *, char *);
 static int ascii_load_uint32(uint32_t *, char *);
 static int ascii_load_time(time_t *, char *);
@@ -55,7 +56,9 @@ static int ascii_load_flags(enum envelope_flags *, char *);
 static int ascii_load_mta_relay_url(struct relayhost *, char *);
 static int ascii_load_mta_relay_flags(uint16_t *, char *);
 static int ascii_load_bounce_type(enum bounce_type *, char *);
+static int ascii_load_dsn_ret(enum dsn_ret *, char *);
 
+static int ascii_dump_uint8(uint8_t, char *, size_t);
 static int ascii_dump_uint16(uint16_t, char *, size_t);
 static int ascii_dump_uint32(uint32_t, char *, size_t);
 static int ascii_dump_time(time_t, char *, size_t);
@@ -67,6 +70,7 @@ static int ascii_dump_flags(enum envelope_flags, char *, 
size_t);
 static int ascii_dump_mta_relay_url(const struct relayhost *, char *, size_t);
 static int ascii_dump_mta_relay_flags(uint16_t, char *, size_t);
 static int ascii_dump_bounce_type(enum bounce_type, char *, size_t);
+static int ascii_dump_dsn_ret(enum dsn_ret, char *, size_t);
 
 void
 envelope_set_errormsg(struct envelope *e, char *fmt, ...)
@@ -120,6 +124,10 @@ envelope_load_buffer(struct envelope *ep, const char 
*ibuf, size_t buflen)
                EVP_BOUNCE_TYPE,
                EVP_BOUNCE_DELAY,
                EVP_BOUNCE_EXPIRE,
+               EVP_DSN_ENVID,
+               EVP_DSN_NOTIFY,
+               EVP_DSN_ORCPT,
+               EVP_DSN_RET
        };
        char    *field, *nextline;
        char     lbuf[sizeof(*ep)], *buf;
@@ -198,7 +206,11 @@ envelope_dump_buffer(const struct envelope *ep, char 
*dest, size_t len)
                EVP_LASTBOUNCE,
                EVP_EXPIRE,
                EVP_RETRY,
-               EVP_FLAGS
+               EVP_FLAGS,
+               EVP_DSN_ENVID,
+               EVP_DSN_NOTIFY,
+               EVP_DSN_ORCPT,
+               EVP_DSN_RET
        };
        enum envelope_field mda_fields[] = {
                EVP_MDA_METHOD,
@@ -345,6 +357,14 @@ envelope_ascii_field_name(enum envelope_field field)
                return "bounce-delay";
        case EVP_BOUNCE_EXPIRE:
                return "bounce-expire";
+       case EVP_DSN_ENVID:
+               return "dsn-envid";
+       case EVP_DSN_NOTIFY:
+               return "dsn-notify";
+       case EVP_DSN_ORCPT:
+               return "dsn-orcpt";
+       case EVP_DSN_RET:
+               return "dsn-ret";
        }
 
        return NULL;
@@ -430,6 +450,15 @@ envelope_ascii_load(enum envelope_field field, struct 
envelope *ep, char *buf)
                return ascii_load_time(&ep->agent.bounce.delay, buf);
        case EVP_BOUNCE_EXPIRE:
                return ascii_load_time(&ep->agent.bounce.expire, buf);
+       case EVP_DSN_NOTIFY:
+               return ascii_load_uint8(&ep->dsn_notify, buf);
+       case EVP_DSN_ORCPT:
+               return ascii_load_mailaddr(&ep->dsn_orcpt, buf);
+       case EVP_DSN_RET:
+               return ascii_load_dsn_ret(&ep->dsn_ret, buf);
+       case EVP_DSN_ENVID:
+               return ascii_load_string(ep->dsn_envid, buf,
+                   sizeof ep->dsn_envid);
        }
        return 0;
 }
@@ -510,11 +539,32 @@ envelope_ascii_dump(enum envelope_field field, const 
struct envelope *ep,
                if (ep->agent.bounce.type != B_WARNING)
                        return (1);
                return ascii_dump_time(ep->agent.bounce.expire, buf, len);
+       case EVP_DSN_NOTIFY:
+               return ascii_dump_uint8(ep->dsn_notify, buf, len);
+       case EVP_DSN_RET:
+               return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
+       case EVP_DSN_ORCPT:
+               if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0])
+                       return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len);
+               return 1;
+       case EVP_DSN_ENVID:
+               return ascii_dump_string(ep->dsn_envid, buf, len);
        }
        return 0;
 }
 
 static int
+ascii_load_uint8(uint8_t *dest, char *buf)
+{
+       const char *errstr;
+
+       *dest = strtonum(buf, 0, 0xff, &errstr);
+       if (errstr)
+               return 0;
+       return 1;
+}
+
+static int
 ascii_load_uint16(uint16_t *dest, char *buf)
 {
        const char *errstr;
@@ -677,12 +727,32 @@ ascii_load_bounce_type(enum bounce_type *dest, char *buf)
                *dest = B_ERROR;
        else if (strcasecmp(buf, "warn") == 0)
                *dest = B_WARNING;
+       else if (strcasecmp(buf, "dsn") == 0)
+               *dest = B_DSN;
+       else
+               return 0;
+       return 1;
+}
+
+static int
+ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
+{
+       if (strcasecmp(buf, "HDRS") == 0)
+               *ret = DSN_RETHDRS;
+       else if (strcasecmp(buf, "FULL") == 0)
+               *ret = DSN_RETFULL;
        else
                return 0;
        return 1;
 }
 
 static int
+ascii_dump_uint8(uint8_t src, char *dest, size_t len)
+{
+       return bsnprintf(dest, len, "%d", src);
+}
+
+static int
 ascii_dump_uint16(uint16_t src, char *dest, size_t len)
 {
        return bsnprintf(dest, len, "%d", src);
@@ -826,8 +896,25 @@ ascii_dump_bounce_type(enum bounce_type type, char *dest, 
size_t len)
        case B_WARNING:
                p = "warn";
                break;
+       case B_DSN:
+               p = "dsn";
+               break;
        default:
                return 0;
        }
        return bsnprintf(dest, len, "%s", p);
 }
+
+static int
+ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
+{
+       size_t cpylen = 0;
+
+       dest[0] = '\0';
+       if (flag == DSN_RETFULL)
+               cpylen = strlcat(dest, "FULL", len);
+       else if (flag == DSN_RETHDRS)
+               cpylen = strlcat(dest, "HDRS", len);
+
+       return cpylen < len ? 1 : 0;
+}
diff --git a/smtpd/mta.c b/smtpd/mta.c
index decdf17..3acd170 100644
--- a/smtpd/mta.c
+++ b/smtpd/mta.c
@@ -244,6 +244,16 @@ mta_imsg(struct mproc *p, struct imsg *imsg)
                        if (strcmp(buf, e->dest))
                                e->rcpt = xstrdup(buf, "mta_envelope:rcpt");
                        e->task = task;
+                       if (evp.dsn_orcpt.user[0] && evp.dsn_orcpt.domain[0]) {
+                               snprintf(buf, sizeof buf, "%s@%s",
+                                   evp.dsn_orcpt.user, evp.dsn_orcpt.domain);
+                               e->dsn_orcpt = xstrdup(buf,
+                                   "mta_envelope:dsn_orcpt");
+                       }
+                       strlcpy(e->dsn_envid, evp.dsn_envid,
+                           sizeof e->dsn_envid);
+                       e->dsn_notify = evp.dsn_notify;
+                       e->dsn_ret = evp.dsn_ret;
 
                        TAILQ_INSERT_TAIL(&task->envelopes, e, entry);
                        log_debug("debug: mta: received evp:%016" PRIx64
@@ -699,7 +709,10 @@ mta_delivery_notify(struct mta_envelope *e, int delivery, 
const char *status,
     uint32_t penalty)
 {
        if (delivery == IMSG_DELIVERY_OK) {
-               queue_ok(e->id);
+               m_create(p_queue, IMSG_DELIVERY_OK, 0, 0, -1);
+               m_add_evpid(p_queue, e->id);
+               m_add_int(p_queue, e->ext);
+               m_close(p_queue);
        }
        else if (delivery == IMSG_DELIVERY_TEMPFAIL) {
                queue_tempfail(e->id, penalty, status);
@@ -1309,6 +1322,7 @@ mta_flush(struct mta_relay *relay, int fail, const char 
*error)
 
                        free(e->dest);
                        free(e->rcpt);
+                       free(e->dsn_orcpt);
                        free(e);
                        n++;
                }
diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c
index f7b65e3..2ca23af 100644
--- a/smtpd/mta_session.c
+++ b/smtpd/mta_session.c
@@ -96,7 +96,7 @@ enum mta_state {
 #define MTA_EXT_AUTH           0x04
 #define MTA_EXT_AUTH_PLAIN             0x08
 #define MTA_EXT_AUTH_LOGIN             0x10
-
+#define MTA_EXT_DSN            0x20
 
 struct failed_evp {
        int                      delivery;
@@ -156,6 +156,8 @@ static void mta_start_tls(struct mta_session *);
 static int mta_verify_certificate(struct mta_session *);
 static struct mta_session *mta_tree_pop(struct tree *, uint64_t);
 static void mta_flush_failedqueue(struct mta_session *);
+static const char * dsn_strret(enum dsn_ret);
+static const char * dsn_strnotify(uint8_t);
 void mta_hoststat_update(const char *, const char *);
 void mta_hoststat_reschedule(const char *);
 void mta_hoststat_cache(const char *, uint64_t);
@@ -590,6 +592,8 @@ mta_connect(struct mta_session *s)
 static void
 mta_enter_state(struct mta_session *s, int newstate)
 {
+       struct mta_envelope      *e;
+       size_t                   envid_sz;
        int                      oldstate;
        ssize_t                  q;
        char                     ibuf[SMTPD_MAXLINESIZE];
@@ -771,15 +775,41 @@ mta_enter_state(struct mta_session *s, int newstate)
                break;
 
        case MTA_MAIL:
+               e = s->currevp;
+               if (e == NULL)
+                       s->currevp = e = TAILQ_FIRST(&s->task->envelopes);
+
                s->hangon = 0;
                s->msgtried++;
-               mta_send(s, "MAIL FROM:<%s>", s->task->sender);
+               envid_sz = strlen(e->dsn_envid);
+               if (s->ext & MTA_EXT_DSN) {
+                       mta_send(s, "MAIL FROM:<%s> %s%s %s%s",
+                           s->task->sender,
+                           e->dsn_ret ? "RET=" : "",
+                           e->dsn_ret ? dsn_strret(e->dsn_ret) : "",
+                           envid_sz ? "ENVID=" : "",
+                           envid_sz ? e->dsn_envid : "");
+               } else
+                       mta_send(s, "MAIL FROM:<%s>", s->task->sender);
                break;
 
        case MTA_RCPT:
-               if (s->currevp == NULL)
-                       s->currevp = TAILQ_FIRST(&s->task->envelopes);
-               mta_send(s, "RCPT TO:<%s>", s->currevp->dest);
+               e = s->currevp;
+               if (e == NULL) {
+                       s->currevp = e = TAILQ_FIRST(&s->task->envelopes);
+                       e->ext = s->ext;
+               }
+
+               if (s->ext & MTA_EXT_DSN) {
+                       mta_send(s, "RCPT TO:<%s> %s%s %s%s",
+                           e->dest,
+                           e->dsn_notify ? "NOTIFY=" : "",
+                           e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "",
+                           e->dsn_orcpt ? "ORCPT=" : "",
+                           e->dsn_orcpt ? e->dsn_orcpt : "");
+               } else
+                       mta_send(s, "RCPT TO:<%s>", e->dest);
+
                s->rcptcount++;
                break;
 
@@ -968,6 +998,8 @@ mta_response(struct mta_session *s, char *line)
                }
 
                s->currevp = TAILQ_NEXT(s->currevp, entry);
+               if (s->currevp != NULL)
+                       s->currevp->ext = s->ext;
                if (line[0] == '2') {
                        mta_flush_failedqueue(s);
                        /*
@@ -1203,6 +1235,8 @@ mta_io(struct io *io, int evt)
                        }
                        else if (strcmp(msg, "PIPELINING") == 0)
                                s->ext |= MTA_EXT_PIPELINING;
+                       else if (strcmp(msg, "DSN") == 0)
+                               s->ext |= MTA_EXT_DSN;
                }
 
                if (cont)
@@ -1591,6 +1625,35 @@ mta_verify_certificate(struct mta_session *s)
        return 1;
 }
 
+static const char *
+dsn_strret(enum dsn_ret ret)
+{
+       if (ret == DSN_RETHDRS)
+               return "HDRS";
+       else if (ret == DSN_RETFULL)
+               return "FULL";
+       else {
+               log_debug("mta: invalid ret %d", ret);
+               return "???";
+       }
+}
+
+static const char *
+dsn_strnotify(uint8_t arg)
+{
+       if (arg & DSN_SUCCESS)
+               return "SUCCESS";
+       else if (arg & DSN_FAILURE)
+               return "FAILURE";
+       else if (arg & DSN_DELAY)
+               return "DELAY";
+       else if (arg & DSN_NEVER)
+               return "NEVER";
+       else {
+               log_debug("mta: invalid notify %u", arg);
+               return "???";
+       }
+}
 
 #define CASE(x) case x : return #x
 
diff --git a/smtpd/queue.c b/smtpd/queue.c
index 3736647..15b7c68 100644
--- a/smtpd/queue.c
+++ b/smtpd/queue.c
@@ -69,7 +69,7 @@ queue_imsg(struct mproc *p, struct imsg *imsg)
        uint32_t                 msgid;
        uint32_t                 penalty;
        time_t                   nexttry;
-       int                      fd, ret, v, flags;
+       int                      fd, mta_ext, ret, v, flags;
 
        if (p->proc == PROC_SMTP) {
 
@@ -336,7 +336,21 @@ queue_imsg(struct mproc *p, struct imsg *imsg)
                case IMSG_DELIVERY_OK:
                        m_msg(&m, imsg);
                        m_get_evpid(&m, &evpid);
+                       if (p->proc == PROC_MTA)
+                               m_get_int(&m, &mta_ext);
                        m_end(&m);
+                       if (queue_envelope_load(evpid, &evp) == 0) {
+                               log_warn("queue: dsn: failed to load envelope");
+                               return;
+                       }
+                       if (p->proc == PROC_MDA) { /* XXX */
+                               if (evp.dsn_notify & DSN_SUCCESS) {
+                                       bounce.type = B_DSN;
+                                       bounce.delay = 0;
+                                       bounce.expire = 0;
+                                       queue_bounce(&evp, &bounce);
+                               }
+                       }
                        queue_envelope_delete(evpid);
                        m_create(p_scheduler, IMSG_DELIVERY_OK, 0, 0, -1);
                        m_add_evpid(p_scheduler, evpid);
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
index b3ab6ba..9f3ad1a 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -163,6 +163,7 @@ static void smtp_enter_state(struct smtp_session *, int);
 static void smtp_reply(struct smtp_session *, char *, ...);
 static void smtp_command(struct smtp_session *, char *);
 static int smtp_parse_mail_args(struct smtp_session *, char *);
+static int smtp_parse_rcpt_args(struct smtp_session *, char *);
 static void smtp_rfc4954_auth_plain(struct smtp_session *, char *);
 static void smtp_rfc4954_auth_login(struct smtp_session *, char *);
 static void smtp_message_write(struct smtp_session *, const char *);
@@ -172,6 +173,7 @@ static void smtp_wait_mfa(struct smtp_session *s, int);
 static void smtp_free(struct smtp_session *, const char *);
 static const char *smtp_strstate(int);
 static int smtp_verify_certificate(struct smtp_session *);
+static uint8_t dsn_notify_str_to_uint8(const char *);
 static void smtp_auth_failure_pause(struct smtp_session *);
 static void smtp_auth_failure_resume(int, short, void *);
 
@@ -678,6 +680,7 @@ smtp_mfa_response(struct smtp_session *s, int status, 
uint32_t code,
                        smtp_reply(s, "250-8BITMIME");
                        smtp_reply(s, "250-ENHANCEDSTATUSCODES");
                        smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
+                       smtp_reply(s, "250-DSN");
                        if (ADVERTISE_TLS(s))
                                smtp_reply(s, "250-STARTTLS");
                        if (ADVERTISE_AUTH(s))
@@ -1136,11 +1139,8 @@ smtp_command(struct smtp_session *s, char *line)
                            "553 Recipient address syntax error");
                        break;
                }
-               if (*args) {
-                       smtp_reply(s,
-                           "553 No option supported on RCPT TO");
+               if (args && smtp_parse_rcpt_args(s, args) == -1)
                        break;
-               }
 
                m_create(p_mfa, IMSG_MFA_REQ_RCPT, 0, 0, -1);
                m_add_id(p_mfa, s->id);
@@ -1313,6 +1313,60 @@ abort:
        smtp_enter_state(s, STATE_HELO);
 }
 
+static uint8_t
+dsn_notify_str_to_uint8(const char *arg)
+{
+       if (strcasecmp(arg, "SUCCESS") == 0)
+               return DSN_SUCCESS;
+       else if (strcasecmp(arg, "FAILURE") == 0)
+               return DSN_FAILURE;
+       else if (strcasecmp(arg, "DELAY") == 0)
+               return DSN_DELAY;
+       else if (strcasecmp(arg, "NEVER") == 0)
+               return DSN_NEVER;
+
+       return (0);
+}
+
+static int
+smtp_parse_rcpt_args(struct smtp_session *s, char *args)
+{
+       char    *b, *p;
+       uint8_t flag;
+
+       while ((b = strsep(&args, " "))) {
+               if (*b == '\0')
+                       continue;
+               
+               if (strncasecmp(b, "NOTIFY=", 7) == 0) {
+                       b += 7;
+                       while ((p = strsep(&b, ","))) {
+                               if (*p == '\0')
+                                       continue;
+
+                               if ((flag = dsn_notify_str_to_uint8(p)) == 0)
+                                       continue;
+
+                               s->evp.dsn_notify |= flag;
+                       }
+                       if (s->evp.dsn_notify > DSN_NEVER) {
+                               smtp_reply(s,
+                                   "553 NOTIFY option NEVER cannot be \
+                                   combined with other options");
+                               return (-1);
+                       }
+               } else if (strncasecmp(b, "ORCPT=", 6) == 0) {
+                       b += 6;
+                       /* XXX */
+               } else {
+                       smtp_reply(s, "503 Unsupported option %s", b);
+                       return (-1);
+               }
+       }
+
+       return (0);
+}
+
 static int
 smtp_parse_mail_args(struct smtp_session *s, char *args)
 {
@@ -1326,12 +1380,21 @@ smtp_parse_mail_args(struct smtp_session *s, char *args)
                        log_debug("debug: smtp: AUTH in MAIL FROM command");
                else if (strncasecmp(b, "SIZE=", 5) == 0)
                        log_debug("debug: smtp: SIZE in MAIL FROM command");
-               else if (!strcasecmp(b, "BODY=7BIT"))
+               else if (strcasecmp(b, "BODY=7BIT") == 0)
                        /* XXX only for this transaction */
                        s->flags &= ~SF_8BITMIME;
                else if (strcasecmp(b, "BODY=8BITMIME") == 0)
                        ;
-               else {
+               else if (strncasecmp(b, "RET=", 4) == 0) {
+                       b += 4;
+                       if (strcasecmp(b, "HDRS") == 0)
+                               s->evp.dsn_ret = DSN_RETHDRS;
+                       else if (strcasecmp(b, "FULL") == 0)
+                               s->evp.dsn_ret = DSN_RETFULL;
+               } else if (strncasecmp(b, "ENVID=", 6) == 0) {
+                       b += 6;
+                       strlcpy(s->evp.dsn_envid, b, sizeof(s->evp.dsn_envid));
+               } else {
                        smtp_reply(s, "503 Unsupported option %s", b);
                        return (-1);
                }
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
index 4a603b8..2d685ce 100644
--- a/smtpd/smtpd.h
+++ b/smtpd/smtpd.h
@@ -378,9 +378,20 @@ struct delivery_mta {
        struct relayhost        relay;
 };
 
+enum dsn_type {
+       DSN_SUCCESS,
+       DSN_FAILURE,
+       DSN_DELAY
+};
+
+struct delivery_dsn {
+       enum dsn_type           type;
+};
+
 enum bounce_type {
        B_ERROR,
        B_WARNING,
+       B_DSN
 };
 
 struct delivery_bounce {
@@ -430,6 +441,16 @@ struct expand {
        struct expandnode               *parent;
 };
 
+#define DSN_SUCCESS 0x01
+#define DSN_FAILURE 0x02
+#define DSN_DELAY   0x04
+#define DSN_NEVER   0x08
+
+enum dsn_ret {
+       DSN_RETFULL = 1,
+       DSN_RETHDRS
+};
+
 #define        SMTPD_ENVELOPE_VERSION          2
 struct envelope {
        TAILQ_ENTRY(envelope)           entry;
@@ -454,6 +475,7 @@ struct envelope {
                struct delivery_mda     mda;
                struct delivery_mta     mta;
                struct delivery_bounce  bounce;
+               struct delivery_dsn     dsn;
        }                               agent;
 
        uint16_t                        retry;
@@ -462,6 +484,11 @@ struct envelope {
        time_t                          lasttry;
        time_t                          nexttry;
        time_t                          lastbounce;
+
+       struct mailaddr                 dsn_orcpt;
+       char                            dsn_envid[101];
+       uint8_t                         dsn_notify;
+       enum dsn_ret                    dsn_ret;
 };
 
 enum envelope_field {
@@ -495,6 +522,10 @@ enum envelope_field {
        EVP_BOUNCE_TYPE,
        EVP_BOUNCE_DELAY,
        EVP_BOUNCE_EXPIRE,
+       EVP_DSN_ENVID,
+       EVP_DSN_NOTIFY,
+       EVP_DSN_ORCPT,
+       EVP_DSN_RET,
 };
 
 struct listener {
@@ -783,6 +814,11 @@ struct mta_envelope {
        char                            *rcpt;
        struct mta_task                 *task;
        int                              delivery;
+       int                              ext;
+       char                            *dsn_orcpt;
+       char                            dsn_envid[101];
+       uint8_t                         dsn_notify;
+       enum dsn_ret                    dsn_ret;
 };
 
 struct mta_task {

-- 
You received this mail because you are subscribed to [email protected]
To unsubscribe, send a mail to: [email protected]

Reply via email to