Rename substructure for ease of debugging
[exim.git] / src / src / deliver.c
index d33cf799ab652a3a13b18c3133f4cbf84d18fa01..e25de7eb82653cc9c106317166bf60d2d7733550 100644 (file)
@@ -63,10 +63,8 @@ static address_item *addr_new = NULL;
 static address_item *addr_remote = NULL;
 static address_item *addr_route = NULL;
 static address_item *addr_succeed = 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;
 static address_item *addr_dsntmp = NULL;
 static address_item *addr_senddsn = NULL;
-#endif
 
 static FILE *message_log = NULL;
 static BOOL update_spool;
 
 static FILE *message_log = NULL;
 static BOOL update_spool;
@@ -129,7 +127,7 @@ deliver_set_expansions(address_item *addr)
 {
 if (addr == NULL)
   {
 {
 if (addr == NULL)
   {
-  uschar ***p = address_expansions;
+  const uschar ***p = address_expansions;
   while (*p != NULL) **p++ = NULL;
   return;
   }
   while (*p != NULL) **p++ = NULL;
   return;
   }
@@ -151,9 +149,9 @@ else
   }
 
 deliver_recipients = addr;
   }
 
 deliver_recipients = addr;
-deliver_address_data = addr->p.address_data;
-deliver_domain_data = addr->p.domain_data;
-deliver_localpart_data = addr->p.localpart_data;
+deliver_address_data = addr->prop.address_data;
+deliver_domain_data = addr->prop.domain_data;
+deliver_localpart_data = addr->prop.localpart_data;
 
 /* These may be unset for multiple addresses */
 
 
 /* These may be unset for multiple addresses */
 
@@ -720,7 +718,7 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
 
 #ifdef EXPERIMENTAL_EVENT
 uschar *
 
 #ifdef EXPERIMENTAL_EVENT
 uschar *
-event_raise(uschar * action, uschar * event, uschar * ev_data)
+event_raise(uschar * action, const uschar * event, uschar * ev_data)
 {
 uschar * s;
 if (action)
 {
 uschar * s;
 if (action)
@@ -754,11 +752,11 @@ return NULL;
 }
 
 static void
 }
 
 static void
-msg_event_raise(uschar * event, address_item * addr)
+msg_event_raise(const uschar * event, const address_item * addr)
 {
 {
-uschar * save_domain = deliver_domain;
+const uschar * save_domain = deliver_domain;
 uschar * save_local =  deliver_localpart;
 uschar * save_local =  deliver_localpart;
-uschar * save_host =   deliver_host;
+const uschar * save_host = deliver_host;
 
 if (!addr->transport)
   return;
 
 if (!addr->transport)
   return;
@@ -819,12 +817,16 @@ else
   s = string_append(s, &size, &ptr, 2, US"> ", log_address);
   }
 
   s = string_append(s, &size, &ptr, 2, US"> ", log_address);
   }
 
+if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
+  s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
+  /* for the port:  string_sprintf("%d", sending_port) */
+
 if ((log_extra_selector & LX_sender_on_delivery) != 0  ||  msg)
   s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
 
 #ifdef EXPERIMENTAL_SRS
 if ((log_extra_selector & LX_sender_on_delivery) != 0  ||  msg)
   s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
 
 #ifdef EXPERIMENTAL_SRS
