/* See the file NOTICE for conditions of use and distribution. */
/* General functions concerned with transportation, and generic options for all
/* See the file NOTICE for conditions of use and distribution. */
/* General functions concerned with transportation, and generic options for all
{ "*expand_group", opt_stringptr|opt_hidden|opt_public,
(void *)offsetof(transport_instance, expand_gid) },
{ "*expand_user", opt_stringptr|opt_hidden|opt_public,
{ "*expand_group", opt_stringptr|opt_hidden|opt_public,
(void *)offsetof(transport_instance, expand_gid) },
{ "*expand_user", opt_stringptr|opt_hidden|opt_public,
uschar buf[64];
options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
uschar buf[64];
options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
- spf(buf, sizeof(buf), "_DRIVER_TRANSPORT_%T", ti->driver_name);
+ spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", ti->driver_name);
builtin_macro_create(buf);
options_from_list(ti->options, (unsigned)*ti->options_count, US"TRANSPORT", ti->driver_name);
}
builtin_macro_create(buf);
options_from_list(ti->options, (unsigned)*ti->options_count, US"TRANSPORT", ti->driver_name);
}
readconf_driver_init(US"transport",
(driver_instance **)(&transports), /* chain anchor */
(driver_info *)transports_available, /* available drivers */
readconf_driver_init(US"transport",
(driver_instance **)(&transports), /* chain anchor */
(driver_info *)transports_available, /* available drivers */
/* Now scan the configured transports and check inconsistencies. A shadow
transport is permitted only for local transports. */
/* Now scan the configured transports and check inconsistencies. A shadow
transport is permitted only for local transports. */
{
if (!t->info->local && t->shadow)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
{
if (!t->info->local && t->shadow)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
static BOOL
transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
{
static BOOL
transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
{
int local_timeout = transport_write_timeout;
int fd = tctx->u.fd;
/* This loop is for handling incomplete writes and other retries. In most
normal cases, it is only ever executed once. */
int local_timeout = transport_write_timeout;
int fd = tctx->u.fd;
/* This loop is for handling incomplete writes and other retries. In most
normal cases, it is only ever executed once. */
-#ifdef SUPPORT_TLS
- tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+#ifndef DISABLE_TLS
+ tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
- more ? send(fd, block, len, MSG_MORE) :
+ more && !(tctx->options & topt_not_socket)
+ ? send(fd, block, len, MSG_MORE) :
-#ifdef SUPPORT_TLS
- tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+#ifndef DISABLE_TLS
+ tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
- more ? send(fd, block, len, MSG_MORE) :
+ more && !(tctx->options & topt_not_socket)
+ ? send(fd, block, len, MSG_MORE) :
-tctx->u.msg = string_catn(tctx->u.msg, &tctx->msg_size, &tctx->msg_ptr, block, len);
+tctx->u.msg = string_catn(tctx->u.msg, block, len);
-if (!string_vformat(big_buffer, big_buffer_size, format, ap))
+if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap))
-return transport_write_block(&tctx, big_buffer, Ustrlen(big_buffer), FALSE);
+return transport_write_block(&tctx, gs.s, gs.ptr, FALSE);
int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
/* The assumption is made that the check string will never stretch over move
int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
/* The assumption is made that the check string will never stretch over move
transport_headers_send(transport_ctx * tctx,
BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
{
transport_headers_send(transport_ctx * tctx,
BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
{
const uschar *list;
transport_instance * tblock = tctx ? tctx->tblock : NULL;
address_item * addr = tctx ? tctx->addr : NULL;
const uschar *list;
transport_instance * tblock = tctx ? tctx->tblock : NULL;
address_item * addr = tctx ? tctx->addr : NULL;
separately and squash any empty ones.
Then check addr->prop.remove_headers too, provided that addr is not NULL. */
separately and squash any empty ones.
Then check addr->prop.remove_headers too, provided that addr is not NULL. */
header_line *hh;
if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
header_line *hh;
if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
anchors for lists of addresses already handled; they have to be defined at
this level because write_env_to() calls itself recursively. */
anchors for lists of addresses already handled; they have to be defined at
this level because write_env_to() calls itself recursively. */
- for (p = tctx->addr; p; p = p->next)
- if (!write_env_to(p, &plist, &dlist, &first, tctx)) goto bad;
+ for (address_item * p = tctx->addr; p; p = p->next)
+ if (!write_env_to(p, &plist, &dlist, &first, tctx))
+ goto bad;
size += body_linecount; /* account for CRLF-expansion */
/* With topt_use_bdat we never do dot-stuffing; no need to
size += body_linecount; /* account for CRLF-expansion */
/* With topt_use_bdat we never do dot-stuffing; no need to
DEBUG(D_transport)
if (!(tctx->options & topt_no_body))
debug_printf("cannot use sendfile for body: %s\n",
DEBUG(D_transport)
if (!(tctx->options & topt_no_body))
debug_printf("cannot use sendfile for body: %s\n",
: tctx->options & topt_end_dot ? "terminating dot wanted"
: nl_check_length ? "dot- or From-stuffing wanted"
: "TLS output wanted");
if (!(tctx->options & topt_no_body))
{
: tctx->options & topt_end_dot ? "terminating dot wanted"
: nl_check_length ? "dot- or From-stuffing wanted"
: "TLS output wanted");
if (!(tctx->options & topt_no_body))
{
nl_check_length = abs(nl_check_length);
nl_partial_match = 0;
if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0)
return FALSE;
nl_check_length = abs(nl_check_length);
nl_partial_match = 0;
if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0)
return FALSE;
&& (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
{
if (!write_chunk(tctx, deliver_in_buffer, len))
&& (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
{
if (!write_chunk(tctx, deliver_in_buffer, len))
transport_write_message(transport_ctx * tctx, int size_limit)
{
BOOL last_filter_was_NL = TRUE;
transport_write_message(transport_ctx * tctx, int size_limit)
{
BOOL last_filter_was_NL = TRUE;
int rc, len, yield, fd_read, fd_write, save_errno;
int pfd[2] = {-1, -1};
pid_t filter_pid, write_pid;
int rc, len, yield, fd_read, fd_write, save_errno;
int pfd[2] = {-1, -1};
pid_t filter_pid, write_pid;
/* If there is no filter command set up, call the internal function that does
the actual work, passing it the incoming fd, and return its result. */
/* If there is no filter command set up, call the internal function that does
the actual work, passing it the incoming fd, and return its result. */
variable is TRUE). The output should always be unix-format as we converted
any wireformat source on writing input to the filter. */
variable is TRUE). The output should always be unix-format as we converted
any wireformat source on writing input to the filter. */
int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int));
int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int));
if ( tctx->options & topt_end_dot
&& ( last_filter_was_NL
? !write_chunk(tctx, US".\n", 2)
if ( tctx->options & topt_end_dot
&& ( last_filter_was_NL
? !write_chunk(tctx, US".\n", 2)
transport_update_waiting(host_item *hostlist, uschar *tpname)
{
const uschar *prevname = US"";
transport_update_waiting(host_item *hostlist, uschar *tpname)
{
const uschar *prevname = US"";
/* Open the database for this transport */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
/* Open the database for this transport */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
return;
/* Scan the list of hosts for which this message is waiting, and ensure
that the message id is in each host record. */
return;
/* Scan the list of hosts for which this message is waiting, and ensure
that the message id is in each host record. */
s += MESSAGE_ID_LENGTH)
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
s += MESSAGE_ID_LENGTH)
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
/* If we haven't found this message in the main record, search any
continuation records that exist. */
/* If we haven't found this message in the main record, search any
continuation records that exist. */
{
dbdata_wait *cont;
sprintf(CS buffer, "%.200s:%d", host->name, i);
if ((cont = dbfn_read(dbm_file, buffer)))
{
int clen = cont->count * MESSAGE_ID_LENGTH;
{
dbdata_wait *cont;
sprintf(CS buffer, "%.200s:%d", host->name, i);
if ((cont = dbfn_read(dbm_file, buffer)))
{
int clen = cont->count * MESSAGE_ID_LENGTH;
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
}
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
}
/* Open the waiting information database. */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
/* Open the waiting information database. */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
- subdir[0] = split_spool_directory ? msgq[i].message_id[5] : 0;
- subdir[1] = 0;
-
- if (Ustat(spool_fname(US"input", subdir, msgq[i].message_id, US"-D"),
- &statbuf) != 0)
+ set_subdir_str(subdir, mid, 0);
+ if (Ustat(spool_fname(US"input", subdir, mid, US"-D"), &statbuf) != 0)
{
sprintf(CS buffer, "%.200s:%d", hostname, i);
newr = dbfn_read(dbm_file, buffer);
{
sprintf(CS buffer, "%.200s:%d", hostname, i);
newr = dbfn_read(dbm_file, buffer);
/* If host_length <= 0 we have emptied a record and not found a good message,
and there are no continuation records. Otherwise there is a continuation
/* If host_length <= 0 we have emptied a record and not found a good message,
and there are no continuation records. Otherwise there is a continuation
} /* we need to process a continuation record */
/* Control gets here when an existing message has been encountered; its
} /* we need to process a continuation record */
/* Control gets here when an existing message has been encountered; its
argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
if (smtp_peer_options & OPTION_CHUNKING) argv[i++] = US"-MCK";
if (smtp_peer_options & OPTION_DSN) argv[i++] = US"-MCD";
if (smtp_peer_options & OPTION_PIPE) argv[i++] = US"-MCP";
if (smtp_peer_options & OPTION_SIZE) argv[i++] = US"-MCS";
if (smtp_peer_options & OPTION_CHUNKING) argv[i++] = US"-MCK";
if (smtp_peer_options & OPTION_DSN) argv[i++] = US"-MCD";
if (smtp_peer_options & OPTION_PIPE) argv[i++] = US"-MCP";
if (smtp_peer_options & OPTION_SIZE) argv[i++] = US"-MCS";
{
argv[i++] = US"-MCt";
argv[i++] = sending_ip_address;
argv[i++] = string_sprintf("%d", sending_port);
{
argv[i++] = US"-MCt";
argv[i++] = sending_ip_address;
argv[i++] = string_sprintf("%d", sending_port);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
BOOL expand_arguments, int expand_failed, address_item *addr,
uschar *etext, uschar **errptr)
{
BOOL expand_arguments, int expand_failed, address_item *addr,
uschar *etext, uschar **errptr)
{
/* Get store in which to build an argument list. Count the number of addresses
supplied, and allow for that many arguments, plus an additional 60, which
should be enough for anybody. Multiple addresses happen only when the local
delivery batch option is set. */
/* Get store in which to build an argument list. Count the number of addresses
supplied, and allow for that many arguments, plus an additional 60, which
should be enough for anybody. Multiple addresses happen only when the local
delivery batch option is set. */
/* Split the command up into arguments terminated by white space. Lose
trailing space at the start and end. Double-quoted arguments can contain \\ and
/* Split the command up into arguments terminated by white space. Lose
trailing space at the start and end. Double-quoted arguments can contain \\ and
- for (i = 0; argv[i] != (uschar *)0; i++)
- debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i]));
+ for (int i = 0; argv[i]; i++)
+ debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i]));
memmove(argv + i + 1 + additional, argv + i + 1,
(argcount - i)*sizeof(uschar *));
memmove(argv + i + 1 + additional, argv + i + 1,
(argcount - i)*sizeof(uschar *));
(Ustrcmp(argv[i], "$address_pipe") == 0 ||
Ustrcmp(argv[i], "${address_pipe}") == 0))
{
(Ustrcmp(argv[i], "$address_pipe") == 0 ||
Ustrcmp(argv[i], "${address_pipe}") == 0))
{
int address_pipe_argcount = 0;
int address_pipe_max_args;
uschar **address_pipe_argv;
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;
/* We can never have more then the argv we will be loading into */
address_pipe_max_args = max_args - argcount + 1;
debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
/* We allocate an additional for (uschar *)0 */
debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
/* We allocate an additional for (uschar *)0 */
/* +1 because addr->local_part[0] == '|' since af_force_command is set */
s = expand_string(addr->local_part + 1);
/* +1 because addr->local_part[0] == '|' since af_force_command is set */
s = expand_string(addr->local_part + 1);
/* 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]
*/
/* 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]
*/
{
uschar *msg = string_sprintf("Expansion of \"%s\" "
"from command \"%s\" in %s failed: %s",
argv[i], cmd, etext, expand_string_message);
{
uschar *msg = string_sprintf("Expansion of \"%s\" "
"from command \"%s\" in %s failed: %s",
argv[i], cmd, etext, expand_string_message);