Merge remote-tracking branch 'exim_github/pr/16'
[exim.git] / src / src / deliver.c
index 6bdfbd4da9894642cab94e3955ea82e39af5b8e4..87b54d88e900b9ab88263c62445560bb17b2c38d 100644 (file)
@@ -63,6 +63,10 @@ static address_item *addr_new = NULL;
 static address_item *addr_remote = NULL;
 static address_item *addr_route = NULL;
 static address_item *addr_succeed = NULL;
+#ifdef EXPERIMENTAL_DSN
+static address_item *addr_dsntmp = NULL;
+static address_item *addr_senddsn = NULL;
+#endif
 
 static FILE *message_log = NULL;
 static BOOL update_spool;
@@ -838,17 +842,17 @@ else
 
 /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
 
-if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
-    addr->message != NULL &&
-    ((addr->host_used != NULL) || (Ustrcmp(addr->transport->driver_name, "lmtp") == 0)))
+if (log_extra_selector & LX_smtp_confirmation &&
+    addr->message &&
+    (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
   {
   int i;
   uschar *p = big_buffer;
   uschar *ss = addr->message;
   *p++ = '\"';
-  for (i = 0; i < 100 && ss[i] != 0; i++)
+  for (i = 0; i < 256 && ss[i] != 0; i++)      /* limit logged amount */
     {
-    if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\';
+    if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */
     *p++ = ss[i];
     }
   *p++ = '\"';
@@ -859,16 +863,12 @@ if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
 /* Time on queue and actual time taken to deliver */
 
 if ((log_extra_selector & LX_queue_time) != 0)
-  {
   s = string_append(s, &size, &ptr, 2, US" QT=",
-    readconf_printtime(time(NULL) - received_time));
-  }
+    readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
 
 if ((log_extra_selector & LX_deliver_time) != 0)
-  {
   s = string_append(s, &size, &ptr, 2, US" DT=",
     readconf_printtime(addr->more_errno));
-  }
 
 /* string_cat() always leaves room for the terminator. Release the
 store we used to build the line after writing it. */
@@ -3015,7 +3015,7 @@ while (!done)
        (void) tls_import_cert(ptr, &addr->ourcert);
       break;
 
-      #ifdef EXPERIMENTAL_OCSP
+      #ifndef DISABLE_OCSP
       case '4':
       addr->ocsp = OCSP_NOT_REQ;
       if (*ptr)
@@ -3049,6 +3049,15 @@ while (!done)
     break;
 #endif
 
+    #ifdef EXPERIMENTAL_DSN
+    case 'D':
+    if (addr == NULL) break;
+    memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
+    ptr += sizeof(addr->dsn_aware);
+    DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware);
+    break;
+    #endif
+
     case 'A':
     if (addr == NULL)
       {
@@ -4154,7 +4163,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
          *ptr++ = 0;
         rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
        }
-      # ifdef EXPERIMENTAL_OCSP
+      #ifndef DISABLE_OCSP
       if (addr->ocsp > OCSP_NOT_REQ)
        {
        ptr = big_buffer;
@@ -4187,11 +4196,18 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
         rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
        }
 
-      #ifdef EXPERIMENTAL_PRDR
+      #ifndef DISABLE_PRDR
       if (addr->flags & af_prdr_used)
        rmt_dlv_checked_write(fd, "P", 1);
       #endif
 
+      #ifdef EXPERIMENTAL_DSN
+      big_buffer[0] = 'D';
+      memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware));
+      rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1);
+      DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware);
+      #endif
+
       /* Retry information: for most success cases this will be null. */
 
       for (r = addr->retries; r != NULL; r = r->next)
@@ -5342,6 +5358,14 @@ if (process_recipients != RECIP_IGNORE)
       if (r->pno >= 0)
         new->onetime_parent = recipients_list[r->pno].address;
 
