Move DSN support to mainline
[exim.git] / src / src / smtp_in.c
index b7e60bfab37faf4387cbef844c9fa50861db479c..d646fe0bdfc5aea41938197ab94bcec895192b1e 100644 (file)
@@ -121,6 +121,7 @@ static BOOL auth_advertised;
 #ifdef SUPPORT_TLS
 static BOOL tls_advertised;
 #endif
+static BOOL dsn_advertised;
 static BOOL esmtp;
 static BOOL helo_required = FALSE;
 static BOOL helo_verify = FALSE;
@@ -217,6 +218,7 @@ enum {
 #ifndef DISABLE_PRDR
   ENV_MAIL_OPT_PRDR,
 #endif
+  ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
   ENV_MAIL_OPT_NULL
   };
 typedef struct {
@@ -232,6 +234,8 @@ static env_mail_type_t env_mail_type_list[] = {
 #ifndef DISABLE_PRDR
     { US"PRDR",   ENV_MAIL_OPT_PRDR,   FALSE },
 #endif
+    { US"RET",    ENV_MAIL_OPT_RET,    TRUE },
+    { US"ENVID",  ENV_MAIL_OPT_ENVID,  TRUE },
     { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE }
   };
 
@@ -1307,7 +1311,8 @@ for (i = 0; i < smtp_ch_index; i++)
 if (s != NULL) s[ptr] = 0; else s = US"";
 log_write(0, LOG_MAIN, "no MAIL in SMTP connection from %s D=%s%s",
   host_and_ident(FALSE),
-  readconf_printtime(time(NULL) - smtp_connection_start), s);
+  readconf_printtime( (int) ((long)time(NULL) - (long)smtp_connection_start)),
+  s);
 }
 
 
@@ -1488,6 +1493,11 @@ sender_address_unrewritten = NULL;  /* Set only after verify rewrite */
 sender_verified_list = NULL;        /* No senders verified */
 memset(sender_address_cache, 0, sizeof(sender_address_cache));
 memset(sender_domain_cache, 0, sizeof(sender_domain_cache));
+
+/* Reset the DSN flags */
+dsn_ret = 0;
+dsn_envid = NULL;
+
 authenticated_sender = NULL;
 #ifdef EXPERIMENTAL_BRIGHTMAIL
 bmi_run = 0;
@@ -1837,6 +1847,7 @@ tls_in.sni = NULL;
 tls_in.ocsp = OCSP_NOT_REQ;
 tls_advertised = FALSE;
 #endif
+dsn_advertised = FALSE;
 
 /* Reset ACL connection variables */
 
@@ -3126,6 +3137,8 @@ while (done <= 0)
   int ptr, size, rc;
   int c, i;
   auth_instance *au;
+  uschar *orcpt = NULL;
+  int flags;
 
   switch(smtp_read_command(TRUE))
     {
@@ -3470,6 +3483,7 @@ while (done <= 0)
     #ifdef SUPPORT_TLS
     tls_advertised = FALSE;
     #endif
+    dsn_advertised = FALSE;
 
     smtp_code = US"250 ";        /* Default response code plus space*/
     if (user_msg == NULL)
@@ -3553,6 +3567,14 @@ while (done <= 0)
         s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11);
         }
 
+      /* Advertise DSN support if configured to do so. */
+      if (verify_check_host(&dsn_advertise_hosts) != FAIL) 
+        {
+        s = string_cat(s, &size, &ptr, smtp_code, 3);
+        s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6);
+        dsn_advertised = TRUE;
+        }
+
       /* Advertise ETRN if there's an ACL checking whether a host is
       permitted to issue it; a check is made when any host actually tries. */
 
@@ -3808,6 +3830,42 @@ while (done <= 0)
           arg_error = TRUE;
           break;
 
