build: use pkg-config for i18n
[exim.git] / src / src / deliver.c
index b828f8094346ec5b3a13459b79511990642ace3f..0ba5f81b07c0ae838b7491882de25f88cd3d0a93 100644 (file)
@@ -680,7 +680,7 @@ else if (testflag(addr, af_homonym))
   {
   if (addr->transport)
     tree_add_nonrecipient(
-      string_sprintf("%s/%s", addr->unique + 3, addr->transport->name));
+      string_sprintf("%s/%s", addr->unique + 3, addr->transport->drinst.name));
   }
 
 /* Non-homonymous child address */
@@ -903,10 +903,11 @@ const uschar * save_domain = deliver_domain;
 const uschar * save_local =  deliver_localpart;
 const uschar * save_host = deliver_host;
 const uschar * save_address = deliver_host_address;
-uschar * save_rn = router_name, * save_tn = transport_name;
+const uschar * save_rn = router_name;
+const uschar * save_tn = transport_name;
 const int      save_port =   deliver_host_port;
 
-router_name =    addr->router ? addr->router->name : NULL;
+router_name =    addr->router ? addr->router->drinst.name : NULL;
 deliver_domain = addr->domain;
 deliver_localpart = addr->local_part;
 deliver_host =   addr->host_used ? addr->host_used->name : NULL;
@@ -924,13 +925,14 @@ if (!addr->transport)
   }
 else
   {
-  transport_name = addr->transport->name;
+  const uschar * dr_name = addr->transport->drinst.driver_name;
 
+  transport_name = addr->transport->drinst.name;
   (void) event_raise(addr->transport->event_action, event,
            addr->host_used
-           || Ustrcmp(addr->transport->driver_name, "smtp") == 0
-           || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
-           || Ustrcmp(addr->transport->driver_name, "autoreply") == 0
+           || Ustrcmp(dr_name, "smtp") == 0
+           || Ustrcmp(dr_name, "lmtp") == 0
+           || Ustrcmp(dr_name, "autoreply") == 0
           ? addr->message : NULL,
           NULL);
   }
@@ -1033,7 +1035,8 @@ so won't necessarily look like a path. Add extra text for this case. */
 if (  testflag(addr, af_pfr)
    || (  success
       && addr->router && addr->router->log_as_local
-      && addr->transport && addr->transport->info->local
+      && addr->transport
+      && ((transport_info *)addr->transport->drinst.info)->local
    )  )
   {
   if (testflag(addr, af_file) && addr->local_part[0] != '/')
@@ -1177,16 +1180,16 @@ if (msg)
 
 /* For a delivery from a system filter, there may not be a router */
 if (addr->router)
-  g = string_append(g, 2, US" R=", addr->router->name);
+  g = string_append(g, 2, US" R=", addr->router->drinst.name);
 
-g = string_append(g, 2, US" T=", addr->transport->name);
+g = string_append(g, 2, US" T=", addr->transport->drinst.name);
 
 if (LOGGING(delivery_size))
   g = string_fmt_append(g, " S=%d", transport_count);
 
 /* Local delivery */
 
-if (addr->transport->info->local)
+if (((transport_info *)addr->transport->drinst.info)->local)
   {
   if (addr->host_list)
     g = string_append(g, 2, US" H=", addr->host_list->name);
@@ -1261,7 +1264,8 @@ else
 
 if (  LOGGING(smtp_confirmation)
    && addr->message
-   && (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)
+   && (  addr->host_used
+      || Ustrcmp(addr->transport->drinst.driver_name, "lmtp") == 0)
    )
   {
   unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024;
@@ -1329,7 +1333,7 @@ so nothing has been done at all, both variables contain null strings. */
 if (driver_name)
   {
   if (driver_kind[1] == 't' && addr->router)
-    g = string_append(g, 2, US" R=", addr->router->name);
+    g = string_append(g, 2, US" R=", addr->router->drinst.name);
   g = string_fmt_append(g, " %c=%s", toupper(driver_kind[1]), driver_name);
   }
 else if (driver_kind)
@@ -1406,9 +1410,9 @@ if (used_return_path && LOGGING(return_path_on_delivery))
   g = string_append(g, 3, US" P=<", used_return_path, US">");
 
 if (addr->router)
-  g = string_append(g, 2, US" R=", addr->router->name);
+  g = string_append(g, 2, US" R=", addr->router->drinst.name);
 if (addr->transport)
-  g = string_append(g, 2, US" T=", addr->transport->name);
+  g = string_append(g, 2, US" T=", addr->transport->drinst.name);
 
 if (addr->host_used)
   g = d_hostlog(g, addr);
@@ -1477,7 +1481,7 @@ if (driver_type == EXIM_DTYPE_TRANSPORT)
   {
   if (addr->transport)
     {
-    driver_name = addr->transport->name;
+    driver_name = addr->transport->drinst.name;
     driver_kind = US" transport";
     f.disable_logging = addr->transport->disable_logging;
     }
@@ -1487,7 +1491,7 @@ else if (driver_type == EXIM_DTYPE_ROUTER)
   {
   if (addr->router)
     {
-    driver_name = addr->router->name;
+    driver_name = addr->router->drinst.name;
     driver_kind = US" router";
     f.disable_logging = addr->router->disable_logging;
     }
@@ -1531,7 +1535,7 @@ if (addr->return_file >= 0 && addr->return_filename)
 
   if (fstat(addr->return_file, &statbuf) == 0 && statbuf.st_size > 0)
     {
-    transport_instance *tb = addr->transport;
+    transport_instance * tb = addr->transport;
 
     /* Handle logging options */
 
@@ -1540,11 +1544,11 @@ if (addr->return_file >= 0 && addr->return_filename)
        || result == DEFER && tb->log_defer_output
        )
       {
-      uschar *s;
-      FILE *f = Ufopen(addr->return_filename, "rb");
+      uschar * s;
+      FILE * f = Ufopen(addr->return_filename, "rb");
       if (!f)
         log_write(0, LOG_MAIN|LOG_PANIC, "failed to open %s to log output "
-          "from %s transport: %s", addr->return_filename, tb->name,
+          "from %s transport: %s", addr->return_filename, tb->drinst.name,
           strerror(errno));
       else
         if ((s = US Ufgets(big_buffer, big_buffer_size, f)))
@@ -1555,7 +1559,7 @@ if (addr->return_file >= 0 && addr->return_filename)
           *p = 0;
           sp = string_printing(big_buffer);
           log_write(0, LOG_MAIN, "<%s>: %s transport output: %s",
-            addr->address, tb->name, sp);
+            addr->address, tb->drinst.name, sp);
           }
       (void)fclose(f);
       }
@@ -1857,8 +1861,8 @@ if (tp->gid_set)
 else if (tp->expand_gid)
   {
   GET_OPTION("group");
-  if (!route_find_expanded_group(tp->expand_gid, tp->name, US"transport", gidp,
-    &addr->message))
+  if (!route_find_expanded_group(tp->expand_gid, tp->drinst.name, US"transport",
+    gidp, &addr->message))
     {
     common_error(FALSE, addr, ERRNO_GIDFAIL, NULL);
     return FALSE;
@@ -1885,8 +1889,8 @@ else if (tp->expand_uid)
   {
   struct passwd *pw;
   GET_OPTION("user");
-  if (!route_find_expanded_user(tp->expand_uid, tp->name, US"transport", &pw,
-       uidp, &(addr->message)))
+  if (!route_find_expanded_user(tp->expand_uid, tp->drinst.name, US"transport",
+       &pw, uidp, &(addr->message)))
     {
     common_error(FALSE, addr, ERRNO_UIDFAIL, NULL);
     return FALSE;
@@ -1939,7 +1943,7 @@ a uid, it must also provide a gid. */
 if (!gid_set)
   {
   common_error(TRUE, addr, ERRNO_GIDFAIL, US"User set without group for "
-    "%s transport", tp->name);
+    "%s transport", tp->drinst.name);
   return FALSE;
   }
 
@@ -1954,7 +1958,7 @@ nuname = check_never_users(*uidp, never_users)
 if (nuname)
   {
   common_error(TRUE, addr, ERRNO_UIDFAIL, US"User %ld set for %s transport "
-    "is on the %s list", (long int)(*uidp), tp->name, nuname);
+    "is on the %s list", (long int)(*uidp), tp->drinst.name, nuname);
   return FALSE;
   }
 
@@ -1998,9 +2002,9 @@ if (expand_string_message)
   rc = DEFER;
   addr->message = size_limit == -1
     ? string_sprintf("failed to expand message_size_limit "
-      "in %s transport: %s", tp->name, expand_string_message)
+      "in %s transport: %s", tp->drinst.name, expand_string_message)
     : string_sprintf("invalid message_size_limit "
-      "in %s transport: %s", tp->name, expand_string_message);
+      "in %s transport: %s", tp->drinst.name, expand_string_message);
   }
 else if (size_limit > 0 && message_size > size_limit)
   {
@@ -2037,13 +2041,14 @@ static BOOL
 previously_transported(address_item *addr, BOOL testing)
 {
 uschar * s = string_sprintf("%s/%s",
-  addr->unique + (testflag(addr, af_homonym) ? 3:0), addr->transport->name);
+  addr->unique + (testflag(addr, af_homonym) ? 3:0),
+  addr->transport->drinst.name);
 
 if (tree_search(tree_nonrecipients, s) != 0)
   {
   DEBUG(D_deliver|D_route|D_transport)
     debug_printf("%s was previously delivered (%s transport): discarded\n",
-    addr->address, addr->transport->name);
+    addr->address, addr->transport->drinst.name);
   if (!testing) child_done(addr, tod_stamp(tod_log));
   return TRUE;
   }
@@ -2141,7 +2146,8 @@ int pfd[2];
 pid_t pid;
 uschar *working_directory;
 address_item *addr2;
-transport_instance *tp = addr->transport;
+transport_instance * tp = addr->transport;
+const uschar * trname = tp->drinst.name;
 
 /* 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. */
@@ -2161,7 +2167,7 @@ if (tp->return_path)
     {
     common_error(TRUE, addr, ERRNO_EXPANDFAIL,
       US"Failed to expand return path \"%s\" in %s transport: %s",
-      tp->return_path, tp->name, expand_string_message);
+      tp->return_path, trname, expand_string_message);
     return;
     }
   }
