build: use pkg-config for i18n
[exim.git] / src / src / transports / pipe.c
index 18f9fd84e852b2dc839c38a6ff67c484ac05e6a2..b765eeae5cdf1bc82ba3e0c19d5f7c4a24699752 100644 (file)
@@ -2,13 +2,15 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim maintainers 2020 - 2022 */
+/* Copyright (c) The Exim maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "../exim.h"
+
+#ifdef TRANSPORT_PIPE  /* Remainder of file */
 #include "pipe.h"
 
 #ifdef HAVE_SETCLASSRESOURCES
@@ -80,7 +82,7 @@ int pipe_transport_options_count =
 
 /* Dummy values */
 pipe_transport_options_block pipe_transport_option_defaults = {0};
-void pipe_transport_init(transport_instance *tblock) {}
+void pipe_transport_init(driver_instance *tblock) {}
 BOOL pipe_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
 
 #else   /*!MACRO_PREDEF*/
@@ -124,8 +126,7 @@ static int
 pipe_transport_setup(transport_instance *tblock, address_item *addrlist,
   transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg)
 {
-pipe_transport_options_block *ob =
-  (pipe_transport_options_block *)(tblock->options_block);
+pipe_transport_options_block * ob = tblock->drinst.options_block;
 
 #ifdef HAVE_SETCLASSRESOURCES
 if (ob->use_classresources)
@@ -175,10 +176,11 @@ enable consistency checks to be done, or anything else that needs
 to be set up. */
 
 void
-pipe_transport_init(transport_instance *tblock)
+pipe_transport_init(driver_instance * t)
 {
-pipe_transport_options_block *ob =
-  (pipe_transport_options_block *)(tblock->options_block);
+transport_instance * tblock = (transport_instance *)t;
+const uschar * trname = t->name;
+pipe_transport_options_block * ob = t->options_block;
 
 /* Set up the setup entry point, to be called in the privileged state */
 
@@ -190,13 +192,13 @@ if (tblock->deliver_as_creator && (tblock->uid_set || tblock->gid_set ||
   tblock->expand_uid != NULL || tblock->expand_gid != NULL))
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "both pipe_as_creator and an explicit uid/gid are set for the %s "
-        "transport", tblock->name);
+        "transport", 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 == NULL)
   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);
 
 /* Temp_errors must consist only of digits and colons, but there can be
 spaces round the colons, so allow them too. */
@@ -207,7 +209,7 @@ if (ob->temp_errors != NULL && Ustrcmp(ob->temp_errors, "*") != 0)
   if (ob->temp_errors[p] != 0)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "temp_errors must be a list of numbers or an asterisk for the %s "
-      "transport", tblock->name);
+      "transport", trname);
   }
 
 /* Only one of return_output/return_fail_output or log_output/log_fail_output
@@ -216,12 +218,12 @@ should be set. */
 if (tblock->return_output && tblock->return_fail_output)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "both return_output and return_fail_output set for %s transport",
-    tblock->name);
+    trname);
 
 if (tblock->log_output && tblock->log_fail_output)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "both log_output and log_fail_output set for the %s transport",
-    tblock->name);
+    trname);
 
 /* If batch SMTP is set, force the check and escape strings, and arrange that
 headers are also escaped. */
@@ -248,14 +250,14 @@ else
 if (ob->restrict_to_path && ob->use_shell)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "both restrict_to_path and use_shell set for %s transport",
-    tblock->name);
+    trname);
 
 /* The allow_commands and use_shell options are incompatible */
 
 if (ob->allow_commands && ob->use_shell)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
     "both allow_commands and use_shell set for %s transport",
-    tblock->name);
+    trname);
 
 /* Set up the bitwise options for transport_write_message from the various
 driver options. Only one of body_only and headers_only can be set. */
@@ -292,9 +294,9 @@ Returns:             TRUE if all went well; otherwise an error will be
 */
 
 static BOOL