-if(addr->p.srs_sender)
-  s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->p.srs_sender, US">");
+if(addr->prop.srs_sender)
+  s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->prop.srs_sender, US">");
 #endif
 
 /* You might think that the return path must always be set for a successful
 #endif
 
 /* You might think that the return path must always be set for a successful
@@ -909,11 +911,12 @@ if (log_extra_selector & LX_smtp_confirmation &&
     addr->message &&
     (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
   {
     addr->message &&
     (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
   {
-  int i;
+  unsigned i;
+  unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024;
   uschar *p = big_buffer;
   uschar *ss = addr->message;
   *p++ = '\"';
   uschar *p = big_buffer;
   uschar *ss = addr->message;
   *p++ = '\"';
-  for (i = 0; i < 256 && ss[i] != 0; i++)      /* limit logged amount */
+  for (i = 0; i < lim && ss[i] != 0; i++)      /* limit logged amount */
     {
     if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */
     *p++ = ss[i];
     {
     if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */
     *p++ = ss[i];
@@ -1016,7 +1019,10 @@ malformed, it won't ever have gone near LDAP.) */
 
 if (addr->message != NULL)
   {
 
 if (addr->message != NULL)
   {
-  addr->message = string_printing(addr->message);
+  const uschar * s = string_printing(addr->message);
+  if (s != addr->message)
+    addr->message = US s;
+    /* deconst cast ok as string_printing known to have alloc'n'copied */
   if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) &&
       (Ustrstr(addr->message, "mysql") != NULL ||
        Ustrstr(addr->message, "pgsql") != NULL ||
   if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) &&
       (Ustrstr(addr->message, "mysql") != NULL ||
        Ustrstr(addr->message, "pgsql") != NULL ||
@@ -1073,11 +1079,12 @@ if (addr->return_file >= 0 && addr->return_filename != NULL)
         if (s != NULL)
           {
           uschar *p = big_buffer + Ustrlen(big_buffer);
         if (s != NULL)
           {
           uschar *p = big_buffer + Ustrlen(big_buffer);
+         const uschar * sp;
           while (p > big_buffer && isspace(p[-1])) p--;
           *p = 0;
           while (p > big_buffer && isspace(p[-1])) p--;
           *p = 0;
-          s = string_printing(big_buffer);
+          sp = string_printing(big_buffer);
           log_write(0, LOG_MAIN, "<%s>: %s transport output: %s",
           log_write(0, LOG_MAIN, "<%s>: %s transport output: %s",
-            addr->address, tb->name, s);
+            addr->address, tb->name, sp);
           }
         (void)fclose(f);
         }
           }
         (void)fclose(f);
         }
@@ -1086,7 +1093,7 @@ if (addr->return_file >= 0 && addr->return_filename != NULL)
     /* Handle returning options, but only if there is an address to return
     the text to. */
 
     /* Handle returning options, but only if there is an address to return
     the text to. */
 
-    if (sender_address[0] != 0 || addr->p.errors_address != NULL)
+    if (sender_address[0] != 0 || addr->prop.errors_address != NULL)
       {
       if (tb->return_output)
         {
       {
       if (tb->return_output)
         {
@@ -1303,7 +1310,7 @@ else
 
   if (!testflag(addr, af_ignore_error) &&
       (addr->special_action == SPECIAL_FREEZE ||
 
   if (!testflag(addr, af_ignore_error) &&
       (addr->special_action == SPECIAL_FREEZE ||
-        (sender_address[0] == 0 && addr->p.errors_address == NULL)
+        (sender_address[0] == 0 && addr->prop.errors_address == NULL)
       ))
     {
     frozen_info = (addr->special_action == SPECIAL_FREEZE)? US"" :
       ))
     {
     frozen_info = (addr->special_action == SPECIAL_FREEZE)? US"" :
@@ -1803,11 +1810,11 @@ transport_instance *tp = addr->transport;
 /* Set up the return path from the errors or sender address. If the transport
 has its own return path setting, expand it and replace the existing value. */
 
 /* Set up the return path from the errors or sender address. If the transport
 has its own return path setting, expand it and replace the existing value. */
 
-if(addr->p.errors_address != NULL)
-  return_path = addr->p.errors_address;
+if(addr->prop.errors_address != NULL)
+  return_path = addr->prop.errors_address;
 #ifdef EXPERIMENTAL_SRS
 #ifdef EXPERIMENTAL_SRS
-else if(addr->p.srs_sender != NULL)
-  return_path = addr->p.srs_sender;
+else if(addr->prop.srs_sender != NULL)
+  return_path = addr->prop.srs_sender;
 #endif
 else
   return_path = sender_address;
 #endif
 else
   return_path = sender_address;
@@ -2419,9 +2426,9 @@ while (addr_local != NULL)
         (addr->flags & (af_pfr|af_file)) == (next->flags & (af_pfr|af_file)) &&
         (!uses_lp  || Ustrcmp(next->local_part, addr->local_part) == 0) &&
         (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) &&
         (addr->flags & (af_pfr|af_file)) == (next->flags & (af_pfr|af_file)) &&
         (!uses_lp  || Ustrcmp(next->local_part, addr->local_part) == 0) &&
         (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) &&
-        same_strings(next->p.errors_address, addr->p.errors_address) &&
-        same_headers(next->p.extra_headers, addr->p.extra_headers) &&
-        same_strings(next->p.remove_headers, addr->p.remove_headers) &&
+        same_strings(next->prop.errors_address, addr->prop.errors_address) &&
+        same_headers(next->prop.extra_headers, addr->prop.extra_headers) &&
+        same_strings(next->prop.remove_headers, addr->prop.remove_headers) &&
         same_ugid(tp, addr, next) &&
         ((addr->host_list == NULL && next->host_list == NULL) ||
          (addr->host_list != NULL && next->host_list != NULL &&
         same_ugid(tp, addr, next) &&
         ((addr->host_list == NULL && next->host_list == NULL) ||
          (addr->host_list != NULL && next->host_list != NULL &&
@@ -2777,7 +2784,7 @@ sort_remote_deliveries(void)
 {
 int sep = 0;
 address_item **aptr = &addr_remote;
 {
 int sep = 0;
 address_item **aptr = &addr_remote;
-uschar *listptr = remote_sort_domains;
+const uschar *listptr = remote_sort_domains;
 uschar *pattern;
 uschar patbuf[256];
 
 uschar *pattern;
 uschar patbuf[256];
 
@@ -2792,7 +2799,7 @@ while (*aptr != NULL &&
     {
     address_item **next;
     deliver_domain = (*aptr)->domain;   /* set $domain */
     {
     address_item **next;
     deliver_domain = (*aptr)->domain;   /* set $domain */
-    if (match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1,
+    if (match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1,
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
       {
       aptr = &((*aptr)->next);
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
       {
       aptr = &((*aptr)->next);
@@ -2802,7 +2809,7 @@ while (*aptr != NULL &&
     next = &((*aptr)->next);
     while (*next != NULL &&
            (deliver_domain = (*next)->domain,  /* Set $domain */
     next = &((*aptr)->next);
     while (*next != NULL &&
            (deliver_domain = (*next)->domain,  /* Set $domain */
-            match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1,
+            match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1,
               &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK)
       next = &((*next)->next);
 
               &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK)
       next = &((*next)->next);
 
@@ -3166,14 +3173,12 @@ while (!done)
     break;
 #endif
 
     break;
 #endif
 
-#ifdef EXPERIMENTAL_DSN
     case 'D':
     if (addr == NULL) goto ADDR_MISMATCH;
     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;
     case 'D':
     if (addr == NULL) goto ADDR_MISMATCH;
     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)
 
     case 'A':
     if (addr == NULL)
@@ -3223,6 +3228,14 @@ while (!done)
     addr = addr->next;
     break;
 
     addr = addr->next;
     break;
 
+    /* Local interface address/port */
+    case 'I':
+    if (*ptr) sending_ip_address = string_copy(ptr);
+    while (*ptr++) ;
+    if (*ptr) sending_port = atoi(CS ptr);
+    while (*ptr++) ;
+    break;
+
     /* Z marks the logical end of the data. It is followed by '0' if
     continue_transport was NULL at the end of transporting, otherwise '1'.
     We need to know when it becomes NULL during a delivery down a passed SMTP
     /* Z marks the logical end of the data. It is followed by '0' if
     continue_transport was NULL at the end of transporting, otherwise '1'.
     We need to know when it becomes NULL during a delivery down a passed SMTP
@@ -3844,9 +3857,20 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
     }
 
   /* Get the flag which specifies whether the transport can handle different
     }
 
   /* Get the flag which specifies whether the transport can handle different
-  domains that nevertheless resolve to the same set of hosts. */
-
-  multi_domain = tp->multi_domain;
+  domains that nevertheless resolve to the same set of hosts. If it needs
+  expanding, get variables set: $address_data, $domain_data, $localpart_data,
+  $host, $host_address, $host_port. */
+  if (tp->expand_multi_domain)
+    deliver_set_expansions(addr);
+
+  if (exp_bool(addr, US"transport", tp->name, D_transport,
+               US"multi_domain", tp->multi_domain, tp->expand_multi_domain,
+               &multi_domain) != OK)
+    {
+    deliver_set_expansions(NULL);
+    remote_post_process(addr, LOG_MAIN|LOG_PANIC, addr->message, fallback);
+    continue;
+    }
 
   /* Get the maximum it can handle in one envelope, with zero meaning
   unlimited, which is forced for the MUA wrapper case. */
 
   /* Get the maximum it can handle in one envelope, with zero meaning
   unlimited, which is forced for the MUA wrapper case. */
@@ -3915,26 +3939,35 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
   entirely different domains. The host list pointers can be NULL in the case
   where the hosts are defined in the transport. There is also a configured
   maximum limit of addresses that can be handled at once (see comments above
   entirely different domains. The host list pointers can be NULL in the case
   where the hosts are defined in the transport. There is also a configured
   maximum limit of addresses that can be handled at once (see comments above
-  for how it is computed). */
+  for how it is computed).
+  If the transport does not handle multiple domains, enforce that also,
+  and if it might need a per-address check for this, re-evaluate it.
+  */
 
   while ((next = *anchor) != NULL && address_count < address_count_max)
     {
 
   while ((next = *anchor) != NULL && address_count < address_count_max)
     {
-    if ((multi_domain || Ustrcmp(next->domain, addr->domain) == 0)
-        &&
-        tp == next->transport
-        &&
-        same_hosts(next->host_list, addr->host_list)
-        &&
-        same_strings(next->p.errors_address, addr->p.errors_address)
-        &&
-        same_headers(next->p.extra_headers, addr->p.extra_headers)
-        &&
-        same_ugid(tp, next, addr)
-        &&
-        (next->p.remove_headers == addr->p.remove_headers ||
-          (next->p.remove_headers != NULL &&
-           addr->p.remove_headers != NULL &&
-           Ustrcmp(next->p.remove_headers, addr->p.remove_headers) == 0)))
+    BOOL md;
+    if (  (multi_domain || Ustrcmp(next->domain, addr->domain) == 0)
+       && tp == next->transport
+       && same_hosts(next->host_list, addr->host_list)
+       && same_strings(next->prop.errors_address, addr->prop.errors_address)
+       && same_headers(next->prop.extra_headers, addr->prop.extra_headers)
+       && same_ugid(tp, next, addr)
+       && (  next->prop.remove_headers == addr->prop.remove_headers
+         || (  next->prop.remove_headers
+            && addr->prop.remove_headers
+            && Ustrcmp(next->prop.remove_headers, addr->prop.remove_headers) == 0
+         )  )
+       && (  !multi_domain
+         || (  (
+               !tp->expand_multi_domain || (deliver_set_expansions(next), 1),
+               exp_bool(addr,
+                   US"transport", next->transport->name, D_transport,
+                   US"multi_domain", next->transport->multi_domain,
+                   next->transport->expand_multi_domain, &md) == OK
+               )
+            && md
+       )  )  )
       {
       *anchor = next->next;
       next->next = NULL;
       {
       *anchor = next->next;
       next->next = NULL;
@@ -3944,6 +3977,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
       address_count++;
       }
     else anchor = &(next->next);
       address_count++;
       }
     else anchor = &(next->next);
+    deliver_set_expansions(NULL);
     }
 
   /* If we are acting as an MUA wrapper, all addresses must go in a single
     }
 
   /* If we are acting as an MUA wrapper, all addresses must go in a single
@@ -3966,11 +4000,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
   /* Compute the return path, expanding a new one if required. The old one
   must be set first, as it might be referred to in the expansion. */
 
   /* Compute the return path, expanding a new one if required. The old one
   must be set first, as it might be referred to in the expansion. */
 
-  if(addr->p.errors_address != NULL)
-    return_path = addr->p.errors_address;
+  if(addr->prop.errors_address != NULL)
+    return_path = addr->prop.errors_address;
 #ifdef EXPERIMENTAL_SRS
 #ifdef EXPERIMENTAL_SRS
-  else if(addr->p.srs_sender != NULL)
-    return_path = addr->p.srs_sender;
+  else if(addr->prop.srs_sender != NULL)
+    return_path = addr->prop.srs_sender;
 #endif
   else
     return_path = sender_address;
 #endif
   else
     return_path = sender_address;
@@ -4349,11 +4383,9 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
        rmt_dlv_checked_write(fd, 'P', '0', NULL, 0);
 #endif
 
        rmt_dlv_checked_write(fd, 'P', '0', NULL, 0);
 #endif
 
-#ifdef EXPERIMENTAL_DSN
       memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
       rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
       DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware);
       memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
       rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
       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. */
 
 
       /* Retry information: for most success cases this will be null. */
 
@@ -4415,6 +4447,18 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
       rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer);
       }
 
       rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer);
       }
 
+    /* Local interface address/port */
+    if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
+      {
+      uschar * ptr = big_buffer;
+      sprintf(CS ptr, "%.128s", sending_ip_address);
+      while(*ptr++);
+      sprintf(CS ptr, "%d", sending_port);
+      while(*ptr++);
+
+      rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer);
+      }
+
     /* Add termination flag, close the pipe, and that's it. The character
     after 'Z' indicates whether continue_transport is now NULL or not.
     A change from non-NULL to NULL indicates a problem with a continuing
     /* Add termination flag, close the pipe, and that's it. The character
     after 'Z' indicates whether continue_transport is now NULL or not.
     A change from non-NULL to NULL indicates a problem with a continuing
@@ -4541,7 +4585,7 @@ if (percent_hack_domains != NULL)
 
   deliver_domain = addr->domain;  /* set $domain */
 
 
   deliver_domain = addr->domain;  /* set $domain */
 
-  while ((rc = match_isinlist(deliver_domain, &percent_hack_domains, 0,
+  while ((rc = match_isinlist(deliver_domain, (const uschar **)&percent_hack_domains, 0,
            &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
              == OK &&
          (t = Ustrrchr(local_part, '%')) != NULL)
            &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
              == OK &&
          (t = Ustrrchr(local_part, '%')) != NULL)
@@ -4793,7 +4837,6 @@ while (*s != 0)
 }
 
 
 }
 
 
-#ifdef EXPERIMENTAL_DSN
 /***********************************************************
 *         Print Diagnostic-Code for an address             *
 ************************************************************/
 /***********************************************************
 *         Print Diagnostic-Code for an address             *
 ************************************************************/
@@ -4842,7 +4885,6 @@ while (*s)
 
 fputc('\n', f);
 }
 
 fputc('\n', f);
 }
-#endif  /* EXPERIMENTAL_DSN */
 
 
 /*************************************************
 
 
 /*************************************************
@@ -5551,18 +5593,20 @@ if (process_recipients != RECIP_IGNORE)
       {
       recipient_item *r = recipients_list + i;
       address_item *new = deliver_make_addr(r->address, FALSE);
       {
       recipient_item *r = recipients_list + i;
       address_item *new = deliver_make_addr(r->address, FALSE);
-      new->p.errors_address = r->errors_to;
+      new->prop.errors_address = r->errors_to;
+#ifdef EXPERIMENTAL_INTERNATIONAL
+      new->prop.utf8 = message_smtputf8;
+      DEBUG(D_deliver) debug_printf("utf8: %c\n", message_smtputf8 ? 'T':'F');
+#endif
 
       if (r->pno >= 0)
         new->onetime_parent = recipients_list[r->pno].address;
 
 
       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);
       /* 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)
         {
 
       switch (process_recipients)
         {
@@ -5637,7 +5681,7 @@ if (process_recipients != RECIP_IGNORE)
       if (process_recipients != RECIP_ACCEPT)
        {
        uschar * save_local =  deliver_localpart;
       if (process_recipients != RECIP_ACCEPT)
        {
        uschar * save_local =  deliver_localpart;
-       uschar * save_domain = deliver_domain;
+       const uschar * save_domain = deliver_domain;
 
        deliver_localpart = expand_string(
                      string_sprintf("${local_part:%s}", new->address));
 
        deliver_localpart = expand_string(
                      string_sprintf("${local_part:%s}", new->address));
@@ -5902,7 +5946,7 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
 
     deliver_domain = addr->domain;  /* set $domain */
     if (!forced && hold_domains != NULL &&
 
     deliver_domain = addr->domain;  /* set $domain */
     if (!forced && hold_domains != NULL &&
-         (rc = match_isinlist(addr->domain, &hold_domains, 0,
+         (rc = match_isinlist(addr->domain, (const uschar **)&hold_domains, 0,
            &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE,
            NULL)) != FAIL)
       {
            &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE,
            NULL)) != FAIL)
       {
@@ -6109,7 +6153,7 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
       addr_route = addr->next;
 
       deliver_domain = addr->domain;  /* set $domain */
       addr_route = addr->next;
 
       deliver_domain = addr->domain;  /* set $domain */
-      if ((rc = match_isinlist(addr->domain, &queue_domains, 0,
+      if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0,
             &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
               != OK)
         {
             &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
               != OK)
         {
@@ -6142,15 +6186,15 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
     {
     int rc;
     address_item *addr = addr_route;
     {
     int rc;
     address_item *addr = addr_route;
-    uschar *old_domain = addr->domain;
+    const uschar *old_domain = addr->domain;
     uschar *old_unique = addr->unique;
     addr_route = addr->next;
     addr->next = NULL;
 
     /* Just in case some router parameter refers to it. */
 
     uschar *old_unique = addr->unique;
     addr_route = addr->next;
     addr->next = NULL;
 
     /* Just in case some router parameter refers to it. */
 
-    return_path = (addr->p.errors_address != NULL)?
-      addr->p.errors_address : sender_address;
+    return_path = (addr->prop.errors_address != NULL)?
+      addr->prop.errors_address : sender_address;
 
     /* If a router defers an address, add a retry item. Whether or not to
     use the local part in the key is a property of the router. */
 
     /* If a router defers an address, add a retry item. Whether or not to
     use the local part in the key is a property of the router. */
@@ -6220,8 +6264,8 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
 
     if (addr_remote == addr &&
         addr->router->same_domain_copy_routing &&
 
     if (addr_remote == addr &&
         addr->router->same_domain_copy_routing &&
-        addr->p.extra_headers == NULL &&
-        addr->p.remove_headers == NULL &&
+        addr->prop.extra_headers == NULL &&
+        addr->prop.remove_headers == NULL &&
         old_domain == addr->domain)
       {
       address_item **chain = &addr_route;
         old_domain == addr->domain)
       {
       address_item **chain = &addr_route;
@@ -6248,7 +6292,7 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
         addr2->transport = addr->transport;
         addr2->host_list = addr->host_list;
         addr2->fallback_hosts = addr->fallback_hosts;
         addr2->transport = addr->transport;
         addr2->host_list = addr->host_list;
         addr2->fallback_hosts = addr->fallback_hosts;
-        addr2->p.errors_address = addr->p.errors_address;
+        addr2->prop.errors_address = addr->prop.errors_address;
         copyflag(addr2, addr, af_hide_child | af_local_host_removed);
 
         DEBUG(D_deliver|D_route)
         copyflag(addr2, addr, af_hide_child | af_local_host_removed);
 
         DEBUG(D_deliver|D_route)
@@ -6507,31 +6551,7 @@ if (addr_remote != NULL)
   /* Precompile some regex that are used to recognize parameters in response
   to an EHLO command, if they aren't already compiled. */
 
   /* Precompile some regex that are used to recognize parameters in response
   to an EHLO command, if they aren't already compiled. */
 
-  if (regex_PIPELINING == NULL) regex_PIPELINING =
-    regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
-
-  if (regex_SIZE == NULL) regex_SIZE =
-    regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
-
-  if (regex_AUTH == NULL) regex_AUTH =
-    regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
-      FALSE, TRUE);
-
-#ifdef SUPPORT_TLS
-  if (regex_STARTTLS == NULL) regex_STARTTLS =
-    regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-#ifndef DISABLE_PRDR
-  if (regex_PRDR == NULL) regex_PRDR =
-    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
+  deliver_init();
 
   /* 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
 
   /* 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
@@ -6640,34 +6660,40 @@ prevents actual delivery. */
 
 else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed);
 
 
 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;
 
 /* Send DSN for successful messages */
 addr_dsntmp = addr_succeed;
 addr_senddsn = NULL;
 
-while(addr_dsntmp != NULL)
+while(addr_dsntmp)
   {
   {
-  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 */
   /* 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);
+  DEBUG(D_deliver)
+    {
+    debug_printf("DSN: processing router : %s\n"
+      "DSN: processing successful delivery address: %s\n"
+      "DSN: Sender_address: %s\n"
+      "DSN: orcpt: %s  flags: %d\n"
+      "DSN: envid: %s  ret: %d\n"
+      "DSN: Final recipient: %s\n"
+      "DSN: Remote SMTP server supports DSN: %d\n",
+      addr_dsntmp->router->name,
+      addr_dsntmp->address,
+      sender_address,
+      addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags,
+      dsn_envid, dsn_ret,
+      addr_dsntmp->address,
+      addr_dsntmp->dsn_aware
+      );
+    }
 
   /* send report if next hop not DSN aware or a router flagged "last DSN hop"
      and a report was requested */
 
   /* 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)))
+  if (  (  addr_dsntmp->dsn_aware != dsn_support_yes
+       || addr_dsntmp->dsn_flags & rf_dsnlasthop
+        )
+     && addr_dsntmp->dsn_flags & rf_dsnflags
+     && addr_dsntmp->dsn_flags & rf_notify_success
+     )
     {
     /* copy and relink address_item and send report with all of them at once later */
     address_item *addr_next;
     {
     /* copy and relink address_item and send report with all of them at once later */
     address_item *addr_next;
@@ -6677,14 +6703,12 @@ while(addr_dsntmp != NULL)
     addr_senddsn->next = addr_next;
     }
   else
     addr_senddsn->next = addr_next;
     }
   else
-    {
-      DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); 
-    }
+    DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); 
 
   addr_dsntmp = addr_dsntmp->next;
   }
 
 
   addr_dsntmp = addr_dsntmp->next;
   }
 
-if (addr_senddsn != NULL)
+if (addr_senddsn)
   {
   pid_t pid;
   int fd;
   {
   pid_t pid;
   int fd;
@@ -6700,8 +6724,7 @@ if (addr_senddsn != NULL)
       "create child process to send failure message: %s", getpid(),
       getppid(), strerror(errno));
 
       "create child process to send failure message: %s", getpid(),
       getppid(), strerror(errno));
 
-      DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
-
+    DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
     }    
   else  /* Creation of child succeeded */
     {
     }    
   else  /* Creation of child succeeded */
     {
@@ -6710,7 +6733,8 @@ if (addr_senddsn != NULL)
     int topt = topt_add_return_path | topt_no_body;
     uschar * bound;
      
     int topt = topt_add_return_path | topt_no_body;
     uschar * bound;
      
-    DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address);
+    DEBUG(D_deliver)
+      debug_printf("sending error message to: %s\n", sender_address);
   
     /* build unique id for MIME boundary */
     bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
   
     /* build unique id for MIME boundary */
     bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
@@ -6733,9 +6757,8 @@ if (addr_senddsn != NULL)
        " ----- The following addresses had successful delivery notifications -----\n",
       qualify_domain_sender, sender_address, bound, bound);
 
        " ----- The following addresses had successful delivery notifications -----\n",
       qualify_domain_sender, sender_address, bound, bound);
 
-    addr_dsntmp = addr_senddsn;
-    while(addr_dsntmp)
-      {
+    for (addr_dsntmp = addr_senddsn; addr_dsntmp;
+        addr_dsntmp = addr_dsntmp->next)
       fprintf(f, "<%s> (relayed %s)\n\n",
        addr_dsntmp->address,
        (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1
       fprintf(f, "<%s> (relayed %s)\n\n",
        addr_dsntmp->address,
        (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1
@@ -6744,15 +6767,14 @@ if (addr_senddsn != NULL)
          ? "to non-DSN-aware mailer"
          : "via non \"Remote SMTP\" router"
        );
          ? "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",
       bound, smtp_active_hostname);
 
     fprintf(f, "--%s\n"
        "Content-type: message/delivery-status\n\n"
        "Reporting-MTA: dns; %s\n",
       bound, smtp_active_hostname);
 
-    if (dsn_envid != NULL) {
-      /* must be decoded from xtext: see RFC 3461:6.3a */
+    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);
       uschar *xdec_envid;
       if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
         fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
@@ -6774,12 +6796,11 @@ if (addr_senddsn != NULL)
        addr_dsntmp->address);
 
       if (addr_dsntmp->host_used && addr_dsntmp->host_used->name)
        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",
+        fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n",
          addr_dsntmp->host_used->name);
       else
          addr_dsntmp->host_used->name);
       else
-       fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n",
+       fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n",
          (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP");
          (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP");
-      fputc('\n', f);
       }
 
     fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound);
       }
 
     fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound);
@@ -6799,7 +6820,6 @@ if (addr_senddsn != NULL)
     rc = child_close(pid, 0);     /* Waits for child to close, no timeout */
     }
   }
     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
 
 /* 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
@@ -6843,10 +6863,10 @@ while (addr_failed != NULL)
   If neither of these cases obtains, something has gone wrong. Log the
   incident, but then ignore the error. */
 
   If neither of these cases obtains, something has gone wrong. Log the
   incident, but then ignore the error. */
 
-  if (sender_address[0] == 0 && addr_failed->p.errors_address == NULL)
+  if (sender_address[0] == 0 && addr_failed->prop.errors_address == NULL)
     {
     {
-    if (!testflag(addr_failed, af_retry_timedout) &&
-        !testflag(addr_failed, af_ignore_error))
+    if (  !testflag(addr_failed, af_retry_timedout)
+       && !testflag(addr_failed, af_ignore_error))
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "internal error: bounce message "
         "failure is neither frozen nor ignored (it's been ignored)");
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "internal error: bounce message "
         "failure is neither frozen nor ignored (it's been ignored)");
@@ -6858,22 +6878,20 @@ while (addr_failed != NULL)
   it from the list, throw away any saved message file, log it, and
   mark the recipient done. */
 
   it from the list, throw away any saved message file, log it, and
   mark the recipient done. */
 
-  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
+  if (  testflag(addr_failed, af_ignore_error)
+     || (  addr_failed->dsn_flags & rf_dsnflags
+        && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure)
      )
      )
-  {
+    {
     addr = addr_failed;
     addr_failed = addr->next;
     if (addr->return_filename != NULL) Uunlink(addr->return_filename);
 
     log_write(0, LOG_MAIN, "%s%s%s%s: error ignored",
       addr->address,
     addr = addr_failed;
     addr_failed = addr->next;
     if (addr->return_filename != NULL) Uunlink(addr->return_filename);
 
     log_write(0, LOG_MAIN, "%s%s%s%s: error ignored",
       addr->address,
-      (addr->parent == NULL)? US"" : US" <",
-      (addr->parent == NULL)? US"" : addr->parent->address,
-      (addr->parent == NULL)? US"" : US">");
+      !addr->parent ? US"" : US" <",
+      !addr->parent ? US"" : addr->parent->address,
+      !addr->parent ? US"" : US">");
 
     address_done(addr, logtod);
     child_done(addr, logtod);
 
     address_done(addr, logtod);
     child_done(addr, logtod);
@@ -6889,16 +6907,12 @@ while (addr_failed != NULL)
 
   else
     {
 
   else
     {
-    bounce_recipient = (addr_failed->p.errors_address == NULL)?
-      sender_address : addr_failed->p.errors_address;
+    bounce_recipient = addr_failed->prop.errors_address
+      ? addr_failed->prop.errors_address : sender_address;
 
     /* Make a subprocess to send a message */
 
 
     /* Make a subprocess to send a message */
 
-    pid = child_open_exim(&fd);
-
-    /* Creation of child failed */
-
-    if (pid < 0)
+    if ((pid = child_open_exim(&fd)) < 0)
       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));
       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));
@@ -6916,12 +6930,10 @@ 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;
       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 * bound;
       uschar *dsnlimitmsg;
       uschar *dsnnotifyhdr;
       int topt;
       uschar * bound;
       uschar *dsnlimitmsg;
       uschar *dsnnotifyhdr;
       int topt;
-#endif
 
       DEBUG(D_deliver)
         debug_printf("sending error message to: %s\n", bounce_recipient);
 
       DEBUG(D_deliver)
         debug_printf("sending error message to: %s\n", bounce_recipient);
@@ -6931,20 +6943,16 @@ while (addr_failed != NULL)
 
       paddr = &addr_failed;
       for (addr = addr_failed; addr != NULL; addr = *paddr)
 
       paddr = &addr_failed;
       for (addr = addr_failed; addr != NULL; addr = *paddr)
-        {
-        if (Ustrcmp(bounce_recipient, (addr->p.errors_address == NULL)?
-              sender_address : addr->p.errors_address) != 0)
-          {
-          paddr = &(addr->next);      /* Not the same; skip */
-          }
-        else                          /* The same - dechain */
-          {
+        if (Ustrcmp(bounce_recipient, addr->prop.errors_address
+             ? addr->prop.errors_address : sender_address) == 0)
+          {                          /* The same - dechain */
           *paddr = addr->next;
           *pmsgchain = addr;
           addr->next = NULL;
           pmsgchain = &(addr->next);
           }
           *paddr = addr->next;
           *pmsgchain = addr;
           addr->next = NULL;
           pmsgchain = &(addr->next);
           }
-        }
+        else
+          paddr = &addr->next;        /* Not the same; skip */
 
       /* Include X-Failed-Recipients: for automatic interpretation, but do
       not let any one header line get too long. We do this by starting a
 
       /* Include X-Failed-Recipients: for automatic interpretation, but do
       not let any one header line get too long. We do this by starting a
@@ -6969,13 +6977,12 @@ while (addr_failed != NULL)
 
       /* Output the standard headers */
 
 
       /* Output the standard headers */
 
-      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", bounce_recipient);
 
         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", bounce_recipient);
 
-#ifdef EXPERIMENTAL_DSN
       /* generate boundary string and output MIME-Headers */
       bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
 
       /* generate boundary string and output MIME-Headers */
       bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
 
@@ -6983,7 +6990,6 @@ while (addr_failed != NULL)
            " report-type=delivery-status; boundary=%s\n"
          "MIME-Version: 1.0\n",
        bound);
            " report-type=delivery-status; boundary=%s\n"
          "MIME-Version: 1.0\n",
        bound);
-#endif
 
       /* Open a template file if one is provided. Log failure to open, but
       carry on - default texts will be used. */
 
       /* Open a template file if one is provided. Log failure to open, but
       carry on - default texts will be used. */
@@ -7008,12 +7014,10 @@ while (addr_failed != NULL)
         fprintf(f, "Subject: Mail delivery failed%s\n\n",
           to_sender? ": returning message to sender" : "");
 
         fprintf(f, "Subject: Mail delivery failed%s\n\n",
           to_sender? ": returning message to sender" : "");
 
-#ifdef EXPERIMENTAL_DSN
       /* output human readable part as text/plain section */
       fprintf(f, "--%s\n"
          "Content-type: text/plain; charset=us-ascii\n\n",
        bound);
       /* output human readable part as text/plain section */
       fprintf(f, "--%s\n"
          "Content-type: text/plain; charset=us-ascii\n\n",
        bound);
-#endif
 
       if ((emf_text = next_emf(emf, US"intro")))
        fprintf(f, "%s", CS emf_text);
 
       if ((emf_text = next_emf(emf, US"intro")))
        fprintf(f, "%s", CS emf_text);
@@ -7140,12 +7144,19 @@ wording. */
        fputc('\n', f);
         }
 
        fputc('\n', f);
         }
 
-#ifdef EXPERIMENTAL_DSN
       /* output machine readable part */
       /* output machine readable part */
-      fprintf(f, "--%s\n"
-         "Content-type: message/delivery-status\n\n"
-         "Reporting-MTA: dns; %s\n",
-       bound, smtp_active_hostname);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+      if (message_smtputf8)
+       fprintf(f, "--%s\n"
+           "Content-type: message/global-delivery-status\n\n"
+           "Reporting-MTA: dns; %s\n",
+         bound, smtp_active_hostname);
+      else
+#endif
+       fprintf(f, "--%s\n"
+           "Content-type: message/delivery-status\n\n"
+           "Reporting-MTA: dns; %s\n",
+         bound, smtp_active_hostname);
 
       if (dsn_envid)
        {
 
       if (dsn_envid)
        {
@@ -7170,8 +7181,8 @@ wording. */
            addr->host_used->name);
           print_dsn_diagnostic_code(addr, f);
           }
            addr->host_used->name);
           print_dsn_diagnostic_code(addr, f);
           }
+       fputc('\n', f);
         }
         }
-#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
 
       /* 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
@@ -7180,65 +7191,6 @@ wording. */
 
       emf_text = next_emf(emf, US"copy");
 
 
       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)
-         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");
-          else fprintf(f,
-"------ This is a copy of the message's headers. ------\n");
-          }
-
-        /* While reading the "truncated" message, set return_size_limit to
-        the actual max testing value, rounded. We need to read the message
-        whether we are going to use it or not. */
-
-          {
-          int temp = bounce_return_size_limit;
-          bounce_return_size_limit = (max/1000)*1000;
-          emf_text = next_emf(emf, US"truncated");
-          bounce_return_size_limit = temp;
-          }
-
-        if (bounce_return_body && bounce_return_size_limit > 0)
-          {
-          struct stat statbuf;
-          if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
-           {
-            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);
-           }
-          }
-
-       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,
-          bounce_return_size_limit, NULL, NULL, NULL, NULL, NULL, 0);
-        }
-
-      /* Write final text and close the template file if one is open */
-
-      if (emf)
-        {
-        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.
       /* add message body
          we ignore the intro text from template and add 
          the text for bounce_return_size_limit at the end.
@@ -7250,7 +7202,7 @@ wording. */
          bounce_return_size_limit is always honored.
       */
   
          bounce_return_size_limit is always honored.
       */
   
-      fprintf(f, "\n--%s\n", bound);
+      fprintf(f, "--%s\n", bound);
 
       dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
       dsnnotifyhdr = NULL;
 
       dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
       dsnnotifyhdr = NULL;
@@ -7279,10 +7231,16 @@ wording. */
             }
           }
   
             }
           }
   