@@ -2192,14 +2198,14 @@ if (  (deliver_home = tp->home_dir)             /* Set in transport, or */
   if (!(deliver_home = expand_string(rawhome)))
     {
     common_error(TRUE, addr, ERRNO_EXPANDFAIL, US"home directory \"%s\" failed "
-      "to expand for %s transport: %s", rawhome, tp->name,
+      "to expand for %s transport: %s", rawhome, trname,
       expand_string_message);
     return;
     }
   if (*deliver_home != '/')
     {
     common_error(TRUE, addr, ERRNO_NOTABSOLUTE, US"home directory path \"%s\" "
-      "is not absolute for %s transport", deliver_home, tp->name);
+      "is not absolute for %s transport", deliver_home, trname);
     return;
     }
   }
@@ -2219,14 +2225,14 @@ if (working_directory)
   if (!(working_directory = expand_string(raw)))
     {
     common_error(TRUE, addr, ERRNO_EXPANDFAIL, US"current directory \"%s\" "
-      "failed to expand for %s transport: %s", raw, tp->name,
+      "failed to expand for %s transport: %s", raw, trname,
       expand_string_message);
     return;
     }
   if (*working_directory != '/')
     {
     common_error(TRUE, addr, ERRNO_NOTABSOLUTE, US"current directory path "
-      "\"%s\" is not absolute for %s transport", working_directory, tp->name);
+      "\"%s\" is not absolute for %s transport", working_directory, trname);
     return;
     }
   }
@@ -2246,12 +2252,12 @@ if (  !shadowing
 
   addr->return_filename =
     spool_fname(US"msglog", message_subdir, message_id,
-      string_sprintf("-%d-%d", getpid(), return_count++));
+      string_sprintf("-%ld-%d", (long)getpid(), return_count++));
 
   if ((addr->return_file = open_msglog_file(addr->return_filename, 0400, &error)) < 0)
     {
     common_error(TRUE, addr, errno, US"Unable to %s file for %s transport "
-      "to return message: %s", error, tp->name, strerror(errno));
+      "to return message: %s", error, trname, strerror(errno));
     return;
     }
   }
@@ -2348,7 +2354,7 @@ if ((pid = exim_fork(US"delivery-local")) == 0)
     FD_CLOEXEC);
   exim_setugid(uid, gid, use_initgroups,
     string_sprintf("local delivery to %s <%s> transport=%s", addr->local_part,
-      addr->address, addr->transport->name));
+      addr->address, addr->transport->drinst.name));
 
   DEBUG(D_deliver)
     {
@@ -2372,14 +2378,14 @@ if ((pid = exim_fork(US"delivery-local")) == 0)
     {
     BOOL ok = TRUE;
     set_process_info("delivering %s to %s using %s", message_id,
-     addr->local_part, tp->name);
+     addr->local_part, trname);
 
     /* Setting these globals in the subprocess means we need never clear them */
 
-    transport_name = tp->name;
-    if (addr->router) router_name = addr->router->name;
-    driver_srcfile = tp->srcfile;
-    driver_srcline = tp->srcline;
+    transport_name = trname;
+    if (addr->router) router_name = addr->router->drinst.name;
+    driver_srcfile = tp->drinst.srcfile;
+    driver_srcline = tp->drinst.srcline;
 
     /* If a transport filter has been specified, set up its argument list.
     Any errors will get put into the address, and FALSE yielded. */
@@ -2395,8 +2401,9 @@ if ((pid = exim_fork(US"delivery-local")) == 0)
 
     if (ok)
       {
+      transport_info * ti = tp->drinst.info;
       debug_print_string(tp->debug_string);
-      replicate = !(tp->info->code)(addr->transport, addr);
+      replicate = !(ti->code)(addr->transport, addr);
       }
     }
 
