Track ACL context through ${acl expansions. Bug 1305.
[exim.git] / src / src / deliver.c
index dda4897b9a762a229e2a0dc2c04d7422b37dcda9..02329d27292bef40c2366c04a9afd11b083d136c 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/deliver.c,v 1.29 2006/02/21 16:24:19 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* The main code for delivering a message. */
@@ -228,11 +226,18 @@ if (addr->next == NULL)
   }
 
 /* For multiple addresses, don't set local part, and leave the domain and
-self_hostname set only if it is the same for all of them. */
+self_hostname set only if it is the same for all of them. It is possible to
+have multiple pipe and file addresses, but only when all addresses have routed
+to the same pipe or file. */
 
 else
   {
   address_item *addr2;
+  if (testflag(addr, af_pfr))
+    {
+    if (testflag(addr, af_file)) address_file = addr->local_part;
+      else if (addr->local_part[0] == '|') address_pipe = addr->local_part;
+    }
   for (addr2 = addr->next; addr2 != NULL; addr2 = addr2->next)
     {
     if (deliver_domain != NULL &&
@@ -617,7 +622,7 @@ for (dup = addr_duplicate; dup != NULL; dup = dup->next)
   {
   if (Ustrcmp(addr->unique, dup->unique) == 0)
     {
-    tree_add_nonrecipient(dup->address);
+    tree_add_nonrecipient(dup->unique);
     child_done(dup, now);
     }
   }
@@ -668,6 +673,150 @@ while (addr->parent != NULL)
 
 
 
+/* If msg is NULL this is a delivery log and logchar is used. Otherwise
+this is a nonstandard call; no two-characher delivery flag is written
+but sender-host and sender are prefixed and "msg" is inserted in the log line.
+
+Arguments:
+  flags                passed to log_write()
+*/
+void
+delivery_log(int flags, address_item * addr, int logchar, uschar * msg)
+{
+uschar *log_address;
+int size = 256;         /* Used for a temporary, */
+int ptr = 0;            /* expanding buffer, for */
+uschar *s;              /* building log lines;   */
+void *reset_point;      /* released afterwards.  */
+
+
+/* Log the delivery on the main log. We use an extensible string to build up
+the log line, and reset the store afterwards. Remote deliveries should always
+have a pointer to the host item that succeeded; local deliveries can have a
+pointer to a single host item in their host list, for use by the transport. */
+
+s = reset_point = store_get(size);
+
+log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE);
+if (msg)
+  s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address);
+else
+  {
+  s[ptr++] = logchar;
+  s = string_append(s, &size, &ptr, 2, US"> ", log_address);
+  }
+
+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">");
+#endif
+
+/* You might think that the return path must always be set for a successful
+delivery; indeed, I did for some time, until this statement crashed. The case
+when it is not set is for a delivery to /dev/null which is optimised by not
+being run at all. */
+
+if (used_return_path != NULL &&
+      (log_extra_selector & LX_return_path_on_delivery) != 0)
+  s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
+
+if (msg)
+  s = string_append(s, &size, &ptr, 2, US" ", msg);
+
+/* For a delivery from a system filter, there may not be a router */
+if (addr->router != NULL)
+  s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name);
+
+s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
+
+if ((log_extra_selector & LX_delivery_size) != 0)
+  s = string_append(s, &size, &ptr, 2, US" S=",
+    string_sprintf("%d", transport_count));
+
+/* Local delivery */
+
+if (addr->transport->info->local)
+  {
+  if (addr->host_list != NULL)
+    s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
+  if (addr->shadow_message != NULL)
+    s = string_cat(s, &size, &ptr, addr->shadow_message,
+      Ustrlen(addr->shadow_message));
+  }
+
+/* Remote delivery */
+
+else
+  {
+  if (addr->host_used != NULL)
+    {
+    s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name,
+      US" [", addr->host_used->address, US"]");
+    if ((log_extra_selector & LX_outgoing_port) != 0)
+      s = string_append(s, &size, &ptr, 2, US":", string_sprintf("%d",
+        addr->host_used->port));
+    if (continue_sequence > 1)
+      s = string_cat(s, &size, &ptr, US"*", 1);
+    }
+
+  #ifdef SUPPORT_TLS
+  if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
+    s = string_append(s, &size, &ptr, 2, US" X=", addr->cipher);
+  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
+       addr->cipher != NULL)
+    s = string_append(s, &size, &ptr, 2, US" CV=",
+      testflag(addr, af_cert_verified)? "yes":"no");
+  if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
+    s = string_append(s, &size, &ptr, 3, US" DN=\"",
+      string_printing(addr->peerdn), US"\"");
+  #endif
+
+  if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
+      addr->message != NULL)
+    {
+    int i;
+    uschar *p = big_buffer;
+    uschar *ss = addr->message;
+    *p++ = '\"';
+    for (i = 0; i < 100 && ss[i] != 0; i++)
+      {
+      if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\';
+      *p++ = ss[i];
+      }
+    *p++ = '\"';
+    *p = 0;
+    s = string_append(s, &size, &ptr, 2, US" C=", big_buffer);
+    }
+  }
+
+/* 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));
+  }
+
+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. */
+
+s[ptr] = 0;
+log_write(0, flags, "%s", s);
+store_reset(reset_point);
+return;
+}
+
+
+
 /*************************************************
 *    Actions at the end of handling an address   *
 *************************************************/
