diff options
author | J. Nick Koston <nick@cpanel.net> | 2013-03-30 02:22:53 -0500 |
---|---|---|
committer | J. Nick Koston <nick@cpanel.net> | 2013-04-01 01:38:18 -0500 |
commit | 09792322d9224b0407783a19c2dd57fd1a8bbd52 (patch) | |
tree | 4c2b15e1d987f5a807bd79a335fad5f14644a035 /src | |
parent | 0fbd9bff71b47e3a32e54629c3f67e7eda1812fe (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.h | 1 | ||||
-rw-r--r-- | src/src/transport.c | 120 | ||||
-rw-r--r-- | src/src/transports/pipe.c | 26 | ||||
-rw-r--r-- | src/src/transports/pipe.h | 1 |
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; |