@@ -2551,7 +2558,7 @@ if (!shadowing)
     if (addr2->transport_return == OK)
       {
       if (testflag(addr2, af_homonym))
-       sprintf(CS big_buffer, "%.500s/%s\n", addr2->unique + 3, tp->name);
+       sprintf(CS big_buffer, "%.500s/%s\n", addr2->unique + 3, trname);
       else
        sprintf(CS big_buffer, "%.500s\n", addr2->unique);
 
@@ -2586,7 +2593,7 @@ while ((rc = wait(&status)) != pid)
   if (rc < 0 && errno == ECHILD)      /* Process has vanished */
     {
     log_write(0, LOG_MAIN, "%s transport process vanished unexpectedly",
-      addr->transport->driver_name);
+      addr->transport->drinst.driver_name);
     status = 0;
     break;
     }
@@ -2600,7 +2607,7 @@ if ((status & 0xffff) != 0)
     addr->special_action = SPECIAL_FREEZE;
   log_write(0, LOG_MAIN|LOG_PANIC, "%s transport process returned non-zero "
     "status 0x%04x: %s %d",
-    addr->transport->driver_name,
+    addr->transport->drinst.driver_name,
     status,
     msb == 0 ? "terminated by signal" : "exit code",
     code);
@@ -2622,7 +2629,7 @@ if (addr->special_action == SPECIAL_WARN)
     if (!(warn_message = expand_string(warn_message)))
       log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand \"%s\" (warning "
        "message for %s transport): %s", addr->transport->warn_message,