@@ -737,17 +886,15 @@ malformed, it won't ever have gone near LDAP.) */
 if (addr->message != NULL)
   {
   addr->message = string_printing(addr->message);
-  if (Ustrstr(addr->message, "failed to expand") != NULL &&
-      (Ustrstr(addr->message, "ldap:") != 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 ||
+       Ustrstr(addr->message, "sqlite") != NULL ||
+       Ustrstr(addr->message, "ldap:") != NULL ||
        Ustrstr(addr->message, "ldapdn:") != NULL ||
        Ustrstr(addr->message, "ldapm:") != NULL))
     {
-    uschar *p = Ustrstr(addr->message, "pass=");
-    if (p != NULL)
-      {
-      p += 5;
-      while (*p != 0 && !isspace(*p)) *p++ = 'x';
-      }
+      addr->message = string_sprintf("Temporary internal error");
     }
   }
 
@@ -767,7 +914,7 @@ if (addr->return_file >= 0 && addr->return_filename != NULL)
   {
   BOOL return_output = FALSE;
   struct stat statbuf;
-  fsync(addr->return_file);
+  (void)EXIMfsync(addr->return_file);
 
   /* If there is no output, do nothing. */
 
@@ -832,12 +979,6 @@ if (addr->return_file >= 0 && addr->return_filename != NULL)
   (void)close(addr->return_file);
   }
 
-/* Create the address string for logging. Must not do this earlier, because
-an OK result may be changed to FAIL when a pipe returns text. */
-
-log_address = string_log_address(addr,
-  (log_write_selector & L_all_parents) != 0, result == OK);
-
 /* The sucess case happens only after delivery by a transport. */
 
 if (result == OK)
@@ -865,119 +1006,7 @@ if (result == OK)
     child_done(addr, now);
     }
 