-set_up_direct_command(const uschar *** argvptr, uschar * cmd,
-  BOOL expand_arguments, int expand_fail, address_item * addr, uschar * tname,
-  pipe_transport_options_block * ob)
+set_up_direct_command(const uschar *** argvptr, const uschar * cmd,
+  BOOL expand_arguments, int expand_fail, address_item * addr,
+  const uschar * tname, pipe_transport_options_block * ob)
 {
 BOOL permitted = FALSE;
 const uschar **argv;
@@ -315,6 +317,7 @@ argv = *argvptr;
 
 /* If allow_commands is set, see if the command is in the permitted list. */
 
+GET_OPTION("allow_commands");
 if (ob->allow_commands)
   {
   int sep = 0;
@@ -368,10 +371,11 @@ for it. */
 if (argv[0][0] != '/')
   {
   int sep = 0;
-  uschar *p;
-  const uschar *listptr = expand_string(ob->path);
+  uschar * p;
 
-  while ((p = string_nextinlist(&listptr, &sep, NULL, 0)))
+  GET_OPTION("path");
+  for (const uschar * listptr = expand_string(ob->path);
+      p = string_nextinlist(&listptr, &sep, NULL, 0); )
     {
     struct stat statbuf;
     sprintf(CS big_buffer, "%.256s/%.256s", p, argv[0]);
@@ -414,8 +418,9 @@ Returns:             TRUE if all went well; otherwise an error will be
 */
 
 static BOOL
-set_up_shell_command(const uschar ***argvptr, uschar *cmd,
-  BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname)
+set_up_shell_command(const uschar *** argvptr, const uschar * cmd,
+  BOOL expand_arguments, int expand_fail, address_item * addr,
+  const uschar * tname)
 {
 const uschar **argv;
 
@@ -462,10 +467,10 @@ if (expand_arguments)
       }
 
     g = string_cat(g, q);
-    argv[2] = (cmd = string_from_gstring(g)) ? expand_string(cmd) : NULL;
+    argv[2] = (cmd = string_from_gstring(g)) ? expand_cstring(cmd) : NULL;
     }
   else
-    argv[2] = expand_string(cmd);
+    argv[2] = expand_cstring(cmd);
 
   f.enable_dollar_recipients = FALSE;
 
@@ -508,21 +513,17 @@ pipe_transport_entry(
   transport_instance *tblock,      /* data for this instantiation */
   address_item *addr)              /* address(es) we are working on */
 {
+pipe_transport_options_block * ob = tblock->drinst.options_block;
+const uschar * trname = tblock->drinst.name;
 pid_t pid, outpid;
 int fd_in, fd_out, rc;
-int envcount = 0;
-int envsep = 0;
-int expand_fail;
-pipe_transport_options_block *ob =
-  (pipe_transport_options_block *)(tblock->options_block);
-int timeout = ob->timeout;
-BOOL written_ok = FALSE;
-BOOL expand_arguments;
-const uschar **argv;
-uschar *envp[50];
-const uschar *envlist = ob->environment;
-uschar *cmd, *ss;
-uschar *eol = ob->use_crlf ? US"\r\n" : US"\n";
+int envcount = 0, envsep = 0, expand_fail, timeout = ob->timeout;
+BOOL written_ok = FALSE, expand_arguments;
+const uschar ** argv;
+uschar * envp[50];
+const uschar * envlist = ob->environment, * cmd;
+uschar * ss;
+uschar * eol = ob->use_crlf ? US"\r\n" : US"\n";
 transport_ctx tctx = {
   .tblock = tblock,
   .addr = addr,
@@ -531,7 +532,7 @@ transport_ctx tctx = {
   ob->options | topt_not_socket /* set at initialization time */
 };
 
-DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name);
+DEBUG(D_transport) debug_printf("%s transport entered\n", trname);
 
 /* Set up for the good case */
 
@@ -549,6 +550,7 @@ if (testflag(addr, af_pfr) && addr->local_part[0] == '|')
     {
     /* Enables expansion of $address_pipe into separate arguments */
     setflag(addr, af_force_command);
+    GET_OPTION("commsnd");
     cmd = ob->cmd;
     expand_arguments = TRUE;
     expand_fail = PANIC;
@@ -556,12 +558,13 @@ if (testflag(addr, af_pfr) && addr->local_part[0] == '|')
   else
     {
     cmd = addr->local_part + 1;
-    while (isspace(*cmd)) cmd++;
+    Uskip_whitespace(&cmd);
     expand_arguments = testflag(addr, af_expand_pipe);
     expand_fail = FAIL;
     }
 else
   {
+  GET_OPTION("commsnd");
   cmd = ob->cmd;
   expand_arguments = TRUE;
   expand_fail = PANIC;
@@ -575,14 +578,14 @@ if (!cmd || !*cmd)
   {
   addr->transport_return = DEFER;
   addr->message = string_sprintf("no command specified for %s transport",
-    tblock->name);
+    trname);
   return FALSE;
   }
 if (is_tainted(cmd))
   {
   DEBUG(D_transport) debug_printf("cmd '%s' is tainted\n", cmd);
   addr->message = string_sprintf("Tainted '%s' (command "
-    "for %s transport) not permitted", cmd, tblock->name);
+    "for %s transport) not permitted", cmd, trname);
   addr->transport_return = PANIC;
   return FALSE;
   }
@@ -611,10 +614,10 @@ there is an option to do that. */
 if (ob->use_shell)
   {
   if (!set_up_shell_command(&argv, cmd, expand_arguments, expand_fail, addr,
-    tblock->name)) return FALSE;
+    trname)) return FALSE;
   }
 else if (!set_up_direct_command(&argv, cmd, expand_arguments, expand_fail, addr,
-  tblock->name, ob)) return FALSE;
+  trname, ob)) return FALSE;
 
 expand_nmax = -1;           /* Reset */
 filter_thisaddress = NULL;