-       addr->transport->name, expand_string_message);
+       addr->transport->drinst.name, expand_string_message);
 
     else if ((pid = child_open_exim(&fd, US"tpt-warning-message")) > 0)
       {
@@ -2655,6 +2662,7 @@ the key for the hints database used for the concurrency count. */
 static BOOL
 tpt_parallel_check(transport_instance * tp, address_item * addr, uschar ** key)
 {
+const uschar * trname = tp->drinst.name;
 unsigned max_parallel;
 
 GET_OPTION("max_parallel");
@@ -2664,20 +2672,20 @@ max_parallel = (unsigned) expand_string_integer(tp->max_parallel, TRUE);
 if (expand_string_message)
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand max_parallel option "
-       "in %s transport (%s): %s", tp->name, addr->address,
+       "in %s transport (%s): %s", trname, addr->address,
        expand_string_message);
   return TRUE;
   }
 
 if (max_parallel > 0)
   {
-  uschar * serialize_key = string_sprintf("tpt-serialize-%s", tp->name);
+  uschar * serialize_key = string_sprintf("tpt-serialize-%s", trname);
   if (!enq_start(serialize_key, max_parallel))
     {
     address_item * next;
     DEBUG(D_transport)
       debug_printf("skipping tpt %s because concurrency limit %u reached\n",
-                 tp->name, max_parallel);
+                 trname, max_parallel);
     do
       {
       next = addr->next;
@@ -2723,8 +2731,9 @@ while (addr_local)
   address_item *addr2, *addr3, *nextaddr;
   int logflags = LOG_MAIN;
   int logchar = f.dont_deliver? '*' : '=';
-  transport_instance *tp;
+  transport_instance * tp;
   uschar * serialize_key = NULL;
+  const uschar * trname;
 
   /* Pick the first undelivered address off the chain */
 
@@ -2742,11 +2751,12 @@ while (addr_local)
     logflags |= LOG_PANIC;
     f.disable_logging = FALSE;  /* Jic */
     addr->message = addr->router
-      ? string_sprintf("No transport set by %s router", addr->router->name)
+      ? string_sprintf("No transport set by %s router", addr->router->drinst.name)
       : US"No transport set by system filter";
     post_process_one(addr, DEFER, logflags, EXIM_DTYPE_TRANSPORT, 0);
     continue;
     }
+  trname = tp->drinst.name;
 
   /* Check that this base address hasn't previously been delivered to this
   transport. The check is necessary at this point to handle homonymic addresses
@@ -2789,7 +2799,7 @@ while (addr_local)
       if (!batch_id)
         {
         log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand batch_id option "
-          "in %s transport (%s): %s", tp->name, addr->address,
+          "in %s transport (%s): %s", trname, addr->address,
           expand_string_message);
         batch_count = tp->batch_max;
         }
@@ -2846,7 +2856,7 @@ while (addr_local)
         if (!bid)
           {
           log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand batch_id option "
-            "in %s transport (%s): %s", tp->name, next->address,
+            "in %s transport (%s): %s", trname, next->address,
             expand_string_message);
           ok = FALSE;
           }
@@ -2896,7 +2906,12 @@ while (addr_local)
   of these checks, rather than for all local deliveries, because some local
   deliveries (e.g. to pipes) can take a substantial time. */
 
-  if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
+  if (continue_retry_db && continue_retry_db != (open_db *)-1)
+    {
+    DEBUG(D_hints_lookup) debug_printf("using cached retry hintsdb handle\n");
+    dbm_file = continue_retry_db;
+    }
+  else if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
     DEBUG(D_deliver|D_retry|D_hints_lookup)
       debug_printf("no retry data available\n");
 
@@ -2905,61 +2920,66 @@ while (addr_local)
   while (addr2)
     {
     BOOL ok = TRUE;   /* to deliver this address */
-    uschar *retry_key;
 
-    /* Set up the retry key to include the domain or not, and change its
-    leading character from "R" to "T". Must make a copy before doing this,
-    because the old key may be pointed to from a "delete" retry item after
-    a routing delay. */
-
-    retry_key = string_copy(
-      tp->retry_use_local_part ? addr2->address_retry_key :
-        addr2->domain_retry_key);
-    *retry_key = 'T';
+    if (f.queue_2stage)
+      {
+      DEBUG(D_deliver)
+       debug_printf_indent("no router retry check (ph1 qrun)\n");
+      }
+    else
+      {
+      /* Set up the retry key to include the domain or not, and change its
+      leading character from "R" to "T". Must make a copy before doing this,
+      because the old key may be pointed to from a "delete" retry item after
+      a routing delay. */
+      uschar * retry_key = string_copy(tp->retry_use_local_part
+                       ? addr2->address_retry_key : addr2->domain_retry_key);
+      *retry_key = 'T';
 
-    /* Inspect the retry data. If there is no hints file, delivery happens. */
+      /* Inspect the retry data. If there is no hints file, delivery happens. */
 
-    if (dbm_file)
-      {
-      dbdata_retry *retry_record = dbfn_read(dbm_file, retry_key);
+      if (dbm_file)
+       {
+       dbdata_retry * retry_record = dbfn_read(dbm_file, retry_key);
 
-      /* If there is no retry record, delivery happens. If there is,
-      remember it exists so it can be deleted after a successful delivery. */
+       /* If there is no retry record, delivery happens. If there is,
+       remember it exists so it can be deleted after a successful delivery. */
 
-      if (retry_record)
-        {
-        setflag(addr2, af_lt_retry_exists);
+       if (retry_record)
+         {
+         setflag(addr2, af_lt_retry_exists);
 
-        /* A retry record exists for this address. If queue running and not
-        forcing, inspect its contents. If the record is too old, or if its
-        retry time has come, or if it has passed its cutoff time, delivery
-        will go ahead. */
+         /* A retry record exists for this address. If queue running and not
+         forcing, inspect its contents. If the record is too old, or if its
+         retry time has come, or if it has passed its cutoff time, delivery
+         will go ahead. */
 
-        DEBUG(D_retry)
-          {
-          debug_printf("retry record exists: age=%s ",
-            readconf_printtime(now - retry_record->time_stamp));
-          debug_printf("(max %s)\n", readconf_printtime(retry_data_expire));
-          debug_printf("  time to retry = %s expired = %d\n",
-            readconf_printtime(retry_record->next_try - now),
-            retry_record->expired);
-          }
+         DEBUG(D_retry)
+           {
+           debug_printf("retry record exists: age=%s ",
+             readconf_printtime(now - retry_record->time_stamp));
+           debug_printf("(max %s)\n", readconf_printtime(retry_data_expire));
+           debug_printf("  time to retry = %s expired = %d\n",
+             readconf_printtime(retry_record->next_try - now),
+             retry_record->expired);
+           }
 
-        if (f.queue_running && !f.deliver_force)
-          {
-          ok = (now - retry_record->time_stamp > retry_data_expire)
-           || (now >= retry_record->next_try)
-           || retry_record->expired;
+         if (f.queue_running && !f.deliver_force)
+           {
+           ok = (now - retry_record->time_stamp > retry_data_expire)
+             || (now >= retry_record->next_try)
+             || retry_record->expired;
 
-          /* If we haven't reached the retry time, there is one more check
-          to do, which is for the ultimate address timeout. */
+           /* If we haven't reached the retry time, there is one more check
+           to do, which is for the ultimate address timeout. */
 
-          if (!ok)
-            ok = retry_ultimate_address_timeout(retry_key, addr2->domain,
-                retry_record, now);
-          }
-        }
-      else DEBUG(D_retry) debug_printf("no retry record exists\n");
+           if (!ok)
+             ok = retry_ultimate_address_timeout(retry_key, addr2->domain,
+                 retry_record, now);
+           }
+         }
+       else DEBUG(D_retry) debug_printf("no retry record exists\n");
+       }
       }
 
     /* This address is to be delivered. Leave it on the chain. */
@@ -2985,7 +3005,11 @@ while (addr_local)
       }
     }
 
-  if (dbm_file) dbfn_close(dbm_file);
+  if (dbm_file)
+    if (dbm_file != continue_retry_db)
+      { dbfn_close(dbm_file); dbm_file = NULL; }
+    else
+      DEBUG(D_hints_lookup) debug_printf("retaining retry hintsdb handle\n");
 
   /* If there are no addresses left on the chain, they all deferred. Loop
   for the next set of addresses. */
@@ -3033,15 +3057,15 @@ while (addr_local)
 
   if (  tp->shadow
      && (  !tp->shadow_condition
-        || expand_check_condition(tp->shadow_condition, tp->name, US"transport")
+        || expand_check_condition(tp->shadow_condition, trname, US"transport")
      )  )
     {
-    transport_instance *stp;
-    address_item *shadow_addr = NULL;
-    address_item **last = &shadow_addr;
+    transport_instance * stp;
+    address_item * shadow_addr = NULL;
+    address_item ** last = &shadow_addr;
 
-    for (stp = transports; stp; stp = stp->next)
-      if (Ustrcmp(stp->name, tp->shadow) == 0) break;
+    for (stp = transports; stp; stp = stp->drinst.next)
+      if (Ustrcmp(stp->drinst.name, tp->shadow) == 0) break;
 
     if (!stp)
       log_write(0, LOG_MAIN|LOG_PANIC, "shadow transport \"%s\" not found ",
@@ -3071,6 +3095,7 @@ while (addr_local)
 
     if (shadow_addr)
       {
+      const uschar * s_trname = stp->drinst.name;
       int save_count = transport_count;
 
       DEBUG(D_deliver|D_transport)
@@ -3082,8 +3107,8 @@ while (addr_local)
         int sresult = shadow_addr->transport_return;
         *(uschar **)shadow_addr->shadow_message =
          sresult == OK
-         ? string_sprintf(" ST=%s", stp->name)
-         : string_sprintf(" ST=%s (%s%s%s)", stp->name,
+         ? string_sprintf(" ST=%s", s_trname)
+         : string_sprintf(" ST=%s (%s%s%s)", s_trname,
              shadow_addr->basic_errno <= 0
              ? US""
              : US strerror(shadow_addr->basic_errno),
@@ -3098,7 +3123,7 @@ while (addr_local)
 
         DEBUG(D_deliver|D_transport)
           debug_printf("%s shadow transport returned %s for %s\n",
-            stp->name, rc_to_string(sresult), shadow_addr->address);
+            s_trname, rc_to_string(sresult), shadow_addr->address);
         }
 
       DEBUG(D_deliver|D_transport)
@@ -3127,7 +3152,7 @@ while (addr_local)
 
     DEBUG(D_deliver|D_transport)
       debug_printf("%s transport returned %s for %s\n",
-        tp->name, rc_to_string(result), addr2->address);
+        trname, rc_to_string(result), addr2->address);
 
     /* If there is a retry_record, or if delivery is deferred, build a retry
     item for setting a new retry time or deleting the old retry record from
@@ -3333,8 +3358,8 @@ same channel (pipe).
 
 */
 
-DEBUG(D_deliver) debug_printf("reading pipe for subprocess %d (%s)\n",
-  (int)p->pid, eop? "ended" : "not ended yet");
+DEBUG(D_deliver) debug_printf("reading pipe for subprocess %ld (%s)\n",
+  (long)p->pid, eop? "ended" : "not ended yet");
 
 while (!done)
   {
@@ -3346,8 +3371,9 @@ while (!done)
   size_t required = PIPE_HEADER_SIZE; /* first the pipehaeder, later the data */
   ssize_t got;
 
-  DEBUG(D_deliver) debug_printf(
-    "expect %lu bytes (pipeheader) from tpt process %d\n", (u_long)required, pid);
+  DEBUG(D_deliver)
+    debug_printf("expect %lu bytes (pipeheader) from tpt process %ld\n",
+    (u_long)required, (long)pid);
 
   /* We require(!) all the PIPE_HEADER_SIZE bytes here, as we know,
   they're written in a timely manner, so waiting for the write shouldn't hurt a lot.
@@ -3357,16 +3383,16 @@ while (!done)
   if ((got = readn(fd, pipeheader, required)) != required)
     {
     msg = string_sprintf("got " SSIZE_T_FMT " of %d bytes (pipeheader) "
-      "from transport process %d for transport %s",
-      got, PIPE_HEADER_SIZE, pid, addr->transport->driver_name);
+      "from transport process %ld for transport %s",
+      got, PIPE_HEADER_SIZE, (long)pid, addr->transport->drinst.driver_name);
     done = TRUE;
     break;
     }
 
   pipeheader[PIPE_HEADER_SIZE] = '\0';
   DEBUG(D_deliver)
-    debug_printf("got %ld bytes (pipeheader) '%c' from transport process %d\n",
-      (long) got, *id, pid);
+    debug_printf("got %ld bytes (pipeheader) '%c' from transport process %ld\n",
+      (long) got, *id, (long)pid);
 
   {
   /* If we can't decode the pipeheader, the subprocess seems to have a
@@ -3376,16 +3402,16 @@ while (!done)
   if (*endc)
     {
     msg = string_sprintf("failed to read pipe "
-      "from transport process %d for transport %s: error decoding size from header",
-      pid, addr->transport->driver_name);
+      "from transport process %ld for transport %s: error decoding size from header",
+      (long)pid, addr ? addr->transport->drinst.driver_name : US"?");
     done = TRUE;
     break;
     }
   }
 
   DEBUG(D_deliver)
-    debug_printf("expect %lu bytes (pipedata) from transport process %d\n",
-      (u_long)required, pid);
+    debug_printf("expect %lu bytes (pipedata) from transport process %ld\n",
+      (u_long)required, (long)pid);
 
   /* Same as above, the transport process will write the bytes announced
   in a timely manner, so we can just wait for the bytes, getting less than expected
@@ -3393,8 +3419,8 @@ while (!done)
   if ((got = readn(fd, big_buffer, required)) != required)
     {
     msg = string_sprintf("got only " SSIZE_T_FMT " of " SIZE_T_FMT
-      " bytes (pipedata) from transport process %d for transport %s",
-      got, required, pid, addr->transport->driver_name);
+      " bytes (pipedata) from transport process %ld for transport %s",
+      got, required, (long)pid, addr->transport->drinst.driver_name);
     done = TRUE;
     break;
     }
@@ -3583,8 +3609,8 @@ while (!done)
        {
        ADDR_MISMATCH:
        msg = string_sprintf("address count mismatch for data read from pipe "
-         "for transport process %d for transport %s", pid,
-           addrlist->transport->driver_name);
+         "for transport process %ld for transport %s",
+           (long)pid, addrlist->transport->drinst.driver_name);
        done = TRUE;
        break;
        }
@@ -3660,16 +3686,20 @@ while (!done)
            h->dnssec = *ptr == '2' ? DS_YES
                      : *ptr == '1' ? DS_NO
                      : DS_UNK;
-           ptr++;
            addr->host_used = h;
            }
-         else ptr++;
+         ptr++;
 
          continue_flags = 0;
+#ifndef DISABLE_TLS
          if (testflag(addr, af_cert_verified)) continue_flags |= CTF_CV;
+# ifdef SUPPORT_DANE
          if (testflag(addr, af_dane_verified)) continue_flags |= CTF_DV;
+# endif
+# ifndef DISABLE_TLS_RESUME
          if (testflag(addr, af_tls_resume))    continue_flags |= CTF_TR;
-
+# endif
+#endif
          /* Finished with this address */
 
          addr = addr->next;
@@ -3724,8 +3754,8 @@ while (!done)
          close(recvd_fd);
 
          DEBUG(D_deliver)
-           debug_printf("continue: tpt '%s' host '%s' addr '%s' seq %d\n",
-                         continue_transport, continue_hostname,
+           debug_printf("continue: fd %d tpt %s host '%s' addr '%s' seq %d\n",
+                         recvd_fd, continue_transport, continue_hostname,
                          continue_host_address, continue_sequence);
          break;
          }
@@ -3767,8 +3797,8 @@ while (!done)
 
     default:
       msg = string_sprintf("malformed data (%d) read from pipe for transport "
-       "process %d for transport %s", ptr[-1], pid,
-         addr->transport->driver_name);
+       "process %ld for transport %s", ptr[-1], (long)pid,
+         addr ? addr->transport->drinst.driver_name : US"?");
       done = TRUE;
       break;
     }
@@ -3801,8 +3831,8 @@ something is wrong. */
 
 if (!msg && addr)
   msg = string_sprintf("insufficient address data read from pipe "
-    "for transport process %d for transport %s", pid,
-      addr->transport->driver_name);
+    "for transport process %ld for transport %s", (long)pid,
+      addr->transport->drinst.driver_name);
 
 /* If an error message is set, something has gone wrong in getting back
 the delivery data. Put the message into each address and freeze it. */
@@ -3813,7 +3843,8 @@ if (msg)
     addr->transport_return = DEFER;
     addr->special_action = SPECIAL_FREEZE;
     addr->message = msg;
-    log_write(0, LOG_MAIN|LOG_PANIC, "Delivery status for %s: %s\n", addr->address, addr->message);
+    log_write(0, LOG_MAIN|LOG_PANIC, "Delivery status for %s: %s\n",
+             addr->address, addr->message);
     }
 
 /* Return TRUE to indicate we have got all we need from this process, even
@@ -4006,8 +4037,8 @@ for (;;)   /* Normally we do not repeat this loop */
         {
         if ((pid = parlist[poffset].pid) != 0 && kill(pid, 0) == 0)
           {
-          DEBUG(D_deliver) debug_printf("process %d still exists: assume "
-            "stolen by strace\n", (int)pid);
+          DEBUG(D_deliver) debug_printf("process %ld still exists: assume "
+            "stolen by strace\n", (long)pid);
           break;   /* With poffset set */
           }
         }