-  /* Log the delivery on the main log. We use an extensible string to build up
-  the log line, and reset the store afterwards. Remote deliveries should always
-  have a pointer to the host item that succeeded; local deliveries can have a
-  pointer to a single host item in their host list, for use by the transport. */
-
-  s = reset_point = store_get(size);
-  s[ptr++] = logchar;
-
-  s = string_append(s, &size, &ptr, 2, US"> ", log_address);
-
-  if ((log_extra_selector & LX_sender_on_delivery) != 0)
-    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">");
-  #endif
-
-  /* You might think that the return path must always be set for a successful
-  delivery; indeed, I did for some time, until this statement crashed. The case
-  when it is not set is for a delivery to /dev/null which is optimised by not
-  being run at all. */
-
-  if (used_return_path != NULL &&
-        (log_extra_selector & LX_return_path_on_delivery) != 0)
-    s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
-
-  /* For a delivery from a system filter, there may not be a router */
-
-  if (addr->router != NULL)
-    s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name);
-
-  s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
-
-  if ((log_extra_selector & LX_delivery_size) != 0)
-    s = string_append(s, &size, &ptr, 2, US" S=",
-      string_sprintf("%d", transport_count));
-
-  /* Local delivery */
-
-  if (addr->transport->info->local)
-    {
-    if (addr->host_list != NULL)
-      s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
-    if (addr->shadow_message != NULL)
-      s = string_cat(s, &size, &ptr, addr->shadow_message,
-        Ustrlen(addr->shadow_message));
-    }
-
-  /* Remote delivery */
-
-  else
-    {
-    if (addr->host_used != NULL)
-      {
-      s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name,
-        US" [", addr->host_used->address, US"]");
-      if ((log_extra_selector & LX_outgoing_port) != 0)
-        s = string_append(s, &size, &ptr, 2, US":", string_sprintf("%d",
-          addr->host_used->port));
-      if (continue_sequence > 1)
-        s = string_cat(s, &size, &ptr, US"*", 1);
-      }
-
-    #ifdef SUPPORT_TLS
-    if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
-      s = string_append(s, &size, &ptr, 2, US" X=", addr->cipher);
-    if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
-         addr->cipher != NULL)
-      s = string_append(s, &size, &ptr, 2, US" CV=",
-        testflag(addr, af_cert_verified)? "yes":"no");
-    if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
-      s = string_append(s, &size, &ptr, 3, US" DN=\"", addr->peerdn, US"\"");
-    #endif
-
-    if ((log_extra_selector & LX_smtp_confirmation) != 0 &&
-        addr->message != NULL)
-      {
-      int i;
-      uschar *p = big_buffer;
-      uschar *ss = addr->message;
-      *p++ = '\"';
-      for (i = 0; i < 100 && ss[i] != 0; i++)
-        {
-        if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\';
-        *p++ = ss[i];
-        }
-      *p++ = '\"';
-      *p = 0;
-      s = string_append(s, &size, &ptr, 2, US" C=", big_buffer);
-      }
-    }
-
-  /* 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));
-    }
-
-  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. */
-
-  s[ptr] = 0;
-  log_write(0, LOG_MAIN, "%s", s);
-  store_reset(reset_point);
+  delivery_log(LOG_MAIN, addr, logchar, NULL);
   }
 
 
@@ -1025,6 +1054,13 @@ else if (result == DEFER || result == PANIC)
     log. */
 
     s = reset_point = store_get(size);
+
+    /* Create the address string for logging. Must not do this earlier, because
+    an OK result may be changed to FAIL when a pipe returns text. */
+
+    log_address = string_log_address(addr,
+      (log_write_selector & L_all_parents) != 0, result == OK);
+
     s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
 
     /* Either driver_name contains something and driver_kind contains
@@ -1125,6 +1161,13 @@ else
   /* Build up the log line for the message and main logs */
 
   s = reset_point = store_get(size);
+
+  /* Create the address string for logging. Must not do this earlier, because
+  an OK result may be changed to FAIL when a pipe returns text. */
+
+  log_address = string_log_address(addr,
+    (log_write_selector & L_all_parents) != 0, result == OK);
+
   s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
 
   if ((log_extra_selector & LX_sender_on_delivery) != 0)
@@ -1209,7 +1252,7 @@ if (format != NULL)
   va_start(ap, format);
   if (!string_vformat(buffer, sizeof(buffer), CS format, ap))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE,
-      "common_error expansion was longer than %d", sizeof(buffer));
+      "common_error expansion was longer than " SIZE_T_FMT, sizeof(buffer));
   va_end(ap);
   addr->message = string_copy(buffer);
   }
@@ -1426,10 +1469,10 @@ int rc = OK;
 int size_limit;
 
 deliver_set_expansions(addr);
