build: use pkg-config for i18n
[exim.git] / src / src / transports / appendfile.c
index 2d008d97d7915db65dd5fe6b7211e8e9478a892c..08a59543c5bfcd96fd63103305c235b84f71114b 100644 (file)
@@ -2,12 +2,15 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2020 */
 /* Copyright (c) University of Cambridge 1995 - 2020 */
-/* Copyright (c) The Exim maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "../exim.h"
 
 
 #include "../exim.h"
+
+#ifdef TRANSPORT_APPENDFILE    /* Remainder of file */
 #include "appendfile.h"
 
 #ifdef SUPPORT_MAILDIR
 #include "appendfile.h"
 
 #ifdef SUPPORT_MAILDIR
@@ -104,7 +107,7 @@ int appendfile_transport_options_count =
 
 /* Dummy values */
 appendfile_transport_options_block appendfile_transport_option_defaults = {0};
 
 /* Dummy values */
 appendfile_transport_options_block appendfile_transport_option_defaults = {0};
-void appendfile_transport_init(transport_instance *tblock) {}
+void appendfile_transport_init(driver_instance *tblock) {}
 BOOL appendfile_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
 
 #else  /*!MACRO_PREDEF*/
 BOOL appendfile_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
 
 #else  /*!MACRO_PREDEF*/
@@ -152,7 +155,6 @@ static const char *mailbox_formats[] = {
   (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0))
 
 
   (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0))
 
 
-
 /*************************************************
 *              Setup entry point                 *
 *************************************************/
 /*************************************************
 *              Setup entry point                 *
 *************************************************/
@@ -174,33 +176,28 @@ Arguments:
 Returns:     OK, FAIL, or DEFER
 */
 
 Returns:     OK, FAIL, or DEFER
 */
 
-void
-open_logs(void);
-
 static int
 appendfile_transport_setup(transport_instance *tblock, address_item *addrlist,
   transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg)
 {
 static int
 appendfile_transport_setup(transport_instance *tblock, address_item *addrlist,
   transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg)
 {
-appendfile_transport_options_block *ob =
-  (appendfile_transport_options_block *)(tblock->options_block);
-uschar *q = ob->quota;
+appendfile_transport_options_block * ob = tblock->drinst.options_block;
+const uschar * trname = tblock->drinst.name;
+uschar * q;
 double default_value = 0.0;
 
 double default_value = 0.0;
 
-addrlist = addrlist;    /* Keep picky compilers happy */
-dummy = dummy;
-uid = uid;
-gid = gid;
-
-/* we can't wait until we're not privileged anymore */
-open_logs();
-
 if (ob->expand_maildir_use_size_file)
 if (ob->expand_maildir_use_size_file)
-       ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file,
-               US"`maildir_use_size_file` in transport", tblock->name);
+  {
+  GET_OPTION("maildir_use_size_file");
+  ob->maildir_use_size_file =
+    expand_check_condition(ob->expand_maildir_use_size_file,
+               US"`maildir_use_size_file` in transport", trname);
+  }
 
 /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
 mailbox_filecount */
 
 
 /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size,
 mailbox_filecount */
 
+GET_OPTION("quota");
+q = ob->quota;
 for (int i = 0; i < 5; i++)
   {
   double d = default_value;
 for (int i = 0; i < 5; i++)
   {
   double d = default_value;
@@ -214,7 +211,7 @@ for (int i = 0; i < 5; i++)
     if (!(s =  expand_string(q)))
       {
       *errmsg = string_sprintf("Expansion of \"%s\" in %s transport failed: "
     if (!(s =  expand_string(q)))
       {
       *errmsg = string_sprintf("Expansion of \"%s\" in %s transport failed: "
-        "%s", q, tblock->name, expand_string_message);
+        "%s", q, trname, expand_string_message);
       return f.search_find_defer ? DEFER : FAIL;
       }
 
       return f.search_find_defer ? DEFER : FAIL;
       }
 
@@ -234,7 +231,7 @@ for (int i = 0; i < 5; i++)
       else if ((int)d < 0 || (int)d > 100)
         {
         *errmsg = string_sprintf("Invalid quota_warn_threshold percentage (%d)"
       else if ((int)d < 0 || (int)d > 100)
         {
         *errmsg = string_sprintf("Invalid quota_warn_threshold percentage (%d)"
-          " for %s transport", (int)d, tblock->name);
+          " for %s transport", (int)d, trname);
         return FAIL;
         }
       ob->quota_warn_threshold_is_percent = TRUE;
         return FAIL;
         }
       ob->quota_warn_threshold_is_percent = TRUE;
@@ -255,7 +252,7 @@ for (int i = 0; i < 5; i++)
     if (*rest)
       {
       *errmsg = string_sprintf("Malformed value \"%s\" (expansion of \"%s\") "
     if (*rest)
       {
       *errmsg = string_sprintf("Malformed value \"%s\" (expansion of \"%s\") "
-        "in %s transport", s, q, tblock->name);
+        "in %s transport", s, q, trname);
       return FAIL;
       }
     }
       return FAIL;
       }
     }
@@ -269,6 +266,7 @@ for (int i = 0; i < 5; i++)
        which = US"quota";
       ob->quota_value = (off_t)d;
       ob->quota_no_check = no_check;
        which = US"quota";
       ob->quota_value = (off_t)d;
       ob->quota_no_check = no_check;
+      GET_OPTION("quota_filecount");
       q = ob->quota_filecount;
       break;
 
       q = ob->quota_filecount;
       break;
 
@@ -277,6 +275,7 @@ for (int i = 0; i < 5; i++)
        which = US"quota_filecount";
       ob->quota_filecount_value = (int)d;
       ob->quota_filecount_no_check = no_check;
        which = US"quota_filecount";
       ob->quota_filecount_value = (int)d;
       ob->quota_filecount_no_check = no_check;
+      GET_OPTION("quota_warn_threshold");
       q = ob->quota_warn_threshold;
       break;
 
       q = ob->quota_warn_threshold;
       break;
 
@@ -284,6 +283,7 @@ for (int i = 0; i < 5; i++)
       if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
          which = US"quota_warn_threshold";
       ob->quota_warn_threshold_value = (off_t)d;
       if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
          which = US"quota_warn_threshold";
       ob->quota_warn_threshold_value = (off_t)d;
+      GET_OPTION("mailbox_size");
       q = ob->mailbox_size_string;
       default_value = -1.0;
       break;
       q = ob->mailbox_size_string;
       default_value = -1.0;
       break;
@@ -292,6 +292,7 @@ for (int i = 0; i < 5; i++)
       if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
        which = US"mailbox_size";;
       ob->mailbox_size_value = (off_t)d;
       if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4)
        which = US"mailbox_size";;
       ob->mailbox_size_value = (off_t)d;