@@ -649,12 +652,13 @@ else if (timezone_string && timezone_string[0])
 
 /* Add any requested items */
 
+GET_OPTION("environment");
 if (envlist)
   if (!(envlist = expand_cstring(envlist)))
     {
     addr->transport_return = DEFER;
     addr->message = string_sprintf("failed to expand string \"%s\" "
-      "for %s transport: %s", ob->environment, tblock->name,
+      "for %s transport: %s", ob->environment, trname,
       expand_string_message);
     return FALSE;
     }
@@ -666,7 +670,7 @@ while ((ss = string_nextinlist(&envlist, &envsep, NULL, 0)))
      addr->transport_return = DEFER;
      addr->basic_errno = E2BIG;
      addr->message = string_sprintf("too many environment settings for "
-       "%s transport", tblock->name);
+       "%s transport", trname);
      return FALSE;
      }
    envp[envcount++] = string_copy(ss);
@@ -680,7 +684,7 @@ if (f.dont_deliver)
   {
   DEBUG(D_transport)
     debug_printf("*** delivery by %s transport bypassed by -N option",
-      tblock->name);
+      trname);
   return FALSE;
   }
 
@@ -711,7 +715,7 @@ if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE,
   {
   addr->transport_return = DEFER;
   addr->message = string_sprintf(
-    "Failed to create child process for %s transport: %s", tblock->name,
+    "Failed to create child process for %s transport: %s", trname,
       strerror(errno));
   return FALSE;
   }
@@ -725,7 +729,7 @@ if ((outpid = exim_fork(US"pipe-tpt-output")) < 0)
   addr->transport_return = DEFER;
   addr->message = string_sprintf(
     "Failed to create process for handling output in %s transport",
-      tblock->name);
+      trname);
   (void)close(fd_in);
   (void)close(fd_out);
   return FALSE;
@@ -795,14 +799,15 @@ transport_count = 0;
 
 /* First write any configured prefix information */
 
+GET_OPTION("message_prefix");
 if (ob->message_prefix)
   {
-  uschar *prefix = expand_string(ob->message_prefix);
+  uschar * prefix = expand_string(ob->message_prefix);
   if (!prefix)
     {
     addr->transport_return = f.search_find_defer? DEFER : PANIC;
     addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s "
-      "transport) failed: %s", ob->message_prefix, tblock->name,
+      "transport) failed: %s", ob->message_prefix, trname,
       expand_string_message);
     return FALSE;
     }