-size_limit = expand_string_integer(tp->message_size_limit);
+size_limit = expand_string_integer(tp->message_size_limit, TRUE);
 deliver_set_expansions(NULL);
 
-if (size_limit < 0)
+if (expand_string_message != NULL)
   {
   rc = DEFER;
   if (size_limit == -1)
@@ -1619,17 +1662,13 @@ return. */
 
 if (!findugid(addr, tp, &uid, &gid, &use_initgroups)) return;
 
-/* See if either the transport or the address specifies a home and/or a current
-working directory. Expand it if necessary. If nothing is set, use "/", for the
-working directory, which is assumed to be a directory to which all users have
-access. It is necessary to be in a visible directory for some operating systems
-when running pipes, as some commands (e.g. "rm" under Solaris 2.5) require
-this. */
+/* See if either the transport or the address specifies a home directory. A
+home directory set in the address may already be expanded; a flag is set to
+indicate that. In other cases we must expand it. */
 
-deliver_home = (tp->home_dir != NULL)? tp->home_dir :
-               (addr->home_dir != NULL)? addr->home_dir : NULL;
-
-if (deliver_home != NULL && !testflag(addr, af_home_expanded))
+if ((deliver_home = tp->home_dir) != NULL ||       /* Set in transport, or */
+     ((deliver_home = addr->home_dir) != NULL &&   /* Set in address and */
+       !testflag(addr, af_home_expanded)))         /*   not expanded */
   {
   uschar *rawhome = deliver_home;
   deliver_home = NULL;                      /* in case it contains $home */
@@ -1649,8 +1688,15 @@ if (deliver_home != NULL && !testflag(addr, af_home_expanded))
     }
   }
 
-working_directory = (tp->current_dir != NULL)? tp->current_dir :
-                    (addr->current_dir != NULL)? addr->current_dir : NULL;
+/* See if either the transport or the address specifies a current directory,
+and if so, expand it. If nothing is set, use the home directory, unless it is
+also unset in which case use "/", which is assumed to be a directory to which
+all users have access. It is necessary to be in a visible directory for some
+operating systems when running pipes, as some commands (e.g. "rm" under Solaris
+2.5) require this. */
+
+working_directory = (tp->current_dir != NULL)?
+  tp->current_dir : addr->current_dir;
 
 if (working_directory != NULL)
   {
@@ -1716,7 +1762,20 @@ if ((pid = fork()) == 0)
   HP-UX doesn't have RLIMIT_CORE; I don't know how to do this in that
   system. Some experimental/developing systems (e.g. GNU/Hurd) may define
   RLIMIT_CORE but not support it in setrlimit(). For such systems, do not
-  complain if the error is "not supported". */
+  complain if the error is "not supported".
+
+  There are two scenarios where changing the max limit has an effect.  In one,
+  the user is using a .forward and invoking a command of their choice via pipe;
+  for these, we do need the max limit to be 0 unless the admin chooses to
+  permit an increased limit.  In the other, the command is invoked directly by
+  the transport and is under administrator control, thus being able to raise
+  the limit aids in debugging.  So there's no general always-right answer.
+
+  Thus we inhibit core-dumps completely but let individual transports, while
+  still root, re-raise the limits back up to aid debugging.  We make the
+  default be no core-dumps -- few enough people can use core dumps in
+  diagnosis that it's reasonable to make them something that has to be explicitly requested.
+  */
 
   #ifdef RLIMIT_CORE
   struct rlimit rl;
@@ -1969,7 +2028,7 @@ if (!shadowing)
 
   /* Ensure the journal file is pushed out to disk. */
 
-  if (fsync(journal_fd) < 0)
+  if (EXIMfsync(journal_fd) < 0)
     log_write(0, LOG_MAIN|LOG_PANIC, "failed to fsync journal: %s",
       strerror(errno));
   }
@@ -2033,9 +2092,7 @@ if (addr->special_action == SPECIAL_WARN &&
           !contains_header(US"Reply-To", warn_message))
         fprintf(f, "Reply-To: %s\n", errors_reply_to);
       fprintf(f, "Auto-Submitted: auto-replied\n");
