X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/bb07bcd32250965a896b0856dd1b839b5795e2f4..d12746bc15d83ab821be36975da0179672708bc1:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index 752e1d98e..0b403bad2 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* The main code for delivering a message. */ @@ -10,6 +10,7 @@ #include "exim.h" #include "transports/smtp.h" +#include #include @@ -79,6 +80,52 @@ static uschar *used_return_path = NULL; +/************************************************* +* read as much as requested * +*************************************************/ + +/* The syscall read(2) doesn't always returns as much as we want. For +several reasons it might get less. (Not talking about signals, as syscalls +are restartable). When reading from a network or pipe connection the sender +might send in smaller chunks, with delays between these chunks. The read(2) +may return such a chunk. + +The more the writer writes and the smaller the pipe between write and read is, +the more we get the chance of reading leass than requested. (See bug 2130) + +This function read(2)s until we got all the data we *requested*. + +Note: This function may block. Use it only if you're sure about the +amount of data you will get. + +Argument: + fd the file descriptor to read from + buffer pointer to a buffer of size len + len the requested(!) amount of bytes + +Returns: the amount of bytes read +*/ +static ssize_t +readn(int fd, void * buffer, size_t len) +{ + void * next = buffer; + void * end = buffer + len; + + while (next < end) + { + ssize_t got = read(fd, next, end - next); + + /* I'm not sure if there are signals that can interrupt us, + for now I assume the worst */ + if (got == -1 && errno == EINTR) continue; + if (got <= 0) return next - buffer; + next += got; + } + + return len; +} + + /************************************************* * Make a new address item * *************************************************/ @@ -714,10 +761,9 @@ d_log_interface(gstring * g) if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) && sending_ip_address) { - g = string_append(g, 2, US" I=[", sending_ip_address); - g = LOGGING(outgoing_port) - ? string_append(g, 2, US"]:", string_sprintf("%d", sending_port)) - : string_catn(g, US"]", 1); + g = string_fmt_append(g, " I=[%s]", sending_ip_address); + if (LOGGING(outgoing_port)) + g = string_fmt_append(g, "%d", sending_port); } return g; } @@ -737,21 +783,21 @@ if (LOGGING(dnssec) && h->dnssec == DS_YES) g = string_append(g, 3, US" [", h->address, US"]"); if (LOGGING(outgoing_port)) - g = string_append(g, 2, US":", string_sprintf("%d", h->port)); + g = string_fmt_append(g, ":%d", h->port); #ifdef SUPPORT_SOCKS if (LOGGING(proxy) && proxy_local_address) { g = string_append(g, 3, US" PRX=[", proxy_local_address, US"]"); if (LOGGING(outgoing_port)) - g = string_append(g, 2, US":", string_sprintf("%d", proxy_local_port)); + g = string_fmt_append(g, ":%d", proxy_local_port); } #endif g = d_log_interface(g); if (testflag(addr, af_tcp_fastopen)) - g = string_catn(g, US" TFO", 4); + g = string_catn(g, US" TFO*", testflag(addr, af_tcp_fastopen_data) ? 5 : 4); return g; } @@ -770,7 +816,7 @@ if (LOGGING(tls_certificate_verified) && addr->cipher) s = string_append(s, 2, US" CV=", testflag(addr, af_cert_verified) ? -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE testflag(addr, af_dane_verified) ? "dane" : @@ -804,7 +850,7 @@ if (action) if (!(s = expand_string(action)) && *expand_string_message) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand event_action %s in %s: %s\n", - event, transport_name, expand_string_message); + event, transport_name ? transport_name : US"main", expand_string_message); event_name = event_data = NULL; @@ -830,20 +876,33 @@ const uschar * save_host = deliver_host; const uschar * save_address = deliver_host_address; const int save_port = deliver_host_port; -if (!addr->transport) - return; - router_name = addr->router ? addr->router->name : NULL; -transport_name = addr->transport->name; deliver_domain = addr->domain; deliver_localpart = addr->local_part; deliver_host = addr->host_used ? addr->host_used->name : NULL; -(void) event_raise(addr->transport->event_action, event, - addr->host_used - || Ustrcmp(addr->transport->driver_name, "smtp") == 0 - || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 - ? addr->message : NULL); +if (!addr->transport) + { + if (Ustrcmp(event, "msg:fail:delivery") == 0) + { + /* An address failed with no transport involved. This happens when + a filter was used which triggered a fail command (in such a case + a transport isn't needed). Convert it to an internal fail event. */ + + (void) event_raise(event_action, US"msg:fail:internal", addr->message); + } + } +else + { + transport_name = addr->transport->name; + + (void) event_raise(addr->transport->event_action, event, + addr->host_used + || Ustrcmp(addr->transport->driver_name, "smtp") == 0 + || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + || Ustrcmp(addr->transport->driver_name, "autoreply") == 0 + ? addr->message : NULL); + } deliver_host_port = save_port; deliver_host_address = save_address; @@ -983,10 +1042,8 @@ else and all parents are not being included, don't add on the top address. First of all, do a caseless comparison; if this succeeds, do a caseful comparison on the local parts. */ - /*XXX dodgy coding. the string at "cmp" might not be nul-terminated if - we had to extend the allocation! */ - g->s[g->ptr] = '\0'; + string_from_gstring(g); /* ensure nul-terminated */ if ( strcmpic(cmp, topaddr->address) == 0 && Ustrncmp(cmp, topaddr->address, Ustrchr(cmp, '@') - cmp) == 0 && !addr->onetime_parent @@ -1042,7 +1099,7 @@ if ((diff->tv_usec -= then->tv_usec) < 0) -static uschar * +uschar * string_timediff(struct timeval * diff) { static uschar buf[sizeof("0.000s")]; @@ -1050,7 +1107,7 @@ static uschar buf[sizeof("0.000s")]; if (diff->tv_sec >= 5 || !LOGGING(millisec)) return readconf_printtime((int)diff->tv_sec); -sprintf(CS buf, "%d.%03ds", (int)diff->tv_sec, (int)diff->tv_usec/1000); +sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000); return buf; } @@ -1138,8 +1195,7 @@ if (addr->router) g = string_append(g, 2, US" T=", addr->transport->name); if (LOGGING(delivery_size)) - g = string_append(g, 2, US" S=", - string_sprintf("%d", transport_count)); + g = string_fmt_append(g, " S=%d", transport_count); /* Local delivery */ @@ -1189,6 +1245,16 @@ else } } + if (LOGGING(pipelining)) + { + if (testflag(addr, af_pipelining)) + g = string_catn(g, US" L", 2); +#ifdef EXPERIMENTAL_PIPE_CONNECT + if (testflag(addr, af_early_pipe)) + g = string_catn(g, US"*", 1); +#endif + } + #ifndef DISABLE_PRDR if (testflag(addr, af_prdr_used)) g = string_catn(g, US" PRDR", 5); @@ -1277,13 +1343,12 @@ if (driver_name) { if (driver_kind[1] == 't' && addr->router) g = string_append(g, 2, US" R=", addr->router->name); - g = string_cat(g, string_sprintf(" %c=%s", toupper(driver_kind[1]), driver_name)); + g = string_fmt_append(g, " %c=%s", toupper(driver_kind[1]), driver_name); } else if (driver_kind) g = string_append(g, 2, US" ", driver_kind); -/*XXX need an s+s+p sprintf */ -g = string_cat(g, string_sprintf(" defer (%d)", addr->basic_errno)); +g = string_fmt_append(g, " defer (%d)", addr->basic_errno); if (addr->basic_errno > 0) g = string_append(g, 2, US": ", @@ -1297,8 +1362,7 @@ if (addr->host_used) if (LOGGING(outgoing_port)) { int port = addr->host_used->port; - g = string_append(g, 2, - US":", port == PORT_NONE ? US"25" : string_sprintf("%d", port)); + g = string_fmt_append(g, ":%d", port == PORT_NONE ? 25 : port); } } @@ -1310,7 +1374,7 @@ if (addr->message) /* Log the deferment in the message log, but don't clutter it up with retry-time defers after the first delivery attempt. */ -if (deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) +if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) deliver_msglog("%s %s\n", now, g->s); /* Write the main log and reset the store. @@ -1335,6 +1399,16 @@ failure_log(address_item * addr, uschar * driver_kind, uschar * now) void * reset_point; gstring * g = reset_point = string_get(256); +#ifndef DISABLE_EVENT +/* Message failures for which we will send a DSN get their event raised +later so avoid doing it here. */ + +if ( !addr->prop.ignore_error + && !(addr->dsn_flags & (rf_dsnflags & ~rf_notify_failure)) + ) + msg_event_raise(US"msg:fail:delivery", addr); +#endif + /* Build up the log line for the message and main logs */ /* Create the address string for logging. Must not do this earlier, because @@ -1383,10 +1457,6 @@ else log_write(0, LOG_MAIN, "** %s", g->s); -#ifndef DISABLE_EVENT -msg_event_raise(US"msg:fail:delivery", addr); -#endif - store_reset(reset_point); return; } @@ -1430,7 +1500,7 @@ if (driver_type == EXIM_DTYPE_TRANSPORT) { driver_name = addr->transport->name; driver_kind = US" transport"; - disable_logging = addr->transport->disable_logging; + f.disable_logging = addr->transport->disable_logging; } else driver_kind = US"transporting"; } @@ -1440,7 +1510,7 @@ else if (driver_type == EXIM_DTYPE_ROUTER) { driver_name = addr->router->name; driver_kind = US" router"; - disable_logging = addr->router->disable_logging; + f.disable_logging = addr->router->disable_logging; } else driver_kind = US"routing"; } @@ -1508,7 +1578,7 @@ if (addr->return_file >= 0 && addr->return_filename) log_write(0, LOG_MAIN, "<%s>: %s transport output: %s", addr->address, tb->name, sp); } - (void)fclose(f); + (void)fclose(f); } /* Handle returning options, but only if there is an address to return @@ -1574,7 +1644,7 @@ if (result == OK) tls_out.cipher = addr->cipher; tls_out.peerdn = addr->peerdn; tls_out.ocsp = addr->ocsp; -# ifdef EXPERIMENTAL_DANE +# ifdef SUPPORT_DANE tls_out.dane_verified = testflag(addr, af_dane_verified); # endif #endif @@ -1587,7 +1657,7 @@ if (result == OK) tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; -# ifdef EXPERIMENTAL_DANE +# ifdef SUPPORT_DANE tls_out.dane_verified = FALSE; # endif #endif @@ -1614,7 +1684,7 @@ else if (result == DEFER || result == PANIC) if (addr->special_action == SPECIAL_FREEZE) { - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); update_spool = TRUE; } @@ -1622,7 +1692,7 @@ else if (result == DEFER || result == PANIC) /* If doing a 2-stage queue run, we skip writing to either the message log or the main log for SMTP defers. */ - if (!queue_2stage || addr->basic_errno != 0) + if (!f.queue_2stage || addr->basic_errno != 0) deferral_log(addr, now, logflags, driver_name, driver_kind); } @@ -1655,10 +1725,10 @@ else { frozen_info = addr->special_action == SPECIAL_FREEZE ? US"" - : sender_local && !local_error_message + : f.sender_local && !f.local_error_message ? US" (message created with -f <>)" : US" (delivery error message)"; - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); update_spool = TRUE; @@ -1683,7 +1753,7 @@ else /* Ensure logging is turned on again in all cases */ -disable_logging = FALSE; +f.disable_logging = FALSE; } @@ -1718,13 +1788,12 @@ addr->basic_errno = code; if (format) { va_list ap; - uschar buffer[512]; + gstring * g; + va_start(ap, format); - if (!string_vformat(buffer, sizeof(buffer), CS format, ap)) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "common_error expansion was longer than " SIZE_T_FMT, sizeof(buffer)); + g = string_vformat(NULL, TRUE, CS format, ap); va_end(ap); - addr->message = string_copy(buffer); + addr->message = string_from_gstring(g); } for (addr2 = addr->next; addr2; addr2 = addr2->next) @@ -2109,7 +2178,7 @@ if (tp->return_path) uschar *new_return_path = expand_string(tp->return_path); if (!new_return_path) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) { common_error(TRUE, addr, ERRNO_EXPANDFAIL, US"Failed to expand return path \"%s\" in %s transport: %s", @@ -2508,7 +2577,7 @@ if (!shadowing) /* In the test harness, wait just a bit to let the subprocess finish off any debug output etc first. */ - if (running_in_test_harness) millisleep(300); + if (f.running_in_test_harness) millisleep(300); DEBUG(D_deliver) debug_printf("journalling %s", big_buffer); len = Ustrlen(big_buffer); @@ -2668,7 +2737,7 @@ while (addr_local) struct timeval deliver_time; address_item *addr2, *addr3, *nextaddr; int logflags = LOG_MAIN; - int logchar = dont_deliver? '*' : '='; + int logchar = f.dont_deliver? '*' : '='; transport_instance *tp; uschar * serialize_key = NULL; @@ -2686,7 +2755,7 @@ while (addr_local) if (!(tp = addr->transport)) { logflags |= LOG_PANIC; - disable_logging = FALSE; /* Jic */ + f.disable_logging = FALSE; /* Jic */ addr->message = addr->router ? string_sprintf("No transport set by %s router", addr->router->name) : string_sprintf("No transport set by system filter"); @@ -2704,7 +2773,7 @@ while (addr_local) /* There are weird cases where logging is disabled */ - disable_logging = tp->disable_logging; + f.disable_logging = tp->disable_logging; /* Check for batched addresses and possible amalgamation. Skip all the work if either batch_max <= 1 or there aren't any other addresses for local @@ -2891,7 +2960,7 @@ while (addr_local) retry_record->expired); } - if (queue_running && !deliver_force) + if (f.queue_running && !f.deliver_force) { ok = (now - retry_record->time_stamp > retry_data_expire) || (now >= retry_record->next_try) @@ -3248,8 +3317,8 @@ small items (less than PIPE_BUF, which seems to be at least 512 in any Unix and often bigger) so even if we are reading while the subprocess is still going, we should never have only a partial item in the buffer. -hs12: This assumption is not true anymore, since we got quit large items (certificate -information and such) +hs12: This assumption is not true anymore, since we get quite large items (certificate +information and such). Argument: poffset the offset of the parlist item @@ -3311,8 +3380,7 @@ while (!done) If we get less, we can assume the subprocess do be done and do not expect any further information from it. */ - got = readn(fd, pipeheader, required); - if (got != required) + if ((got = readn(fd, pipeheader, required)) != required) { msg = string_sprintf("got " SSIZE_T_FMT " of %d bytes (pipeheader) " "from transport process %d for transport %s", @@ -3348,8 +3416,7 @@ while (!done) /* Same as above, the transport process will write the bytes announced in a timely manner, so we can just wait for the bytes, getting less than expected is considered a problem of the subprocess, we do not expect anything else from it. */ - got = readn(fd, big_buffer, required); - if (got != required) + if ((got = readn(fd, big_buffer, required)) != required) { msg = string_sprintf("got only " SSIZE_T_FMT " of " SIZE_T_FMT " bytes (pipedata) from transport process %d for transport %s", @@ -3508,6 +3575,16 @@ while (!done) break; #endif + case 'L': + switch (*subid) + { +#ifdef EXPERIMENTAL_PIPE_CONNECT + case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/ +#endif + case 1: setflag(addr, af_pipelining); break; + } + break; + case 'K': setflag(addr, af_chunking_used); break; @@ -3515,6 +3592,7 @@ while (!done) case 'T': setflag(addr, af_tcp_fastopen_conn); if (*subid > '0') setflag(addr, af_tcp_fastopen); + if (*subid > '1') setflag(addr, af_tcp_fastopen_data); break; case 'D': @@ -4208,7 +4286,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (!(tp = addr->transport)) { - disable_logging = FALSE; /* Jic */ + f.disable_logging = FALSE; /* Jic */ panicmsg = US"No transport set by router"; goto panic_continue; } @@ -4403,7 +4481,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) uschar *new_return_path = expand_string(tp->return_path); if (new_return_path) return_path = new_return_path; - else if (!expand_string_forcedfail) + else if (!f.expand_string_forcedfail) { panicmsg = string_sprintf("Failed to expand return path \"%s\": %s", tp->return_path, expand_string_message); @@ -4435,7 +4513,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) treat it as if it is a continued connection (apart from the counter used for the log line mark). */ - if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) + if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only) { DEBUG(D_deliver) debug_printf("lazy-callout-close: have conn still open from verification\n"); @@ -4454,7 +4532,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) we must check that the continue host is on the list. Otherwise, the host is set in the transport. */ - continue_more = FALSE; /* In case got set for the last lot */ + f.continue_more = FALSE; /* In case got set for the last lot */ if (continue_transport) { BOOL ok = Ustrcmp(continue_transport, tp->name) == 0; @@ -4529,12 +4607,12 @@ for (delivery_count = 0; addr_remote; delivery_count++) connected to is too hard to manage. Perhaps we need a finer-grain interface to the transport. */ - for (next = addr_remote; next && !continue_more; next = next->next) + for (next = addr_remote; next && !f.continue_more; next = next->next) { host_item *h; for (h = next->host_list; h; h = h->next) if (Ustrcmp(h->name, continue_hostname) == 0) - { continue_more = TRUE; break; } + { f.continue_more = TRUE; break; } } } @@ -4613,18 +4691,16 @@ all pipes, so I do not see a reason to use non-blocking IO here search_tidyup(); - if ((pid = fork()) == 0) { int fd = pfd[pipe_write]; host_item *h; - DEBUG(D_deliver) debug_selector |= D_pid; // hs12 /* Setting this global in the subprocess means we need never clear it */ transport_name = tp->name; /* There are weird circumstances in which logging is disabled */ - disable_logging = tp->disable_logging; + f.disable_logging = tp->disable_logging; /* Show pids on debug output if parallelism possible */ @@ -4639,7 +4715,7 @@ all pipes, so I do not see a reason to use non-blocking IO here predictable settings for each delivery process, so do something explicit here rather they rely on the fixed reset in the random number function. */ - random_seed = running_in_test_harness ? 42 + 2*delivery_count : 0; + random_seed = f.running_in_test_harness ? 42 + 2*delivery_count : 0; /* Set close-on-exec on the pipe so that it doesn't get passed on to a new process that may be forked to do another delivery down the same @@ -4742,7 +4818,7 @@ all pipes, so I do not see a reason to use non-blocking IO here /* The certificate verification status goes into the flags */ if (tls_out.certificate_verified) setflag(addr, af_cert_verified); -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE if (tls_out.dane_verified) setflag(addr, af_dane_verified); #endif @@ -4813,12 +4889,22 @@ all pipes, so I do not see a reason to use non-blocking IO here rmt_dlv_checked_write(fd, 'P', '0', NULL, 0); #endif + if (testflag(addr, af_pipelining)) +#ifdef EXPERIMENTAL_PIPE_CONNECT + if (testflag(addr, af_early_pipe)) + rmt_dlv_checked_write(fd, 'L', '2', NULL, 0); + else +#endif + rmt_dlv_checked_write(fd, 'L', '1', NULL, 0); + if (testflag(addr, af_chunking_used)) rmt_dlv_checked_write(fd, 'K', '0', NULL, 0); if (testflag(addr, af_tcp_fastopen_conn)) rmt_dlv_checked_write(fd, 'T', - testflag(addr, af_tcp_fastopen) ? '1' : '0', NULL, 0); + testflag(addr, af_tcp_fastopen) ? testflag(addr, af_tcp_fastopen_data) + ? '2' : '1' : '0', + NULL, 0); memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware)); rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware)); @@ -4942,12 +5028,13 @@ all pipes, so I do not see a reason to use non-blocking IO here release its TLS library context (if any) as responsibility was passed to the delivery child process. */ - if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) + if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only) { #ifdef SUPPORT_TLS - tls_close(FALSE, FALSE); + if (cutthrough.is_tls) + tls_close(cutthrough.cctx.tls_ctx, TLS_NO_SHUTDOWN); #endif - (void) close(cutthrough.fd); + (void) close(cutthrough.cctx.sock); release_cutthrough_connection(US"passed to transport proc"); } @@ -4989,7 +5076,7 @@ all pipes, so I do not see a reason to use non-blocking IO here newly created process get going before we create another process. This should ensure repeatability in the tests. We only need to wait a tad. */ - else if (running_in_test_harness) millisleep(500); + else if (f.running_in_test_harness) millisleep(500); continue; @@ -5512,8 +5599,9 @@ message size. This use of strcpy() is OK because the length id is checked when it is obtained from a command line (the -M or -q options), and otherwise it is known to be a valid message id. */ -Ustrcpy(message_id, id); -deliver_force = forced; +if (id != message_id) + Ustrcpy(message_id, id); +f.deliver_force = forced; return_count = 0; message_size = 0; @@ -5661,7 +5749,7 @@ Otherwise it might be needed again. */ can happen, but in the default situation, unless forced, no delivery is attempted. */ -if (deliver_freeze) +if (f.deliver_freeze) { #ifdef SUPPORT_MOVE_FROZEN_MESSAGES /* Moving to another directory removes the message from Exim's view. Other @@ -5704,8 +5792,8 @@ if (deliver_freeze) || auto_thaw <= 0 || now <= deliver_frozen_at + auto_thaw ) - && ( !forced || !deliver_force_thaw - || !admin_user || continue_hostname + && ( !forced || !f.deliver_force_thaw + || !f.admin_user || continue_hostname ) ) { (void)close(deliver_datafile); @@ -5719,7 +5807,7 @@ if (deliver_freeze) if (forced) { - deliver_manual_thaw = TRUE; + f.deliver_manual_thaw = TRUE; log_write(0, LOG_MAIN, "Unfrozen by forced delivery"); } else log_write(0, LOG_MAIN, "Unfrozen by auto-thaw"); @@ -5727,7 +5815,7 @@ if (deliver_freeze) /* We get here if any of the rules for unfreezing have triggered. */ - deliver_freeze = FALSE; + f.deliver_freeze = FALSE; update_spool = TRUE; } @@ -5802,8 +5890,8 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) } return_path = sender_address; - enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */ - system_filtering = TRUE; + f.enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */ + f.system_filtering = TRUE; /* Any error in the filter file causes a delivery to be abandoned. */ @@ -5851,8 +5939,8 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) /* Reset things. If the filter message is an empty string, which can happen for a filter "fail" or "freeze" command with no text, reset it to NULL. */ - system_filtering = FALSE; - enable_dollar_recipients = FALSE; + f.system_filtering = FALSE; + f.enable_dollar_recipients = FALSE; if (filter_message && filter_message[0] == 0) filter_message = NULL; /* Save the values of the system filter variables so that user filters @@ -5875,9 +5963,9 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) unset "delivered", which is forced by the "freeze" command to make -bF work properly. */ - else if (rc == FF_FREEZE && !deliver_manual_thaw) + else if (rc == FF_FREEZE && !f.deliver_manual_thaw) { - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); process_recipients = RECIP_DEFER; frozen_info = string_sprintf(" by the system filter%s%s", @@ -6107,26 +6195,26 @@ if (process_recipients != RECIP_IGNORE) new->dsn_flags = r->dsn_flags & rf_dsnflags; new->dsn_orcpt = r->orcpt; DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", - new->dsn_orcpt, new->dsn_flags); + new->dsn_orcpt ? new->dsn_orcpt : US"", new->dsn_flags); switch (process_recipients) { /* RECIP_DEFER is set when a system filter freezes a message. */ case RECIP_DEFER: - new->next = addr_defer; - addr_defer = new; - break; + new->next = addr_defer; + addr_defer = new; + break; /* RECIP_FAIL_FILTER is set when a system filter has obeyed a "fail" command. */ case RECIP_FAIL_FILTER: - new->message = - filter_message ? filter_message : US"delivery cancelled"; - setflag(new, af_pass_message); - goto RECIP_QUEUE_FAILED; /* below */ + new->message = + filter_message ? filter_message : US"delivery cancelled"; + setflag(new, af_pass_message); + goto RECIP_QUEUE_FAILED; /* below */ /* RECIP_FAIL_TIMEOUT is set when a message is frozen, but is older @@ -6136,15 +6224,15 @@ if (process_recipients != RECIP_IGNORE) been logged. */ case RECIP_FAIL_TIMEOUT: - new->message = US"delivery cancelled; message timed out"; - goto RECIP_QUEUE_FAILED; /* below */ + new->message = US"delivery cancelled; message timed out"; + goto RECIP_QUEUE_FAILED; /* below */ /* RECIP_FAIL is set when -Mg has been used. */ case RECIP_FAIL: - new->message = US"delivery cancelled by administrator"; - /* Fall through */ + new->message = US"delivery cancelled by administrator"; + /* Fall through */ /* Common code for the failure cases above. If this is not a bounce message, put the address on the failed list so that it is used to @@ -6152,11 +6240,11 @@ if (process_recipients != RECIP_IGNORE) The incident has already been logged. */ RECIP_QUEUE_FAILED: - if (sender_address[0] != 0) - { - new->next = addr_failed; - addr_failed = new; - } + if (sender_address[0]) + { + new->next = addr_failed; + addr_failed = new; + } break; @@ -6165,17 +6253,17 @@ if (process_recipients != RECIP_IGNORE) is a bounce message, it will get frozen. */ case RECIP_FAIL_LOOP: - new->message = US"Too many \"Received\" headers - suspected mail loop"; - post_process_one(new, FAIL, LOG_MAIN, EXIM_DTYPE_ROUTER, 0); - break; + new->message = US"Too many \"Received\" headers - suspected mail loop"; + post_process_one(new, FAIL, LOG_MAIN, EXIM_DTYPE_ROUTER, 0); + break; /* Value should be RECIP_ACCEPT; take this as the safe default. */ default: - if (!addr_new) addr_new = new; else addr_last->next = new; - addr_last = new; - break; + if (!addr_new) addr_new = new; else addr_last->next = new; + addr_last = new; + break; } #ifndef DISABLE_EVENT @@ -6183,17 +6271,23 @@ if (process_recipients != RECIP_IGNORE) { uschar * save_local = deliver_localpart; const uschar * save_domain = deliver_domain; + uschar * addr = new->address, * errmsg = NULL; + int start, end, dom; - deliver_localpart = expand_string( - string_sprintf("${local_part:%s}", new->address)); - deliver_domain = expand_string( - string_sprintf("${domain:%s}", new->address)); + if (!parse_extract_address(addr, &errmsg, &start, &end, &dom, TRUE)) + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to parse address '%.100s': %s\n", addr, errmsg); + else + { + deliver_localpart = + string_copyn(addr+start, dom ? (dom-1) - start : end - start); + deliver_domain = dom ? CUS string_copyn(addr+dom, end - dom) : CUS""; - (void) event_raise(event_action, - US"msg:fail:internal", new->message); + event_raise(event_action, US"msg:fail:internal", new->message); - deliver_localpart = save_local; - deliver_domain = save_domain; + deliver_localpart = save_local; + deliver_domain = save_domain; + } } #endif } @@ -6252,7 +6346,7 @@ deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE); . If new addresses have been generated by the routers, da capo. */ -header_rewritten = FALSE; /* No headers rewritten yet */ +f.header_rewritten = FALSE; /* No headers rewritten yet */ while (addr_new) /* Loop until all addresses dealt with */ { address_item *addr, *parent; @@ -6598,7 +6692,7 @@ while (addr_new) /* Loop until all addresses dealt with */ which keep the retry record fresh, which can lead to us perpetually deferring messages. */ - else if ( ( queue_running && !deliver_force + else if ( ( f.queue_running && !f.deliver_force || continue_hostname ) && ( ( domain_retry_record @@ -6642,7 +6736,7 @@ while (addr_new) /* Loop until all addresses dealt with */ those domains. During queue runs, queue_domains is forced to be unset. Optimize by skipping this pass through the addresses if nothing is set. */ - if (!deliver_force && queue_domains) + if (!f.deliver_force && queue_domains) { address_item *okaddr = NULL; while (addr_route) @@ -6936,14 +7030,14 @@ remember them for all subsequent deliveries. This can be delayed till later if there is only address to be delivered - if it succeeds the spool write need not happen. */ -if ( header_rewritten +if ( f.header_rewritten && ( addr_local && (addr_local->next || addr_remote) || addr_remote && addr_remote->next ) ) { /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); - header_rewritten = FALSE; + f.header_rewritten = FALSE; } @@ -7023,13 +7117,13 @@ if (addr_local) DEBUG(D_deliver|D_transport) debug_printf(">>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>\n"); do_local_deliveries(); - disable_logging = FALSE; + f.disable_logging = FALSE; } /* If queue_run_local is set, we do not want to attempt any remote deliveries, so just queue them all. */ -if (queue_run_local) +if (f.queue_run_local) while (addr_remote) { address_item *addr = addr_remote; @@ -7081,7 +7175,7 @@ if (addr_remote) if (remote_sort_domains) sort_remote_deliveries(); do_remote_deliveries(TRUE); } - disable_logging = FALSE; + f.disable_logging = FALSE; } @@ -7160,7 +7254,7 @@ retry cutoff time has expired for all alternative destinations. Bypass the updating of the database if the -N flag is set, which is a debugging thing that prevents actual delivery. */ -else if (!dont_deliver) +else if (!f.dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); /* Send DSN for successful messages if requested */ @@ -7176,11 +7270,12 @@ for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next) "DSN: envid: %s ret: %d\n" "DSN: Final recipient: %s\n" "DSN: Remote SMTP server supports DSN: %d\n", - addr_dsntmp->router->name, + addr_dsntmp->router ? addr_dsntmp->router->name : US"(unknown)", addr_dsntmp->address, sender_address, - addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags, - dsn_envid, dsn_ret, + addr_dsntmp->dsn_orcpt ? addr_dsntmp->dsn_orcpt : US"NULL", + addr_dsntmp->dsn_flags, + dsn_envid ? dsn_envid : US"NULL", dsn_ret, addr_dsntmp->address, addr_dsntmp->dsn_aware ); @@ -7190,7 +7285,6 @@ for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next) if ( ( addr_dsntmp->dsn_aware != dsn_support_yes || addr_dsntmp->dsn_flags & rf_dsnlasthop ) - && addr_dsntmp->dsn_flags & rf_dsnflags && addr_dsntmp->dsn_flags & rf_notify_success ) { @@ -7224,7 +7318,7 @@ if (addr_senddsn) } else /* Creation of child succeeded */ { - FILE *f = fdopen(fd, "wb"); + FILE * f = fdopen(fd, "wb"); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ uschar * bound; transport_ctx tctx = {{0}}; @@ -7257,11 +7351,9 @@ if (addr_senddsn) addr_dsntmp = addr_dsntmp->next) fprintf(f, "<%s> (relayed %s)\n\n", addr_dsntmp->address, - (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 - ? "via non DSN router" - : addr_dsntmp->dsn_aware == dsn_support_no - ? "to non-DSN-aware mailer" - : "via non \"Remote SMTP\" router" + addr_dsntmp->dsn_flags & rf_dsnlasthop ? "via non DSN router" + : addr_dsntmp->dsn_aware == dsn_support_no ? "to non-DSN-aware mailer" + : "via non \"Remote SMTP\" router" ); fprintf(f, "--%s\n" @@ -7296,7 +7388,7 @@ if (addr_senddsn) addr_dsntmp->host_used->name); else fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", - (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP"); + addr_dsntmp->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP"); } fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); @@ -7307,8 +7399,10 @@ if (addr_senddsn) /* Write the original email out */ - tctx.u.fd = fileno(f); + tctx.u.fd = fd; tctx.options = topt_add_return_path | topt_no_body; + /*XXX hmm, retval ignored. + Could error for any number of reasons, and they are not handled. */ transport_write_message(&tctx, 0); fflush(f); @@ -7339,9 +7433,9 @@ while (addr_failed) /* There are weird cases when logging is disabled in the transport. However, there may not be a transport (address failed by a router). */ - disable_logging = FALSE; + f.disable_logging = FALSE; if (addr_failed->transport) - disable_logging = addr_failed->transport->disable_logging; + f.disable_logging = addr_failed->transport->disable_logging; DEBUG(D_deliver) debug_printf("processing failed address %s\n", addr_failed->address); @@ -7377,14 +7471,16 @@ while (addr_failed) mark the recipient done. */ if ( addr_failed->prop.ignore_error - || ( addr_failed->dsn_flags & rf_dsnflags - && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure - ) ) + || addr_failed->dsn_flags & (rf_dsnflags & ~rf_notify_failure) + ) { addr = addr_failed; addr_failed = addr->next; if (addr->return_filename) Uunlink(addr->return_filename); +#ifndef DISABLE_EVENT + msg_event_raise(US"msg:fail:delivery", addr); +#endif log_write(0, LOG_MAIN, "%s%s%s%s: error ignored", addr->address, !addr->parent ? US"" : US" <", @@ -7423,8 +7519,8 @@ while (addr_failed) int filecount = 0; int rcount = 0; uschar *bcc, *emf_text; - FILE *f = fdopen(fd, "wb"); - FILE *emf = NULL; + FILE * fp = fdopen(fd, "wb"); + FILE * emf = NULL; BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0; int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) * DELIVER_IN_BUFFER_SIZE; @@ -7462,10 +7558,10 @@ while (addr_failed) if (testflag(addr, af_hide_child)) continue; if (rcount >= 50) { - fprintf(f, "\n"); + fprintf(fp, "\n"); rcount = 0; } - fprintf(f, "%s%s", + fprintf(fp, "%s%s", rcount++ == 0 ? "X-Failed-Recipients: " : ",\n ", @@ -7473,20 +7569,20 @@ while (addr_failed) ? string_printing(addr->parent->address) : string_printing(addr->address)); } - if (rcount > 0) fprintf(f, "\n"); + if (rcount > 0) fprintf(fp, "\n"); /* Output the standard headers */ if (errors_reply_to) - fprintf(f, "Reply-To: %s\n", errors_reply_to); - fprintf(f, "Auto-Submitted: auto-replied\n"); - moan_write_from(f); - fprintf(f, "To: %s\n", bounce_recipient); + fprintf(fp, "Reply-To: %s\n", errors_reply_to); + fprintf(fp, "Auto-Submitted: auto-replied\n"); + moan_write_from(fp); + fprintf(fp, "To: %s\n", bounce_recipient); /* generate boundary string and output MIME-Headers */ bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); - fprintf(f, "Content-Type: multipart/report;" + fprintf(fp, "Content-Type: multipart/report;" " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n", bound); @@ -7502,46 +7598,46 @@ while (addr_failed) /* Quietly copy to configured additional addresses if required. */ if ((bcc = moan_check_errorcopy(bounce_recipient))) - fprintf(f, "Bcc: %s\n", bcc); + fprintf(fp, "Bcc: %s\n", bcc); /* The texts for the message can be read from a template file; if there isn't one, or if it is too short, built-in texts are used. The first emf text is a Subject: and any other headers. */ if ((emf_text = next_emf(emf, US"header"))) - fprintf(f, "%s\n", emf_text); + fprintf(fp, "%s\n", emf_text); else - fprintf(f, "Subject: Mail delivery failed%s\n\n", + fprintf(fp, "Subject: Mail delivery failed%s\n\n", to_sender? ": returning message to sender" : ""); /* output human readable part as text/plain section */ - fprintf(f, "--%s\n" + fprintf(fp, "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n", bound); if ((emf_text = next_emf(emf, US"intro"))) - fprintf(f, "%s", CS emf_text); + fprintf(fp, "%s", CS emf_text); else { - fprintf(f, + fprintf(fp, /* This message has been reworded several times. It seems to be confusing to somebody, however it is worded. I have retreated to the original, simple wording. */ "This message was created automatically by mail delivery software.\n"); if (bounce_message_text) - fprintf(f, "%s", CS bounce_message_text); + fprintf(fp, "%s", CS bounce_message_text); if (to_sender) - fprintf(f, + fprintf(fp, "\nA message that you sent could not be delivered to one or more of its\n" "recipients. This is a permanent error. The following address(es) failed:\n"); else - fprintf(f, + fprintf(fp, "\nA message sent by\n\n <%s>\n\n" "could not be delivered to one or more of its recipients. The following\n" "address(es) failed:\n", sender_address); } - fputc('\n', f); + fputc('\n', fp); /* Process the addresses, leaving them on the msgchain if they have a file name for a return message. (There has already been a check in @@ -7552,12 +7648,12 @@ wording. */ paddr = &msgchain; for (addr = msgchain; addr; addr = *paddr) { - if (print_address_information(addr, f, US" ", US"\n ", US"")) - print_address_error(addr, f, US""); + if (print_address_information(addr, fp, US" ", US"\n ", US"")) + print_address_error(addr, fp, US""); /* End the final line for the address */ - fputc('\n', f); + fputc('\n', fp); /* Leave on msgchain if there's a return file. */ @@ -7578,7 +7674,7 @@ wording. */ } } - fputc('\n', f); + fputc('\n', fp); /* Get the next text, whether we need it or not, so as to be positioned for the one after. */ @@ -7597,9 +7693,9 @@ wording. */ address_item *nextaddr; if (emf_text) - fprintf(f, "%s", CS emf_text); + fprintf(fp, "%s", CS emf_text); else - fprintf(f, + fprintf(fp, "The following text was generated during the delivery " "attempt%s:\n", (filecount > 1)? "s" : ""); @@ -7610,24 +7706,24 @@ wording. */ /* List all the addresses that relate to this file */ - fputc('\n', f); + fputc('\n', fp); while(addr) /* Insurance */ { - print_address_information(addr, f, US"------ ", US"\n ", + print_address_information(addr, fp, US"------ ", US"\n ", US" ------\n"); if (addr->return_filename) break; addr = addr->next; } - fputc('\n', f); + fputc('\n', fp); /* Now copy the file */ if (!(fm = Ufopen(addr->return_filename, "rb"))) - fprintf(f, " +++ Exim error... failed to open text file: %s\n", + fprintf(fp, " +++ Exim error... failed to open text file: %s\n", strerror(errno)); else { - while ((ch = fgetc(fm)) != EOF) fputc(ch, f); + while ((ch = fgetc(fm)) != EOF) fputc(ch, fp); (void)fclose(fm); } Uunlink(addr->return_filename); @@ -7639,19 +7735,19 @@ wording. */ addr->next = handled_addr; handled_addr = topaddr; } - fputc('\n', f); + fputc('\n', fp); } /* output machine readable part */ #ifdef SUPPORT_I18N if (message_smtputf8) - fprintf(f, "--%s\n" + fprintf(fp, "--%s\n" "Content-type: message/global-delivery-status\n\n" "Reporting-MTA: dns; %s\n", bound, smtp_active_hostname); else #endif - fprintf(f, "--%s\n" + fprintf(fp, "--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", bound, smtp_active_hostname); @@ -7661,40 +7757,42 @@ wording. */ /* must be decoded from xtext: see RFC 3461:6.3a */ uschar *xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) - fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); + fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); + fprintf(fp, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); } - fputc('\n', f); + fputc('\n', fp); for (addr = handled_addr; addr; addr = addr->next) { host_item * hu; - fprintf(f, "Action: failed\n" + fprintf(fp, "Action: failed\n" "Final-Recipient: rfc822;%s\n" "Status: 5.0.0\n", addr->address); if ((hu = addr->host_used) && hu->name) { - const uschar * s; - fprintf(f, "Remote-MTA: dns; %s\n", hu->name); + fprintf(fp, "Remote-MTA: dns; %s\n", hu->name); #ifdef EXPERIMENTAL_DSN_INFO + { + const uschar * s; if (hu->address) { uschar * p = hu->port == 25 ? US"" : string_sprintf(":%d", hu->port); - fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); + fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); } if ((s = addr->smtp_greeting) && *s) - fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s); + fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s); if ((s = addr->helo_response) && *s) - fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s); + fprintf(fp, "X-Remote-MTA-helo-response: X-str; %s\n", s); if ((s = addr->message) && *s) - fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s); + fprintf(fp, "X-Exim-Diagnostic: X-str; %s\n", s); + } #endif - print_dsn_diagnostic_code(addr, f); + print_dsn_diagnostic_code(addr, fp); } - fputc('\n', f); + fputc('\n', fp); } /* Now copy the message, trying to give an intelligible comment if @@ -7715,7 +7813,7 @@ wording. */ bounce_return_size_limit is always honored. */ - fprintf(f, "--%s\n", bound); + fprintf(fp, "--%s\n", bound); dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; dsnnotifyhdr = NULL; @@ -7753,44 +7851,45 @@ wording. */ if (message_smtputf8) fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" : "Content-type: message/global\n\n", - f); + fp); else #endif fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" : "Content-type: message/rfc822\n\n", - f); + fp); - fflush(f); + fflush(fp); transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ { /* Dummy transport for headers add */ transport_ctx tctx = {{0}}; transport_instance tb = {0}; - tctx.u.fd = fileno(f); + tctx.u.fd = fileno(fp); tctx.tblock = &tb; tctx.options = topt; tb.add_headers = dsnnotifyhdr; + /*XXX no checking for failure! buggy! */ transport_write_message(&tctx, 0); } - fflush(f); + fflush(fp); /* we never add the final text. close the file */ if (emf) (void)fclose(emf); - fprintf(f, "\n--%s--\n", bound); + fprintf(fp, "\n--%s--\n", bound); /* Close the file, which should send an EOF to the child process that is receiving the message. Wait for it to finish. */ - (void)fclose(f); + (void)fclose(fp); rc = child_close(pid, 0); /* Waits for child to close, no timeout */ /* In the test harness, let the child do it's thing first. */ - if (running_in_test_harness) millisleep(500); + if (f.running_in_test_harness) millisleep(500); /* If the process failed, there was some disaster in setting up the error message. Unless the message is very old, ensure that addr_defer @@ -7807,7 +7906,7 @@ wording. */ if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer) { addr_defer = (address_item *)(+1); - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); @@ -7836,7 +7935,7 @@ wording. */ } } -disable_logging = FALSE; /* In case left set */ +f.disable_logging = FALSE; /* In case left set */ /* Come here from the mua_wrapper case if routing goes wrong */ @@ -7889,13 +7988,12 @@ if (!addr_defer) /* Log the end of this message, with queue time if requested. */ if (LOGGING(queue_time_overall)) - log_write(0, LOG_MAIN, "Completed QT=%s", - string_timesince(&received_time)); + log_write(0, LOG_MAIN, "Completed QT=%s", string_timesince(&received_time)); else log_write(0, LOG_MAIN, "Completed"); /* Unset deliver_freeze so that we won't try to move the spool files further down */ - deliver_freeze = FALSE; + f.deliver_freeze = FALSE; #ifndef DISABLE_EVENT (void) event_raise(event_action, US"msg:complete", NULL); @@ -8013,10 +8111,10 @@ else if (addr_defer != (address_item *)(+1)) is not sent. Another attempt will be made at the next delivery attempt (if it also defers). */ - if ( !queue_2stage + if ( !f.queue_2stage && delivery_attempted - && ( ((addr_defer->dsn_flags & rf_dsnflags) == 0) - || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay + && ( !(addr_defer->dsn_flags & rf_dsnflags) + || addr_defer->dsn_flags & rf_notify_delay ) && delay_warning[1] > 0 && sender_address[0] != 0 @@ -8035,7 +8133,7 @@ else if (addr_defer != (address_item *)(+1)) time off the list. In queue runs, the list pointer gets updated in the calling process. */ - if (running_in_test_harness && fudged_queue_times[0] != 0) + if (f.running_in_test_harness && fudged_queue_times[0] != 0) { int qt = readconf_readtime(fudged_queue_times, '/', FALSE); if (qt >= 0) @@ -8239,6 +8337,7 @@ else if (addr_defer != (address_item *)(+1)) return_path = sender_address; /* In case not previously set */ /* Write the original email out */ + /*XXX no checking for failure! buggy! */ transport_write_message(&tctx, 0); fflush(f); @@ -8266,9 +8365,9 @@ else if (addr_defer != (address_item *)(+1)) /* If this was a first delivery attempt, unset the first time flag, and ensure that the spool gets updated. */ - if (deliver_firsttime) + if (f.deliver_firsttime) { - deliver_firsttime = FALSE; + f.deliver_firsttime = FALSE; update_spool = TRUE; } @@ -8279,9 +8378,9 @@ else if (addr_defer != (address_item *)(+1)) For the "tell" message, we turn \n back into newline. Also, insert a newline near the start instead of the ": " string. */ - if (deliver_freeze) + if (f.deliver_freeze) { - if (freeze_tell && freeze_tell[0] != 0 && !local_error_message) + if (freeze_tell && freeze_tell[0] != 0 && !f.local_error_message) { uschar *s = string_copy(frozen_info); uschar *ss = Ustrstr(s, " by the system filter: "); @@ -8322,9 +8421,9 @@ else if (addr_defer != (address_item *)(+1)) DEBUG(D_deliver) debug_printf("delivery deferred: update_spool=%d header_rewritten=%d\n", - update_spool, header_rewritten); + update_spool, f.header_rewritten); - if (update_spool || header_rewritten) + if (update_spool || f.header_rewritten) /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); } @@ -8359,7 +8458,7 @@ if (remove_journal) /* Move the message off the spool if requested */ #ifdef SUPPORT_MOVE_FROZEN_MESSAGES - if (deliver_freeze && move_frozen_messages) + if (f.deliver_freeze && move_frozen_messages) (void)spool_move_message(id, message_subdir, US"", US"F"); #endif } @@ -8391,7 +8490,7 @@ deliver_init(void) #ifdef EXIM_TFO_PROBE tfo_probe(); #else -tcp_fastopen_ok = TRUE; +f.tcp_fastopen_ok = TRUE; #endif @@ -8402,12 +8501,16 @@ if (!regex_SIZE) regex_SIZE = regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE); if (!regex_AUTH) regex_AUTH = - regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", - FALSE, TRUE); + regex_must_compile(AUTHS_REGEX, FALSE, TRUE); #ifdef SUPPORT_TLS if (!regex_STARTTLS) regex_STARTTLS = regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); + +# ifdef EXPERIMENTAL_REQUIRETLS +if (!regex_REQUIRETLS) regex_REQUIRETLS = + regex_must_compile(US"\\n250[\\s\\-]REQUIRETLS(\\s|\\n|$)", FALSE, TRUE); +# endif #endif if (!regex_CHUNKING) regex_CHUNKING = @@ -8428,6 +8531,11 @@ if (!regex_DSN) regex_DSN = if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); + +#ifdef EXPERIMENTAL_PIPE_CONNECT +if (!regex_EARLY_PIPE) regex_EARLY_PIPE = + regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE); +#endif } @@ -8437,17 +8545,17 @@ deliver_get_sender_address (uschar * id) int rc; uschar * new_sender_address, * save_sender_address; -BOOL save_qr = queue_running; +BOOL save_qr = f.queue_running; uschar * spoolname; /* make spool_open_datafile non-noisy on fail */ -queue_running = TRUE; +f.queue_running = TRUE; /* Side effect: message_subdir is set for the (possibly split) spool directory */ deliver_datafile = spool_open_datafile(id); -queue_running = save_qr; +f.queue_running = save_qr; if (deliver_datafile < 0) return NULL; @@ -8483,9 +8591,9 @@ delivery_re_exec(int exec_type) { uschar * where; -if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) +if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only) { - int pfd[2], channel_fd = cutthrough.fd, pid; + int channel_fd = cutthrough.cctx.sock; smtp_peer_options = cutthrough.peer_options; continue_sequence = 0; @@ -8493,6 +8601,8 @@ if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) #ifdef SUPPORT_TLS if (cutthrough.is_tls) { + int pfd[2], pid; + smtp_peer_options |= OPTION_TLS; sending_ip_address = cutthrough.snd_ip; sending_port = cutthrough.snd_port; @@ -8507,13 +8617,13 @@ if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) else if (pid == 0) /* child: fork again to totally disconnect */ { - close(pfd[1]); - if ((pid = fork())) - _exit(pid ? EXIT_FAILURE : EXIT_SUCCESS); - smtp_proxy_tls(big_buffer, big_buffer_size, pfd[0], 5*60); - exim_exit(0); + if (f.running_in_test_harness) millisleep(100); /* let parent debug out */ + /* does not return */ + smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size, + pfd, 5*60); } + DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid); close(pfd[0]); waitpid(pid, NULL, 0); (void) close(channel_fd); /* release the client socket */ @@ -8531,6 +8641,7 @@ else } return; /* compiler quietening; control does not reach here. */ +#ifdef SUPPORT_TLS fail: log_write(0, LOG_MAIN | (exec_type == CEE_EXEC_EXIT ? LOG_PANIC : LOG_PANIC_DIE), @@ -8540,6 +8651,7 @@ fail: Note: this must be _exit(), not exit(). */ _exit(EX_EXECFAILED); +#endif } /* vi: aw ai sw=2