+      #ifdef EXPERIMENTAL_DSN
+      /* If DSN support is enabled, set the dsn flags and the original receipt 
+         to be passed on to other DSN enabled MTAs */
+      new->dsn_flags = r->dsn_flags & rf_dsnflags;
+      new->dsn_orcpt = r->orcpt;
+      DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s  flags: %d\n", new->dsn_orcpt, new->dsn_flags);
+      #endif
+
       switch (process_recipients)
         {
         /* RECIP_DEFER is set when a system filter freezes a message. */
@@ -6286,6 +6310,12 @@ if (addr_remote != NULL)
     regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
   #endif
 
+  #ifdef EXPERIMENTAL_DSN
+  /* Set the regex to check for DSN support on remote MTA */
+  if (regex_DSN == NULL) regex_DSN  =
+    regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+  #endif
+
   /* Now sort the addresses if required, and do the deliveries. The yield of
   do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
   cannot be delivered in one transaction. */
@@ -6390,6 +6420,169 @@ prevents actual delivery. */
 
 else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed);
 
+#ifdef EXPERIMENTAL_DSN
+/* Send DSN for successful messages */
+addr_dsntmp = addr_succeed;
+addr_senddsn = NULL;
+
+while(addr_dsntmp != NULL)
+  {
+  DEBUG(D_deliver)
+    debug_printf("DSN: processing router : %s\n", addr_dsntmp->router->name);
+
+  DEBUG(D_deliver)
+    debug_printf("DSN: processing successful delivery address: %s\n", addr_dsntmp->address);
+
+  /* af_ignore_error not honored here. it's not an error */
+
+  DEBUG(D_deliver) debug_printf("DSN: Sender_address: %s\n", sender_address);
+  DEBUG(D_deliver) debug_printf("DSN: orcpt: %s  flags: %d\n", addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags);
+  DEBUG(D_deliver) debug_printf("DSN: envid: %s  ret: %d\n", dsn_envid, dsn_ret);
+  DEBUG(D_deliver) debug_printf("DSN: Final recipient: %s\n", addr_dsntmp->address);
+  DEBUG(D_deliver) debug_printf("DSN: Remote SMTP server supports DSN: %d\n", addr_dsntmp->dsn_aware);
+
+  /* send report if next hop not DSN aware or a router flagged "last DSN hop"
+     and a report was requested */
+  if (((addr_dsntmp->dsn_aware != dsn_support_yes) ||
+       ((addr_dsntmp->dsn_flags & rf_dsnlasthop) != 0))
+      &&
+      (((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) &&
+        ((addr_dsntmp->dsn_flags & rf_notify_success) != 0)))
+    {
+    /* copy and relink address_item and send report with all of them at once later */
+    address_item *addr_next;
+    addr_next = addr_senddsn;
+    addr_senddsn = store_get(sizeof(address_item));
+    memcpy(addr_senddsn, addr_dsntmp, sizeof(address_item));
+    addr_senddsn->next = addr_next;
+    }
+  else
+    {
+      DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); 
+    }
+
+  addr_dsntmp = addr_dsntmp->next;
+  }
+
+if (addr_senddsn != NULL)
+  {
+  pid_t pid;
+  int fd;
+
+  /* create exim process to send message */      
+  pid = child_open_exim(&fd);
+
+  DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid);
+     
+  if (pid < 0)  /* Creation of child failed */
+    {
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
+      "create child process to send failure message: %s", getpid(),
+      getppid(), strerror(errno));
+
+      DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
+
+    }    
+  else  /* Creation of child succeeded */
+    {
+    FILE *f = fdopen(fd, "wb");
+    /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
+    int topt = topt_add_return_path | topt_no_body;
+    uschar boundaryStr[64];
+     
+    DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address);
+  
+    /* build unique id for MIME boundary */
+    snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d",
+      time(NULL), rand());
+    DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr);
+  
+    if (errors_reply_to)
+      fprintf(f, "Reply-To: %s\n", errors_reply_to);
+    fprintf(f, "Auto-Submitted: auto-generated\n"
+       "From: Mail Delivery System <Mailer-Daemon@%s>\n"
+       "To: %s\n"
+       "Subject: Delivery Status Notification\n"
+       "Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n"
+       "MIME-Version: 1.0\n\n"
+
+       "--%s\n"
+       "Content-type: text/plain; charset=us-ascii\n\n"
+   
+       "This message was created automatically by mail delivery software.\n"
+       " ----- The following addresses had successful delivery notifications -----\n",
+      qualify_domain_sender, sender_address, boundaryStr, boundaryStr);
+
+    addr_dsntmp = addr_senddsn;
+    while(addr_dsntmp)
+      {
+      fprintf(f, "<%s> (relayed %s)\n\n",
+       addr_dsntmp->address,
+       (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1
+         ? "via non DSN router"
+         : addr_dsntmp->dsn_aware == dsn_support_no
+         ? "to non-DSN-aware mailer"
+         : "via non \"Remote SMTP\" router"
+       );
+      addr_dsntmp = addr_dsntmp->next;
+      }
+    fprintf(f, "--%s\n"
+       "Content-type: message/delivery-status\n\n"
+       "Reporting-MTA: dns; %s\n",
+      boundaryStr, smtp_active_hostname);
+
+    if (dsn_envid != NULL) {
+      /* must be decoded from xtext: see RFC 3461:6.3a */
+      uschar *xdec_envid;
+      if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+        fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
+      else
+        fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+      }
+    fputc('\n', f);
+
+    for (addr_dsntmp = addr_senddsn;
+        addr_dsntmp;
+        addr_dsntmp = addr_dsntmp->next)
+      {
+      if (addr_dsntmp->dsn_orcpt)
+        fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt);
+
+      fprintf(f, "Action: delivered\n"
+         "Final-Recipient: rfc822;%s\n"
+         "Status: 2.0.0\n",
+       addr_dsntmp->address);
+
+      if (addr_dsntmp->host_used && addr_dsntmp->host_used->name)
+        fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n",
+         addr_dsntmp->host_used->name);
+      else
+       fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n",
+         (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP");
+      fputc('\n', f);
+      }
+
+    fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr);
+           
+    fflush(f);
+    transport_filter_argv = NULL;   /* Just in case */
+    return_path = sender_address;   /* In case not previously set */
+           
+    /* Write the original email out */
+    transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
+    fflush(f);
+
+    fprintf(f,"\n");       
+    fprintf(f,"--%s--\n", boundaryStr);
+
+    fflush(f);
+    fclose(f);
+    rc = child_close(pid, 0);     /* Waits for child to close, no timeout */
+    }
+  }
+#endif /*EXPERIMENTAL_DSN*/
+
 /* If any addresses failed, we must send a message to somebody, unless
 af_ignore_error is set, in which case no action is taken. It is possible for
 several messages to get sent if there are addresses with different
@@ -6447,8 +6640,13 @@ while (addr_failed != NULL)
   it from the list, throw away any saved message file, log it, and
   mark the recipient done. */
 
