build: use pkg-config for i18n
[exim.git] / src / src / transports / queuefile.c
index bc4ed3f4096d1665619ec6c5e233ae3cc0374a25..64031e55db41701ebe8e26b4f41ded6a547c3027 100644 (file)
@@ -2,14 +2,23 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 1995 - 2024 */
 /* Copyright (c) Andrew Colin Kissa <andrew@topdog.za.net> 2016 */
 /* Copyright (c) University of Cambridge 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* Copyright (c) Andrew Colin Kissa <andrew@topdog.za.net> 2016 */
 /* Copyright (c) University of Cambridge 2016 */
 /* 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 EXPERIMENTAL_QUEUEFILE  /* whole file */
 #include "queuefile.h"
 
 #include "queuefile.h"
 
+#ifndef EXIM_HAVE_OPENAT
+# error queuefile transport reqires openat() support
+#endif
+
 /* Options specific to the appendfile transport. They must be in alphabetic
 order (note that "_" comes before the lower case letters). Some of them are
 stored in the publicly visible instance block - these are flagged with the
 /* Options specific to the appendfile transport. They must be in alphabetic
 order (note that "_" comes before the lower case letters). Some of them are
 stored in the publicly visible instance block - these are flagged with the
@@ -17,7 +26,7 @@ opt_public flag. */
 
 optionlist queuefile_transport_options[] = {
   { "directory", opt_stringptr,
 
 optionlist queuefile_transport_options[] = {
   { "directory", opt_stringptr,
-    (void *)offsetof(queuefile_transport_options_block, dirname) },
+    OPT_OFF(queuefile_transport_options_block, dirname) },
 };
 
 
 };
 
 
@@ -32,7 +41,7 @@ int queuefile_transport_options_count =
 
 /* Dummy values */
 queuefile_transport_options_block queuefile_transport_option_defaults = {0};
 
 /* Dummy values */
 queuefile_transport_options_block queuefile_transport_option_defaults = {0};
-void queuefile_transport_init(transport_instance *tblock) {}
+void queuefile_transport_init(driver_instance *tblock) {}
 BOOL queuefile_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
 
 #else   /*!MACRO_PREDEF*/
 BOOL queuefile_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
 
 #else   /*!MACRO_PREDEF*/
