build: use pkg-config for i18n
[exim.git] / src / src / transports / appendfile.c
index ec41ca035a660ac595891de62beddf139314427e..08a59543c5bfcd96fd63103305c235b84f71114b 100644 (file)
@@ -2,13 +2,15 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim maintainers 2020 - 2023 */
+/* Copyright (c) The Exim maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "../exim.h"
+
+#ifdef TRANSPORT_APPENDFILE    /* Remainder of file */
 #include "appendfile.h"
 
 #ifdef SUPPORT_MAILDIR
@@ -105,7 +107,7 @@ int appendfile_transport_options_count =
 
 /* 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*/
@@ -153,7 +155,6 @@ static const char *mailbox_formats[] = {
   (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0))
 
 
-
 /*************************************************
 *              Setup entry point                 *
 *************************************************/
@@ -179,18 +180,24 @@ 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;
 
 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 */
 
+GET_OPTION("quota");
+q = ob->quota;
 for (int i = 0; i < 5; i++)
   {
   double d = default_value;
@@ -204,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: "
-        "%s", q, tblock->name, expand_string_message);
+        "%s", q, trname, expand_string_message);
       return f.search_find_defer ? DEFER : FAIL;
       }
 
@@ -224,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)"
-          " for %s transport", (int)d, tblock->name);
+          " for %s transport", (int)d, trname);
         return FAIL;
         }
       ob->quota_warn_threshold_is_percent = TRUE;
@@ -245,7 +252,7 @@ for (int i = 0; i < 5; i++)
     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;
       }
     }
@@ -259,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;
+      GET_OPTION("quota_filecount");
       q = ob->quota_filecount;
       break;
 
@@ -267,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;
+      GET_OPTION("quota_warn_threshold");
       q = ob->quota_warn_threshold;
       break;
 
@@ -274,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;
+      GET_OPTION("mailbox_size");
       q = ob->mailbox_size_string;
       default_value = -1.0;
       break;
@@ -282,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;
+      GET_OPTION("mailbox_filecount");
       q = ob->mailbox_filecount_string;
       break;
 
@@ -295,7 +306,7 @@ for (int i = 0; i < 5; i++)
   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;
     }
   }
@@ -314,10 +325,11 @@ enable consistency checks to be done, or anything else that needs
 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 */
@@ -332,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  "
-  "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. */
@@ -341,10 +353,10 @@ if (ob->filename)
   {
   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  "
-      "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
@@ -360,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 "
-    "binary was built", tblock->name);
+    "binary was built", trname);
   #endif  /* NO_FLOCK */
   if (!ob->set_use_fcntl) ob->use_fcntl = FALSE;
   }
@@ -384,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  "
-    "no locking configured", tblock->name);
+    "no locking configured", trname);
 
 /* Unset timeouts for non-used locking types */
 
@@ -399,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  "
-      "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  "
-      "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  "
-      "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,
-    "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. */
@@ -426,7 +438,7 @@ if ((s = ob->create_file_string ) && *s)
   else
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "invalid value given for \"create_file\" for the %s transport: '%s'",
-      tblock->name, s);
+      trname, s);
   ob->create_file = val;
   }
 
@@ -583,8 +595,8 @@ Returns:       pointer to the required transport, or NULL
 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;
@@ -607,11 +619,11 @@ while ((s = string_nextinlist(&format, &sep, big_buffer, big_buffer_size)))
 
   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_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;
@@ -661,13 +673,14 @@ Returns:        the sum of the sizes of the stattable files
 off_t
 check_dir_size(const uschar * dirname, int * countptr, const pcre2_code * re)
 {
-DIR *dir;
+DIR * dir;
 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;
 
-for (struct dirent *ent; ent = readdir(dir); )
+for (struct dirent * ent; ent = readdir(dir); )
   {
   uschar * path, * name = US ent->d_name;
   struct stat statbuf;
@@ -675,6 +688,11 @@ for (struct dirent *ent; ent = readdir(dir); )
   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 */
 
@@ -726,6 +744,7 @@ DEBUG(D_transport)
   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;
 }
@@ -1136,37 +1155,27 @@ appendfile_transport_entry(
   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;
-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;
-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;
-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 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;
@@ -1215,13 +1224,14 @@ if (!fdname)
   {
   if (!(fdname = ob->filename))
     {
+    GET_OPTION("directory");
     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;
     }
   }
@@ -1232,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",
-    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 "
-    "name for %s transport) failed: %s", fdname, tblock->name,
+    "name for %s transport) failed: %s", fdname, trname,
     expand_string_message);
   goto ret_panic;
   }
@@ -1314,7 +1324,7 @@ if (f.dont_deliver)
   {
   DEBUG(D_transport)
     debug_printf("*** delivery by %s transport bypassed by -N option\n",
-      tblock->name);
+      trname);
   addr->transport_return = OK;
   return FALSE;
   }
@@ -1349,7 +1359,7 @@ if (!isdirectory)
      && ob->create_file == create_belowhome)
     if (is_tainted(path))
       {
-      DEBUG(D_transport) debug_printf("de-tainting path '%s'\n", path);
+      DEBUG(D_transport) debug_printf("below-home: de-tainting path '%s'\n", path);
       path = string_copy_taint(path, GET_UNTAINTED);
       }
 
@@ -1371,7 +1381,7 @@ if (!isdirectory)
       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;
       }
     }
@@ -1399,11 +1409,12 @@ if (!isdirectory)
         {
         if (tt)
           {
+         transport_info * ti = tt->drinst.info;
           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;
-          (tt->info->code)(tt, addr);
+          (ti->code)(tt, addr);
           }
         return FALSE;
         }
@@ -2188,7 +2199,7 @@ else
   if (is_tainted(path))
     if (ob->create_file == create_belowhome)
       {
-      DEBUG(D_transport) debug_printf("de-tainting path '%s'\n", path);
+      DEBUG(D_transport) debug_printf("below-home: de-tainting path '%s'\n", path);
       path = string_copy_taint(path, GET_UNTAINTED);
       }
     else
@@ -2228,13 +2239,14 @@ else
 
     /* 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,
-          tblock->name, expand_string_message);
+          trname, expand_string_message);
         goto ret_panic;
         }
 
@@ -2425,6 +2437,7 @@ else
     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
@@ -2434,7 +2447,7 @@ else
     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;
       }
@@ -2553,7 +2566,7 @@ else
       {
       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;
@@ -2561,16 +2574,17 @@ else
 
     /* Write the envelope file, then close it. */
 
+    GET_OPTION("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,
-            tblock->name, expand_string_message);
+            trname, expand_string_message);
           (void)fclose(env_file);
           Uunlink(filename);
           goto ret_panic;
@@ -2589,16 +2603,17 @@ else
     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)
       {
-      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,
-            tblock->name, expand_string_message);
+            trname, expand_string_message);
           (void)fclose(env_file);
           Uunlink(filename);
           goto ret_panic;
@@ -2751,18 +2766,21 @@ transport_newlines = 0;
 
 /* 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
@@ -2814,18 +2832,21 @@ if (yield == OK)
 
 /* 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. */
@@ -3129,7 +3150,7 @@ else
             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;
             }
 
@@ -3292,11 +3313,37 @@ return FALSE;
 
 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;
 }
 
+
+
+
+# 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 /*TRANSPORT_APPENDFILE*/
 /* End of transport/appendfile.c */