-  if (testflag(addr_failed, af_ignore_error))
-    {
+  if (testflag(addr_failed, af_ignore_error)
+#ifdef EXPERIMENTAL_DSN
+      || (((addr_failed->dsn_flags & rf_dsnflags) != 0)
+         && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure))
+#endif
+     )
+  {
     addr = addr_failed;
     addr_failed = addr->next;
     if (addr->return_filename != NULL) Uunlink(addr->return_filename);
@@ -6500,6 +6698,12 @@ while (addr_failed != NULL)
       BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0;
       int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) *
         DELIVER_IN_BUFFER_SIZE;
+#ifdef EXPERIMENTAL_DSN
+      uschar boundaryStr[64];
+      uschar *dsnlimitmsg;
+      uschar *dsnnotifyhdr;
+      int topt;
+#endif
 
       DEBUG(D_deliver)
         debug_printf("sending error message to: %s\n", bounce_recipient);
@@ -6553,57 +6757,70 @@ while (addr_failed != NULL)
       moan_write_from(f);
       fprintf(f, "To: %s\n", bounce_recipient);
 
+#ifdef EXPERIMENTAL_DSN
+      /* generate boundary string and output MIME-Headers */
+      snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d",
+       time(NULL), rand());
+
+      fprintf(f, "Content-Type: multipart/report;"
+           " report-type=delivery-status; boundary=%s\n"
+         "MIME-Version: 1.0\n",
+       boundaryStr);
+#endif
+
       /* Open a template file if one is provided. Log failure to open, but
       carry on - default texts will be used. */
 
