tctx transport context: file descriptor or string to write to
block block of bytes to write
len number of bytes to write
+ more further data expected soon
Returns: TRUE on success, FALSE on failure (with errno preserved);
transport_count is incremented by the number of bytes written
{
rc =
#ifdef SUPPORT_TLS
- (tls_out.active == fd) ? tls_write(FALSE, block, len) :
+ tls_out.active == fd ? tls_write(FALSE, block, len, more) :
#endif
#ifdef MSG_MORE
more ? send(fd, block, len, MSG_MORE) :
rc =
#ifdef SUPPORT_TLS
- (tls_out.active == fd) ? tls_write(FALSE, block, len) :
+ tls_out.active == fd ? tls_write(FALSE, block, len, more) :
#endif
#ifdef MSG_MORE
more ? send(fd, block, len, MSG_MORE) :
/* 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->options & topt_use_bdat && tctx->chunk_cb)
{
if ( tctx->chunk_cb(tctx, (unsigned)len, 0) != OK
|| !transport_write_block(tctx, deliver_out_buffer, len, FALSE)
chunk_ptr = deliver_out_buffer;
}
+ /* Remove CR before NL if required */
+
+ if ( *ptr == '\r' && ptr[1] == '\n'
+ && !(tctx->options & topt_use_crlf)
+ && spool_file_wireformat
+ )
+ ptr++;
+
if ((ch = *ptr) == '\n')
{
int left = end - ptr - 1; /* count of chars left after NL */
/* Insert CR before NL if required */
- if (tctx && tctx->options & topt_use_crlf) *chunk_ptr++ = '\r';
+ if (tctx->options & topt_use_crlf && !spool_file_wireformat)
+ *chunk_ptr++ = '\r';
*chunk_ptr++ = '\n';
transport_newlines++;
/* Header removed */
else
- {
DEBUG(D_transport) debug_printf("removed header line:\n%s---\n", h->text);
- }
}
/* Add on any address-specific headers. If there are multiple addresses,
is incremented by the number of bytes written.
*/
-BOOL
+static BOOL
internal_transport_write_message(transport_ctx * tctx, int size_limit)
{
-int len;
+int len, size = 0;
/* Initialize pointer in output buffer. */
nl_escape_length = Ustrlen(nl_escape);
}
+/* Whether the escaping mechanism is applied to headers or not is controlled by
+an option (set for SMTP, not otherwise). Negate the length if not wanted till
+after the headers. */
+
+if (!(tctx->options & topt_escape_headers))
+ nl_check_length = -nl_check_length;
+
/* Write the headers if required, including any that have to be added. If there
-are header rewriting rules, apply them. */
+are header rewriting rules, apply them. The datasource is not the -D spoolfile
+so temporarily hide the global that adjusts for its format. */
if (!(tctx->options & topt_no_headers))
{
- /* Whether the escaping mechanism is applied to headers or not is controlled by
- an option (set for SMTP, not otherwise). Negate the length if not wanted till
- after the headers. */
-
- if (!(tctx->options & topt_escape_headers))
- nl_check_length = -nl_check_length;
+ BOOL save_wireformat = spool_file_wireformat;
+ spool_file_wireformat = FALSE;
/* Add return-path: if requested. */
uschar buffer[ADDRESS_MAXLENGTH + 20];
int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH,
return_path);
- if (!write_chunk(tctx, buffer, n)) return FALSE;
+ if (!write_chunk(tctx, buffer, n)) goto bad;
}
/* Add envelope-to: if requested */
struct aci *dlist = NULL;
void *reset_point = store_get(0);
- if (!write_chunk(tctx, US"Envelope-to: ", 13)) return FALSE;
+ if (!write_chunk(tctx, US"Envelope-to: ", 13)) goto bad;
/* 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 because write_env_to() calls itself recursively. */
for (p = tctx->addr; p; p = p->next)
- if (!write_env_to(p, &plist, &dlist, &first, tctx))
- return FALSE;
+ if (!write_env_to(p, &plist, &dlist, &first, tctx)) goto bad;
/* Add a final newline and reset the store used for tracking duplicates */
- if (!write_chunk(tctx, US"\n", 1)) return FALSE;
+ if (!write_chunk(tctx, US"\n", 1)) goto bad;
store_reset(reset_point);
}
if (tctx->options & topt_add_delivery_date)
{
- uschar buffer[100];
- int n = sprintf(CS buffer, "Delivery-date: %s\n", tod_stamp(tod_full));
- if (!write_chunk(tctx, buffer, n)) return FALSE;
+ uschar * s = tod_stamp(tod_full);
+
+ if ( !write_chunk(tctx, US"Delivery-date: ", 15)
+ || !write_chunk(tctx, s, Ustrlen(s))
+ || !write_chunk(tctx, US"\n", 1)) goto bad;
}
/* Then the message's headers. Don't write any that are flagged as "old";
addr is not NULL. */
if (!transport_headers_send(tctx, &write_chunk))
+ {
+bad:
+ spool_file_wireformat = save_wireformat;
return FALSE;
+ }
+
+ spool_file_wireformat = save_wireformat;
}
/* When doing RFC3030 CHUNKING output, work out how much data would be in a
if (tctx->options & topt_use_bdat)
{
off_t fsize;
- int hsize, size = 0;
+ int hsize;
if ((hsize = chunk_ptr - deliver_out_buffer) < 0)
hsize = 0;
if (size_limit > 0 && fsize > size_limit)
fsize = size_limit;
size = hsize + fsize;
- if (tctx->options & topt_use_crlf)
+ if (tctx->options & topt_use_crlf && !spool_file_wireformat)
size += body_linecount; /* account for CRLF-expansion */
/* With topt_use_bdat we never do dot-stuffing; no need to
is positioned at the start of its file (following the message id), then write
it, applying the size limit if required. */
+/* If we have a wireformat -D file (CRNL lines, non-dotstuffed, no ending dot)
+and we want to send a body without dotstuffing or ending-dot, in-clear,
+then we can just dump it using sendfile.
+This should get used for CHUNKING output and also for writing the -K file for
+dkim signing, when we had CHUNKING input. */
+
+#ifdef OS_SENDFILE
+if ( spool_file_wireformat
+ && !(tctx->options & (topt_no_body | topt_end_dot))
+ && !nl_check_length
+ && tls_out.active != tctx->u.fd
+ )
+ {
+ ssize_t copied = 0;
+ off_t offset = SPOOL_DATA_START_OFFSET;
+
+ /* Write out any header data in the buffer */
+
+ if ((len = chunk_ptr - deliver_out_buffer) > 0)
+ {
+ if (!transport_write_block(tctx, deliver_out_buffer, len, TRUE))
+ return FALSE;
+ size -= len;
+ }
+
+ DEBUG(D_transport) debug_printf("using sendfile for body\n");
+
+ while(size > 0)
+ {
+ if ((copied = os_sendfile(tctx->u.fd, deliver_datafile, &offset, size)) <= 0) break;
+ size -= copied;
+ }
+ return copied >= 0;
+ }
+#else
+DEBUG(D_transport) debug_printf("cannot use sendfile for body: no support\n");
+#endif
+
+DEBUG(D_transport)
+ if (!(tctx->options & topt_no_body))
+ debug_printf("cannot use sendfile for body: %s\n",
+ !spool_file_wireformat ? "spoolfile not wireformat"
+ : 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))
{
int size = size_limit;
+
/*************************************************
* External interface to write the message *
*************************************************/
transport_write_message(transport_ctx * tctx, int size_limit)
{
BOOL last_filter_was_NL = TRUE;
+BOOL save_spool_file_wireformat = spool_file_wireformat;
int rc, len, yield, fd_read, fd_write, save_errno;
int pfd[2] = {-1, -1};
pid_t filter_pid, write_pid;
/* Copy the output of the filter, remembering if the last character was NL. If
no data is returned, that counts as "ended with NL" (default setting of the
-variable is TRUE). */
+variable is TRUE). The output should always be unix-format as we converted
+any wireformat source on writing input to the filter. */
+spool_file_wireformat = FALSE;
chunk_ptr = deliver_out_buffer;
for (;;)
sure. Also apply a paranoia timeout. */
TIDY_UP:
+spool_file_wireformat = save_spool_file_wireformat;
save_errno = errno;
(void)close(fd_read);
write the log, etc., so that the output is always in the same order for
automatic comparison. */
- if ((pid = fork()) != 0) _exit(EXIT_SUCCESS);
+ if ((pid = fork()) != 0)
+ {
+ DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
+ _exit(EXIT_SUCCESS);
+ }
if (running_in_test_harness) sleep(1);
transport_do_pass_socket(transport_name, hostname, hostaddress,
{
int rc;
while ((rc = wait(&status)) != pid && (rc >= 0 || errno != ECHILD));
- DEBUG(D_transport) debug_printf("transport_pass_socket succeeded\n");
+ DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (inter-pid %d)\n", pid);
return TRUE;
}
else