@@ -4068,8 +4099,8 @@ for (;;)   /* Normally we do not repeat this loop */
             if (endedpid == pid) goto PROCESS_DONE;
             if (endedpid != (pid_t)(-1) || errno != EINTR)
               log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Unexpected error return "
-                "%d (errno = %d) from waitpid() for process %d",
-                (int)endedpid, errno, (int)pid);
+                "%d (errno = %d) from waitpid() for process %ld",
+                (int)endedpid, errno, (long)pid);
             }
         }
       }
@@ -4091,8 +4122,8 @@ for (;;)   /* Normally we do not repeat this loop */
   /* This situation is an error, but it's probably better to carry on looking
   for another process than to give up (as we used to do). */
 
-  log_write(0, LOG_MAIN|LOG_PANIC, "Process %d finished: not found in remote "
-    "transport process list", pid);
+  log_write(0, LOG_MAIN|LOG_PANIC, "Process %ld finished: not found in remote "
+    "transport process list", (long)pid);
   }  /* End of the "for" loop */
 
 /* Come here when all the data was completely read after a poll(), and
@@ -4103,9 +4134,9 @@ PROCESS_DONE:
 DEBUG(D_deliver)
   {
   if (status == 0)
-    debug_printf("remote delivery process %d ended\n", (int)pid);
+    debug_printf("remote delivery process %ld ended\n", (long)pid);
   else
-    debug_printf("remote delivery process %d ended: status=%04x\n", (int)pid,
+    debug_printf("remote delivery process %ld ended: status=%04x\n", (long)pid,
       status);
   }
 
@@ -4128,7 +4159,7 @@ if ((status & 0xffff) != 0)
 
   msg = string_sprintf("%s transport process returned non-zero status 0x%04x: "
     "%s %d",
-    addrlist->transport->driver_name,
+    addrlist->transport->drinst.driver_name,
     status,
     msb == 0 ? "terminated by signal" : "exit code",
     code);
@@ -4196,7 +4227,7 @@ while (parcount > max)
     {
     transport_instance * tp = doneaddr->transport;
     if (tp->max_parallel)
-      enq_end(string_sprintf("tpt-serialize-%s", tp->name));
+      enq_end(string_sprintf("tpt-serialize-%s", tp->drinst.name));
 
     remote_post_process(doneaddr, LOG_MAIN, NULL, fallback);
     }
@@ -4371,7 +4402,7 @@ So look out for the place it gets used.
   if (tp->expand_multi_domain)
     deliver_set_expansions(addr);
 
-  if (exp_bool(addr, US"transport", tp->name, D_transport,
+  if (exp_bool(addr, US"transport", tp->drinst.name, D_transport,
                US"multi_domain", tp->multi_domain, tp->expand_multi_domain,
                &multi_domain) != OK)
     {
@@ -4482,7 +4513,7 @@ nonmatch domains
          || (  (
                (void)(!tp->expand_multi_domain || ((void)deliver_set_expansions(next), 1)),
                exp_bool(addr,
-                   US"transport", next->transport->name, D_transport,
+                   US"transport", next->transport->drinst.name, D_transport,
                    US"multi_domain", next->transport->multi_domain,
                    next->transport->expand_multi_domain, &md) == OK
                )
@@ -4593,7 +4624,7 @@ nonmatch domains
   f.continue_more = FALSE;           /* In case got set for the last lot */
   if (continue_transport)
     {
-    BOOL ok = Ustrcmp(continue_transport, tp->name) == 0;
+    BOOL ok = Ustrcmp(continue_transport, tp->drinst.name) == 0;
 /*XXX do we need to check for a DANEd conn vs. a change of domain? */
 
     /* If the transport is about to override the host list do not check
@@ -4604,11 +4635,11 @@ nonmatch domains
 
     if (ok)
       {
-      smtp_transport_options_block * ob;
+      transport_info * ti = tp->drinst.info;
+      smtp_transport_options_block * ob = tp->drinst.options_block;
 
-      if (  !(  Ustrcmp(tp->info->driver_name, "smtp") == 0
-            && (ob = (smtp_transport_options_block *)tp->options_block)
-            && ob->hosts_override && ob->hosts
+      if (  !(  Ustrcmp(ti->drinfo.driver_name, "smtp") == 0
+            && ob && ob->hosts_override && ob->hosts
             )
         && addr->host_list
         )
@@ -4627,8 +4658,8 @@ nonmatch domains
     if (!ok)
       {
       DEBUG(D_deliver) debug_printf("not suitable for continue_transport (%s)\n",
-       Ustrcmp(continue_transport, tp->name) != 0
-       ? string_sprintf("tpt %s vs %s", continue_transport, tp->name)
+       Ustrcmp(continue_transport, tp->drinst.name) != 0
+       ? string_sprintf("tpt %s vs %s", continue_transport, tp->drinst.name)
        : string_sprintf("no host matching %s", continue_hostname));
       if (serialize_key) enq_end(serialize_key);
 
@@ -4637,7 +4668,8 @@ nonmatch domains
        for (next = addr; ; next = next->next)
           {
           next->host_list = next->fallback_hosts;
-          DEBUG(D_deliver) debug_printf("%s queued for fallback host(s)\n", next->address);
+          DEBUG(D_deliver)
+           debug_printf("%s queued for fallback host(s)\n", next->address);
           if (!next->next) break;
           }
         next->next = addr_fallback;
@@ -4776,6 +4808,10 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
 */
 
   /*XXX what about firsttime? */
+  /*XXX also, ph1? Note tp->name would possibly change per message,
+  so a check/close/open would be needed. Might was to change that var name
+  "continue_wait_db" as we'd be using it for a non-continued-transport
+  context. */
   if (continue_transport && !exim_lockfile_needed())
     if (!continue_wait_db)
       {
@@ -4786,17 +4822,17 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
       continue_next_id[0] = '\0';
       }
 
-  if ((pid = exim_fork(US"transport")) == 0)
+  if ((pid = exim_fork(f.queue_2stage ? US"transport ph1":US"transport")) == 0)
     {
     int fd = pfd[pipe_write];
     host_item *h;
 
     /* Setting these globals in the subprocess means we need never clear them */
 
-    transport_name = tp->name;
-    if (addr->router) router_name = addr->router->name;
-    driver_srcfile = tp->srcfile;
-    driver_srcline = tp->srcline;
+    transport_name = tp->drinst.name;
+    if (addr->router) router_name = addr->router->drinst.name;
+    driver_srcfile = tp->drinst.srcfile;
+    driver_srcline = tp->drinst.srcline;
 
     /* There are weird circumstances in which logging is disabled */
     f.disable_logging = tp->disable_logging;
@@ -4852,19 +4888,24 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
 
     exim_setugid(uid, gid, use_initgroups,
       string_sprintf("remote delivery to %s with transport=%s",
-        addr->address, tp->name));
+        addr->address, tp->drinst.name));
 
     /* Close the unwanted half of this process' pipe, set the process state,
     and run the transport. Afterwards, transport_count will contain the number
     of bytes written. */
 
     (void)close(pfd[pipe_read]);