-      if (bounce_message_file != NULL)
-        {
-        emf = Ufopen(bounce_message_file, "rb");
-        if (emf == NULL)
+      if (bounce_message_file)
+        if (!(emf = Ufopen(bounce_message_file, "rb")))
           log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for error "
             "message texts: %s", bounce_message_file, strerror(errno));
-        }
 
       /* Quietly copy to configured additional addresses if required. */
 
-      bcc = moan_check_errorcopy(bounce_recipient);
-      if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc);
+      if ((bcc = moan_check_errorcopy(bounce_recipient)))
+       fprintf(f, "Bcc: %s\n", bcc);
 
       /* The texts for the message can be read from a template file; if there
       isn't one, or if it is too short, built-in texts are used. The first
       emf text is a Subject: and any other headers. */
 
-      emf_text = next_emf(emf, US"header");
-      if (emf_text != NULL) fprintf(f, "%s\n", emf_text); else
-        {
+      if ((emf_text = next_emf(emf, US"header")))
+       fprintf(f, "%s\n", emf_text);
+      else
         fprintf(f, "Subject: Mail delivery failed%s\n\n",
           to_sender? ": returning message to sender" : "");
-        }
 
-      emf_text = next_emf(emf, US"intro");
-      if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
+#ifdef EXPERIMENTAL_DSN
+      /* output human readable part as text/plain section */
+      fprintf(f, "--%s\n"
+         "Content-type: text/plain; charset=us-ascii\n\n",
+       boundaryStr);
+#endif
+
+      if ((emf_text = next_emf(emf, US"intro")))
+       fprintf(f, "%s", CS emf_text);
+      else
         {
         fprintf(f,
 /* This message has been reworded several times. It seems to be confusing to
 somebody, however it is worded. I have retreated to the original, simple
 wording. */
 "This message was created automatically by mail delivery software.\n");
-        if (bounce_message_text != NULL) fprintf(f, "%s", CS bounce_message_text);
+
+        if (bounce_message_text)
+         fprintf(f, "%s", CS bounce_message_text);
         if (to_sender)
-          {
           fprintf(f,
 "\nA message that you sent could not be delivered to one or more of its\n"
 "recipients. This is a permanent error. The following address(es) failed:\n");
-          }
         else
-          {
           fprintf(f,
 "\nA message sent by\n\n  <%s>\n\n"
 "could not be delivered to one or more of its recipients. The following\n"
 "address(es) failed:\n", sender_address);
-          }
         }
-      fprintf(f, "\n");
+      fputc('\n', f);
 
       /* Process the addresses, leaving them on the msgchain if they have a
       file name for a return message. (There has already been a check in
@@ -6640,7 +6857,7 @@ wording. */
           }
         }
 
-      fprintf(f, "\n");
+      fputc('\n', f);
 
       /* Get the next text, whether we need it or not, so as to be
       positioned for the one after. */
@@ -6654,11 +6871,13 @@ wording. */
       fd, and the return_filename field in the *last* one will be set (to the
       name of the file). */
 
-      if (msgchain != NULL)
+      if (msgchain)
         {
         address_item *nextaddr;
 
-        if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
+        if (emf_text)
+         fprintf(f, "%s", CS emf_text);
+       else
           fprintf(f,
             "The following text was generated during the delivery "
             "attempt%s:\n", (filecount > 1)? "s" : "");
@@ -6670,15 +6889,15 @@ wording. */
 
           /* List all the addresses that relate to this file */
 
-          fprintf(f, "\n");
-          while(addr != NULL)                   /* Insurance */
+         fputc('\n', f);
+          while(addr)                   /* Insurance */
             {
             print_address_information(addr, f, US"------ ",  US"\n       ",
               US" ------\n");
-            if (addr->return_filename != NULL) break;
+            if (addr->return_filename) break;
             addr = addr->next;
             }
-          fprintf(f, "\n");
+         fputc('\n', f);
 
           /* Now copy the file */
 
@@ -6701,8 +6920,38 @@ wording. */
           addr->next = handled_addr;
           handled_addr = topaddr;
           }