+      GET_OPTION("mailbox_filecount");
       q = ob->mailbox_filecount_string;
       break;
 
       q = ob->mailbox_filecount_string;
       break;
 
@@ -305,7 +306,7 @@ for (int i = 0; i < 5; i++)
   if (which)
     {
     *errmsg = string_sprintf("%s value %.10g is too large (overflow) in "
   if (which)
     {
     *errmsg = string_sprintf("%s value %.10g is too large (overflow) in "
-      "%s transport", which, d, tblock->name);
+      "%s transport", which, d, trname);
     return FAIL;
     }
   }
     return FAIL;
     }
   }
@@ -324,10 +325,11 @@ enable consistency checks to be done, or anything else that needs
 to be set up. */
 
 void
 to be set up. */
 
 void
-appendfile_transport_init(transport_instance *tblock)
+appendfile_transport_init(driver_instance * t)
 {
 {
-appendfile_transport_options_block *ob =
-  (appendfile_transport_options_block *)(tblock->options_block);
+transport_instance * tblock = (transport_instance *)t;
+appendfile_transport_options_block * ob = tblock->drinst.options_block;
+const uschar * trname = tblock->drinst.name;
 uschar * s;
 
 /* Set up the setup entry point, to be called in the privileged state */
 uschar * s;
 
 /* Set up the setup entry point, to be called in the privileged state */
@@ -342,7 +344,7 @@ if (ob->lock_retries == 0) ob->lock_retries = 1;
 
 if (ob->filename && ob->dirname)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
 
 if (ob->filename && ob->dirname)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-  "only one of \"file\" or \"directory\" can be specified", tblock->name);
+  "only one of \"file\" or \"directory\" can be specified", trname);
 
 /* If a file name was specified, neither quota_filecount nor quota_directory
 must be given. */
 
 /* If a file name was specified, neither quota_filecount nor quota_directory
 must be given. */
@@ -351,10 +353,10 @@ if (ob->filename)
   {
   if (ob->quota_filecount)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
   {
   if (ob->quota_filecount)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-      "quota_filecount must not be set without \"directory\"", tblock->name);
+      "quota_filecount must not be set without \"directory\"", trname);
   if (ob->quota_directory)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
   if (ob->quota_directory)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-      "quota_directory must not be set without \"directory\"", tblock->name);
+      "quota_directory must not be set without \"directory\"", trname);
   }
 
 /* The default locking depends on whether MBX is set or not. Change the
   }
 
 /* The default locking depends on whether MBX is set or not. Change the
@@ -370,7 +372,7 @@ if (ob->use_flock)
   #ifdef NO_FLOCK
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
     "flock() support was not available in the operating system when this "
   #ifdef NO_FLOCK
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
     "flock() support was not available in the operating system when this "
-    "binary was built", tblock->name);
+    "binary was built", trname);
   #endif  /* NO_FLOCK */
   if (!ob->set_use_fcntl) ob->use_fcntl = FALSE;
   }
   #endif  /* NO_FLOCK */
   if (!ob->set_use_fcntl) ob->use_fcntl = FALSE;
   }
@@ -394,7 +396,7 @@ if (ob->mbx_format)
 
 if (!ob->use_fcntl && !ob->use_flock && !ob->use_lockfile && !ob->use_mbx_lock)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
 
 if (!ob->use_fcntl && !ob->use_flock && !ob->use_lockfile && !ob->use_mbx_lock)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-    "no locking configured", tblock->name);
+    "no locking configured", trname);
 
 /* Unset timeouts for non-used locking types */
 
 
 /* Unset timeouts for non-used locking types */
 
