[exim-cvs] Add the force_command option to the pipe transpor…

Αρχική Σελίδα
Delete this message
Reply to this message
Συντάκτης: Exim Git Commits Mailing List
Ημερομηνία:  
Προς: exim-cvs
Αντικείμενο: [exim-cvs] Add the force_command option to the pipe transport
Gitweb: http://git.exim.org/exim.git/commitdiff/09792322d9224b0407783a19c2dd57fd1a8bbd52
Commit:     09792322d9224b0407783a19c2dd57fd1a8bbd52
Parent:     0fbd9bff71b47e3a32e54629c3f67e7eda1812fe
Author:     J. Nick Koston <nick@???>
AuthorDate: Sat Mar 30 02:22:53 2013 -0500
Committer:  J. Nick Koston <nick@???>
CommitDate: Mon Apr 1 01:38:18 2013 -0500


    Add the force_command option to the pipe transport


    Normally when a router redirects an address directly to a pipe command
    the command option on the transport is ignored.  If force_command
    is set, the command option will expanded and used. This is especially
    useful for forcing a wrapper or additional argument to be added to the
    command.
---
 doc/doc-docbook/spec.xfpt |   27 +++++++++--
 src/src/structs.h         |    1 +
 src/src/transport.c       |  120 ++++++++++++++++++++++++++++++++++++++++++++-
 src/src/transports/pipe.c |   26 ++++++++--
 src/src/transports/pipe.h |    1 +
 5 files changed, 163 insertions(+), 12 deletions(-)


diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 92d0a22..c3994a7 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -21601,10 +21601,10 @@ that are routed to the transport.
.vindex "&$address_pipe$&"
A router redirects an address directly to a pipe command (for example, from an
alias or forward file). In this case, &$address_pipe$& contains the text of the
-pipe command, and the &%command%& option on the transport is ignored. If only
-one address is being transported (&%batch_max%& is not greater than one, or
-only one address was redirected to this pipe command), &$local_part$& contains
-the local part that was redirected.
+pipe command, and the &%command%& option on the transport is ignored unless
+&%force_command%& is set. If only one address is being transported
+(&%batch_max%& is not greater than one, or only one address was redirected to
+this pipe command), &$local_part$& contains the local part that was redirected.
.endlist


@@ -21712,6 +21712,12 @@ inserted in the argument list at that point &'as a separate argument'&. This
avoids any problems with spaces or shell metacharacters, and is of use when a
&(pipe)& transport is handling groups of addresses in a batch.

+If &(force_command)& is enabled on the transport, Special handling takes place
+for an argument that consists of precisely the text &`$address_pipe`&. It
+is handled much like &`$pipe_addresses`& above. It is expanded and each
+argument is inserted in the argument list at that point
+&'as a separate argument'&.
+
After splitting up into arguments and expansion, the resulting command is run
in a subprocess directly from the transport, &'not'& under a shell. The
message that is being delivered is supplied on the standard input, and the
@@ -21864,6 +21870,19 @@ a bounce message is sent. If &%freeze_signal%& is set, the message will be
frozen in Exim's queue instead.


+.option force_command pipe boolean false
+.cindex "force command"
+.cindex "&(pipe)& transport", "force command"
+Normally when a router redirects an address directly to a pipe command
+the &%command%& option on the transport is ignored.  If &%force_command%&
+is set, the &%command%& option will used. This is especially
+useful for forcing a wrapper or additional argument to be added to the
+command. For example:
+.code
+command = /usr/bin/remote_exec myhost -- $address_pipe
+force_command
+.endd
+
 .option ignore_status pipe boolean false
 If this option is true, the status returned by the subprocess that is set up to
 run the command is ignored, and Exim behaves as if zero had been returned.