-    set_process_info("delivering %s using %s", message_id, tp->name);
+    set_process_info("delivering %s using %s", message_id, tp->drinst.name);
     debug_print_string(tp->debug_string);
-    if (!(tp->info->code)(addr->transport, addr)) replicate_status(addr);
+
+      {
+      transport_info * ti = tp->drinst.info;
+      if (!(ti->code)(addr->transport, addr))  /* Call the transport */
+       replicate_status(addr);
+      }
 
     set_process_info("delivering %s (just run %s for %s%s in subprocess)",
-      message_id, tp->name, addr->address, addr->next ? ", ..." : "");
+      message_id, tp->drinst.name, addr->address, addr->next ? ", ..." : "");
 
     /* Ensure any cached resources that we used are now released */
 
@@ -4912,17 +4953,17 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
       {
       uschar * ptr;
 
+#ifndef DISABLE_TLS
       /* The certificate verification status goes into the flags, in A0 */
       if (tls_out.certificate_verified) setflag(addr, af_cert_verified);
-#ifdef SUPPORT_DANE
+# ifdef SUPPORT_DANE
       if (tls_out.dane_verified)        setflag(addr, af_dane_verified);
-#endif
-#ifndef DISABLE_TLS_RESUME
+# endif
+# ifndef DISABLE_TLS_RESUME
       if (tls_out.resumption & RESUME_USED) setflag(addr, af_tls_resume);
-#endif
+# endif
 
       /* Use an X item only if there's something to send */
-#ifndef DISABLE_TLS
       if (addr->cipher)
         {
         ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1;
@@ -5143,7 +5184,7 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
     wait-db ones, but for all continue-more ones (though any after the
     delivery proc has the info are pointless). */
 
-    if (continue_hostname)
+    if (continue_hostname && continue_fd >= 0)
       {
        {
        uschar * ptr = big_buffer;
@@ -5257,7 +5298,7 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
   if (continue_transport)
     {
     par_reduce(0, fallback);
-    if (!continue_next_id && continue_wait_db)
+    if (!*continue_next_id && continue_wait_db)
        { dbfn_close_multi(continue_wait_db); continue_wait_db = NULL; }
     }
 
@@ -5453,10 +5494,11 @@ static int
 continue_closedown(void)
 {
 if (continue_transport)
-  for (transport_instance * t = transports; t; t = t->next)
-    if (Ustrcmp(t->name, continue_transport) == 0)
+  for (transport_instance * t = transports; t; t = t->drinst.next)
+    if (Ustrcmp(t->drinst.name, continue_transport) == 0)
       {
-      if (t->info->closedown) (t->info->closedown)(t);
+      transport_info * ti = t->drinst.info;
+      if (ti->closedown) (ti->closedown)(t);
       continue_transport = NULL;
       break;
       }
@@ -5815,9 +5857,9 @@ if (!(bounce_recipient = addr_failed->prop.errors_address))
 /* Make a subprocess to send a message, using its stdin */
 
 if ((pid = child_open_exim(&fd, US"bounce-message")) < 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 %ld (parent %ld) failed to "
+    "create child process to send failure message: %s",
+    (long)getpid(), (long)getppid(), strerror(errno));
 
 /* Creation of child succeeded */
 
@@ -6063,7 +6105,7 @@ wording. */
     {
     /* must be decoded from xtext: see RFC 3461:6.3a */
     uschar * xdec_envid;
-    if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+    if (xtextdecode(dsn_envid, &xdec_envid) > 0)
       fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid);
     else
       fprintf(fp, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
@@ -6368,7 +6410,7 @@ if (dsn_envid)
   {
   /* must be decoded from xtext: see RFC 3461:6.3a */
   uschar *xdec_envid;
-  if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+  if (xtextdecode(dsn_envid, &xdec_envid) > 0)
     fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid);
   else
     fprintf(f,"X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
@@ -6435,7 +6477,7 @@ for (const address_item * a = addr_succeed; a; a = a->next)
       "DSN: envid: %s  ret: %d\n"
       "DSN: Final recipient: %s\n"
       "DSN: Remote SMTP server supports DSN: %d\n",
-      a->router ? a->router->name : US"(unknown)",
+      a->router ? a->router->drinst.name : US"(unknown)",
       a->address,
       sender_address,
       a->dsn_orcpt ? a->dsn_orcpt : US"NULL",
@@ -6467,13 +6509,13 @@ if (addr_senddsn)
   int fd;
   pid_t pid = child_open_exim(&fd, US"DSN");
 
-  DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid);
+  DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %ld\n", (long)pid);
 
   if (pid < 0)  /* Creation of child failed */
     {
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
-      "create child process to send success-dsn message: %s", getpid(),
-      getppid(), strerror(errno));
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %ld (parent %ld) failed to "
+      "create child process to send success-dsn message: %s",
+      (long)getpid(), (long)getppid(), strerror(errno));
 
     DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
     }
@@ -6527,7 +6569,7 @@ if (addr_senddsn)
     if (dsn_envid)
       {                        /* must be decoded from xtext: see RFC 3461:6.3a */
       uschar * xdec_envid;
-      if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
+      if (xtextdecode(dsn_envid, &xdec_envid) > 0)
         fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
       else
         fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
@@ -6618,7 +6660,7 @@ int i, rc, final_yield, process_recipients;
 time_t now;
 address_item * addr_last;
 uschar * filter_message, * info;
-open_db dbblock, * dbm_file;
+open_db dbblock, * dbm_file = NULL;
 extern int acl_where;
 
 CONTINUED_ID:
@@ -6634,7 +6676,7 @@ report_time_since(&timestamp_startup, US"delivery start");        /* testcase 0022, 210
 
 info = queue_run_pid == (pid_t)0
   ? string_sprintf("delivering %s", id)
-  : string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid);
+  : string_sprintf("delivering %s (queue run pid %ld)", id, (long)queue_run_pid);
 
 /* If the D_process_info bit is on, set_process_info() will output debugging
 information. If not, we want to show this initial information if D_deliver or
@@ -6980,10 +7022,7 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT)
       RDO_REALLOG |
       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!) */
+    NULL,                   /* No sieve info (not sieve!) */
     &ugid,                  /* uid/gid data */
     &addr_new,              /* Where to hang generated addresses */
     &filter_message,        /* Where to put error message */
@@ -7189,8 +7228,8 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT)
         if (tpname)
           {
           transport_instance *tp;
-          for (tp = transports; tp; tp = tp->next)
-            if (Ustrcmp(tp->name, tpname) == 0)
+          for (tp = transports; tp; tp = tp->drinst.next)
+            if (Ustrcmp(tp->drinst.name, tpname) == 0)
               { p->transport = tp; break; }
           if (!tp)
             p->message = string_sprintf("failed to find \"%s\" transport "
@@ -7430,23 +7469,29 @@ while (addr_new)           /* Loop until all addresses dealt with */
   queue run don't bother checking domain- or address-retry info; they will take
   effect on the second stage. */
 
-  if (f.queue_2stage)
-    dbm_file = NULL;
-  else
+  if (!f.queue_2stage)
     {
     /* If we have transaction-capable hintsdbs, open the retry db without
     locking, and leave open for the transport process and for subsequent
-    deliveries. If the open fails, tag that explicitly for the transport but
-    retry the open next time around, in case it was created in the interim. */
+    deliveries. Use a writeable open as we can keep it open all the way through
+    to writing retry records if needed due to message fails.
+    If the open fails, tag that explicitly for the transport but retry the open
+    next time around, in case it was created in the interim.
+    If non-transaction, we are only reading records at this stage and
+    we close the db before running the transport.
+    Either way we do a non-creating open. */
 
     if (continue_retry_db == (open_db *)-1)
       continue_retry_db = NULL;
 
     if (continue_retry_db)
+      {
+      DEBUG(D_hints_lookup) debug_printf("using cached retry hintsdb handle\n");
       dbm_file = continue_retry_db;
-    else if (!exim_lockfile_needed() && continue_transport)
+      }
+    else if (!exim_lockfile_needed())
       {
-      dbm_file = dbfn_open_multi(US"retry", O_RDONLY, &dbblock);
+      dbm_file = dbfn_open_multi(US"retry", O_RDWR, &dbblock);
       continue_retry_db = dbm_file ? dbm_file : (open_db *)-1;
       }
     else
@@ -7464,7 +7509,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
     {
     int rc;
     tree_node * tnode;
-    dbdata_retry * domain_retry_record, * address_retry_record;
+    dbdata_retry * domain_retry_record = NULL, * address_retry_record = NULL;
 
     addr = addr_new;
     addr_new = addr->next;
@@ -7599,7 +7644,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
        transport_instance * save_t = addr->transport;
        transport_instance * t = store_get(sizeof(*t), save_t);
        *t = *save_t;
-       t->name = US"**bypassed**";
+       t->drinst.name = US"**bypassed**";
        addr->transport = t;
         (void)post_process_one(addr, OK, LOG_MAIN, EXIM_DTYPE_TRANSPORT, '=');
         addr->transport= save_t;
@@ -7610,7 +7655,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
       delivery. */
 
       DEBUG(D_deliver|D_route)
-        debug_printf("queued for %s transport\n", addr->transport->name);
+        debug_printf("queued for %s transport\n", addr->transport->drinst.name);
       addr->next = addr_local;
       addr_local = addr;
       continue;       /* with the next new address */
@@ -7693,70 +7738,82 @@ while (addr_new)           /* Loop until all addresses dealt with */
       continue;
       }
 
-    /* Get the routing retry status, saving the two retry keys (with and
-    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,
-      addr->domain);
-
-    if (dbm_file)
+    if (f.queue_2stage)
       {
-      domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key);
-      if (  domain_retry_record
-         && now - domain_retry_record->time_stamp > retry_data_expire
-        )
+      DEBUG(D_deliver)
+       debug_printf_indent("no router retry check (ph1 qrun)\n");
+      }
+    else
+      {
+      /* Get the routing retry status, saving the two retry keys (with and
+      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. */
+
+      DEBUG(D_deliver|D_retry)
        {
-       DEBUG(D_deliver|D_retry)
-         debug_printf("domain retry record present but expired\n");
-        domain_retry_record = NULL;    /* Ignore if too old */
+       debug_printf_indent("checking router retry status\n");
+       acl_level++;
        }
+      addr->domain_retry_key = string_sprintf("R:%s", addr->domain);
+      addr->address_retry_key = string_sprintf("R:%s@%s", addr->local_part,
+       addr->domain);
 
-      address_retry_record = dbfn_read(dbm_file, addr->address_retry_key);
-      if (  address_retry_record
-         && now - address_retry_record->time_stamp > retry_data_expire
-        )
+      if (dbm_file)
        {
-       DEBUG(D_deliver|D_retry)
-         debug_printf("address retry record present but expired\n");
-        address_retry_record = NULL;   /* Ignore if too old */
-       }
+       domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key);
+       if (  domain_retry_record
+          && now - domain_retry_record->time_stamp > retry_data_expire
+          )
+         {
+         DEBUG(D_deliver|D_retry)
+           debug_printf_indent("domain retry record present but expired\n");
+         domain_retry_record = NULL;    /* Ignore if too old */
+         }
 
-      if (!address_retry_record)
-        {
-        uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
-          sender_address);
-        address_retry_record = dbfn_read(dbm_file, altkey);
-        if (  address_retry_record
-          && now - address_retry_record->time_stamp > retry_data_expire)
+       address_retry_record = dbfn_read(dbm_file, addr->address_retry_key);
+       if (  address_retry_record
+          && now - address_retry_record->time_stamp > retry_data_expire
+          )
          {
          DEBUG(D_deliver|D_retry)
-           debug_printf("address<sender> retry record present but expired\n");
-          address_retry_record = NULL;   /* Ignore if too old */
+           debug_printf_indent("address retry record present but expired\n");
+         address_retry_record = NULL;   /* Ignore if too old */
          }
-        }
-      }
-    else
-      domain_retry_record = address_retry_record = NULL;
 
-    DEBUG(D_deliver|D_retry)
-      {
-      if (!domain_retry_record)
-       debug_printf("no   domain  retry record\n");
-      else
-       debug_printf("have domain  retry record; next_try = now%+d\n",
-                     f.running_in_test_harness ? 0 :
-                     (int)(domain_retry_record->next_try - now));
+       if (!address_retry_record)
+         {
+         uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
+           sender_address);
+         address_retry_record = dbfn_read(dbm_file, altkey);
+         if (  address_retry_record
+            && now - address_retry_record->time_stamp > retry_data_expire)
+           {
+           DEBUG(D_deliver|D_retry)
+             debug_printf_indent("address<sender> retry record present but expired\n");
+           address_retry_record = NULL;   /* Ignore if too old */
+           }
+         }
+       }
 
-      if (!address_retry_record)
-       debug_printf("no   address retry record\n");
-      else
-       debug_printf("have address retry record; next_try = now%+d\n",
-                     f.running_in_test_harness ? 0 :
-                     (int)(address_retry_record->next_try - now));
+      DEBUG(D_deliver|D_retry)
+       {
+       if (!domain_retry_record)
+         debug_printf_indent("no   domain  retry record\n");
+       else
+         debug_printf_indent("have domain  retry record; next_try = now%+d\n",
+                       f.running_in_test_harness ? 0 :
+                       (int)(domain_retry_record->next_try - now));
+
+       if (!address_retry_record)
+         debug_printf_indent("no   address retry record\n");
+       else
+         debug_printf_indent("have address retry record; next_try = now%+d\n",
+                       f.running_in_test_harness ? 0 :
+                       (int)(address_retry_record->next_try - now));
+       acl_level--;
+       }
       }
 
     /* If we are sending a message down an existing SMTP connection, we must
@@ -7860,11 +7917,15 @@ while (addr_new)           /* Loop until all addresses dealt with */
       }
     }
 