@@ -409,20 +411,20 @@ if (ob->dirname)
   {
   if (ob->maildir_format && ob->mailstore_format)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
   {
   if (ob->maildir_format && ob->mailstore_format)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-      "only one of maildir and mailstore may be specified", tblock->name);
-  if (ob->quota_filecount != NULL && ob->quota == NULL)
+      "only one of maildir and mailstore may be specified", trname);
+  if (ob->quota_filecount != NULL && !ob->quota)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-      "quota must be set if quota_filecount is set", tblock->name);
-  if (ob->quota_directory != NULL && ob->quota == NULL)
+      "quota must be set if quota_filecount is set", trname);
+  if (ob->quota_directory != NULL && !ob->quota)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n  "
-      "quota must be set if quota_directory is set", tblock->name);
+      "quota must be set if quota_directory is set", trname);
   }
 
 /* If a fixed uid field is set, then a gid field must also be set. */
 
 if (tblock->uid_set && !tblock->gid_set && !tblock->expand_gid)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
   }
 
 /* If a fixed uid field is set, then a gid field must also be set. */
 
 if (tblock->uid_set && !tblock->gid_set && !tblock->expand_gid)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-    "user set without group for the %s transport", tblock->name);
+    "user set without group for the %s transport", trname);
 
 /* If "create_file" is set, check that a valid option is given, and set the
 integer variable. */
 
 /* If "create_file" is set, check that a valid option is given, and set the
 integer variable. */
@@ -435,8 +437,8 @@ if ((s = ob->create_file_string ) && *s)
   else if (Ustrcmp(s, "inhome") == 0)                  val = create_inhome;
   else
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
   else if (Ustrcmp(s, "inhome") == 0)                  val = create_inhome;
   else
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-      "invalid value given for \"file_create\" for the %s transport: '%s'",
-      tblock->name, s);
+      "invalid value given for \"create_file\" for the %s transport: '%s'",
+      trname, s);
   ob->create_file = val;
   }
 
   ob->create_file = val;
   }
 
@@ -512,7 +514,7 @@ Returns:     nothing
 */
 
 static void
 */
 
 static void