-      if (!contains_header(US"From", warn_message))
-        fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
-          qualify_domain_sender);
+      if (!contains_header(US"From", warn_message)) moan_write_from(f);
       fprintf(f, "%s", CS warn_message);
 
       /* Close and wait for child process to complete, without a timeout. */
@@ -2119,15 +2176,17 @@ while (addr_local != NULL)
 
   disable_logging = tp->disable_logging;
 
-  /* Check for batched addresses and possible amalgamation. File deliveries can
-  never be batched. Skip all the work if either batch_max <= 1 or there aren't
-  any other addresses for local delivery. */
+  /* Check for batched addresses and possible amalgamation. Skip all the work
+  if either batch_max <= 1 or there aren't any other addresses for local
+  delivery. */
 
-  if (!testflag(addr, af_file) && tp->batch_max > 1 && addr_local != NULL)
+  if (tp->batch_max > 1 && addr_local != NULL)
     {
     int batch_count = 1;
     BOOL uses_dom = readconf_depends((driver_instance *)tp, US"domain");
-    BOOL uses_lp = readconf_depends((driver_instance *)tp, US"local_part");
+    BOOL uses_lp = (testflag(addr, af_pfr) &&
+      (testflag(addr, af_file) || addr->local_part[0] == '|')) ||
+      readconf_depends((driver_instance *)tp, US"local_part");
     uschar *batch_id = NULL;
     address_item **anchor = &addr_local;
     address_item *last = addr;
@@ -2156,6 +2215,7 @@ while (addr_local != NULL)
       same transport
       not previously delivered (see comment about 50 lines above)
       same local part if the transport's configuration contains $local_part
+        or if this is a file or pipe delivery from a redirection
       same domain if the transport's configuration contains $domain
       same errors address
       same additional headers
@@ -2169,6 +2229,7 @@ while (addr_local != NULL)
       BOOL ok =
         tp == next->transport &&
         !previously_transported(next, TRUE) &&
+        (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) &&
@@ -2314,8 +2375,13 @@ while (addr_local != NULL)
                 retry_record->more_errno);
 
             DEBUG(D_deliver|D_retry)
+              {
               debug_printf("retry time not reached for %s: "
                 "checking ultimate address timeout\n", addr2->address);
+              debug_printf("  now=%d first_failed=%d next_try=%d expired=%d\n",
+                (int)now, (int)retry_record->first_failed,
+                (int)retry_record->next_try, retry_record->expired);
+              }
 
             if (retry != NULL && retry->rules != NULL)
               {
@@ -2324,9 +2390,8 @@ while (addr_local != NULL)
                    last_rule->next != NULL;
                    last_rule = last_rule->next);
               DEBUG(D_deliver|D_retry)
-                debug_printf("now=%d received_time=%d diff=%d timeout=%d\n",
-                  (int)now, received_time, (int)now - received_time,
-                  last_rule->timeout);
+                debug_printf("  received_time=%d diff=%d timeout=%d\n",
+                  received_time, (int)now - received_time, last_rule->timeout);
               if (now - received_time > last_rule->timeout) ok = TRUE;
               }
             else
@@ -3897,7 +3962,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
 
       /* The certificate verification status goes into the flags */
 
-      if (tls_certificate_verified) setflag(addr, af_cert_verified);
+      if (tls_out.certificate_verified) setflag(addr, af_cert_verified);
 
       /* Use an X item only if there's something to send */
 
@@ -4299,15 +4364,15 @@ introducing newlines. All lines are indented by 4; the initial printing
 position must be set before calling.
 
 This function used always to print the error. Nowadays we want to restrict it
-to cases such as SMTP errors from a remote host, and errors from :fail: and
-filter "fail". We no longer pass other information willy-nilly in bounce and
-warning messages. Text in user_message is always output; text in message only
-if the af_pass_message flag is set.
+to cases such as LMTP/SMTP errors from a remote host, and errors from :fail:
+and filter "fail". We no longer pass other information willy-nilly in bounce
+and warning messages. Text in user_message is always output; text in message
+only if the af_pass_message flag is set.
 
 Arguments:
   addr         the address
   f            the FILE to print on