@@ -49,14 +58,14 @@ queuefile_transport_options_block queuefile_transport_option_defaults = {
 *          Initialization entry point            *
 *************************************************/
 
 *          Initialization entry point            *
 *************************************************/
 
-void queuefile_transport_init(transport_instance *tblock)
+void queuefile_transport_init(driver_instance * t)
 {
 {
-queuefile_transport_options_block *ob =
-  (queuefile_transport_options_block *) tblock->options_block;
+transport_instance * tblock = (transport_instance *)t;
+queuefile_transport_options_block * ob = t->options_block;
 
 if (!ob->dirname)
   log_write(0, LOG_PANIC_DIE | LOG_CONFIG,
 
 if (!ob->dirname)
   log_write(0, LOG_PANIC_DIE | LOG_CONFIG,
-    "directory must be set for the %s transport", tblock->name);
+    "directory must be set for the %s transport", t->name);
 }
 
 /* This function will copy from a file to another
 }
 
 /* This function will copy from a file to another
@@ -73,14 +82,13 @@ copy_spool_file(int dst, int src)
 {
 int i, j;
 uschar buffer[16384];
 {
 int i, j;
 uschar buffer[16384];
-uschar * s;
 
 if (lseek(src, 0, SEEK_SET) != 0)
   return FALSE;
 
 do
   if ((j = read(src, buffer, sizeof(buffer))) > 0)
 
 if (lseek(src, 0, SEEK_SET) != 0)
   return FALSE;
 
 do
   if ((j = read(src, buffer, sizeof(buffer))) > 0)
-    for (s = buffer; (i = write(dst, s, j)) != j; s += i, j -= i)
+    for (uschar * s = buffer; (i = write(dst, s, j)) != j; s += i, j -= i)
       if (i < 0)
        return FALSE;
   else if (j < 0)
       if (i < 0)
        return FALSE;
   else if (j < 0)
@@ -95,6 +103,7 @@ and data files to the destination directory
 Arguments:
   tb           the transport block
   addr          address_item being processed
 Arguments:
   tb           the transport block
   addr          address_item being processed
+  dstpath      destination directory name
   sdfd          int Source directory fd
   ddfd          int Destination directory fd
   link_file     BOOL use linkat instead of data copy
   sdfd          int Source directory fd
   ddfd          int Destination directory fd
   link_file     BOOL use linkat instead of data copy
@@ -105,23 +114,22 @@ Returns:       TRUE if all went well, FALSE otherwise
 
 static BOOL
 copy_spool_files(transport_instance * tb, address_item * addr,
 
 static BOOL
 copy_spool_files(transport_instance * tb, address_item * addr,
-  int sdfd, int ddfd, BOOL link_file, int srcfd)
+  const uschar * dstpath, int sdfd, int ddfd, BOOL link_file, int srcfd)
 {
 {
+const uschar * trname = tb->drinst.name;
 BOOL is_hdr_file = srcfd < 0;
 const uschar * suffix = srcfd < 0 ? US"H" : US"D";
 int dstfd;
 const uschar * filename = string_sprintf("%s-%s", message_id, suffix);
 const uschar * srcpath = spool_fname(US"input", message_subdir, message_id, suffix);
 BOOL is_hdr_file = srcfd < 0;
 const uschar * suffix = srcfd < 0 ? US"H" : US"D";
 int dstfd;
 const uschar * filename = string_sprintf("%s-%s", message_id, suffix);
 const uschar * srcpath = spool_fname(US"input", message_subdir, message_id, suffix);
-const uschar * dstpath = string_sprintf("%s/%s-%s",
-  ((queuefile_transport_options_block *) tb->options_block)->dirname,
-  message_id, suffix);
-const uschar * s;
-const uschar * op;
+const uschar * s, * op;
+
+dstpath = string_sprintf("%s/%s-%s", dstpath, message_id, suffix);
 
 if (link_file)
   {
   DEBUG(D_transport) debug_printf("%s transport, linking %s => %s\n",
 
 if (link_file)
   {
   DEBUG(D_transport) debug_printf("%s transport, linking %s => %s\n",
-    tb->name, srcpath, dstpath);
+    trname, srcpath, dstpath);
 
   if (linkat(sdfd, CCS filename, ddfd, CCS filename, 0) >= 0)
     return TRUE;
 
   if (linkat(sdfd, CCS filename, ddfd, CCS filename, 0) >= 0)
     return TRUE;
@@ -132,14 +140,14 @@ if (link_file)
 else                                   /* use data copy */
   {
   DEBUG(D_transport) debug_printf("%s transport, copying %s => %s\n",
 else                                   /* use data copy */
   {
   DEBUG(D_transport) debug_printf("%s transport, copying %s => %s\n",
-    tb->name, srcpath, dstpath);
+    trname, srcpath, dstpath);
 
   if (  (s = dstpath,
 
   if (  (s = dstpath,
-        (dstfd = openat(ddfd, CCS filename, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE))
+        (dstfd = exim_openat4(ddfd, CCS filename, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE))
         < 0
        )
      ||    is_hdr_file
         < 0
        )
      ||    is_hdr_file
-       && (s = srcpath, (srcfd = openat(sdfd, CCS filename, O_RDONLY)) < 0)
+       && (s = srcpath, (srcfd = exim_openat(sdfd, CCS filename, O_RDONLY)) < 0)
      )
     op = US"opening";
 
      )
     op = US"opening";
 
@@ -155,7 +163,7 @@ else                                        /* use data copy */
 
 addr->basic_errno = errno;
 addr->message = string_sprintf("%s transport %s file: %s failed with error: %s",
 
 addr->basic_errno = errno;
 addr->message = string_sprintf("%s transport %s file: %s failed with error: %s",
-  tb->name, op, s, strerror(errno));
+  trname, op, s, strerror(errno));
 addr->transport_return = DEFER;
 return FALSE;
 }
 addr->transport_return = DEFER;
 return FALSE;
 }
@@ -170,16 +178,16 @@ the first address is the status for all addresses in a batch. */
 BOOL
 queuefile_transport_entry(transport_instance * tblock, address_item * addr)
 {
 BOOL
 queuefile_transport_entry(transport_instance * tblock, address_item * addr)
 {
-queuefile_transport_options_block * ob =
-  (queuefile_transport_options_block *) tblock->options_block;
+queuefile_transport_options_block * ob = tblock->drinst.options_block;
+const uschar * trname = tblock->drinst.name;
 BOOL can_link;
 uschar * sourcedir = spool_dname(US"input", message_subdir);
 BOOL can_link;
 uschar * sourcedir = spool_dname(US"input", message_subdir);
-uschar * s;
+uschar * s, * dstdir;
 struct stat dstatbuf, sstatbuf;
 int ddfd = -1, sdfd = -1;
 
 DEBUG(D_transport)
 struct stat dstatbuf, sstatbuf;
 int ddfd = -1, sdfd = -1;
 
 DEBUG(D_transport)
-  debug_printf("%s transport entered\n", tblock->name);
+  debug_printf("%s transport entered\n", trname);
 
 #ifndef O_DIRECTORY
 # define O_DIRECTORY 0
 
 #ifndef O_DIRECTORY
 # define O_DIRECTORY 0
@@ -188,18 +196,26 @@ DEBUG(D_transport)
 # define O_NOFOLLOW 0
 #endif
 
 # define O_NOFOLLOW 0
 #endif
 
-if (ob->dirname[0] != '/')
+GET_OPTION("directory");
+if (!(dstdir = expand_string(ob->dirname)))
+  {
+  addr->message = string_sprintf("%s transport: failed to expand dirname option",
+    trname);
+  addr->transport_return = DEFER;
+  return FALSE;
+  }
+if (*dstdir != '/')
   {
   addr->transport_return = PANIC;
   addr->message = string_sprintf("%s transport directory: "
   {
   addr->transport_return = PANIC;
   addr->message = string_sprintf("%s transport directory: "
-    "%s is not absolute", tblock->name, ob->dirname);
+    "%s is not absolute", trname, dstdir);
   return FALSE;
   }
 
 /* Open the source and destination directories and check if they are
 on the same filesystem, so we can hard-link files rather than copying. */
 
   return FALSE;
   }
 
 /* Open the source and destination directories and check if they are
 on the same filesystem, so we can hard-link files rather than copying. */
 
-if (  (s = ob->dirname,
+if (  (s = dstdir,
        (ddfd = Uopen(s, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0)) < 0)
    || (s = sourcedir,
        (sdfd = Uopen(sourcedir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0)) < 0)
        (ddfd = Uopen(s, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0)) < 0)
    || (s = sourcedir,
        (sdfd = Uopen(sourcedir, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0)) < 0)
@@ -208,28 +224,28 @@ if (  (s = ob->dirname,
   addr->transport_return = PANIC;
   addr->basic_errno = errno;
   addr->message = string_sprintf("%s transport accessing directory: %s "
   addr->transport_return = PANIC;
   addr->basic_errno = errno;
   addr->message = string_sprintf("%s transport accessing directory: %s "
-    "failed with error: %s", tblock->name, s, strerror(errno));
+    "failed with error: %s", trname, s, strerror(errno));
   if (ddfd >= 0) (void) close(ddfd);
   return FALSE;
   }
 
   if (ddfd >= 0) (void) close(ddfd);
   return FALSE;
   }
 
-if (  (s = ob->dirname, fstat(ddfd, &dstatbuf) < 0)
-   || (s = sourcedir,   fstat(sdfd, &sstatbuf) < 0)
+if (  (s = dstdir,    fstat(ddfd, &dstatbuf) < 0)
+   || (s = sourcedir, fstat(sdfd, &sstatbuf) < 0)
    )
   {
   addr->transport_return = PANIC;
   addr->basic_errno = errno;
   addr->message = string_sprintf("%s transport fstat on directory fd: "
    )
   {
   addr->transport_return = PANIC;
   addr->basic_errno = errno;
   addr->message = string_sprintf("%s transport fstat on directory fd: "
-    "%s failed with error: %s", tblock->name, s, strerror(errno));
+    "%s failed with error: %s", trname, s, strerror(errno));
   goto RETURN;
   }
 can_link = (dstatbuf.st_dev == sstatbuf.st_dev);
 
   goto RETURN;
   }
 can_link = (dstatbuf.st_dev == sstatbuf.st_dev);
 
-if (dont_deliver)
+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;
   goto RETURN;
   }
   addr->transport_return = OK;
   goto RETURN;
   }
@@ -237,25 +253,26 @@ if (dont_deliver)
 /* Link or copy the header and data spool files */
 
 DEBUG(D_transport)
 /* Link or copy the header and data spool files */
 
 DEBUG(D_transport)
-  debug_printf("%s transport, copying header file\n", tblock->name);
+  debug_printf("%s transport, copying header file\n", trname);
 
 
-if (!copy_spool_files(tblock, addr, sdfd, ddfd, can_link, -1))
+if (!copy_spool_files(tblock, addr, dstdir, sdfd, ddfd, can_link, -1))
   goto RETURN;
 
 DEBUG(D_transport)
   goto RETURN;
 
 DEBUG(D_transport)
-  debug_printf("%s transport, copying data file\n", tblock->name);
+  debug_printf("%s transport, copying data file\n", trname);
 
 
-if (!copy_spool_files(tblock, addr, sdfd, ddfd, can_link, deliver_datafile))
+if (!copy_spool_files(tblock, addr, dstdir, sdfd, ddfd, can_link,
+       deliver_datafile))
   {
   DEBUG(D_transport)
     debug_printf("%s transport, copying data file failed, "
   {
   DEBUG(D_transport)
     debug_printf("%s transport, copying data file failed, "
-      "unlinking the header file\n", tblock->name);
-  Uunlink(string_sprintf("%s/%s-H", ob->dirname, message_id));
+      "unlinking the header file\n", trname);
+  Uunlink(string_sprintf("%s/%s-H", dstdir, message_id));
   goto RETURN;
   }
 
 DEBUG(D_transport)
   goto RETURN;
   }
 
 DEBUG(D_transport)
-  debug_printf("%s transport succeeded\n", tblock->name);
+  debug_printf("%s transport succeeded\n", trname);
 
 addr->transport_return = OK;
 
 
 addr->transport_return = OK;
 
@@ -268,4 +285,30 @@ put in the first address of a batch. */
 return FALSE;
 }
 
 return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define queuefile_transport_info _transport_info
+# endif
+
+transport_info queuefile_transport_info = {
+.drinfo = {
+  .driver_name =       US"queuefile",
+  .options =           queuefile_transport_options,
+  .options_count =     &queuefile_transport_options_count,
+  .options_block =     &queuefile_transport_option_defaults,
+  .options_len =       sizeof(queuefile_transport_options_block),
+  .init =              queuefile_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                queuefile_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
 #endif /*!MACRO_PREDEF*/
+#endif /*EXPERIMENTAL_QUEUEFILE*/