@@ -837,14 +842,15 @@ if (!transport_write_message(&tctx, 0))
 
 /* Now any configured suffix */
 
+GET_OPTION("message_suffix");
 if (ob->message_suffix)
   {
-  uschar *suffix = expand_string(ob->message_suffix);
+  uschar * suffix = expand_string(ob->message_suffix);
   if (!suffix)
     {
     addr->transport_return = f.search_find_defer? DEFER : PANIC;
     addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s "
-      "transport) failed: %s", ob->message_suffix, tblock->name,
+      "transport) failed: %s", ob->message_suffix, trname,
       expand_string_message);
     return FALSE;
     }
@@ -938,7 +944,7 @@ if ((rc = child_close(pid, timeout)) != 0)
     {
     addr->transport_return = PANIC;
     addr->message = string_sprintf("Wait() failed for child process of %s "
-      "transport: %s%s", tblock->name, strerror(errno), tmsg);
+      "transport: %s%s", trname, strerror(errno), tmsg);
     }
 
   /* Since the transport_filter timed out we assume it has sent the child process
@@ -966,14 +972,14 @@ if ((rc = child_close(pid, timeout)) != 0)
       addr->transport_return = DEFER;
       addr->special_action = SPECIAL_FREEZE;
       addr->message = string_sprintf("Child process of %s transport (running "
-        "command \"%s\") was terminated by signal %d (%s)%s", tblock->name, cmd,
+        "command \"%s\") was terminated by signal %d (%s)%s", trname, cmd,
         -rc, os_strsignal(-rc), tmsg);
       }
     else if (!ob->ignore_status)
       {
       addr->transport_return = FAIL;
       addr->message = string_sprintf("Child process of %s transport (running "
-        "command \"%s\") was terminated by signal %d (%s)%s", tblock->name, cmd,
+        "command \"%s\") was terminated by signal %d (%s)%s", trname, cmd,
         -rc, os_strsignal(-rc), tmsg);
       }
     }
@@ -1059,7 +1065,7 @@ if ((rc = child_close(pid, timeout)) != 0)
       doesn't have to be brilliantly efficient - it is an error situation. */
 
       addr->message = string_sprintf("Child process of %s transport returned "
-        "%d", tblock->name, rc);
+        "%d", trname, rc);
       g = string_cat(NULL, addr->message);
 
       /* If the return code is > 128, it often means that a shell command
@@ -1109,7 +1115,7 @@ are complete before we pass this point. */
 
 while (wait(&rc) >= 0);
 
-DEBUG(D_transport) debug_printf("%s transport yielded %d\n", tblock->name,
+DEBUG(D_transport) debug_printf("%s transport yielded %d\n", trname,
   addr->transport_return);
 
 /* If there has been a problem, the message in addr->message contains details
@@ -1122,5 +1128,31 @@ if (addr->transport_return != OK)
 return FALSE;
 }
 
+
+
+
+# ifdef DYNLOOKUP
+#  define pipe_transport_info _transport_info
+# endif
+
+transport_info pipe_transport_info = {
+.drinfo = {
+  .driver_name =       US"pipe",
+  .options =           pipe_transport_options,
+  .options_count =     &pipe_transport_options_count,
+  .options_block =     &pipe_transport_option_defaults,
+  .options_len =       sizeof(pipe_transport_options_block),
+  .init =              pipe_transport_init,
+# ifdef DYNLOOKUP
+  .dyn_magic =         TRANSPORT_MAGIC,
+# endif
+  },
+.code =                pipe_transport_entry,
+.tidyup =      NULL,
+.closedown =   NULL,
+.local =       TRUE
+};
+
 #endif /*!MACRO_PREDEF*/
+#endif /*TRASPORT_PIPE*/
 /* End of transport/pipe.c */