-      if (topt & topt_no_body)
-        fprintf(f,"Content-type: text/rfc822-headers\n\n");
+#ifdef EXPERIMENTAL_INTERNATIONAL
+      if (message_smtputf8)
+       fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n"
+                                 : "Content-type: message/global\n\n",
+             f);
       else
       else
-        fprintf(f,"Content-type: message/rfc822\n\n");
+#endif
+       fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n"
+                                 : "Content-type: message/rfc822\n\n",
+             f);
 
       fflush(f);
       transport_filter_argv = NULL;   /* Just in case */
 
       fflush(f);
       transport_filter_argv = NULL;   /* Just in case */
@@ -7296,7 +7254,6 @@ wording. */
         (void)fclose(emf);
  
       fprintf(f, "\n--%s--\n", bound);
         (void)fclose(emf);
  
       fprintf(f, "\n--%s--\n", bound);
-#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. */
 
       /* Close the file, which should send an EOF to the child process
       that is receiving the message. Wait for it to finish. */
@@ -7383,11 +7340,9 @@ if (addr_defer == NULL)
           "msglog.OLD directory", spoolname);
       }
     else
           "msglog.OLD directory", spoolname);
       }
     else
-      {
       if (Uunlink(spoolname) < 0)
         log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
                  spoolname, strerror(errno));
       if (Uunlink(spoolname) < 0)
         log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
                  spoolname, strerror(errno));