-  s            some leading text
+  t            some leading text
 
 Returns:       nothing
 */
@@ -4316,14 +4381,11 @@ static void
 print_address_error(address_item *addr, FILE *f, uschar *t)
 {
 int count = Ustrlen(t);
-uschar *s = (addr->user_message != NULL)? addr->user_message : addr->message;
+uschar *s = testflag(addr, af_pass_message)? addr->message : NULL;
 
-if (addr->user_message != NULL)
-  s = addr->user_message;
-else
+if (s == NULL)
   {
-  if (!testflag(addr, af_pass_message) || addr->message == NULL) return;
-  s = addr->message;
+  if (addr->user_message != NULL) s = addr->user_message; else return;
   }
 
 fprintf(f, "\n    %s", t);
@@ -4361,16 +4423,13 @@ while (*s != 0)
 /* This function was introduced when the test for duplicate addresses that are
 not pipes, files, or autoreplies was moved from the middle of routing to when
 routing was complete. That was to fix obscure cases when the routing history
-affects the subsequent routing of identical addresses. If that change has to be
-reversed, this function is no longer needed. For a while, the old code that was
-affected by this change is commented with !!!OLD-DE-DUP!!! so it can be found
-easily.
+affects the subsequent routing of identical addresses. This function is called
+after routing, to check that the final routed addresses are not duplicates.
 
-This function is called after routing, to check that the final routed addresses
-are not duplicates. If we detect a duplicate, we remember what it is a
-duplicate of. Note that pipe, file, and autoreply de-duplication is handled
-during routing, so we must leave such "addresses" alone here, as otherwise they
-will incorrectly be discarded.
+If we detect a duplicate, we remember what it is a duplicate of. Note that
+pipe, file, and autoreply de-duplication is handled during routing, so we must
+leave such "addresses" alone here, as otherwise they will incorrectly be
+discarded.
 
 Argument:     address of list anchor
 Returns:      nothing
@@ -4456,6 +4515,7 @@ FILE *jread;
 int process_recipients = RECIP_ACCEPT;
 open_db dbblock;
 open_db *dbm_file;
+extern int acl_where;
 
 uschar *info = (queue_run_pid == (pid_t)0)?
   string_sprintf("delivering %s", id) :
@@ -4506,6 +4566,9 @@ message_size = 0;
 update_spool = FALSE;
 remove_journal = TRUE;
 
+/* Set a known context for any ACLs we call via expansions */
+acl_where = ACL_WHERE_DELIVERY;
+
 /* Reset the random number generator, so that if several delivery processes are
 started from a queue runner that has already used random numbers (for sorting),
 they don't all get the same sequence. */
@@ -4796,6 +4859,7 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT)
       RDO_REWRITE,
     NULL,                   /* No :include: restriction (not used in filter) */
     NULL,                   /* No sieve vacation directory (not sieve!) */
+    NULL,                   /* No sieve enotify mailto owner (not sieve!) */
     NULL,                   /* No sieve user address (not sieve!) */
     NULL,                   /* No sieve subaddress (not sieve!) */
     &ugid,                  /* uid/gid data */
@@ -4929,6 +4993,9 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT)
 
     while (p != NULL)
       {
+      if (parent->child_count == SHRT_MAX)
+        log_write(0, LOG_MAIN|LOG_PANIC_DIE, "system filter generated more "
+          "than %d delivery addresses", SHRT_MAX);
       parent->child_count++;
       p->parent = parent;
 
@@ -5439,40 +5506,11 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
       continue;
       }
 