-notify_comsat(uschar *user, off_t offset)
+notify_comsat(const uschar * user, off_t offset)
 {
 struct servent *sp;
 host_item host;
 {
 struct servent *sp;
 host_item host;
@@ -593,8 +595,8 @@ Returns:       pointer to the required transport, or NULL
 transport_instance *
 check_file_format(int cfd, transport_instance *tblock, address_item *addr)
 {
 transport_instance *
 check_file_format(int cfd, transport_instance *tblock, address_item *addr)
 {
-const uschar *format =
-  ((appendfile_transport_options_block *)(tblock->options_block))->file_format;
+appendfile_transport_options_block * ob = tblock->drinst.options_block;
+const uschar * format = ob->file_format;
 uschar data[256];
 int len = read(cfd, data, sizeof(data));
 int sep = 0;
 uschar data[256];
 int len = read(cfd, data, sizeof(data));
 int sep = 0;
@@ -617,11 +619,11 @@ while ((s = string_nextinlist(&format, &sep, big_buffer, big_buffer_size)))
 
   if (match && tp)
     {
 
   if (match && tp)
     {
-    for (transport_instance * tt = transports; tt; tt = tt->next)
-      if (Ustrcmp(tp, tt->name) == 0)
+    for (transport_instance * tt = transports; tt; tt = tt->drinst.next)
+      if (Ustrcmp(tp, tt->drinst.name) == 0)
         {
         DEBUG(D_transport)
         {
         DEBUG(D_transport)
-          debug_printf("file format -> %s transport\n", tt->name);
+          debug_printf("file format -> %s transport\n", tt->drinst.name);
         return tt;
         }
     addr->basic_errno = ERRNO_BADTRANSPORT;
         return tt;
         }
     addr->basic_errno = ERRNO_BADTRANSPORT;
@@ -662,22 +664,23 @@ the log, because we are running as an unprivileged user here.
 Arguments:
   dirname       the name of the directory
   countptr      where to add the file count (because this function recurses)
 Arguments:
   dirname       the name of the directory
   countptr      where to add the file count (because this function recurses)
-  regex         a compiled regex to get the size from a name
+  re            a compiled regex to get the size from a name
 
 Returns:        the sum of the sizes of the stattable files
                 zero if the directory cannot be opened
 */
 
 off_t
 
 Returns:        the sum of the sizes of the stattable files
                 zero if the directory cannot be opened
 */
 
 off_t
-check_dir_size(const uschar * dirname, int *countptr, const pcre *regex)
+check_dir_size(const uschar * dirname, int * countptr, const pcre2_code * re)
 {
 {
-DIR *dir;
+DIR * dir;
 off_t sum = 0;
 off_t sum = 0;
-int count = *countptr;
+int count = *countptr, lcount = REGEX_LOOPCOUNT_STORE_RESET;
+rmark reset_point = store_mark();
 
 if (!(dir = exim_opendir(dirname))) return 0;
 
 
 if (!(dir = exim_opendir(dirname))) return 0;
 
-for (struct dirent *ent; ent = readdir(dir); )
+for (struct dirent * ent; ent = readdir(dir); )
   {
   uschar * path, * name = US ent->d_name;
   struct stat statbuf;
   {
   uschar * path, * name = US ent->d_name;
   struct stat statbuf;
@@ -685,25 +688,36 @@ for (struct dirent *ent; ent = readdir(dir); )
   if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
 
   count++;
   if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue;
 
   count++;
+  if (--lcount == 0)
+    {
+    store_reset(reset_point); reset_point = store_mark();
+    lcount = REGEX_LOOPCOUNT_STORE_RESET;
+    }
 
   /* If there's a regex, try to find the size using it */
 
 
   /* If there's a regex, try to find the size using it */
 
-  if (regex)
+  if (re)
     {
     {
-    int ovector[6];
-    if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2)
+    pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
+    int rc = pcre2_match(re, (PCRE2_SPTR)name, PCRE2_ZERO_TERMINATED,
+                         0, 0, md, pcre_gen_mtc_ctx);
+    PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+    if (  rc >= 0
+       && (rc = pcre2_get_ovector_count(md)) >= 2)
       {
       uschar *endptr;
       {
       uschar *endptr;
-      off_t size = (off_t)Ustrtod(name + ovector[2], &endptr);
-      if (endptr == name + ovector[3])
+      off_t size = (off_t)Ustrtod(name + ovec[2], &endptr);
+      if (endptr == name + ovec[3])
         {
         sum += size;
         DEBUG(D_transport)
           debug_printf("check_dir_size: size from %s is " OFF_T_FMT "\n", name,
             size);
         {
         sum += size;
         DEBUG(D_transport)
           debug_printf("check_dir_size: size from %s is " OFF_T_FMT "\n", name,
             size);
+       /* pcre2_match_data_free(md);   gen ctx needs no free */
         continue;
         }
       }
         continue;
         }
       }
+    /* pcre2_match_data_free(md);      gen ctx needs no free */
     DEBUG(D_transport)
       debug_printf("check_dir_size: regex did not match %s\n", name);
     }
     DEBUG(D_transport)
       debug_printf("check_dir_size: regex did not match %s\n", name);
     }
@@ -722,7 +736,7 @@ for (struct dirent *ent; ent = readdir(dir); )
     if ((statbuf.st_mode & S_IFMT) == S_IFREG)
       sum += statbuf.st_size / statbuf.st_nlink;
     else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
     if ((statbuf.st_mode & S_IFMT) == S_IFREG)
       sum += statbuf.st_size / statbuf.st_nlink;
     else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
-      sum += check_dir_size(path, &count, regex);
+      sum += check_dir_size(path, &count, re);
   }
 
 closedir(dir);
   }
 
 closedir(dir);
@@ -730,6 +744,7 @@ DEBUG(D_transport)
   debug_printf("check_dir_size: dir=%s sum=" OFF_T_FMT " count=%d\n", dirname,
     sum, count);
 
   debug_printf("check_dir_size: dir=%s sum=" OFF_T_FMT " count=%d\n", dirname,
     sum, count);
 
+store_reset(reset_point);
 *countptr = count;
 return sum;
 }
 *countptr = count;
 return sum;
 }
@@ -1140,37 +1155,27 @@ appendfile_transport_entry(
   transport_instance *tblock,      /* data for this instantiation */
   address_item *addr)              /* address we are working on */
 {
   transport_instance *tblock,      /* data for this instantiation */
   address_item *addr)              /* address we are working on */
 {
-appendfile_transport_options_block *ob =
-  (appendfile_transport_options_block *)(tblock->options_block);
+appendfile_transport_options_block * ob = tblock->drinst.options_block;
+const uschar * trname = tblock->drinst.name;
 struct stat statbuf;
 const uschar * deliver_dir;
 struct stat statbuf;
 const uschar * deliver_dir;
-uschar *fdname = NULL;
-uschar *filename = NULL;
-uschar *hitchname = NULL;
-uschar *dataname = NULL;
-uschar *lockname = NULL;
-uschar *newname = NULL;
-uschar *nametag = NULL;
-uschar *cr = US"";
-uschar *filecount_msg = US"";
-uschar *path;
+uschar * fdname = NULL, * filename = NULL;
+uschar * hitchname = NULL, * dataname = NULL;
+uschar * lockname = NULL, * newname = NULL;
+uschar * nametag = NULL, * cr = US"";
+uschar * filecount_msg = US"";
+uschar * path;
 struct utimbuf times;
 struct timeval msg_tv;
 struct utimbuf times;
 struct timeval msg_tv;
-BOOL disable_quota = FALSE;
-BOOL isdirectory = FALSE;
-BOOL isfifo = FALSE;
+BOOL disable_quota = FALSE, isdirectory = FALSE, isfifo = FALSE;
 BOOL wait_for_tick = FALSE;
 uid_t uid = geteuid();     /* See note above */
 gid_t gid = getegid();
 int mbformat;
 BOOL wait_for_tick = FALSE;
 uid_t uid = geteuid();     /* See note above */
 gid_t gid = getegid();
 int mbformat;
-int mode = (addr->mode > 0) ? addr->mode : ob->mode;
-off_t saved_size = -1;
-off_t mailbox_size = ob->mailbox_size_value;
+int mode = addr->mode > 0 ? addr->mode : ob->mode;
+off_t saved_size = -1, mailbox_size = ob->mailbox_size_value;
 int mailbox_filecount = ob->mailbox_filecount_value;
 int mailbox_filecount = ob->mailbox_filecount_value;
-int hd = -1;
-int fd = -1;
-int yield = FAIL;
-int i;
+int hd = -1, fd = -1, yield = FAIL, i;
 
 #ifdef SUPPORT_MBX
 int save_fd = 0;
 
 #ifdef SUPPORT_MBX
 int save_fd = 0;
@@ -1219,13 +1224,14 @@ if (!fdname)
   {
   if (!(fdname = ob->filename))
     {
   {
   if (!(fdname = ob->filename))
     {
+    GET_OPTION("directory");
     fdname = ob->dirname;
     isdirectory = TRUE;
     }
   if (!fdname)
     {
     addr->message = string_sprintf("Mandatory file or directory option "
     fdname = ob->dirname;
     isdirectory = TRUE;
     }
   if (!fdname)
     {
     addr->message = string_sprintf("Mandatory file or directory option "
-      "missing from %s transport", tblock->name);
+      "missing from %s transport", trname);
     goto ret_panic;
     }
   }
     goto ret_panic;
     }
   }
@@ -1236,14 +1242,14 @@ if ((ob->maildir_format || ob->mailstore_format) && !isdirectory)
   {
   addr->message = string_sprintf("mail%s_format requires \"directory\" "
     "to be specified for the %s transport",
   {
   addr->message = string_sprintf("mail%s_format requires \"directory\" "
     "to be specified for the %s transport",
-    ob->maildir_format ? "dir" : "store", tblock->name);
+    ob->maildir_format ? "dir" : "store", trname);
   goto ret_panic;
   }
 
 if (!(path = expand_string(fdname)))
   {
   addr->message = string_sprintf("Expansion of \"%s\" (file or directory "
   goto ret_panic;
   }
 
 if (!(path = expand_string(fdname)))
   {
   addr->message = string_sprintf("Expansion of \"%s\" (file or directory "
-    "name for %s transport) failed: %s", fdname, tblock->name,
+    "name for %s transport) failed: %s", fdname, trname,
     expand_string_message);
   goto ret_panic;
   }
     expand_string_message);
   goto ret_panic;
   }
@@ -1318,7 +1324,7 @@ if (f.dont_deliver)
   {
   DEBUG(D_transport)
     debug_printf("*** delivery by %s transport bypassed by -N option\n",
   {
   DEBUG(D_transport)
     debug_printf("*** delivery by %s transport bypassed by -N option\n",
-      tblock->name);
+      trname);
   addr->transport_return = OK;
   return FALSE;
   }
   addr->transport_return = OK;
   return FALSE;
   }
@@ -1353,8 +1359,8 @@ if (!isdirectory)
      && ob->create_file == create_belowhome)
     if (is_tainted(path))
       {
      && ob->create_file == create_belowhome)
     if (is_tainted(path))
       {
-      DEBUG(D_transport) debug_printf("de-tainting path '%s'\n", path);
-      path = string_copy_taint(path, FALSE);
+      DEBUG(D_transport) debug_printf("below-home: de-tainting path '%s'\n", path);
+      path = string_copy_taint(path, GET_UNTAINTED);
       }
 
   if (is_tainted(path)) goto tainted_ret_panic;
       }
 
   if (is_tainted(path)) goto tainted_ret_panic;
@@ -1375,7 +1381,7 @@ if (!isdirectory)
       addr->message =
         string_sprintf("failed to create directories for %s: %s", path,
           exim_errstr(errno));
       addr->message =
         string_sprintf("failed to create directories for %s: %s", path,
           exim_errstr(errno));
-      DEBUG(D_transport) debug_printf("%s transport: %s\n", tblock->name, path);
+      DEBUG(D_transport) debug_printf("%s transport: %s\n", trname, path);
       return FALSE;
       }
     }
       return FALSE;
       }
     }
