summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJ. Nick Koston <nick@cpanel.net>2013-03-30 02:22:53 -0500
committerJ. Nick Koston <nick@cpanel.net>2013-04-01 01:38:18 -0500
commit09792322d9224b0407783a19c2dd57fd1a8bbd52 (patch)
tree4c2b15e1d987f5a807bd79a335fad5f14644a035 /src
parent0fbd9bff71b47e3a32e54629c3f67e7eda1812fe (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/src/structs.h1
-rw-r--r--src/src/transport.c120
-rw-r--r--src/src/transports/pipe.c26
-rw-r--r--src/src/transports/pipe.h1
4 files changed, 140 insertions, 8 deletions
diff --git a/src/src/structs.h b/src/src/structs.h
index d11e91adb..1105bb718 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 160d080b9..a112820fd 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 fe94e8575..39a9d8b47 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 343628e20..e10117458 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;