* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* 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
software for alternative value types. Some options are stored in the transport
instance block so as to be publicly visible; these are flagged with opt_public.
*/
+#define LOFF(field) OPT_OFF(pipe_transport_options_block, field)
optionlist pipe_transport_options[] = {
- { "allow_commands", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, allow_commands) },
+ { "allow_commands", opt_stringptr, LOFF(allow_commands) },
{ "batch_id", opt_stringptr | opt_public,
- (void *)offsetof(transport_instance, batch_id) },
+ OPT_OFF(transport_instance, batch_id) },
{ "batch_max", opt_int | opt_public,
- (void *)offsetof(transport_instance, batch_max) },
- { "check_string", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, check_string) },
- { "command", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, cmd) },
- { "environment", opt_stringptr,
- (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,
- (void *)offsetof(pipe_transport_options_block, freeze_signal) },
- { "ignore_status", opt_bool,
- (void *)offsetof(pipe_transport_options_block, ignore_status) },
+ OPT_OFF(transport_instance, batch_max) },
+ { "check_string", opt_stringptr, LOFF(check_string) },
+ { "command", opt_stringptr, LOFF(cmd) },
+ { "environment", opt_stringptr, LOFF(environment) },
+ { "escape_string", opt_stringptr, LOFF(escape_string) },
+ { "force_command", opt_bool, LOFF(force_command) },
+ { "freeze_exec_fail", opt_bool, LOFF(freeze_exec_fail) },
+ { "freeze_signal", opt_bool, LOFF(freeze_signal) },
+ { "ignore_status", opt_bool, LOFF(ignore_status) },
{ "log_defer_output", opt_bool | opt_public,
- (void *)offsetof(transport_instance, log_defer_output) },
+ OPT_OFF(transport_instance, log_defer_output) },
{ "log_fail_output", opt_bool | opt_public,
- (void *)offsetof(transport_instance, log_fail_output) },
+ OPT_OFF(transport_instance, log_fail_output) },
{ "log_output", opt_bool | opt_public,
- (void *)offsetof(transport_instance, log_output) },
- { "max_output", opt_mkint,
- (void *)offsetof(pipe_transport_options_block, max_output) },
- { "message_prefix", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, message_prefix) },
- { "message_suffix", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, message_suffix) },
- { "path", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, path) },
- { "permit_coredump", opt_bool,
- (void *)offsetof(pipe_transport_options_block, permit_coredump) },
+ OPT_OFF(transport_instance, log_output) },
+ { "max_output", opt_mkint, LOFF(max_output) },
+ { "message_prefix", opt_stringptr, LOFF(message_prefix) },
+ { "message_suffix", opt_stringptr, LOFF(message_suffix) },
+ { "path", opt_stringptr, LOFF(path) },
+ { "permit_coredump", opt_bool, LOFF(permit_coredump) },
{ "pipe_as_creator", opt_bool | opt_public,
- (void *)offsetof(transport_instance, deliver_as_creator) },
- { "restrict_to_path", opt_bool,
- (void *)offsetof(pipe_transport_options_block, restrict_to_path) },
+ OPT_OFF(transport_instance, deliver_as_creator) },
+ { "restrict_to_path", opt_bool, LOFF(restrict_to_path) },
{ "return_fail_output",opt_bool | opt_public,
- (void *)offsetof(transport_instance, return_fail_output) },
+ OPT_OFF(transport_instance, return_fail_output) },
{ "return_output", opt_bool | opt_public,
- (void *)offsetof(transport_instance, return_output) },
- { "temp_errors", opt_stringptr,
- (void *)offsetof(pipe_transport_options_block, temp_errors) },
- { "timeout", opt_time,
- (void *)offsetof(pipe_transport_options_block, timeout) },
- { "timeout_defer", opt_bool,
- (void *)offsetof(pipe_transport_options_block, timeout_defer) },
- { "umask", opt_octint,
- (void *)offsetof(pipe_transport_options_block, umask) },
- { "use_bsmtp", opt_bool,
- (void *)offsetof(pipe_transport_options_block, use_bsmtp) },
+ OPT_OFF(transport_instance, return_output) },
+ { "temp_errors", opt_stringptr, LOFF(temp_errors) },
+ { "timeout", opt_time, LOFF(timeout) },
+ { "timeout_defer", opt_bool, LOFF(timeout_defer) },
+ { "umask", opt_octint, LOFF(umask) },
+ { "use_bsmtp", opt_bool, LOFF(use_bsmtp) },
#ifdef HAVE_SETCLASSRESOURCES
- { "use_classresources", opt_bool,
- (void *)offsetof(pipe_transport_options_block, use_classresources) },
+ { "use_classresources", opt_bool, LOFF(use_classresources) },
#endif
- { "use_crlf", opt_bool,
- (void *)offsetof(pipe_transport_options_block, use_crlf) },
- { "use_shell", opt_bool,
- (void *)offsetof(pipe_transport_options_block, use_shell) },
+ { "use_crlf", opt_bool, LOFF(use_crlf) },
+ { "use_shell", opt_bool, LOFF(use_shell) },
};
/* Size of the options list. An extern variable has to be used so that its
int pipe_transport_options_count =
sizeof(pipe_transport_options)/sizeof(optionlist);
+
+#ifdef MACRO_PREDEF
+
+/* Dummy values */
+pipe_transport_options_block pipe_transport_option_defaults = {0};
+void pipe_transport_init(driver_instance *tblock) {}
+BOOL pipe_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
+
+#else /*!MACRO_PREDEF*/
+
+
/* Default private options block for the pipe transport. */
pipe_transport_options_block pipe_transport_option_defaults = {
- NULL, /* cmd */
- NULL, /* allow_commands */
- NULL, /* environment */
- US"/bin:/usr/bin", /* path */
- NULL, /* message_prefix (reset in init if not bsmtp) */
- NULL, /* message_suffix (ditto) */
- US mac_expanded_string(EX_TEMPFAIL) ":" /* temp_errors */
- mac_expanded_string(EX_CANTCREAT),
- NULL, /* check_string */
- NULL, /* escape_string */
- 022, /* umask */
- 20480, /* max_output */
- 60*60, /* timeout */
- 0, /* options */
- FALSE, /* force_command */
- FALSE, /* freeze_exec_fail */
- FALSE, /* freeze_signal */
- FALSE, /* ignore_status */
- FALSE, /* permit_coredump */
- FALSE, /* restrict_to_path */
- FALSE, /* timeout_defer */
- FALSE, /* use_shell */
- FALSE, /* use_bsmtp */
- FALSE, /* use_classresources */
- FALSE /* use_crlf */
+ .path = US"/bin:/usr/bin",
+ .temp_errors = US mac_expanded_string(EX_TEMPFAIL) ":"
+ mac_expanded_string(EX_CANTCREAT),
+ .umask = 022,
+ .max_output = 20480,
+ .timeout = 60*60,
+ /* all others null/zero/false */
};
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);
-
-addrlist = addrlist; /* Keep compiler happy */
-dummy = dummy;
-uid = uid;
-gid = gid;
-errmsg = errmsg;
-ob = ob;
+pipe_transport_options_block * ob = tblock->drinst.options_block;
#ifdef HAVE_SETCLASSRESOURCES
if (ob->use_classresources)
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 */
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. */
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
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. */
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. */
ob->options |=
- (tblock->body_only? topt_no_headers : 0) |
- (tblock->headers_only? topt_no_body : 0) |
- (tblock->return_path_add? topt_add_return_path : 0) |
- (tblock->delivery_date_add? topt_add_delivery_date : 0) |
- (tblock->envelope_to_add? topt_add_envelope_to : 0) |
- (ob->use_crlf? topt_use_crlf : 0);
+ (tblock->body_only ? topt_no_headers : 0)
+ | (tblock->headers_only ? topt_no_body : 0)
+ | (tblock->return_path_add ? topt_add_return_path : 0)
+ | (tblock->delivery_date_add ? topt_add_delivery_date : 0)
+ | (tblock->envelope_to_add ? topt_add_envelope_to : 0)
+ | (ob->use_crlf ? topt_use_crlf : 0);
}
*/
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;
the items if necessary. If it fails, this function fails (error information
is in the addresses). */
-if (!transport_set_up_command(argvptr, cmd, expand_arguments, expand_fail,
- addr, string_sprintf("%.50s transport", tname), NULL))
+if (!transport_set_up_command(argvptr, cmd,
+ expand_arguments ? TSUC_EXPAND_ARGS : 0,
+ expand_fail, addr, string_sprintf("%.50s transport", tname), NULL))
return FALSE;
/* Point to the set-up arguments. */
/* 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;
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]);
*/
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;
-*argvptr = argv = store_get((4)*sizeof(uschar *));
+*argvptr = argv = store_get((4)*sizeof(uschar *), GET_UNTAINTED);
argv[0] = US"/bin/sh";
argv[1] = US"-c";
/* We have to take special action to handle the special "variable" called
$pipe_addresses, which is not recognized by the normal expansion function. */
-DEBUG(D_transport)
- debug_printf("shell pipe command before expansion:\n %s\n", cmd);
-
if (expand_arguments)
{
- uschar *s = cmd;
- uschar *p = Ustrstr(cmd, "pipe_addresses");
+ uschar * p = Ustrstr(cmd, "pipe_addresses");
+ gstring * g = NULL;
+
+ DEBUG(D_transport)
+ debug_printf("shell pipe command before expansion:\n %s\n", cmd);
+
+ /* Allow $recipients in the expansion iff it comes from a system filter */
+
+ f.enable_dollar_recipients = addr && addr->parent &&
+ Ustrcmp(addr->parent->address, "system-filter") == 0;
if (p != NULL && (
(p > cmd && p[-1] == '$') ||
(p > cmd + 1 && p[-2] == '$' && p[-1] == '{' && p[14] == '}')))
{
- address_item *ad;
uschar *q = p + 14;
- int size = Ustrlen(cmd) + 64;
- int offset;
if (p[-1] == '{') { q++; p--; }
- s = store_get(size);
- offset = p - cmd - 1;
- Ustrncpy(s, cmd, offset);
+ g = string_get(Ustrlen(cmd) + 64);
+ g = string_catn(g, cmd, p - cmd - 1);
- for (ad = addr; ad != NULL; ad = ad->next)
+ for (address_item * ad = addr; ad; ad = ad->next)
{
+ DEBUG(D_transport) if (is_tainted(ad->address))
+ debug_printf("tainted element '%s' from $pipe_addresses\n", ad->address);
+
/*XXX string_append_listele() ? */
- if (ad != addr) s = string_catn(s, &size, &offset, US" ", 1);
- s = string_cat(s, &size, &offset, ad->address);
+ if (ad != addr) g = string_catn(g, US" ", 1);
+ g = string_cat(g, ad->address);
}
- s = string_cat(s, &size, &offset, q);
- s[offset] = 0;
+ g = string_cat(g, q);
+ argv[2] = (cmd = string_from_gstring(g)) ? expand_cstring(cmd) : NULL;
}
+ else
+ argv[2] = expand_cstring(cmd);
- /* Allow $recipients in the expansion iff it comes from a system filter */
-
- enable_dollar_recipients = addr != NULL &&
- addr->parent != NULL &&
- Ustrcmp(addr->parent->address, "system-filter") == 0;
- argv[2] = expand_string(s);
- enable_dollar_recipients = FALSE;
+ f.enable_dollar_recipients = FALSE;
- if (argv[2] == NULL)
+ if (!argv[2])
{
- addr->transport_return = search_find_defer? DEFER : expand_fail;
+ addr->transport_return = f.search_find_defer ? DEFER : expand_fail;
addr->message = string_sprintf("Expansion of command \"%s\" "
"in %s transport failed: %s",
cmd, tname, expand_string_message);
DEBUG(D_transport)
debug_printf("shell pipe command after expansion:\n %s\n", argv[2]);
}
-else argv[2] = cmd;
+else
+ {
+ DEBUG(D_transport)
+ debug_printf("shell pipe command (no expansion):\n %s\n", cmd);
+ argv[2] = cmd;
+ }
-argv[3] = (uschar *)0;
+argv[3] = US 0;
return TRUE;
}
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,
- addr,
- ob->check_string,
- ob->escape_string,
- ob->options /* set at initialization time */
+ .tblock = tblock,
+ .addr = addr,
+ .check_string = ob->check_string,
+ .escape_string = ob->escape_string,
+ 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 */
options. */
if (testflag(addr, af_pfr) && addr->local_part[0] == '|')
- {
if (ob->force_command)
{
- /* Enables expansion of $address_pipe into seperate arguments */
+ /* 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;
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;
}
/* 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] == '|'
- */
+We also check for an empty string since it may be
+coming from addr->local_part[0] == '|' */
-if (cmd == NULL || *cmd == '\0')
+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, trname);
+ addr->transport_return = PANIC;
return FALSE;
}
{
uschar **ss = addr->pipe_expandn;
expand_nmax = -1;
- if (*ss != NULL) filter_thisaddress = *ss++;
- while (*ss != NULL)
+ if (*ss) filter_thisaddress = *ss++;
+ while (*ss)
{
expand_nstring[++expand_nmax] = *ss;
expand_nlength[expand_nmax] = Ustrlen(*ss++);
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;
envp[envcount++] = string_sprintf("SENDER=%s", sender_address);
envp[envcount++] = US"SHELL=/bin/sh";
-if (addr->host_list != NULL)
+if (addr->host_list)
envp[envcount++] = string_sprintf("HOST=%s", addr->host_list->name);
-if (timestamps_utc) envp[envcount++] = US"TZ=UTC";
-else if (timezone_string != NULL && timezone_string[0] != 0)
+if (f.timestamps_utc)
+ envp[envcount++] = US"TZ=UTC";
+else if (timezone_string && timezone_string[0])
envp[envcount++] = string_sprintf("TZ=%s", timezone_string);
/* Add any requested items */
+GET_OPTION("environment");
if (envlist)
- {
- envlist = expand_cstring(envlist);
- if (envlist == NULL)
+ 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;
}
- }
-while ((ss = string_nextinlist(&envlist, &envsep, big_buffer, big_buffer_size))
- != NULL)
+while ((ss = string_nextinlist(&envlist, &envsep, NULL, 0)))
{
- if (envcount > sizeof(envp)/sizeof(uschar *) - 2)
+ if (envcount > nelem(envp) - 2)
{
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);
/* If the -N option is set, can't do any more. */
-if (dont_deliver)
+if (f.dont_deliver)
{
DEBUG(D_transport)
debug_printf("*** delivery by %s transport bypassed by -N option",
- tblock->name);
+ trname);
return FALSE;
}
uid/gid and current directory. Request that the new process be a process group
leader, so we can kill it and all its children on a timeout. */
-if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0)
+if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE,
+ US"pipe-tpt-cmd")) < 0)
{
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;
}
+tctx.u.fd = fd_in;
/* Now fork a process to handle the output that comes down the pipe. */
-if ((outpid = fork()) < 0)
+if ((outpid = exim_fork(US"pipe-tpt-output")) < 0)
{
addr->basic_errno = errno;
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;
ignore all writing errors. (When in the test harness, we do do a short sleep so
any debugging output is likely to be in the same order.) */
-if (running_in_test_harness) millisleep(500);
+testharness_pause_ms(500);
DEBUG(D_transport) debug_printf("Writing message to pipe\n");
/* First write any configured prefix information */
-if (ob->message_prefix != NULL)
+GET_OPTION("message_prefix");
+if (ob->message_prefix)
{
- uschar *prefix = expand_string(ob->message_prefix);
- if (prefix == NULL)
+ uschar * prefix = expand_string(ob->message_prefix);
+ if (!prefix)
{
- addr->transport_return = search_find_defer? DEFER : PANIC;
+ 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;
}
- if (!transport_write_block(fd_in, prefix, Ustrlen(prefix)))
+ if (!transport_write_block(&tctx, prefix, Ustrlen(prefix), FALSE))
goto END_WRITE;
}
if (ob->use_bsmtp)
{
- address_item *a;
-
if (!transport_write_string(fd_in, "MAIL FROM:<%s>%s", return_path, eol))
goto END_WRITE;
- for (a = addr; a; a = a->next)
+ for (address_item * a = addr; a; a = a->next)
if (!transport_write_string(fd_in,
"RCPT TO:<%s>%s",
transport_rcpt_address(a, tblock->rcpt_include_affixes),
/* Now the actual message */
-if (!transport_write_message(fd_in, &tctx, 0))
+if (!transport_write_message(&tctx, 0))
goto END_WRITE;
/* 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 = search_find_defer? DEFER : PANIC;
+ 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;
}
- if (!transport_write_block(fd_in, suffix, Ustrlen(suffix)))
+ if (!transport_write_block(&tctx, suffix, Ustrlen(suffix), FALSE))
goto END_WRITE;
}
if (errno == ETIMEDOUT)
{
addr->message = string_sprintf("%stimeout while writing to pipe",
- transport_filter_timed_out? "transport filter " : "");
+ f.transport_filter_timed_out ? "transport filter " : "");
addr->transport_return = ob->timeout_defer? DEFER : FAIL;
timeout = 1;
}
addr->more_errno,
(addr->more_errno == EX_EXECFAILED)? ": unable to execute command" : "");
else if (errno == ERRNO_WRITEINCOMPLETE)
- addr->message = string_sprintf("Failed repeatedly to write data");
+ addr->message = US"Failed repeatedly to write data";
else
addr->message = string_sprintf("Error %d", errno);
return FALSE;
if ((rc = child_close(pid, timeout)) != 0)
{
- uschar *tmsg = (addr->message == NULL)? US"" :
- string_sprintf(" (preceded by %s)", addr->message);
+ uschar * tmsg = addr->message
+ ? string_sprintf(" (preceded by %s)", addr->message) : US"";
/* The process did not complete in time; kill its process group and fail
the delivery. It appears to be necessary to kill the output process too, as
{
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
This prevents the transport_filter timeout message from getting overwritten
by the exit error which is not the cause of the problem. */
- else if (transport_filter_timed_out)
+ else if (f.transport_filter_timed_out)
{
killpg(pid, SIGKILL);
kill(outpid, SIGKILL);
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);
}
}
the command that was given is a non-existent path). By default this is
treated as just another failure, but if freeze_exec_fail is set, the reaction
is to freeze the message rather than bounce the address. Exim used to signal
- this failure with EX_UNAVAILABLE, which is definined in many systems as
+ this failure with EX_UNAVAILABLE, which is defined in many systems as
#define EX_UNAVAILABLE 69
{
/* Always handle execve() failure specially if requested to */
- if (ob->freeze_exec_fail && (rc == EX_EXECFAILED))
+ if (ob->freeze_exec_fail && rc == EX_EXECFAILED)
{
addr->transport_return = DEFER;
addr->special_action = SPECIAL_FREEZE;
else if (!ob->ignore_status)
{
uschar *ss;
- int size, ptr, i;
+ gstring * g;
- /* If temp_errors is "*" all codes are temporary. Initializion checks
+ /* If temp_errors is "*" all codes are temporary. Initialization checks
that it's either "*" or a list of numbers. If not "*", scan the list of
temporary failure codes; if any match, the result is DEFER. */
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);
-
- ptr = Ustrlen(addr->message);
- size = ptr + 1;
+ "%d", trname, rc);
+ g = string_cat(NULL, addr->message);
/* If the return code is > 128, it often means that a shell command
was terminated by a signal. */
rc-128, os_strsignal(rc-128)) :
US os_strexit(rc);
- if (*ss != 0)
+ if (*ss)
{
- addr->message = string_catn(addr->message, &size, &ptr, US" ", 1);
- addr->message = string_cat (addr->message, &size, &ptr, ss);
+ g = string_catn(g, US" ", 1);
+ g = string_cat (g, ss);
}
/* Now add the command and arguments */
- addr->message = string_catn(addr->message, &size, &ptr,
- US" from command:", 14);
+ g = string_catn(g, US" from command:", 14);
- for (i = 0; i < sizeof(argv)/sizeof(int *) && argv[i] != NULL; i++)
+ for (int i = 0; i < sizeof(argv)/sizeof(int *) && argv[i] != NULL; i++)
{
BOOL quote = FALSE;
- addr->message = string_catn(addr->message, &size, &ptr, US" ", 1);
+ g = string_catn(g, US" ", 1);
if (Ustrpbrk(argv[i], " \t") != NULL)
{
quote = TRUE;
- addr->message = string_catn(addr->message, &size, &ptr, US"\"", 1);
+ g = string_catn(g, US"\"", 1);
}
- addr->message = string_cat(addr->message, &size, &ptr, argv[i]);
+ g = string_cat(g, argv[i]);
if (quote)
- addr->message = string_catn(addr->message, &size, &ptr, US"\"", 1);
+ g = string_catn(g, US"\"", 1);
}
/* Add previous filter timeout message, if present. */
if (*tmsg)
- addr->message = string_cat(addr->message, &size, &ptr, tmsg);
+ g = string_cat(g, tmsg);
- addr->message[ptr] = 0; /* Ensure concatenated string terminated */
+ addr->message = string_from_gstring(g);
}
}
}
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
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 */