+        /* Handle the two DSN options, but only if configured to do so (which
+        will have caused "DSN" to be given in the EHLO response). The code itself
+        is included only if configured in at build time. */
+
+        case ENV_MAIL_OPT_RET:
+          if (dsn_advertised) {
+            /* Check if RET has already been set */
+            if (dsn_ret > 0) {
+              synprot_error(L_smtp_syntax_error, 501, NULL,
+                US"RET can be specified once only");
+              goto COMMAND_LOOP;
+            }
+            dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs :
+                    (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0;
+            DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret);
+            /* Check for invalid invalid value, and exit with error */
+            if (dsn_ret == 0) {
+              synprot_error(L_smtp_syntax_error, 501, NULL,
+                US"Value for RET is invalid");
+              goto COMMAND_LOOP;
+            }
+          }
+          break;
+        case ENV_MAIL_OPT_ENVID:
+          if (dsn_advertised) {
+            /* Check if the dsn envid has been already set */
+            if (dsn_envid != NULL) {
+              synprot_error(L_smtp_syntax_error, 501, NULL,
+                US"ENVID can be specified once only");
+              goto COMMAND_LOOP;
+            }
+            dsn_envid = string_copy(value);
+            DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid);
+          }
+          break;
+
         /* Handle the AUTH extension. If the value given is not "<>" and either
         the ACL says "yes" or there is no ACL but the sending host is
         authenticated, we set it up as the authenticated sender. However, if the
@@ -4085,6 +4143,89 @@ while (done <= 0)
       break;
       }
 
+    /* Set the DSN flags orcpt and dsn_flags from the session*/
+    orcpt = NULL;
+    flags = 0;
+
+    if (esmtp) for(;;)
+      {
+      uschar *name, *value;
+
+      if (!extract_option(&name, &value))
+        break;
+
+      if (dsn_advertised && strcmpic(name, US"ORCPT") == 0)
+        {
+        /* Check whether orcpt has been already set */
+        if (orcpt)
+         {
+          synprot_error(L_smtp_syntax_error, 501, NULL,
+            US"ORCPT can be specified once only");
+          goto COMMAND_LOOP;
+          }
+        orcpt = string_copy(value);
+        DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt);
+        }
+
+      else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0)
+        {
+        /* Check if the notify flags have been already set */
+        if (flags > 0)
+         {
+          synprot_error(L_smtp_syntax_error, 501, NULL,
+              US"NOTIFY can be specified once only");
+          goto COMMAND_LOOP;
+          }
+        if (strcmpic(value, US"NEVER") == 0)
+         flags |= rf_notify_never;
+       else
+          {
+          uschar *p = value;
+          while (*p != 0)
+            {
+            uschar *pp = p;
+            while (*pp != 0 && *pp != ',') pp++;
+           if (*pp == ',') *pp++ = 0;
+            if (strcmpic(p, US"SUCCESS") == 0)
+             {
+             DEBUG(D_receive) debug_printf("DSN: Setting notify success\n");
+             flags |= rf_notify_success;
+              }
+            else if (strcmpic(p, US"FAILURE") == 0)
+             {
+             DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n");
+             flags |= rf_notify_failure;
+              }
+            else if (strcmpic(p, US"DELAY") == 0)
+             {
+             DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n");
+             flags |= rf_notify_delay;
+              }
+            else
+             {
+              /* Catch any strange values */
+              synprot_error(L_smtp_syntax_error, 501, NULL,
+                US"Invalid value for NOTIFY parameter");
+              goto COMMAND_LOOP;
+              }
+            p = pp;
+            }
+            DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags);
+          }
+        }
+
+      /* Unknown option. Stick back the terminator characters and break
+      the loop. An error for a malformed address will occur. */
+
+      else
+        {
+        DEBUG(D_receive) debug_printf("Invalid RCPT option: %s : %s\n", name, value);
+        name[-1] = ' ';
+        value[-1] = '=';
+        break;
+        }
+      }
+
     /* Apply SMTP rewriting then extract the working address. Don't allow "<>"
     as a recipient address */
 
@@ -4198,6 +4339,18 @@ while (done <= 0)
       if (user_msg == NULL) smtp_printf("250 Accepted\r\n");
         else smtp_user_msg(US"250", user_msg);
       receive_add_recipient(recipient, -1);
+      /* Set the dsn flags in the recipients_list */
+      if (orcpt != NULL)
+        recipients_list[recipients_count-1].orcpt = orcpt;
+      else
+        recipients_list[recipients_count-1].orcpt = NULL;
+
+      if (flags != 0)
+        recipients_list[recipients_count-1].dsn_flags = flags;
+      else
+        recipients_list[recipients_count-1].dsn_flags = 0;
+      DEBUG(D_receive) debug_printf("DSN: orcpt: %s  flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags);
       }
 
     /* The recipient was discarded */