-        fprintf(f, "\n");
+       fputc('\n', f);
+        }
+
+#ifdef EXPERIMENTAL_DSN
+      /* output machine readable part */
+      fprintf(f, "--%s\n"
+         "Content-type: message/delivery-status\n\n"
+         "Reporting-MTA: dns; %s\n",
+       boundaryStr, smtp_active_hostname);
+
+      if (dsn_envid)
+       {
+        /* must be decoded from xtext: see RFC 3461:6.3a */
+        uschar *xdec_envid;
+        if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+          fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
+        else
+          fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+        }
+      fputc('\n', f);
+      for (addr = handled_addr; addr; addr = addr->next)
+        {
+        fprintf(f, "Action: failed\n"
+           "Final-Recipient: rfc822;%s\n"
+           "Status: 5.0.0\n",
+           addr->address);
+        if (addr->host_used && addr->host_used->name)
+          fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n",
+           addr->host_used->name, addr->basic_errno);
         }
+#endif
 
       /* Now copy the message, trying to give an intelligible comment if
       it is too long for it all to be copied. The limit isn't strictly
@@ -6711,12 +6960,15 @@ wording. */
 
       emf_text = next_emf(emf, US"copy");
 
+#ifndef EXPERIMENTAL_DSN
       if (bounce_return_message)
         {
         int topt = topt_add_return_path;
         if (!bounce_return_body) topt |= topt_no_body;
 
-        if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
+        if (emf_text)
+         fprintf(f, "%s", CS emf_text);
+       else
           {
           if (bounce_return_body) fprintf(f,
 "------ This is a copy of the message, including all the headers. ------\n");
@@ -6739,18 +6991,17 @@ wording. */
           {
           struct stat statbuf;
           if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
-            {
-            if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
-              {
+            if (emf_text)
+             fprintf(f, "%s", CS emf_text);
+           else
               fprintf(f,
 "------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
 "------ %d or so are included here.\n", statbuf.st_size, max);
-              }
-            }
           }
 
-        fprintf(f, "\n");
+       fputc('\n', f);
         fflush(f);
+
         transport_filter_argv = NULL;   /* Just in case */
         return_path = sender_address;   /* In case not previously set */
         transport_write_message(NULL, fileno(f), topt,
@@ -6759,12 +7010,71 @@ wording. */
 
       /* Write final text and close the template file if one is open */
 
-      if (emf != NULL)
+      if (emf)
         {
-        emf_text = next_emf(emf, US"final");
-        if (emf_text != NULL) fprintf(f, "%s", CS emf_text);
+        if ((emf_text = next_emf(emf, US"final")))
+         fprintf(f, "%s", CS emf_text);
         (void)fclose(emf);
         }
+#else
+      /* add message body
+         we ignore the intro text from template and add 
+         the text for bounce_return_size_limit at the end.
+  
+         bounce_return_message is ignored
+         in case RET= is defined we honor these values
+         otherwise bounce_return_body is honored.
+         
+         bounce_return_size_limit is always honored.
+      */
+  
+      fprintf(f, "\n--%s\n", boundaryStr);
+
+      dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
+      dsnnotifyhdr = NULL;
+      topt = topt_add_return_path;
+
+      /* RET=HDRS? top priority */
+      if (dsn_ret == dsn_ret_hdrs)
+        topt |= topt_no_body;
+      else
+        /* no full body return at all? */
+        if (!bounce_return_body)
+          {
+          topt |= topt_no_body;
+          /* add header if we overrule RET=FULL */
+          if (dsn_ret == dsn_ret_full)
+            dsnnotifyhdr = dsnlimitmsg;
+          }
+        /* size limited ... return headers only if limit reached */
+        else if (bounce_return_size_limit > 0)
+          {
+          struct stat statbuf;
+          if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
+            {
+              topt |= topt_no_body;
+              dsnnotifyhdr = dsnlimitmsg;
+            }
+          }
+  
+      if (topt & topt_no_body)
+        fprintf(f,"Content-type: text/rfc822-headers\n\n");
+      else
+        fprintf(f,"Content-type: message/rfc822\n\n");
+
+      fflush(f);
+      transport_filter_argv = NULL;   /* Just in case */
+      return_path = sender_address;   /* In case not previously set */
+      transport_write_message(NULL, fileno(f), topt,
+        0, dsnnotifyhdr, NULL, NULL, NULL, NULL, 0);
+      fflush(f);
+      /* we never add the final text. close the file */
+      if (emf)
+        (void)fclose(emf);
+      fprintf(f, "\n--%s--\n", boundaryStr);
+#endif /*EXPERIMENTAL_DSN*/
 
       /* Close the file, which should send an EOF to the child process
       that is receiving the message. Wait for it to finish. */
@@ -6873,7 +7183,7 @@ if (addr_defer == NULL)
 
   if ((log_extra_selector & LX_queue_time_overall) != 0)
     log_write(0, LOG_MAIN, "Completed QT=%s",
-      readconf_printtime(time(NULL) - received_time));
+      readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
   else
     log_write(0, LOG_MAIN, "Completed");
 
@@ -6996,6 +7306,10 @@ else if (addr_defer != (address_item *)(+1))
   it also defers). */
 
   if (!queue_2stage && delivery_attempted &&
+#ifdef EXPERIMENTAL_DSN
+      (((addr_defer->dsn_flags & rf_dsnflags) == 0) ||
+       (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay) &&
+#endif
       delay_warning[1] > 0 && sender_address[0] != 0 &&
        (delay_warning_condition == NULL ||
           expand_check_condition(delay_warning_condition,
@@ -7060,8 +7374,11 @@ else if (addr_defer != (address_item *)(+1))
         uschar *wmf_text;
         FILE *wmf = NULL;
         FILE *f = fdopen(fd, "wb");
+#ifdef EXPERIMENTAL_DSN
+       uschar boundaryStr[64];
+#endif
 
-        if (warn_message_file != NULL)
+        if (warn_message_file)
           {
           wmf = Ufopen(warn_message_file, "rb");
           if (wmf == NULL)
@@ -7074,21 +7391,39 @@ else if (addr_defer != (address_item *)(+1))
           string_sprintf("%d minutes", show_time/60):
           string_sprintf("%d hours", show_time/3600);
 
-        if (errors_reply_to != NULL)
+        if (errors_reply_to)
           fprintf(f, "Reply-To: %s\n", errors_reply_to);
         fprintf(f, "Auto-Submitted: auto-replied\n");
         moan_write_from(f);
         fprintf(f, "To: %s\n", recipients);
 
-        wmf_text = next_emf(wmf, US"header");
-        if (wmf_text != NULL)
+#ifdef EXPERIMENTAL_DSN
+        /* generated boundary string and output MIME-Headers */
+        snprintf(boundaryStr, sizeof(boundaryStr)-1,
+         TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
+
+        fprintf(f, "Content-Type: multipart/report;"
+           " report-type=delivery-status; boundary=%s\n"
+           "MIME-Version: 1.0\n",
+         boundaryStr);
+#endif
+
+        if ((wmf_text = next_emf(wmf, US"header")))
           fprintf(f, "%s\n", wmf_text);
         else
           fprintf(f, "Subject: Warning: message %s delayed %s\n\n",
             message_id, warnmsg_delay);
 
-        wmf_text = next_emf(wmf, US"intro");
-        if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text); else
+#ifdef EXPERIMENTAL_DSN
+        /* output human readable part as text/plain section */
+        fprintf(f, "--%s\n"
+           "Content-type: text/plain; charset=us-ascii\n\n",
+         boundaryStr);
+#endif
+
+        if ((wmf_text = next_emf(wmf, US"intro")))
+         fprintf(f, "%s", CS wmf_text);
+       else
           {
           fprintf(f,
 "This message was created automatically by mail delivery software.\n");
@@ -7098,49 +7433,52 @@ else if (addr_defer != (address_item *)(+1))
 "A message that you sent has not yet been delivered to one or more of its\n"
 "recipients after more than ");
 
-          else fprintf(f,
+          else
+           fprintf(f,
 "A message sent by\n\n  <%s>\n\n"
 "has not yet been delivered to one or more of its recipients after more than \n",
-          sender_address);
+             sender_address);
 
-          fprintf(f, "%s on the queue on %s.\n\n", warnmsg_delay,
-            primary_hostname);
-          fprintf(f, "The message identifier is:     %s\n", message_id);
+          fprintf(f, "%s on the queue on %s.\n\n"
+             "The message identifier is:     %s\n",
+           warnmsg_delay, primary_hostname, message_id);
 
           for (h = header_list; h != NULL; h = h->next)
-            {
             if (strncmpic(h->text, US"Subject:", 8) == 0)
               fprintf(f, "The subject of the message is: %s", h->text + 9);
             else if (strncmpic(h->text, US"Date:", 5) == 0)
               fprintf(f, "The date of the message is:    %s", h->text + 6);
-            }
-          fprintf(f, "\n");
+          fputc('\n', f);
 
           fprintf(f, "The address%s to which the message has not yet been "
             "delivered %s:\n",
-            (addr_defer->next == NULL)? "" : "es",
-            (addr_defer->next == NULL)? "is": "are");
+            !addr_defer->next ? "" : "es",
+            !addr_defer->next ? "is": "are");
           }
 
         /* List the addresses, with error information if allowed */
 
-        fprintf(f, "\n");
-        while (addr_defer != NULL)
+#ifdef EXPERIMENTAL_DSN
+        /* store addr_defer for machine readable part */
+        address_item *addr_dsndefer = addr_defer;
+#endif
+        fputc('\n', f);
+        while (addr_defer)
           {
           address_item *addr = addr_defer;
           addr_defer = addr->next;
           if (print_address_information(addr, f, US"  ", US"\n    ", US""))
             print_address_error(addr, f, US"Delay reason: ");
-          fprintf(f, "\n");
+          fputc('\n', f);
           }
-        fprintf(f, "\n");
+        fputc('\n', f);
 
         /* Final text */
 
-        if (wmf != NULL)
+        if (wmf)
           {
-          wmf_text = next_emf(wmf, US"final");
-          if (wmf_text != NULL) fprintf(f, "%s", CS wmf_text);
+          if ((wmf_text = next_emf(wmf, US"final")))
+           fprintf(f, "%s", CS wmf_text);
           (void)fclose(wmf);
           }
         else
@@ -7152,6 +7490,58 @@ else if (addr_defer != (address_item *)(+1))
 "and when that happens, the message will be returned to you.\n");
           }
 
+#ifdef EXPERIMENTAL_DSN
+        /* output machine readable part */
+        fprintf(f, "\n--%s\n"
+           "Content-type: message/delivery-status\n\n"
+           "Reporting-MTA: dns; %s\n",
+         boundaryStr,
+         smtp_active_hostname);
+
+        if (dsn_envid)
+         {
+          /* must be decoded from xtext: see RFC 3461:6.3a */
+          uschar *xdec_envid;
+          if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+            fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid);
+          else
+            fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+          }
+        fputc('\n', f);
+
+        while (addr_dsndefer)
+          {
+          if (addr_dsndefer->dsn_orcpt)
+            fprintf(f,"Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt);
+
+          fprintf(f,"Action: delayed\n");
+          fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsndefer->address);
+          fprintf(f,"Status: 4.0.0\n");
+          if (addr_dsndefer->host_used && addr_dsndefer->host_used->name)
+            fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", 
+                   addr_dsndefer->host_used->name, addr_dsndefer->basic_errno);
+          addr_dsndefer = addr_dsndefer->next;
+          }
+
+        fprintf(f, "\n--%s\n"
+           "Content-type: text/rfc822-headers\n\n",
+         boundaryStr);
+
+        fflush(f);
+        /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
+        int topt = topt_add_return_path | topt_no_body;
+        transport_filter_argv = NULL;   /* Just in case */
+        return_path = sender_address;   /* In case not previously set */
+        /* Write the original email out */
+        transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
+        fflush(f);
+
+        fprintf(f,"\n--%s--\n", boundaryStr);
+
+        fflush(f);
+#endif /*EXPERIMENTAL_DSN*/
+
         /* Close and wait for child process to complete, without a timeout.
         If there's an error, don't update the count. */