-      }
     }
 
   /* Remove the two message files. */
     }
 
   /* Remove the two message files. */
@@ -7461,7 +7416,8 @@ else if (addr_defer != (address_item *)(+1))
 
     if (deliver_domain != NULL)
       {
 
     if (deliver_domain != NULL)
       {
-      uschar *d = (testflag(addr, af_pfr))? addr->parent->domain : addr->domain;
+      const uschar *d = testflag(addr, af_pfr)
+       ? addr->parent->domain : addr->domain;
 
       /* The domain may be unset for an address that has never been routed
       because the system filter froze the message. */
 
       /* The domain may be unset for an address that has never been routed
       because the system filter froze the message. */
@@ -7499,7 +7455,7 @@ else if (addr_defer != (address_item *)(+1))
         DEBUG(D_deliver) debug_printf("one_time: adding %s in place of %s\n",
           otaddr->address, otaddr->parent->address);
         receive_add_recipient(otaddr->address, t);
         DEBUG(D_deliver) debug_printf("one_time: adding %s in place of %s\n",
           otaddr->address, otaddr->parent->address);
         receive_add_recipient(otaddr->address, t);
-        recipients_list[recipients_count-1].errors_to = otaddr->p.errors_address;
+        recipients_list[recipients_count-1].errors_to = otaddr->prop.errors_address;
         tree_add_nonrecipient(otaddr->parent->address);
         update_spool = TRUE;
         }
         tree_add_nonrecipient(otaddr->parent->address);
         update_spool = TRUE;
         }