@@ -1403,11 +1409,12 @@ if (!isdirectory)
         {
         if (tt)
           {
         {
         if (tt)
           {
+         transport_info * ti = tt->drinst.info;
           set_process_info("delivering %s to %s using %s", message_id,
           set_process_info("delivering %s to %s using %s", message_id,
-            addr->local_part, tt->name);
+            addr->local_part, tt->drinst.name);
           debug_print_string(tt->debug_string);
           addr->transport = tt;
           debug_print_string(tt->debug_string);
           addr->transport = tt;
-          (tt->info->code)(tt, addr);
+          (ti->code)(tt, addr);
           }
         return FALSE;
         }
           }
         return FALSE;
         }
@@ -2175,7 +2182,7 @@ scanning is expensive; for maildirs some fudges have been invented:
 else
   {
   uschar *check_path;          /* Default quota check path */
 else
   {
   uschar *check_path;          /* Default quota check path */
-  const pcre *regex = NULL;     /* Regex for file size from file name */
+  const pcre2_code * re = NULL;     /* Regex for file size from file name */
 
   if (!check_creation(string_sprintf("%s/any", path),
                      ob->create_file, deliver_dir))
 
   if (!check_creation(string_sprintf("%s/any", path),
                      ob->create_file, deliver_dir))
@@ -2192,8 +2199,8 @@ else
   if (is_tainted(path))
     if (ob->create_file == create_belowhome)
       {
   if (is_tainted(path))
     if (ob->create_file == create_belowhome)
       {
-      DEBUG(D_transport) debug_printf("de-tainting path '%s'\n", path);
-      path = string_copy_taint(path, FALSE);
+      DEBUG(D_transport) debug_printf("below-home: de-tainting path '%s'\n", path);
+      path = string_copy_taint(path, GET_UNTAINTED);
       }
     else
       goto tainted_ret_panic;
       }
     else
       goto tainted_ret_panic;
@@ -2218,34 +2225,28 @@ else
 
   if (ob->quota_value > 0 || THRESHOLD_CHECK || ob->maildir_use_size_file)
     {
 
   if (ob->quota_value > 0 || THRESHOLD_CHECK || ob->maildir_use_size_file)
     {
-    const uschar *error;
-    int offset;
-
     /* Compile the regex if there is one. */
 
     if (ob->quota_size_regex)
       {
     /* Compile the regex if there is one. */
 
     if (ob->quota_size_regex)
       {
-      if (!(regex = pcre_compile(CS ob->quota_size_regex, PCRE_COPT,
-         CCSS &error, &offset, NULL)))
-        {
-        addr->message = string_sprintf("appendfile: regular expression "
-          "error: %s at offset %d while compiling %s", error, offset,
-          ob->quota_size_regex);
+      if (!(re = regex_compile(ob->quota_size_regex,
+                 MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx)))
         return FALSE;
         return FALSE;
-        }
+
       DEBUG(D_transport) debug_printf("using regex for file sizes: %s\n",
         ob->quota_size_regex);
       }
 
     /* Use an explicitly configured directory if set */
 
       DEBUG(D_transport) debug_printf("using regex for file sizes: %s\n",
         ob->quota_size_regex);
       }
 
     /* Use an explicitly configured directory if set */
 
+    GET_OPTION("quota_directory");
     if (ob->quota_directory)
       {
       if (!(check_path = expand_string(ob->quota_directory)))
         {
         addr->message = string_sprintf("Expansion of \"%s\" (quota_directory "
          "name for %s transport) failed: %s", ob->quota_directory,
     if (ob->quota_directory)
       {
       if (!(check_path = expand_string(ob->quota_directory)))
         {
         addr->message = string_sprintf("Expansion of \"%s\" (quota_directory "
          "name for %s transport) failed: %s", ob->quota_directory,
-          tblock->name, expand_string_message);
+          trname, expand_string_message);
         goto ret_panic;
         }
 
         goto ret_panic;
         }
 
@@ -2304,22 +2305,15 @@ else
   #ifdef SUPPORT_MAILDIR
   if (ob->maildir_use_size_file)
     {
   #ifdef SUPPORT_MAILDIR
   if (ob->maildir_use_size_file)
     {
-    const pcre *dir_regex = NULL;
-    const uschar *error;
-    int offset;
+    const pcre2_code * dir_regex = NULL;
 
     if (ob->maildir_dir_regex)
       {
       int check_path_len = Ustrlen(check_path);
 
 
     if (ob->maildir_dir_regex)
       {
       int check_path_len = Ustrlen(check_path);
 
-      if (!(dir_regex = pcre_compile(CS ob->maildir_dir_regex, PCRE_COPT,
-         CCSS &error, &offset, NULL)))
-        {
-        addr->message = string_sprintf("appendfile: regular expression "
-          "error: %s at offset %d while compiling %s", error, offset,
-          ob->maildir_dir_regex);
+      if (!(dir_regex = regex_compile(ob->maildir_dir_regex,
+           MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx)))
         return FALSE;
         return FALSE;
-        }
 
       DEBUG(D_transport)
         debug_printf("using regex for maildir directory selection: %s\n",
 
       DEBUG(D_transport)
         debug_printf("using regex for maildir directory selection: %s\n",
@@ -2335,7 +2329,7 @@ else
         uschar *s = path + check_path_len;
         while (*s == '/') s++;
         s = *s ? string_sprintf("%s/new", s) : US"new";
         uschar *s = path + check_path_len;
         while (*s == '/') s++;
         s = *s ? string_sprintf("%s/new", s) : US"new";
-        if (pcre_exec(dir_regex, NULL, CS s, Ustrlen(s), 0, 0, NULL, 0) < 0)
+       if (!regex_match(dir_regex, s, -1, NULL))
           {
           disable_quota = TRUE;
           DEBUG(D_transport) debug_printf("delivery directory does not match "
           {
           disable_quota = TRUE;
           DEBUG(D_transport) debug_printf("delivery directory does not match "
@@ -2356,7 +2350,7 @@ else
       off_t size;
       int filecount;
 
       off_t size;
       int filecount;
 
-      if ((maildirsize_fd = maildir_ensure_sizefile(check_path, ob, regex, dir_regex,
+      if ((maildirsize_fd = maildir_ensure_sizefile(check_path, ob,  re, dir_regex,
          &size, &filecount)) == -1)
         {
         addr->basic_errno = errno;
          &size, &filecount)) == -1)
         {
         addr->basic_errno = errno;
@@ -2381,7 +2375,7 @@ else
  *    (void)unlink(CS string_sprintf("%s/maildirsize", check_path));
  *    if (THRESHOLD_CHECK)
  *      mailbox_size = maildir_compute_size(check_path, &mailbox_filecount, &old_latest,
  *    (void)unlink(CS string_sprintf("%s/maildirsize", check_path));
  *    if (THRESHOLD_CHECK)
  *      mailbox_size = maildir_compute_size(check_path, &mailbox_filecount, &old_latest,
- *        regex, dir_regex, FALSE);
+ *         re, dir_regex, FALSE);
  *    }
 */
 
  *    }
 */
 
@@ -2403,7 +2397,7 @@ else
     int filecount = 0;
     DEBUG(D_transport)
       debug_printf("quota checks on directory %s\n", check_path);
     int filecount = 0;
     DEBUG(D_transport)
       debug_printf("quota checks on directory %s\n", check_path);
-    size = check_dir_size(check_path, &filecount, regex);
+    size = check_dir_size(check_path, &filecount,  re);
     if (mailbox_size < 0) mailbox_size = size;
     if (mailbox_filecount < 0) mailbox_filecount = filecount;
     }
     if (mailbox_size < 0) mailbox_size = size;
     if (mailbox_filecount < 0) mailbox_filecount = filecount;
     }
@@ -2443,6 +2437,7 @@ else
     DEBUG(D_transport)
       debug_printf("delivering in maildir format in %s\n", path);
 
     DEBUG(D_transport)
       debug_printf("delivering in maildir format in %s\n", path);
 
+    GET_OPTION("maildir_tag");
     nametag = ob->maildir_tag;
 
     /* Check that nametag expands successfully; a hard failure causes a panic
     nametag = ob->maildir_tag;
 
     /* Check that nametag expands successfully; a hard failure causes a panic
@@ -2452,7 +2447,7 @@ else
     if (nametag && !expand_string(nametag) && !f.expand_string_forcedfail)
       {
       addr->message = string_sprintf("Expansion of \"%s\" (maildir_tag "
     if (nametag && !expand_string(nametag) && !f.expand_string_forcedfail)
       {
       addr->message = string_sprintf("Expansion of \"%s\" (maildir_tag "
-        "for %s transport) failed: %s", nametag, tblock->name,
+        "for %s transport) failed: %s", nametag, trname,
         expand_string_message);
       goto ret_panic;
       }
         expand_string_message);
       goto ret_panic;
       }
@@ -2530,9 +2525,9 @@ else
 
   else
     {
 
   else
     {
-    FILE *env_file;
+    FILE * env_file;
     mailstore_basename = string_sprintf("%s/%s-%s", path, message_id,
     mailstore_basename = string_sprintf("%s/%s-%s", path, message_id,
-      string_base62((long int)getpid()));
+      string_base62_64((long int)getpid()));
 
     DEBUG(D_transport)
       debug_printf("delivering in mailstore format in %s\n", path);
 
     DEBUG(D_transport)
       debug_printf("delivering in mailstore format in %s\n", path);
@@ -2571,7 +2566,7 @@ else
       {
       addr->basic_errno = errno;
       addr->message = string_sprintf("fdopen of %s ("
       {
       addr->basic_errno = errno;
       addr->message = string_sprintf("fdopen of %s ("
-        "for %s transport) failed", filename, tblock->name);
+        "for %s transport) failed", filename, trname);
       (void)close(fd);
       Uunlink(filename);
       goto ret_panic;
       (void)close(fd);
       Uunlink(filename);
       goto ret_panic;
@@ -2579,16 +2574,17 @@ else
 
     /* Write the envelope file, then close it. */
 
 
     /* Write the envelope file, then close it. */
 
+    GET_OPTION("mailstore_prefix");
     if (ob->mailstore_prefix)
       {
     if (ob->mailstore_prefix)
       {
-      uschar *s = expand_string(ob->mailstore_prefix);
+      uschar * s = expand_string(ob->mailstore_prefix);
       if (!s)
         {
         if (!f.expand_string_forcedfail)
           {
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "prefix for %s transport) failed: %s", ob->mailstore_prefix,
       if (!s)
         {
         if (!f.expand_string_forcedfail)
           {
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "prefix for %s transport) failed: %s", ob->mailstore_prefix,
-            tblock->name, expand_string_message);
+            trname, expand_string_message);
           (void)fclose(env_file);
           Uunlink(filename);
           goto ret_panic;
           (void)fclose(env_file);
           Uunlink(filename);
           goto ret_panic;
@@ -2607,16 +2603,17 @@ else
     for (address_item * taddr = addr; taddr; taddr = taddr->next)
       fprintf(env_file, "%s@%s\n", taddr->local_part, taddr->domain);
 
     for (address_item * taddr = addr; taddr; taddr = taddr->next)
       fprintf(env_file, "%s@%s\n", taddr->local_part, taddr->domain);
 
+    GET_OPTION("mailstore_suffix");
     if (ob->mailstore_suffix)
       {
     if (ob->mailstore_suffix)
       {
-      uschar *s = expand_string(ob->mailstore_suffix);
+      uschar * s = expand_string(ob->mailstore_suffix);
       if (!s)
         {
         if (!f.expand_string_forcedfail)
           {
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "suffix for %s transport) failed: %s", ob->mailstore_suffix,
       if (!s)
         {
         if (!f.expand_string_forcedfail)
           {
           addr->message = string_sprintf("Expansion of \"%s\" (mailstore "
             "suffix for %s transport) failed: %s", ob->mailstore_suffix,
-            tblock->name, expand_string_message);
+            trname, expand_string_message);
           (void)fclose(env_file);
           Uunlink(filename);
           goto ret_panic;
           (void)fclose(env_file);
           Uunlink(filename);
           goto ret_panic;
@@ -2769,18 +2766,21 @@ transport_newlines = 0;
 
 /* Write any configured prefix text first */
 
 
 /* Write any configured prefix text first */
 
-if (yield == OK && ob->message_prefix && *ob->message_prefix)
+if (yield == OK)
   {
   {
-  uschar *prefix = expand_string(ob->message_prefix);
-  if (!prefix)
-    {
-    errno = ERRNO_EXPANDFAIL;
-    addr->transport_return = PANIC;
-    addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s "
-      "transport) failed", ob->message_prefix, tblock->name);
-    yield = DEFER;
-    }
-  else if (!transport_write_string(fd, "%s", prefix)) yield = DEFER;
+  uschar * prefix = ob->message_prefix;
+  GET_OPTION("message_prefix");
+  if (prefix && *prefix)
+    if (!(prefix = expand_string(prefix)))
+      {
+      errno = ERRNO_EXPANDFAIL;
+      addr->transport_return = PANIC;
+      addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s "
+       "transport) failed", ob->message_prefix, trname);
+      yield = DEFER;
+      }
+    else if (!transport_write_string(fd, "%s", prefix))
+      yield = DEFER;
   }
 
 /* If the use_bsmtp option is on, we need to write SMTP prefix information. The
   }
 
 /* If the use_bsmtp option is on, we need to write SMTP prefix information. The
@@ -2832,18 +2832,21 @@ if (yield == OK)
 
 /* Now a configured suffix. */
 
 
 /* Now a configured suffix. */
 
-if (yield == OK && ob->message_suffix && *ob->message_suffix)
+if (yield == OK)
   {
   {
-  uschar *suffix = expand_string(ob->message_suffix);
-  if (!suffix)
-    {
-    errno = ERRNO_EXPANDFAIL;
-    addr->transport_return = PANIC;
-    addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s "
-      "transport) failed", ob->message_suffix, tblock->name);
-    yield = DEFER;
-    }
-  else if (!transport_write_string(fd, "%s", suffix)) yield = DEFER;
+  uschar * suffix = ob->message_suffix;
+  GET_OPTION("message_suffix");
+  if (suffix && *suffix)
+    if (!(suffix = expand_string(suffix)))
+      {
+      errno = ERRNO_EXPANDFAIL;
+      addr->transport_return = PANIC;
+      addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s "
+       "transport) failed", ob->message_suffix, trname);
+      yield = DEFER;
+      }
+    else if (!transport_write_string(fd, "%s", suffix))
+      yield = DEFER;
   }
 
 /* If batch smtp, write the terminating dot. */
   }
 
 /* If batch smtp, write the terminating dot. */
@@ -3147,7 +3150,7 @@ else
             addr->transport_return = PANIC;
             addr->message = string_sprintf("Expansion of \"%s\" "
               "(directory_file for %s transport) failed: %s",
             addr->transport_return = PANIC;
             addr->message = string_sprintf("Expansion of \"%s\" "
               "(directory_file for %s transport) failed: %s",
-              ob->dirfilename, tblock->name, expand_string_message);
+              ob->dirfilename, trname, expand_string_message);
             goto RETURN;
             }
 
             goto RETURN;
             }
 
@@ -3194,7 +3197,7 @@ else
           uschar *iptr = expand_string(nametag);
           if (iptr)
             {
           uschar *iptr = expand_string(nametag);
           if (iptr)
             {
-            uschar *etag = store_get(Ustrlen(iptr) + 2, is_tainted(iptr));
+            uschar *etag = store_get(Ustrlen(iptr) + 2, iptr);
             uschar *optr = etag;
             for ( ; *iptr; iptr++)
               if (mac_isgraph(*iptr) && *iptr != '/')
             uschar *optr = etag;
             for ( ; *iptr; iptr++)
               if (mac_isgraph(*iptr) && *iptr != '/')
@@ -3310,11 +3313,37 @@ return FALSE;
 
 tainted_ret_panic:
   addr->message = string_sprintf("Tainted '%s' (file or directory "
 
 tainted_ret_panic:
   addr->message = string_sprintf("Tainted '%s' (file or directory "
-      "name for %s transport) not permitted", path, tblock->name);
+      "name for %s transport) not permitted", path, trname);
 ret_panic:
   addr->transport_return = PANIC;
   return FALSE;
 }
 
 ret_panic:
   addr->transport_return = PANIC;
   return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define appendfile_transport_info _transport_info
+# endif
+
+transport_info appendfile_transport_info = {
+.drinfo = {
+  .driver_name =       US"appendfile",
+  .options =           appendfile_transport_options,
+  .options_count =     &appendfile_transport_options_count,
+  .options_block =     &appendfile_transport_option_defaults,  /* private options defaults */
+  .options_len =       sizeof(appendfile_transport_options_block),
+  .init =              appendfile_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                appendfile_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*!MACRO_PREDEF*/
+#endif /*TRANSPORT_APPENDFILE*/
 /* End of transport/appendfile.c */
 /* End of transport/appendfile.c */