-
-    /* !!!OLD-DE-DUP!!!  We used to test for duplicates at this point, in order
-    to save effort on routing duplicate addresses. However, facilities have
-    been added to Exim so that now two identical addresses that are children of
-    other addresses may be routed differently as a result of their previous
-    routing history. For example, different redirect routers may have given
-    them different redirect_router values, but there are other cases too.
-    Therefore, tests for duplicates now take place when routing is complete.
-    This is the old code, kept for a while for the record, and in case this
-    radical change has to be backed out for some reason. */
-
-    #ifdef NEVER
-    /* If it's a duplicate, remember what it's a duplicate of */
-
-    if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL)
-      {
-      DEBUG(D_deliver|D_route)
-        debug_printf("%s is a duplicate address: discarded\n", addr->unique);
-      addr->dupof = tnode->data.ptr;
-      addr->next = addr_duplicate;
-      addr_duplicate = addr;
-      continue;
-      }
-
-    /* Record this address, so subsequent duplicates get picked up. */
-
-    tree_add_duplicate(addr->unique, addr);
-    #endif
-
-
-
     /* Get the routing retry status, saving the two retry keys (with and
-    without the local part) for subsequent use. Ignore retry records that
-    are too old. */
+    without the local part) for subsequent use. If there is no retry record for
+    the standard address routing retry key, we look for the same key with the
+    sender attached, because this form is used by the smtp transport after a
+    4xx response to RCPT when address_retry_include_sender is true. */
 
     addr->domain_retry_key = string_sprintf("R:%s", addr->domain);
     addr->address_retry_key = string_sprintf("R:%s@%s", addr->local_part,
@@ -5485,12 +5523,22 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
       domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key);
       if (domain_retry_record != NULL &&
           now - domain_retry_record->time_stamp > retry_data_expire)
-        domain_retry_record = NULL;
+        domain_retry_record = NULL;    /* Ignore if too old */
 
       address_retry_record = dbfn_read(dbm_file, addr->address_retry_key);
       if (address_retry_record != NULL &&
           now - address_retry_record->time_stamp > retry_data_expire)
-        address_retry_record = NULL;
+        address_retry_record = NULL;   /* Ignore if too old */
+
+      if (address_retry_record == NULL)
+        {
+        uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
+          sender_address);
+        address_retry_record = dbfn_read(dbm_file, altkey);
+        if (address_retry_record != NULL &&
+            now - address_retry_record->time_stamp > retry_data_expire)
+          address_retry_record = NULL;   /* Ignore if too old */
+        }
       }
 
     DEBUG(D_deliver|D_retry)
@@ -5522,19 +5570,29 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
       (void)post_process_one(addr, DEFER, LOG_MAIN, DTYPE_ROUTER, 0);
       }
 
-    /* If queue_running, defer routing unless no retry data or we've
-    passed the next retry time, or this message is forced. However,
-    if the retry time has expired, allow the routing attempt.
-    If it fails again, the address will be failed. This ensures that
+    /* If we are in a queue run, defer routing unless there is no retry data or
+    we've passed the next retry time, or this message is forced. In other
+    words, ignore retry data when not in a queue run.
+
+    However, if the domain retry time has expired, always allow the routing
+    attempt. If it fails again, the address will be failed. This ensures that
     each address is routed at least once, even after long-term routing
     failures.
 
     If there is an address retry, check that too; just wait for the next
     retry time. This helps with the case when the temporary error on the
     address was really message-specific rather than address specific, since
-    it allows other messages through. */
+    it allows other messages through.
+
+    We also wait for the next retry time if this is a message sent down an
+    existing SMTP connection (even though that will be forced). Otherwise there
+    will be far too many attempts for an address that gets a 4xx error. In
+    fact, after such an error, we should not get here because, the host should
+    not be remembered as one this message needs. However, there was a bug that
+    used to cause this to  happen, so it is best to be on the safe side. */
 