-  /* The database is closed while routing is actually happening. Requests to
-  update it are put on a chain and all processed together at the end. */
+  /* If not transaction-capable, the database is closed while routing is
+  actually happening. Requests to update it are put on a chain and all processed
+  together at the end. */
 
-  if (dbm_file && !continue_retry_db)
-    { dbfn_close(dbm_file); dbm_file = NULL; }
+  if (dbm_file)
+    if (exim_lockfile_needed())
+      { dbfn_close(dbm_file); continue_retry_db = dbm_file = NULL; }
+    else
+      DEBUG(D_hints_lookup) debug_printf("retaining retry hintsdb handle\n");
 
   /* If queue_domains is set, we don't even want to try routing addresses in
   those domains. During queue runs, queue_domains is forced to be unset.
@@ -8035,9 +8096,6 @@ while (addr_new)           /* Loop until all addresses dealt with */
   }    /* Loop to process any child addresses that the routers created, and
        any rerouted addresses that got put back on the new chain. */
 
-if (dbm_file)          /* Can only be continue_retry_db */
-  { dbfn_close_multi(continue_retry_db); continue_retry_db = dbm_file = NULL; }
-
 /* Debugging: show the results of the routing */
 
 DEBUG(D_deliver|D_retry|D_route)
@@ -8136,12 +8194,7 @@ if (  mua_wrapper
 
 
 /* If this is a run to continue deliveries to an external channel that is
-already set up, defer any local deliveries.
-
-jgh 2020/12/20: I don't see why; locals should be quick.
-The defer goes back to version 1.62 in 1997.  A local being still deliverable
-during a continued run might result from something like a defer during the
-original delivery, eg. in a DB lookup.  Unlikely but possible.
+already set up, defer any local deliveries because we are handling remotes.
 
 To avoid delaying a local when combined with a callout-hold for a remote
 delivery, test continue_sequence rather than continue_transport. */
@@ -8389,9 +8442,10 @@ if (mua_wrapper)
 
 /* In a normal configuration, we now update the retry database. This is done in
 one fell swoop at the end in order not to keep opening and closing (and
-locking) the database. The code for handling retries is hived off into a
-separate module for convenience. We pass it the addresses of the various
-chains, because deferred addresses can get moved onto the failed chain if the
+locking) the database (at least, for non-transaction-capable DBs.
+The code for handling retries is hived off into a separate module for
+convenience. We pass it the addresses of the various chains,
+because deferred addresses can get moved onto the failed chain if the
 retry cutoff time has expired for all alternative destinations. Bypass the
 updating of the database if the -N flag is set, which is a debugging thing that
 prevents actual delivery. */
@@ -8493,6 +8547,13 @@ f.disable_logging = FALSE;  /* In case left set */
 
 DELIVERY_TIDYUP:
 
+if (dbm_file)          /* Can only be continue_retry_db */
+  {
+  DEBUG(D_hints_lookup) debug_printf("final close of cached retry db\n");
+  dbfn_close_multi(continue_retry_db);
+  continue_retry_db = dbm_file = NULL;
+  }
+
 /* If there are now no deferred addresses, we are done. Preserve the
 message log if so configured, and we are using them. Otherwise, sling it.
 Then delete the message itself. */