(void *)offsetof(transport_instance, uid) }
};
-int optionlist_transports_size =
- sizeof(optionlist_transports)/sizeof(optionlist);
+int optionlist_transports_size = nelem(optionlist_transports);
+void
+readconf_options_transports(void)
+{
+struct transport_info * ti;
+
+readconf_options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
+
+for (ti = transports_available; ti->driver_name[0]; ti++)
+ {
+ macro_create(string_sprintf("_DRIVER_TRANSPORT_%T", ti->driver_name), US"y", FALSE, TRUE);
+ readconf_options_from_list(ti->options, (unsigned)*ti->options_count, US"TRANSPORT", ti->driver_name);
+ }
+}
+
/*************************************************
* Initialize transport list *
*************************************************/
/* Now scan the configured transports and check inconsistencies. A shadow
transport is permitted only for local transports. */
-for (t = transports; t != NULL; t = t->next)
+for (t = transports; t; t = t->next)
{
- if (!t->info->local)
- {
- if (t->shadow != NULL)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "shadow transport not allowed on non-local transport %s", t->name);
- }
+ if (!t->info->local && t->shadow)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+ "shadow transport not allowed on non-local transport %s", t->name);
if (t->body_only && t->headers_only)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
fd file descript to write to
chunk pointer to data to write
len length of data to write
- flags bitmap of topt_ flags for processing options
- use_crlf terminate lines with CRLF
- use_bdat prepend chunks with RFC3030 BDAT header
+ tctx transport context - processing to be done during output
In addition, the static nl_xxx variables must be set as required.
*/
static BOOL
-write_chunk(int fd, uschar *chunk, int len, unsigned flags)
+write_chunk(int fd, transport_ctx * tctx, uschar *chunk, int len)
{
uschar *start = chunk;
uschar *end = chunk + len;
for (ptr = start; ptr < end; ptr++)
{
- int ch;
+ int ch, len;
/* Flush the buffer if it has reached the threshold - we want to leave enough
room for the next uschar, plus a possible extra CR for an LF, plus the escape
string. */
-/*XXX CHUNKING: need to prefix write_block with a BDAT cmd. Also possibly
-reap a response from a previous BDAT first. NEED a callback into the tpt
-for that */
- if (chunk_ptr - deliver_out_buffer > mlen)
+ if ((len = chunk_ptr - deliver_out_buffer) > mlen)
{
- if (!transport_write_block(fd, deliver_out_buffer,
- chunk_ptr - deliver_out_buffer))
- return FALSE;
+ DEBUG(D_transport) debug_printf("flushing headers buffer\n");
+
+ /* If CHUNKING, prefix with BDAT (size) NON-LAST. Also, reap responses
+ from previous SMTP commands. */
+
+ if (tctx && tctx->options & topt_use_bdat && tctx->chunk_cb)
+ {
+ if ( tctx->chunk_cb(tctx, (unsigned)len, 0) != OK
+ || !transport_write_block(fd, deliver_out_buffer, len)
+ || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
+ )
+ return FALSE;
+ }
+ else
+ if (!transport_write_block(fd, deliver_out_buffer, len))
+ return FALSE;
chunk_ptr = deliver_out_buffer;
}
/* Insert CR before NL if required */
- if (flags & topt_use_crlf) *chunk_ptr++ = '\r';
+ if (tctx && tctx->options & topt_use_crlf) *chunk_ptr++ = '\r';
*chunk_ptr++ = '\n';
transport_newlines++;
pdlist address of anchor of the list of processed addresses
first TRUE if this is the first address; set it FALSE afterwards
fd the file descriptor to write to
- flags to be passed on to write_chunk()
+ tctx transport context - processing to be done during output
Returns: FALSE if writing failed
*/
static BOOL
write_env_to(address_item *p, struct aci **pplist, struct aci **pdlist,
- BOOL *first, int fd, unsigned flags)
+ BOOL *first, int fd, transport_ctx * tctx)
{
address_item *pp;
struct aci *ppp;
address_item *dup;
for (dup = addr_duplicate; dup; dup = dup->next)
if (dup->dupof == pp) /* a dup of our address */
- if (!write_env_to(dup, pplist, pdlist, first, fd, flags))
+ if (!write_env_to(dup, pplist, pdlist, first, fd, tctx))
return FALSE;
if (!pp->parent) break;
}
*pplist = ppp;
ppp->ptr = pp;
-if (!*first && !write_chunk(fd, US",\n ", 3, flags)) return FALSE;
+if (!*first && !write_chunk(fd, tctx, US",\n ", 3)) return FALSE;
*first = FALSE;
-return write_chunk(fd, pp->address, Ustrlen(pp->address), flags);
+return write_chunk(fd, tctx, pp->address, Ustrlen(pp->address));
}
-/* Add/remove/rewwrite headers, and send them plus the empty-line sparator.
+/* Add/remove/rewrite headers, and send them plus the empty-line separator.
Globals:
header_list
addr (chain of) addresses (for extra headers), or NULL;
only the first address is used
fd file descriptor to write the message to
+ tctx transport context
sendfn function for output (transport or verify)
- use_crlf turn NL into CR LF
- rewrite_rules chain of header rewriting rules
- rewrite_existflags flags for the rewriting rules
Returns: TRUE on success; FALSE on failure.
*/
BOOL
-transport_headers_send(address_item *addr, int fd, transport_instance * tblock,
- BOOL (*sendfn)(int fd, uschar * s, int len, unsigned options),
- BOOL use_crlf)
+transport_headers_send(int fd, transport_ctx * tctx,
+ BOOL (*sendfn)(int fd, transport_ctx * tctx, uschar * s, int len))
{
header_line *h;
const uschar *list;
+transport_instance * tblock = tctx ? tctx->tblock : NULL;
+address_item * addr = tctx ? tctx->addr : NULL;
/* Then the message's headers. Don't write any that are flagged as "old";
that means they were rewritten, or are a record of envelope rewriting, or
if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
tblock->rewrite_existflags, FALSE)))
{
- if (!sendfn(fd, hh->text, hh->slen, use_crlf)) return FALSE;
+ if (!sendfn(fd, tctx, hh->text, hh->slen)) return FALSE;
store_reset(reset_point);
continue; /* With the next header line */
}
/* Either no rewriting rules, or it didn't get rewritten */
- if (!sendfn(fd, h->text, h->slen, use_crlf)) return FALSE;
+ if (!sendfn(fd, tctx, h->text, h->slen)) return FALSE;
}
/* Header removed */
hprev = h;
if (i == 1)
{
- if (!sendfn(fd, h->text, h->slen, use_crlf)) return FALSE;
+ if (!sendfn(fd, tctx, h->text, h->slen)) return FALSE;
DEBUG(D_transport)
debug_printf("added header line(s):\n%s---\n", h->text);
}
int len = Ustrlen(s);
if (len > 0)
{
- if (!sendfn(fd, s, len, use_crlf)) return FALSE;
- if (s[len-1] != '\n' && !sendfn(fd, US"\n", 1, use_crlf))
+ if (!sendfn(fd, tctx, s, len)) return FALSE;
+ if (s[len-1] != '\n' && !sendfn(fd, tctx, US"\n", 1))
return FALSE;
DEBUG(D_transport)
{
/* Separate headers from body with a blank line */
-return sendfn(fd, US"\n", 1, use_crlf);
+return sendfn(fd, tctx, US"\n", 1);
}
end_dot if TRUE, send a terminating "." line at the end
no_headers if TRUE, omit the headers
no_body if TRUE, omit the body
- size_limit if > 0, this is a limit to the size of message written;
+ check_string a string to check for at the start of lines, or NULL
+ escape_string a string to insert in front of any check string
+ size_limit if > 0, this is a limit to the size of message written;
it is used when returning messages to their senders,
and is approximate rather than exact, owing to chunk
buffering
- check_string a string to check for at the start of lines, or NULL
- escape_string a string to insert in front of any check string
Returns: TRUE on success; FALSE (with errno) on failure.
In addition, the global variable transport_count
internal_transport_write_message(int fd, transport_ctx * tctx, int size_limit)
{
int len;
-unsigned wck_flags = (unsigned) tctx->options;
-off_t fsize;
-int size;
/* Initialize pointer in output buffer. */
uschar buffer[ADDRESS_MAXLENGTH + 20];
int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH,
return_path);
- if (!write_chunk(fd, buffer, n, wck_flags)) return FALSE;
+ if (!write_chunk(fd, tctx, buffer, n)) return FALSE;
}
/* Add envelope-to: if requested */
struct aci *dlist = NULL;
void *reset_point = store_get(0);
- if (!write_chunk(fd, US"Envelope-to: ", 13, wck_flags)) return FALSE;
+ if (!write_chunk(fd, tctx, US"Envelope-to: ", 13)) return FALSE;
/* Pick up from all the addresses. The plist and dlist variables are
anchors for lists of addresses already handled; they have to be defined at
- this level becuase write_env_to() calls itself recursively. */
+ 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, fd, wck_flags))
+ if (!write_env_to(p, &plist, &dlist, &first, fd, tctx))
return FALSE;
/* Add a final newline and reset the store used for tracking duplicates */
- if (!write_chunk(fd, US"\n", 1, wck_flags)) return FALSE;
+ if (!write_chunk(fd, tctx, US"\n", 1)) return FALSE;
store_reset(reset_point);
}
{
uschar buffer[100];
int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full));
- if (!write_chunk(fd, buffer, n, wck_flags)) return FALSE;
+ if (!write_chunk(fd, tctx, buffer, n)) return FALSE;
}
/* Then the message's headers. Don't write any that are flagged as "old";
match any entries therein. Then check addr->prop.remove_headers too, provided that
addr is not NULL. */
- if (!transport_headers_send(tctx->addr, fd, tctx->tblock, &write_chunk, wck_flags))
+ if (!transport_headers_send(fd, tctx, &write_chunk))
return FALSE;
}
-/* When doing RFC3030 CHUNKING output, work out how much data will be in the
-last BDAT, consisting of the current write_chunk() output buffer fill
+/* When doing RFC3030 CHUNKING output, work out how much data would be in a
+last-BDAT, consisting of the current write_chunk() output buffer fill
(optimally, all of the headers - but it does not matter if we already had to
flush that buffer with non-last BDAT prependix) plus the amount of body data
-(as expanded for CRLF lines). Then create and write the BDAT, and ensure
-that further use of write_chunk() will not prepend BDATs. */
+(as expanded for CRLF lines). Then create and write BDAT(s), and ensure
+that further use of write_chunk() will not prepend BDATs.
+The first BDAT written will also first flush any outstanding MAIL and RCPT
+commands which were buffered thans to PIPELINING.
+Commands go out (using a send()) from a different buffer to data (using a
+write()). They might not end up in the same TCP segment, which is
+suboptimal. */
if (tctx->options & topt_use_bdat)
{
- if ((size = chunk_ptr - deliver_out_buffer) < 0)
- size = 0;
+ off_t fsize;
+ int hsize, size = 0;
+
+ if ((hsize = chunk_ptr - deliver_out_buffer) < 0)
+ hsize = 0;
if (!(tctx->options & topt_no_body))
{
if ((fsize = lseek(deliver_datafile, 0, SEEK_END)) < 0) return FALSE;
fsize -= SPOOL_DATA_START_OFFSET;
if (size_limit > 0 && fsize > size_limit)
fsize = size_limit;
- size += fsize;
+ size = hsize + fsize;
if (tctx->options & topt_use_crlf)
size += body_linecount; /* account for CRLF-expansion */
}
- /*XXX need an smtp_outblock here; can't really use the smtp
- tpts one. so that had better have been flushed.
-
- WORRY: smtp cmd response sync, needs an inblock and a LOT
- of tpt info. NEED a callback into the tpt.
+ /* If the message is large, emit first a non-LAST chunk with just the
+ headers, and reap the command responses. This lets us error out early
+ on RCPT rejects rather than sending megabytes of data. Include headers
+ on the assumption they are cheap enough and some clever implementations
+ might errorcheck them too, on-the-fly, and reject that chunk. */
-#ifdef notdef
- smtp_write_command(&outblock, FALSE, "BDAT %d LAST\r\n", size);
- if (count < 0) return FALSE;
- if (count > 0)
+ if (size > DELIVER_OUT_BUFFER_SIZE && hsize > 0)
{
+ DEBUG(D_transport)
+ debug_printf("sending small initial BDAT; hsize=%d\n", hsize);
+ if ( tctx->chunk_cb(tctx, hsize, 0) != OK
+ || !transport_write_block(fd, deliver_out_buffer, hsize)
+ || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
+ )
+ return FALSE;
+ chunk_ptr = deliver_out_buffer;
+ size -= hsize;
}
-#endif
- */
- wck_flags &= ~topt_use_bdat;
+ /* Emit a LAST datachunk command, and unmark the context for further
+ BDAT commands. */
+
+ if (tctx->chunk_cb(tctx, size, tc_chunk_last) != OK)
+ return FALSE;
+ tctx->options &= ~topt_use_bdat;
}
/* If the body is required, ensure that the data for check strings (formerly
if (!(tctx->options & topt_no_body))
{
+ int size = size_limit;
+
nl_check_length = abs(nl_check_length);
nl_partial_match = 0;
if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0)
return FALSE;
while ( (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0
&& (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
- if (!write_chunk(fd, deliver_in_buffer, len, wck_flags))
+ {
+ if (!write_chunk(fd, tctx, deliver_in_buffer, len))
return FALSE;
+ size -= len;
+ }
/* A read error on the body will have left len == -1 and errno set. */
/* If requested, add a terminating "." line (SMTP output). */
-if (tctx->options & topt_end_dot && !write_chunk(fd, US".\n", 2, wck_flags))
+if (tctx->options & topt_end_dot && !write_chunk(fd, tctx, US".\n", 2))
return FALSE;
/* Write out any remaining data in the buffer before returning. */
BOOL
dkim_transport_write_message(int out_fd, transport_ctx * tctx,
- struct ob_dkim * dkim)
+ struct ob_dkim * dkim, const uschar ** err)
{
int dkim_fd;
int save_errno = 0;
BOOL rc;
uschar * dkim_spool_name;
-int sread = 0;
-int wwritten = 0;
-uschar *dkim_signature = NULL;
-int siglen;
+uschar * dkim_signature = NULL;
+int sread = 0, wwritten = 0, siglen = 0, options;
off_t k_file_size;
+const uschar * errstr;
/* If we can't sign, just call the original function. */
/* Can't create spool file. Ugh. */
rc = FALSE;
save_errno = errno;
+ *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
goto CLEANUP;
}
-/* Call original function to write the -K file; does the CRLF expansion */
+/* Call original function to write the -K file; does the CRLF expansion
+(but, in the CHUNKING case, not dot-stuffing and dot-termination). */
+options = tctx->options;
tctx->options &= ~topt_use_bdat;
rc = transport_write_message(dkim_fd, tctx, 0);
+tctx->options = options;
/* Save error state. We must clean up before returning. */
if (!rc)
goto CLEANUP;
}
-if (dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
+/* Rewind file and feed it to the goats^W DKIM lib */
+dkim->dot_stuffed = !!(options & topt_end_dot);
+lseek(dkim_fd, 0, SEEK_SET);
+if ((dkim_signature = dkim_exim_sign(dkim_fd, dkim, &errstr)))
+ siglen = Ustrlen(dkim_signature);
+else if (dkim->dkim_strict)
{
- /* Rewind file and feed it to the goats^W DKIM lib */
- lseek(dkim_fd, 0, SEEK_SET);
- dkim_signature = dkim_exim_sign(dkim_fd,
- dkim->dkim_private_key,
- dkim->dkim_domain,
- dkim->dkim_selector,
- dkim->dkim_canon,
- dkim->dkim_sign_headers);
- if (!dkim_signature)
- {
- if (dkim->dkim_strict)
+ uschar *dkim_strict_result = expand_string(dkim->dkim_strict);
+ if (dkim_strict_result)
+ if ( (strcmpic(dkim->dkim_strict,US"1") == 0) ||
+ (strcmpic(dkim->dkim_strict,US"true") == 0) )
{
- uschar *dkim_strict_result = expand_string(dkim->dkim_strict);
- if (dkim_strict_result)
- if ( (strcmpic(dkim->dkim_strict,US"1") == 0) ||
- (strcmpic(dkim->dkim_strict,US"true") == 0) )
- {
- /* Set errno to something halfway meaningful */
- save_errno = EACCES;
- log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
- " and dkim_strict is set. Deferring message delivery.");
- rc = FALSE;
- goto CLEANUP;
- }
+ /* Set errno to something halfway meaningful */
+ save_errno = EACCES;
+ log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
+ " and dkim_strict is set. Deferring message delivery.");
+ *err = errstr;
+ rc = FALSE;
+ goto CLEANUP;
}
- }
-
- siglen = 0;
}
-if (dkim_signature)
+#ifndef HAVE_LINUX_SENDFILE
+if (options & topt_use_bdat)
+#endif
+ k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */
+
+if (options & topt_use_bdat)
{
- siglen = Ustrlen(dkim_signature);
- while(siglen > 0)
+
+ /* On big messages output a precursor chunk to get any pipelined
+ MAIL & RCPT commands flushed, then reap the responses so we can
+ error out on RCPT rejects before sending megabytes. */
+
+ if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
{
-#ifdef SUPPORT_TLS
- wwritten = tls_out.active == out_fd
- ? tls_write(FALSE, dkim_signature, siglen)
- : write(out_fd, dkim_signature, siglen);
-#else
- wwritten = write(out_fd, dkim_signature, siglen);
-#endif
- if (wwritten == -1)
- {
- /* error, bail out */
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
- siglen -= wwritten;
- dkim_signature += wwritten;
+ if ( tctx->chunk_cb(tctx, siglen, 0) != OK
+ || !transport_write_block(out_fd, dkim_signature, siglen)
+ || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
+ )
+ goto err;
+ siglen = 0;
}
+
+ /* Send the BDAT command for the entire message, as a single LAST-marked
+ chunk. */
+
+ if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
+ goto err;
}
+if(siglen > 0 && !transport_write_block(out_fd, dkim_signature, siglen))
+ goto err;
+
#ifdef HAVE_LINUX_SENDFILE
/* We can use sendfile() to shove the file contents
to the socket. However only if we don't use TLS,
ssize_t copied = 0;
off_t offset = 0;
- k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */
-
/* Rewind file */
lseek(dkim_fd, 0, SEEK_SET);
while(copied >= 0 && offset < k_file_size)
copied = sendfile(out_fd, dkim_fd, &offset, k_file_size - offset);
if (copied < 0)
- {
- save_errno = errno;
- rc = FALSE;
- }
+ goto err;
}
else
/* Send file down the original fd */
while((sread = read(dkim_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0)
{
- char *p = deliver_out_buffer;
+ uschar * p = deliver_out_buffer;
/* write the chunk */
while (sread)
{
#ifdef SUPPORT_TLS
wwritten = tls_out.active == out_fd
- ? tls_write(FALSE, US p, sread)
- : write(out_fd, p, sread);
+ ? tls_write(FALSE, p, sread)
+ : write(out_fd, CS p, sread);
#else
- wwritten = write(out_fd, p, sread);
+ wwritten = write(out_fd, CS p, sread);
#endif
if (wwritten == -1)
- {
- /* error, bail out */
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
+ goto err;
p += wwritten;
sread -= wwritten;
}
}
CLEANUP:
-/* unlink -K file */
-(void)close(dkim_fd);
-Uunlink(dkim_spool_name);
-errno = save_errno;
-return rc;
+ /* unlink -K file */
+ (void)close(dkim_fd);
+ Uunlink(dkim_spool_name);
+ errno = save_errno;
+ return rc;
+
+err:
+ save_errno = errno;
+ rc = FALSE;
+ goto CLEANUP;
}
#endif
to write to the filter, and in this process just suck from the filter and write
down the given fd. At the end, tidy up the pipes and the processes.
-XXX
Arguments: as for internal_transport_write_message() above
Returns: TRUE on success; FALSE (with errno) for any failure
BOOL
transport_write_message(int fd, transport_ctx * tctx, int size_limit)
{
-unsigned wck_flags;
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;
-static transport_ctx dummy_tctx = { NULL, NULL, NULL, NULL, 0 };
+static transport_ctx dummy_tctx = {0};
if (!tctx) tctx = &dummy_tctx;
before being written to the incoming fd. First set up the special processing to
be done during the copying. */
-wck_flags = tctx->options & topt_use_crlf;
nl_partial_match = -1;
if (tctx->check_string && tctx->escape_string)
yield = FALSE;
write_pid = (pid_t)(-1);
-(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
-filter_pid = child_open(USS transport_filter_argv, NULL, 077,
- &fd_write, &fd_read, FALSE);
-(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC);
+ {
+ int bits = fcntl(fd, F_GETFD);
+ (void)fcntl(fd, F_SETFD, bits | FD_CLOEXEC);
+ filter_pid = child_open(USS transport_filter_argv, NULL, 077,
+ &fd_write, &fd_read, FALSE);
+ (void)fcntl(fd, F_SETFD, bits & ~FD_CLOEXEC);
+ }
if (filter_pid < 0) goto TIDY_UP; /* errno set */
DEBUG(D_transport)
if (len > 0)
{
- if (!write_chunk(fd, deliver_in_buffer, len, wck_flags)) goto TIDY_UP;
+ if (!write_chunk(fd, tctx, deliver_in_buffer, len)) goto TIDY_UP;
last_filter_was_NL = (deliver_in_buffer[len-1] == '\n');
}
{
rc = child_close(write_pid, 30);
if (yield)
- {
if (rc == 0)
{
BOOL ok;
- int dummy = read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
- if (!ok)
+ if (read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)) != sizeof(BOOL))
+ {
+ DEBUG(D_transport)
+ debug_printf("pipe read from writing process: %s\n", strerror(errno));
+ save_errno = ERRNO_FILTER_FAIL;
+ yield = FALSE;
+ }
+ else if (!ok)
{
- dummy = read(pfd[pipe_read], (void *)&save_errno, 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));
yield = FALSE;
}
tctx->addr->more_errno = rc;
DEBUG(D_transport) debug_printf("writing process returned %d\n", rc);
}
- }
}
(void)close(pfd[pipe_read]);
nl_check_length = nl_escape_length = 0;
if ( tctx->options & topt_end_dot
&& ( last_filter_was_NL
- ? !write_chunk(fd, US".\n", 2, wck_flags)
- : !write_chunk(fd, US"\n.\n", 3, wck_flags)
+ ? !write_chunk(fd, tctx, US".\n", 2)
+ : !write_chunk(fd, tctx, US"\n.\n", 3)
) )
yield = FALSE;
void
transport_update_waiting(host_item *hostlist, uschar *tpname)
{
-uschar buffer[256];
const uschar *prevname = US"";
host_item *host;
open_db dbblock;
/* Open the database for this transport */
-sprintf(CS buffer, "wait-%.200s", tpname);
-dbm_file = dbfn_open(buffer, O_RDWR, &dbblock, TRUE);
-if (dbm_file == NULL) return;
+if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
+ O_RDWR, &dbblock, TRUE)))
+ return;
/* Scan the list of hosts for which this message is waiting, and ensure
that the message id is in each host record. */
-for (host = hostlist; host!= NULL; host = host->next)
+for (host = hostlist; host; host = host->next)
{
BOOL already = FALSE;
dbdata_wait *host_record;
uschar *s;
int i, host_length;
+ uschar buffer[256];
/* Skip if this is the same host as we just processed; otherwise remember
the name for next time. */
/* Look up the host record; if there isn't one, make an empty one. */
- host_record = dbfn_read(dbm_file, host->name);
- if (host_record == NULL)
+ if (!(host_record = dbfn_read(dbm_file, host->name)))
{
host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH);
host_record->count = host_record->sequence = 0;
for (s = host_record->text; s < host_record->text + host_length;
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. */
{
dbdata_wait *cont;
sprintf(CS buffer, "%.200s:%d", host->name, i);
- cont = dbfn_read(dbm_file, buffer);
- if (cont != NULL)
+ if ((cont = dbfn_read(dbm_file, buffer)))
{
int clen = cont->count * MESSAGE_ID_LENGTH;
for (s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
- {
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
- }
}
}
int host_length;
open_db dbblock;
open_db *dbm_file;
-uschar buffer[256];
int i;
struct stat statbuf;
/* Open the waiting information database. */
-sprintf(CS buffer, "wait-%.200s", transport_name);
-dbm_file = dbfn_open(buffer, O_RDWR, &dbblock, TRUE);
-if (dbm_file == NULL) return FALSE;
+if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
+ O_RDWR, &dbblock, TRUE)))
+ return FALSE;
/* See if there is a record for this host; if not, there's nothing to do. */
/* create an array to read entire message queue into memory for processing */
- msgq = (msgq_t*) malloc(sizeof(msgq_t) * host_record->count);
+ msgq = store_malloc(sizeof(msgq_t) * host_record->count);
msgq_count = host_record->count;
msgq_actual = msgq_count;
}
}
-/* Jeremy: check for a continuation record, this code I do not know how to
-test but the code should work */
+ /* Check for a continuation record. */
while (host_length <= 0)
{
int i;
dbdata_wait * newr = NULL;
+ uschar buffer[256];
/* Search for a continuation */
if (bFound) /* Usual exit from main loop */
{
- free (msgq);
+ store_free (msgq);
break;
}
return FALSE;
}
- free(msgq);
+ store_free(msgq);
} /* we need to process a continuation record */
/* Control gets here when an existing message has been encountered; its
* Deliver waiting message down same socket *
*************************************************/
+/* Just the regain-root-privilege exec portion */
+void
+transport_do_pass_socket(const uschar *transport_name, const uschar *hostname,
+ const uschar *hostaddress, uschar *id, int socket_fd)
+{
+pid_t pid;
+int status;
+int i = 20;
+const uschar **argv;
+
+/* Set up the calling arguments; use the standard function for the basics,
+but we have a number of extras that may be added. */
+
+argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
+
+if (smtp_authenticated) argv[i++] = US"-MCA";
+if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
+if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD";
+if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP";
+if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS";
+#ifdef SUPPORT_TLS
+if (smtp_peer_options & PEER_OFFERED_TLS)
+ if (tls_out.active >= 0 || continue_proxy_cipher)
+ {
+ argv[i++] = US"-MCt";
+ argv[i++] = sending_ip_address;
+ argv[i++] = string_sprintf("%d", sending_port);
+ argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher;
+ }
+ else
+ argv[i++] = US"-MCT";
+#endif
+
+if (queue_run_pid != (pid_t)0)
+ {
+ argv[i++] = US"-MCQ";
+ argv[i++] = string_sprintf("%d", queue_run_pid);
+ argv[i++] = string_sprintf("%d", queue_run_pipe);
+ }
+
+argv[i++] = US"-MC";
+argv[i++] = US transport_name;
+argv[i++] = US hostname;
+argv[i++] = US hostaddress;
+argv[i++] = string_sprintf("%d", continue_sequence + 1);
+argv[i++] = id;
+argv[i++] = NULL;
+
+/* Arrange for the channel to be on stdin. */
+
+if (socket_fd != 0)
+ {
+ (void)dup2(socket_fd, 0);
+ (void)close(socket_fd);
+ }
+
+DEBUG(D_exec) debug_print_argv(argv);
+exim_nullstd(); /* Ensure std{out,err} exist */
+execv(CS argv[0], (char *const *)argv);
+
+DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
+_exit(errno); /* Note: must be _exit(), NOT exit() */
+}
+
+
+
/* Fork a new exim process to deliver the message, and do a re-exec, both to
get a clean delivery process, and to regain root privilege in cases where it
has been given away.
if ((pid = fork()) == 0)
{
- int i = 16;
- const uschar **argv;
-
/* Disconnect entirely from the parent process. If we are running in the
test harness, wait for a bit to allow the previous process time to finish,
write the log, etc., so that the output is always in the same order for
if ((pid = fork()) != 0) _exit(EXIT_SUCCESS);
if (running_in_test_harness) sleep(1);
- /* Set up the calling arguments; use the standard function for the basics,
- but we have a number of extras that may be added. */
-
- argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
-
- if (smtp_use_dsn) argv[i++] = US"-MCD";
-
- if (smtp_authenticated) argv[i++] = US"-MCA";
-
- #ifdef SUPPORT_TLS
- if (tls_offered) argv[i++] = US"-MCT";
- #endif
-
- if (smtp_use_size) argv[i++] = US"-MCS";
- if (smtp_use_pipelining) argv[i++] = US"-MCP";
-
- if (queue_run_pid != (pid_t)0)
- {
- argv[i++] = US"-MCQ";
- argv[i++] = string_sprintf("%d", queue_run_pid);
- argv[i++] = string_sprintf("%d", queue_run_pipe);
- }
-
- argv[i++] = US"-MC";
- argv[i++] = US transport_name;
- argv[i++] = US hostname;
- argv[i++] = US hostaddress;
- argv[i++] = string_sprintf("%d", continue_sequence + 1);
- argv[i++] = id;
- argv[i++] = NULL;
-
- /* Arrange for the channel to be on stdin. */
-
- if (socket_fd != 0)
- {
- (void)dup2(socket_fd, 0);
- (void)close(socket_fd);
- }
-
- DEBUG(D_exec) debug_print_argv(argv);
- exim_nullstd(); /* Ensure std{out,err} exist */
- execv(CS argv[0], (char *const *)argv);
-
- DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
- _exit(errno); /* Note: must be _exit(), NOT exit() */
+ transport_do_pass_socket(transport_name, hostname, hostaddress,
+ id, socket_fd);
}
/* If the process creation succeeded, wait for the first-level child, which
*/
if (address_pipe_argcount > 1)
memmove(
- /* current position + additonal args */
+ /* current position + additional args */
argv + i + address_pipe_argcount,
/* current position + 1 (for the (uschar *)0 at the end) */
argv + i + 1,