@@ -7511,7 +7467,7 @@ else if (addr_defer != (address_item *)(+1))
 
     if (sender_address[0] != 0)
       {
 
     if (sender_address[0] != 0)
       {
-      if (addr->p.errors_address == NULL)
+      if (addr->prop.errors_address == NULL)
         {
         if (Ustrstr(recipients, sender_address) == NULL)
           recipients = string_sprintf("%s%s%s", recipients,
         {
         if (Ustrstr(recipients, sender_address) == NULL)
           recipients = string_sprintf("%s%s%s", recipients,
@@ -7519,9 +7475,9 @@ else if (addr_defer != (address_item *)(+1))
         }
       else
         {
         }
       else
         {
-        if (Ustrstr(recipients, addr->p.errors_address) == NULL)
+        if (Ustrstr(recipients, addr->prop.errors_address) == NULL)
           recipients = string_sprintf("%s%s%s", recipients,
           recipients = string_sprintf("%s%s%s", recipients,
-            (recipients[0] == 0)? "" : ",", addr->p.errors_address);
+            (recipients[0] == 0)? "" : ",", addr->prop.errors_address);
         }
       }
     }
         }
       }
     }
@@ -7531,15 +7487,18 @@ else if (addr_defer != (address_item *)(+1))
   is not sent. Another attempt will be made at the next delivery attempt (if
   it also defers). */
 
   is not sent. Another attempt will be made at the next delivery attempt (if
   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,
-            US"delay_warning", US"option")))
+  if (  !queue_2stage
+     && delivery_attempted
+     && (  ((addr_defer->dsn_flags & rf_dsnflags) == 0)
+        || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay
+       )
+     && delay_warning[1] > 0
+     && sender_address[0] != 0
+     && (  delay_warning_condition == NULL
+        || expand_check_condition(delay_warning_condition,
+            US"delay_warning", US"option")
+       )
+     )
     {
     int count;
     int show_time;
     {
     int count;
     int show_time;
@@ -7600,9 +7559,7 @@ else if (addr_defer != (address_item *)(+1))
         uschar *wmf_text;
         FILE *wmf = NULL;
         FILE *f = fdopen(fd, "wb");
         uschar *wmf_text;
         FILE *wmf = NULL;
         FILE *f = fdopen(fd, "wb");
-#ifdef EXPERIMENTAL_DSN
        uschar * bound;
        uschar * bound;
-#endif
 
         if (warn_message_file)
           {
 
         if (warn_message_file)
           {
@@ -7623,7 +7580,6 @@ else if (addr_defer != (address_item *)(+1))
         moan_write_from(f);
         fprintf(f, "To: %s\n", recipients);
 
         moan_write_from(f);
         fprintf(f, "To: %s\n", recipients);
 
-#ifdef EXPERIMENTAL_DSN
         /* generated boundary string and output MIME-Headers */
         bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
 
         /* generated boundary string and output MIME-Headers */
         bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
 
@@ -7631,7 +7587,6 @@ else if (addr_defer != (address_item *)(+1))
            " report-type=delivery-status; boundary=%s\n"
            "MIME-Version: 1.0\n",
          bound);
            " report-type=delivery-status; boundary=%s\n"
            "MIME-Version: 1.0\n",
          bound);
-#endif
 
         if ((wmf_text = next_emf(wmf, US"header")))
           fprintf(f, "%s\n", wmf_text);
 
         if ((wmf_text = next_emf(wmf, US"header")))
           fprintf(f, "%s\n", wmf_text);
@@ -7639,12 +7594,10 @@ else if (addr_defer != (address_item *)(+1))
           fprintf(f, "Subject: Warning: message %s delayed %s\n\n",
             message_id, warnmsg_delay);
 
           fprintf(f, "Subject: Warning: message %s delayed %s\n\n",
             message_id, warnmsg_delay);
 
-#ifdef EXPERIMENTAL_DSN
         /* output human readable part as text/plain section */
         fprintf(f, "--%s\n"
            "Content-type: text/plain; charset=us-ascii\n\n",
          bound);
         /* output human readable part as text/plain section */
         fprintf(f, "--%s\n"
            "Content-type: text/plain; charset=us-ascii\n\n",
          bound);
-#endif
 
         if ((wmf_text = next_emf(wmf, US"intro")))
          fprintf(f, "%s", CS wmf_text);
 
         if ((wmf_text = next_emf(wmf, US"intro")))
          fprintf(f, "%s", CS wmf_text);
@@ -7683,10 +7636,8 @@ else if (addr_defer != (address_item *)(+1))
 
         /* List the addresses, with error information if allowed */
 
 
         /* List the addresses, with error information if allowed */
 
-#ifdef EXPERIMENTAL_DSN
         /* store addr_defer for machine readable part */
         address_item *addr_dsndefer = addr_defer;
         /* store addr_defer for machine readable part */
         address_item *addr_dsndefer = addr_defer;
-#endif
         fputc('\n', f);
         while (addr_defer)
           {
         fputc('\n', f);
         while (addr_defer)
           {
@@ -7715,7 +7666,6 @@ else if (addr_defer != (address_item *)(+1))
 "and when that happens, the message will be returned to you.\n");
           }
 
 "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"
         /* output machine readable part */
         fprintf(f, "\n--%s\n"
            "Content-type: message/delivery-status\n\n"
@@ -7735,24 +7685,25 @@ else if (addr_defer != (address_item *)(+1))
           }
         fputc('\n', f);
 
           }
         fputc('\n', f);
 
-        while (addr_dsndefer)
+        for ( ; addr_dsndefer; addr_dsndefer = addr_dsndefer->next)
           {
           if (addr_dsndefer->dsn_orcpt)
           {
           if (addr_dsndefer->dsn_orcpt)
-            fprintf(f,"Original-Recipient: %s\n", 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");
+          fprintf(f, "Action: delayed\n"
+             "Final-Recipient: rfc822;%s\n"
+             "Status: 4.0.0\n",
+           addr_dsndefer->address);
           if (addr_dsndefer->host_used && addr_dsndefer->host_used->name)
             {
           if (addr_dsndefer->host_used && addr_dsndefer->host_used->name)
             {
-            fprintf(f,"Remote-MTA: dns; %s\n", 
+            fprintf(f, "Remote-MTA: dns; %s\n", 
                    addr_dsndefer->host_used->name);
             print_dsn_diagnostic_code(addr_dsndefer, f);
             }
                    addr_dsndefer->host_used->name);
             print_dsn_diagnostic_code(addr_dsndefer, f);
             }
-          addr_dsndefer = addr_dsndefer->next;
+         fputc('\n', f);
           }
 
           }
 
-        fprintf(f, "\n--%s\n"
+        fprintf(f, "--%s\n"
            "Content-type: text/rfc822-headers\n\n",
          bound);
 
            "Content-type: text/rfc822-headers\n\n",
          bound);
 
@@ -7768,7 +7719,6 @@ else if (addr_defer != (address_item *)(+1))
         fprintf(f,"\n--%s--\n", bound);
 
         fflush(f);
         fprintf(f,"\n--%s--\n", bound);
 
         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. */
 
         /* Close and wait for child process to complete, without a timeout.
         If there's an error, don't update the count. */
@@ -7906,6 +7856,60 @@ acl_where = ACL_WHERE_UNKNOWN;
 return final_yield;
 }
 
 return final_yield;
 }
 
+
+
+void
+deliver_init(void)
+{
+if (!regex_PIPELINING) regex_PIPELINING =
+  regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_SIZE) regex_SIZE =
+  regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_AUTH) regex_AUTH =
+  regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
+    FALSE, TRUE);
+
+#ifdef SUPPORT_TLS
+if (!regex_STARTTLS) regex_STARTTLS =
+  regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+#ifndef DISABLE_PRDR
+if (!regex_PRDR) regex_PRDR =
+  regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+#ifdef SUPPORT_TLS
+if (!regex_UTF8) regex_UTF8 =
+  regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+if (!regex_DSN) regex_DSN  =
+  regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+  regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+}
+
+
+uschar *
+deliver_get_sender_address (uschar * id)
+{
+if (!spool_open_datafile(id))
+  return NULL;
+
+sprintf(CS spoolname, "%s-H", id);
+if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
+  return NULL;
+
+(void)close(deliver_datafile);
+deliver_datafile = -1;
+
+return sender_address;
+}
+
 /* vi: aw ai sw=2
 */
 /* End of deliver.c */
 /* vi: aw ai sw=2
 */
 /* End of deliver.c */