-    else if (!deliver_force && queue_running &&
+    else if (((queue_running && !deliver_force) || continue_hostname != NULL)
+            &&
             ((domain_retry_record != NULL &&
               now < domain_retry_record->next_try &&
               !domain_retry_record->expired)
@@ -5633,12 +5691,16 @@ while (addr_new != NULL)           /* Loop until all addresses dealt with */
         string_sprintf("R:%s", addr->domain), 0);
 
     /* Otherwise, if there is an existing retry record in the database, add
-    retry items to delete both forms. Since the domain might have been
-    rewritten (expanded to fully qualified) as a result of routing, ensure
-    that the rewritten form is also deleted. */
+    retry items to delete both forms. We must also allow for the possibility
+    of a routing retry that includes the sender address. Since the domain might
+    have been rewritten (expanded to fully qualified) as a result of routing,
+    ensure that the rewritten form is also deleted. */
 
     else if (testflag(addr, af_dr_retry_exists))
       {
+      uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
+        sender_address);
+      retry_add_item(addr, altkey, rf_delete);
       retry_add_item(addr, addr->address_retry_key, rf_delete);
       retry_add_item(addr, addr->domain_retry_key, rf_delete);
       if (Ustrcmp(addr->domain, old_domain) != 0)
@@ -5781,11 +5843,6 @@ Ensure they are not set in transports. */
 local_user_gid = (gid_t)(-1);
 local_user_uid = (uid_t)(-1);
 
-
-/* !!!OLD-DE-DUP!!! The next two statement were introduced when checking for
-duplicates was moved from within routing to afterwards. If that change has to
-be backed out, they should be removed. */
-
 /* Check for any duplicate addresses. This check is delayed until after
 routing, because the flexibility of the routing configuration means that
 identical addresses with different parentage may end up being redirected to
@@ -5795,7 +5852,6 @@ to) makes this kind of thing not work. */
 do_duplicate_check(&addr_local);
 do_duplicate_check(&addr_remote);
 
-
 /* When acting as an MUA wrapper, we proceed only if all addresses route to a
 remote transport. The check that they all end up in one transaction happens in
 the do_remote_deliveries() function. */
@@ -6248,8 +6304,7 @@ while (addr_failed != NULL)
       if (errors_reply_to != NULL)
         fprintf(f, "Reply-To: %s\n", errors_reply_to);
       fprintf(f, "Auto-Submitted: auto-replied\n");
-      fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
-        qualify_domain_sender);
+      moan_write_from(f);
       fprintf(f, "To: %s\n", bounce_recipient);
 
       /* Open a template file if one is provided. Log failure to open, but
@@ -6552,7 +6607,8 @@ if (addr_defer == NULL)
     else
       {
       if (Uunlink(spoolname) < 0)
-        log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname);
+        log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
+                 spoolname, strerror(errno));
       }
     }
 
@@ -6560,10 +6616,12 @@ if (addr_defer == NULL)
 
   sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id);
   if (Uunlink(spoolname) < 0)
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname);
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
+      spoolname, strerror(errno));
   sprintf(CS spoolname, "%s/input/%s/%s-H", spool_directory, message_subdir, id);
   if (Uunlink(spoolname) < 0)
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname);
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s",
+      spoolname, strerror(errno));
 
   /* Log the end of this message, with queue time if requested. */
 
@@ -6572,6 +6630,9 @@ if (addr_defer == NULL)
       readconf_printtime(time(NULL) - received_time));
   else
     log_write(0, LOG_MAIN, "Completed");
+
+  /* Unset deliver_freeze so that we won't try to move the spool files further down */
+  deliver_freeze = FALSE;
   }
 
 /* If there are deferred addresses, we are keeping this message because it is
@@ -6770,8 +6831,7 @@ else if (addr_defer != (address_item *)(+1))
         if (errors_reply_to != NULL)
           fprintf(f, "Reply-To: %s\n", errors_reply_to);
         fprintf(f, "Auto-Submitted: auto-replied\n");
-        fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
-          qualify_domain_sender);
+        moan_write_from(f);
         fprintf(f, "To: %s\n", recipients);
 
         wmf_text = next_emf(wmf, US"header");
@@ -6978,6 +7038,7 @@ expand_check_condition) to do a lookup. We must therefore be sure everything is
 released. */
 
 search_tidyup();
+acl_where = ACL_WHERE_UNKNOWN;
 return final_yield;
 }