diff --git a/src/src/structs.h b/src/src/structs.h
index d11e91a..1105bb7 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -482,6 +482,7 @@ typedef struct address_item_propagated {
 #define af_cert_verified       0x01000000 /* delivered with verified TLS cert */
 #define af_pass_message        0x02000000 /* pass message in bounces */
 #define af_bad_reply           0x04000000 /* filter could not generate autoreply */
+#define af_force_command       0x08000000 /* force command */


 #ifdef EXPERIMENTAL_PRDR
 # define af_prdr_used          0x08000000 /* delivery used SMTP PRDR */
diff --git a/src/src/transport.c b/src/src/transport.c
index 160d080..a112820 100644
--- a/src/src/transport.c
+++ b/src/src/transport.c
@@ -1997,8 +1997,124 @@ if (expand_arguments)
         memmove(argv + i + 1 + additional, argv + i + 1,
           (argcount - i)*sizeof(uschar *));


-      for (ad = addr; ad != NULL; ad = ad->next) argv[i++] = ad->address;
-      i--;
+      for (ad = addr; ad != NULL; ad = ad->next) {
+          argv[i++] = ad->address;
+          argcount++;
+      }
+
+        /* Subtract one since we replace $pipe_addresses */
+        argcount--;
+        i--;
+
+      }
+
+      /* Handle special case of $address_pipe when af_force_command is set */
+
+    else if (addr != NULL && testflag(addr,af_force_command) &&
+        (Ustrcmp(argv[i], "$address_pipe") == 0 ||
+         Ustrcmp(argv[i], "${address_pipe}") == 0))
+      {
+      int address_pipe_i;
+      int address_pipe_argcount = 0;
+      int address_pipe_max_args;
+      uschar **address_pipe_argv;
+
+      /* We can never have more then the argv we will be loading into */
+      address_pipe_max_args = max_args - argcount + 1;
+
+      DEBUG(D_transport)
+        debug_printf("address_pipe_max_args=%d\n",
+          address_pipe_max_args);
+
+      /* We allocate an additional for (uschar *)0 */
+      address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *));
+
+      /* +1 because addr->local_part[0] == '|' since af_force_command is set */
+      s = expand_string(addr->local_part + 1);
+
+      if (s == NULL || *s == '\0') {
+       addr->transport_return = FAIL;
+       addr->message = string_sprintf("Expansion of \"%s\" "
+          "from command \"%s\" in %s failed: %s",
+          (addr->local_part + 1), cmd, etext, expand_string_message);
+        return FALSE;
+      }
+
+      while (isspace(*s)) s++; /* strip leading space */
+
+      while (*s != 0 && address_pipe_argcount < address_pipe_max_args)
+        {
+        if (*s == '\'')
+          {
+          ss = s + 1;
+          while (*ss != 0 && *ss != '\'') ss++;
+          address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++);
+          while (*s != 0 && *s != '\'') *ss++ = *s++;
+          if (*s != 0) s++;
+          *ss++ = 0;
+          }
+        else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s);
+        while (isspace(*s)) s++; /* strip space after arg */
+        }
+
+      address_pipe_argv[address_pipe_argcount] = (uschar *)0;
+
+      /* If *s != 0 we have run out of argument slots. */
+      if (*s != 0)
+        {
+        uschar *msg = string_sprintf("Too many arguments in $address_pipe "
+          "\"%s\" in %s", addr->local_part + 1, etext);
+        if (addr != NULL)
+          {
+          addr->transport_return = FAIL;
+          addr->message = msg;
+          }
+        else *errptr = msg;
+        return FALSE;
+        }
+
+        /* address_pipe_argcount - 1
+         * because we are replacing $address_pipe in the argument list
+         * with the first thing it expands to */
+      if (argcount + address_pipe_argcount - 1 > max_args)
+        {
+        addr->transport_return = FAIL;
+        addr->message = string_sprintf("Too many arguments to command "
+          "\"%s\" after expanding $address_pipe in %s", cmd, etext);
+        return FALSE;
+        }
+
+      /* If we are not just able to replace the slot that contained
+       * $address_pipe (address_pipe_argcount == 1)
+       * We have to move the existing argv by address_pipe_argcount
+       * Visually if address_pipe_argcount == 2:
+       * [argv 0][argv 1][argv 2($address_pipe)][argv 3][0]
+       * [argv 0][argv 1][XXXXXX][XXXXXX][old argv 3][0]
+       */
+      if (address_pipe_argcount > 1)
+        memmove(
+          /* current position + additonal args */
+          argv + i + address_pipe_argcount,
+          /* current position + 1 (for the (uschar *)0 at the end) */
+          argv + i + 1,
+          /* -1 for the (uschar *)0 at the end)*/
+          (argcount - i)*sizeof(uschar *)
+        );
+
+      /* Now we fill in the slots we just moved argv out of
+       * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
+       */
+       for (address_pipe_i = 0;
+            address_pipe_argv[address_pipe_i] != (uschar *)0;
+            address_pipe_i++) {
+         argv[i++] = address_pipe_argv[address_pipe_i];
+         argcount++;
+        }
+
+        /* Subtract one since we replace $address_pipe */
+        argcount--;
+        i--;
+
       }


     /* Handle normal expansion string */
diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c
index fe94e85..39a9d8b 100644
--- a/src/src/transports/pipe.c
+++ b/src/src/transports/pipe.c
@@ -37,6 +37,8 @@ optionlist pipe_transport_options[] = {
       (void *)offsetof(pipe_transport_options_block, environment) },
   { "escape_string",     opt_stringptr,
       (void *)offsetof(pipe_transport_options_block, escape_string) },
+  { "force_command",         opt_bool,
+      (void *)offsetof(pipe_transport_options_block, force_command) },
   { "freeze_exec_fail",  opt_bool,
       (void *)offsetof(pipe_transport_options_block, freeze_exec_fail) },
   { "freeze_signal",     opt_bool,
@@ -110,6 +112,7 @@ pipe_transport_options_block pipe_transport_option_defaults = {
   20480,          /* max_output */
   60*60,          /* timeout */
   0,              /* options */
+  FALSE,          /* force_command */
   FALSE,          /* freeze_exec_fail */
   FALSE,          /* freeze_signal */
   FALSE,          /* ignore_status */
@@ -569,10 +572,18 @@ options. */


 if (testflag(addr, af_pfr) && addr->local_part[0] == '|')
   {
-  cmd = addr->local_part + 1;
-  while (isspace(*cmd)) cmd++;
-  expand_arguments = testflag(addr, af_expand_pipe);
-  expand_fail = FAIL;
+    if (ob->force_command) {
+     /* Enables expansion of $address_pipe into seperate arguments */
+     setflag(addr,af_force_command);
+     cmd = ob->cmd;
+     expand_arguments = TRUE;
+     expand_fail = PANIC;
+    } else {
+     cmd = addr->local_part + 1;
+     while (isspace(*cmd)) cmd++;
+     expand_arguments = testflag(addr, af_expand_pipe);
+     expand_fail = FAIL;
+    }
   }
 else
   {
@@ -581,9 +592,12 @@ else
   expand_fail = PANIC;
   }


-/* If no command has been supplied, we are in trouble. */
+/* If no command has been supplied, we are in trouble.
+ * We also check for an empty string since it may be
+ * coming from addr->local_part[0] == '|'
+ */

-if (cmd == NULL)
+if (cmd == NULL || *cmd == '\0')
{
addr->transport_return = DEFER;
addr->message = string_sprintf("no command specified for %s transport",
diff --git a/src/src/transports/pipe.h b/src/src/transports/pipe.h
index 343628e..e101174 100644
--- a/src/src/transports/pipe.h
+++ b/src/src/transports/pipe.h
@@ -21,6 +21,7 @@ typedef struct {
int max_output;
int timeout;
int options;
+ BOOL force_command;
BOOL freeze_exec_fail;
BOOL freeze_signal;
BOOL ignore_status;