X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fdeliver.c;h=b5aa9b9a75d1a211622ca73b0a3b75dc6ad3f9f3;hp=4462d5c4af9c81f5891ab4169ace2eb571f67c54;hb=dadff1d47e54962b0fdf98e8ce5cef42b6cb7fb5;hpb=74954688e4f61475b2451b2cb22045ab8e7f0150 diff --git a/src/src/deliver.c b/src/src/deliver.c index 4462d5c4a..b5aa9b9a7 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2,13 +2,14 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The main code for delivering a message. */ #include "exim.h" +#include /* Data block for keeping track of subprocesses for parallel remote @@ -63,10 +64,8 @@ static address_item *addr_new = NULL; static address_item *addr_remote = NULL; static address_item *addr_route = NULL; static address_item *addr_succeed = NULL; -#ifdef EXPERIMENTAL_DSN static address_item *addr_dsntmp = NULL; static address_item *addr_senddsn = NULL; -#endif static FILE *message_log = NULL; static BOOL update_spool; @@ -129,7 +128,7 @@ deliver_set_expansions(address_item *addr) { if (addr == NULL) { - uschar ***p = address_expansions; + const uschar ***p = address_expansions; while (*p != NULL) **p++ = NULL; return; } @@ -151,9 +150,9 @@ else } deliver_recipients = addr; -deliver_address_data = addr->p.address_data; -deliver_domain_data = addr->p.domain_data; -deliver_localpart_data = addr->p.localpart_data; +deliver_address_data = addr->prop.address_data; +deliver_domain_data = addr->prop.domain_data; +deliver_localpart_data = addr->prop.localpart_data; /* These may be unset for multiple addresses */ @@ -678,67 +677,105 @@ while (addr->parent != NULL) +/************************************************* +* Delivery logging support functions * +*************************************************/ + +/* The LOGGING() checks in d_log_interface() are complicated for backwards +compatibility. When outgoing interface logging was originally added, it was +conditional on just incoming_interface (which is off by default). The +outgoing_interface option is on by default to preserve this behaviour, but +you can enable incoming_interface and disable outgoing_interface to get I= +fields on incoming lines only. + +Arguments: + s The log line buffer + sizep Pointer to the buffer size + ptrp Pointer to current index into buffer + addr The address to be logged + +Returns: New value for s +*/ + +static uschar * +d_log_interface(uschar *s, int *sizep, int *ptrp) +{ +if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) + && sending_ip_address != NULL) + { + s = string_append(s, sizep, ptrp, 2, US" I=[", sending_ip_address); + if (LOGGING(outgoing_port)) + s = string_append(s, sizep, ptrp, 2, US"]:", + string_sprintf("%d", sending_port)); + else + s = string_cat(s, sizep, ptrp, "]", 1); + } +return s; +} + + static uschar * -d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr) +d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr) { - s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, - US" [", addr->host_used->address, US"]"); - if ((log_extra_selector & LX_outgoing_port) != 0) - s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", - addr->host_used->port)); - return s; +s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, + US" [", addr->host_used->address, US"]"); +if (LOGGING(outgoing_port)) + s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", + addr->host_used->port)); +return d_log_interface(s, sizep, ptrp); } + + #ifdef SUPPORT_TLS static uschar * d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) { - if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL) - s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); - if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - addr->cipher != NULL) - s = string_append(s, sizep, ptrp, 2, US" CV=", - testflag(addr, af_cert_verified) - ? +if (LOGGING(tls_cipher) && addr->cipher != NULL) + s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); +if (LOGGING(tls_certificate_verified) && addr->cipher != NULL) + s = string_append(s, sizep, ptrp, 2, US" CV=", + testflag(addr, af_cert_verified) + ? #ifdef EXPERIMENTAL_DANE - testflag(addr, af_dane_verified) - ? "dane" - : + testflag(addr, af_dane_verified) + ? "dane" + : #endif - "yes" - : "no"); - if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL) - s = string_append(s, sizep, ptrp, 3, US" DN=\"", - string_printing(addr->peerdn), US"\""); - return s; + "yes" + : "no"); +if (LOGGING(tls_peerdn) && addr->peerdn != NULL) + s = string_append(s, sizep, ptrp, 3, US" DN=\"", + string_printing(addr->peerdn), US"\""); +return s; } #endif -#ifdef EXPERIMENTAL_TPDA -int -tpda_raise_event(uschar * action, uschar * event, uschar * ev_data) +#ifdef EXPERIMENTAL_EVENT +uschar * +event_raise(uschar * action, const uschar * event, uschar * ev_data) { uschar * s; if (action) { DEBUG(D_deliver) - debug_printf("TPDA(%s): tpda_event_action=|%s| tpda_delivery_IP=%s\n", + debug_printf("Event(%s): event_action=|%s| delivery_IP=%s\n", event, action, deliver_host_address); - tpda_event = event; - tpda_data = ev_data; + event_name = event; + event_data = ev_data; if (!(s = expand_string(action)) && *expand_string_message) log_write(0, LOG_MAIN|LOG_PANIC, - "failed to expand tpda_event_action %s in %s: %s\n", + "failed to expand event_action %s in %s: %s\n", event, transport_name, expand_string_message); - tpda_event = tpda_data = NULL; + event_name = event_data = NULL; /* If the expansion returns anything but an empty string, flag for the caller to modify his normal processing @@ -746,19 +783,19 @@ if (action) if (s && *s) { DEBUG(D_deliver) - debug_printf("TPDA(%s): event_action returned \"%s\"\n", event, s); - return DEFER; + debug_printf("Event(%s): event_action returned \"%s\"\n", event, s); + return s; } } -return OK; +return NULL; } static void -tpda_msg_event(uschar * event, address_item * addr) +msg_event_raise(const uschar * event, const address_item * addr) { -uschar * save_domain = deliver_domain; +const uschar * save_domain = deliver_domain; uschar * save_local = deliver_localpart; -uschar * save_host = deliver_host; +const uschar * save_host = deliver_host; if (!addr->transport) return; @@ -769,7 +806,7 @@ deliver_domain = addr->domain; deliver_localpart = addr->local_part; deliver_host = addr->host_used ? addr->host_used->name : NULL; -(void) tpda_raise_event(addr->transport->tpda_event_action, event, +(void) event_raise(addr->transport->event_action, event, addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 ? addr->message : NULL); @@ -778,7 +815,7 @@ deliver_localpart = save_local; deliver_domain = save_domain; router_name = transport_name = NULL; } -#endif /*EXPERIMENTAL_TPDA*/ +#endif /*EXPERIMENTAL_EVENT*/ @@ -803,14 +840,14 @@ the log line, and reset the store afterwards. Remote deliveries should always have a pointer to the host item that succeeded; local deliveries can have a pointer to a single host item in their host list, for use by the transport. */ -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT /* presume no successful remote delivery */ lookup_dnssec_authenticated = NULL; #endif s = reset_point = store_get(size); -log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE); +log_address = string_log_address(addr, LOGGING(all_parents), TRUE); if (msg) s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address); else @@ -819,12 +856,19 @@ else s = string_append(s, &size, &ptr, 2, US"> ", log_address); } -if ((log_extra_selector & LX_sender_on_delivery) != 0 || msg) - s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); +if (LOGGING(sender_on_delivery) || msg) + s = string_append(s, &size, &ptr, 3, US" F=<", +#ifdef EXPERIMENTAL_INTERNATIONAL + testflag(addr, af_utf8_downcvt) + ? string_address_utf8_to_alabel(sender_address, NULL) + : +#endif + sender_address, + US">"); #ifdef EXPERIMENTAL_SRS -if(addr->p.srs_sender) - s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->p.srs_sender, US">"); +if(addr->prop.srs_sender) + s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->prop.srs_sender, US">"); #endif /* You might think that the return path must always be set for a successful @@ -832,8 +876,7 @@ delivery; indeed, I did for some time, until this statement crashed. The case when it is not set is for a delivery to /dev/null which is optimised by not being run at all. */ -if (used_return_path != NULL && - (log_extra_selector & LX_return_path_on_delivery) != 0) +if (used_return_path != NULL && LOGGING(return_path_on_delivery)) s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); if (msg) @@ -845,7 +888,7 @@ if (addr->router != NULL) s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); -if ((log_extra_selector & LX_delivery_size) != 0) +if (LOGGING(delivery_size)) s = string_append(s, &size, &ptr, 2, US" S=", string_sprintf("%d", transport_count)); @@ -855,6 +898,7 @@ if (addr->transport->info->local) { if (addr->host_list) s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); + s = d_log_interface(s, &size, &ptr); if (addr->shadow_message != NULL) s = string_cat(s, &size, &ptr, addr->shadow_message, Ustrlen(addr->shadow_message)); @@ -870,7 +914,7 @@ else if (continue_sequence > 1) s = string_cat(s, &size, &ptr, US"*", 1); -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT deliver_host_address = addr->host_used->address; deliver_host_port = addr->host_used->port; deliver_host = addr->host_used->name; @@ -892,7 +936,7 @@ else if (addr->auth_id) { s = string_append(s, &size, &ptr, 2, US":", addr->auth_id); - if (log_extra_selector & LX_smtp_mailauth && addr->auth_sndr) + if (LOGGING(smtp_mailauth) && addr->auth_sndr) s = string_append(s, &size, &ptr, 2, US":", addr->auth_sndr); } } @@ -905,15 +949,15 @@ else /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ -if (log_extra_selector & LX_smtp_confirmation && - addr->message && +if (LOGGING(smtp_confirmation) && addr->message && (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)) { - int i; + unsigned i; + unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024; uschar *p = big_buffer; uschar *ss = addr->message; *p++ = '\"'; - for (i = 0; i < 256 && ss[i] != 0; i++) /* limit logged amount */ + for (i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */ { if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */ *p++ = ss[i]; @@ -925,11 +969,11 @@ if (log_extra_selector & LX_smtp_confirmation && /* Time on queue and actual time taken to deliver */ -if ((log_extra_selector & LX_queue_time) != 0) +if (LOGGING(queue_time)) s = string_append(s, &size, &ptr, 2, US" QT=", readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); -if ((log_extra_selector & LX_deliver_time) != 0) +if (LOGGING(deliver_time)) s = string_append(s, &size, &ptr, 2, US" DT=", readconf_printtime(addr->more_errno)); @@ -939,8 +983,8 @@ store we used to build the line after writing it. */ s[ptr] = 0; log_write(0, flags, "%s", s); -#ifdef EXPERIMENTAL_TPDA -if (!msg) tpda_msg_event(US"msg:delivery", addr); +#ifdef EXPERIMENTAL_EVENT +if (!msg) msg_event_raise(US"msg:delivery", addr); #endif store_reset(reset_point); @@ -1016,7 +1060,10 @@ malformed, it won't ever have gone near LDAP.) */ if (addr->message != NULL) { - addr->message = string_printing(addr->message); + const uschar * s = string_printing(addr->message); + if (s != addr->message) + addr->message = US s; + /* deconst cast ok as string_printing known to have alloc'n'copied */ if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && (Ustrstr(addr->message, "mysql") != NULL || Ustrstr(addr->message, "pgsql") != NULL || @@ -1073,11 +1120,12 @@ if (addr->return_file >= 0 && addr->return_filename != NULL) if (s != NULL) { uschar *p = big_buffer + Ustrlen(big_buffer); + const uschar * sp; while (p > big_buffer && isspace(p[-1])) p--; *p = 0; - s = string_printing(big_buffer); + sp = string_printing(big_buffer); log_write(0, LOG_MAIN, "<%s>: %s transport output: %s", - addr->address, tb->name, s); + addr->address, tb->name, sp); } (void)fclose(f); } @@ -1086,7 +1134,7 @@ if (addr->return_file >= 0 && addr->return_filename != NULL) /* Handle returning options, but only if there is an address to return the text to. */ - if (sender_address[0] != 0 || addr->p.errors_address != NULL) + if (sender_address[0] != 0 || addr->prop.errors_address != NULL) { if (tb->return_output) { @@ -1138,7 +1186,7 @@ if (result == OK) child_done(addr, now); } - /* Certificates for logging (via TPDA) */ + /* Certificates for logging (via events) */ #ifdef SUPPORT_TLS tls_out.ourcert = addr->ourcert; addr->ourcert = NULL; @@ -1156,16 +1204,8 @@ if (result == OK) delivery_log(LOG_MAIN, addr, logchar, NULL); #ifdef SUPPORT_TLS - if (tls_out.ourcert) - { - tls_free_cert(tls_out.ourcert); - tls_out.ourcert = NULL; - } - if (tls_out.peercert) - { - tls_free_cert(tls_out.peercert); - tls_out.peercert = NULL; - } + tls_free_cert(&tls_out.ourcert); + tls_free_cert(&tls_out.peercert); tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; @@ -1224,8 +1264,7 @@ else if (result == DEFER || result == PANIC) /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ - log_address = string_log_address(addr, - (log_write_selector & L_all_parents) != 0, result == OK); + log_address = string_log_address(addr, LOGGING(all_parents), result == OK); s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); @@ -1303,7 +1342,7 @@ else if (!testflag(addr, af_ignore_error) && (addr->special_action == SPECIAL_FREEZE || - (sender_address[0] == 0 && addr->p.errors_address == NULL) + (sender_address[0] == 0 && addr->prop.errors_address == NULL) )) { frozen_info = (addr->special_action == SPECIAL_FREEZE)? US"" : @@ -1336,18 +1375,16 @@ else /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ - log_address = string_log_address(addr, - (log_write_selector & L_all_parents) != 0, result == OK); + log_address = string_log_address(addr, LOGGING(all_parents), result == OK); s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); - if ((log_extra_selector & LX_sender_on_delivery) != 0) + if (LOGGING(sender_on_delivery)) s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); /* Return path may not be set if no delivery actually happened */ - if (used_return_path != NULL && - (log_extra_selector & LX_return_path_on_delivery) != 0) + if (used_return_path != NULL && LOGGING(return_path_on_delivery)) s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); if (addr->router != NULL) @@ -1381,8 +1418,8 @@ else log_write(0, LOG_MAIN, "** %s", s); -#ifdef EXPERIMENTAL_TPDA - tpda_msg_event(US"msg:fail:delivery", addr); +#ifdef EXPERIMENTAL_EVENT + msg_event_raise(US"msg:fail:delivery", addr); #endif store_reset(reset_point); @@ -1803,11 +1840,11 @@ transport_instance *tp = addr->transport; /* Set up the return path from the errors or sender address. If the transport has its own return path setting, expand it and replace the existing value. */ -if(addr->p.errors_address != NULL) - return_path = addr->p.errors_address; +if(addr->prop.errors_address != NULL) + return_path = addr->prop.errors_address; #ifdef EXPERIMENTAL_SRS -else if(addr->p.srs_sender != NULL) - return_path = addr->p.srs_sender; +else if(addr->prop.srs_sender != NULL) + return_path = addr->prop.srs_sender; #endif else return_path = sender_address; @@ -2419,9 +2456,9 @@ while (addr_local != NULL) (addr->flags & (af_pfr|af_file)) == (next->flags & (af_pfr|af_file)) && (!uses_lp || Ustrcmp(next->local_part, addr->local_part) == 0) && (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) && - same_strings(next->p.errors_address, addr->p.errors_address) && - same_headers(next->p.extra_headers, addr->p.extra_headers) && - same_strings(next->p.remove_headers, addr->p.remove_headers) && + same_strings(next->prop.errors_address, addr->prop.errors_address) && + same_headers(next->prop.extra_headers, addr->prop.extra_headers) && + same_strings(next->prop.remove_headers, addr->prop.remove_headers) && same_ugid(tp, addr, next) && ((addr->host_list == NULL && next->host_list == NULL) || (addr->host_list != NULL && next->host_list != NULL && @@ -2777,7 +2814,7 @@ sort_remote_deliveries(void) { int sep = 0; address_item **aptr = &addr_remote; -uschar *listptr = remote_sort_domains; +const uschar *listptr = remote_sort_domains; uschar *pattern; uschar patbuf[256]; @@ -2792,7 +2829,7 @@ while (*aptr != NULL && { address_item **next; deliver_domain = (*aptr)->domain; /* set $domain */ - if (match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1, + if (match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK) { aptr = &((*aptr)->next); @@ -2802,7 +2839,7 @@ while (*aptr != NULL && next = &((*aptr)->next); while (*next != NULL && (deliver_domain = (*next)->domain, /* Set $domain */ - match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1, + match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK) next = &((*next)->next); @@ -3121,15 +3158,17 @@ while (!done) break; case '2': - addr->peercert = NULL; if (*ptr) (void) tls_import_cert(ptr, &addr->peercert); + else + addr->peercert = NULL; break; case '3': - addr->ourcert = NULL; if (*ptr) (void) tls_import_cert(ptr, &addr->ourcert); + else + addr->ourcert = NULL; break; # ifndef DISABLE_OCSP @@ -3166,14 +3205,12 @@ while (!done) break; #endif -#ifdef EXPERIMENTAL_DSN case 'D': if (addr == NULL) goto ADDR_MISMATCH; memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); ptr += sizeof(addr->dsn_aware); DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); break; -#endif case 'A': if (addr == NULL) @@ -3223,6 +3260,14 @@ while (!done) addr = addr->next; break; + /* Local interface address/port */ + case 'I': + if (*ptr) sending_ip_address = string_copy(ptr); + while (*ptr++) ; + if (*ptr) sending_port = atoi(CS ptr); + while (*ptr++) ; + break; + /* Z marks the logical end of the data. It is followed by '0' if continue_transport was NULL at the end of transporting, otherwise '1'. We need to know when it becomes NULL during a delivery down a passed SMTP @@ -3844,9 +3889,20 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) } /* Get the flag which specifies whether the transport can handle different - domains that nevertheless resolve to the same set of hosts. */ - - multi_domain = tp->multi_domain; + domains that nevertheless resolve to the same set of hosts. If it needs + expanding, get variables set: $address_data, $domain_data, $localpart_data, + $host, $host_address, $host_port. */ + if (tp->expand_multi_domain) + deliver_set_expansions(addr); + + if (exp_bool(addr, US"transport", tp->name, D_transport, + US"multi_domain", tp->multi_domain, tp->expand_multi_domain, + &multi_domain) != OK) + { + deliver_set_expansions(NULL); + remote_post_process(addr, LOG_MAIN|LOG_PANIC, addr->message, fallback); + continue; + } /* Get the maximum it can handle in one envelope, with zero meaning unlimited, which is forced for the MUA wrapper case. */ @@ -3915,26 +3971,35 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) entirely different domains. The host list pointers can be NULL in the case where the hosts are defined in the transport. There is also a configured maximum limit of addresses that can be handled at once (see comments above - for how it is computed). */ + for how it is computed). + If the transport does not handle multiple domains, enforce that also, + and if it might need a per-address check for this, re-evaluate it. + */ while ((next = *anchor) != NULL && address_count < address_count_max) { - if ((multi_domain || Ustrcmp(next->domain, addr->domain) == 0) - && - tp == next->transport - && - same_hosts(next->host_list, addr->host_list) - && - same_strings(next->p.errors_address, addr->p.errors_address) - && - same_headers(next->p.extra_headers, addr->p.extra_headers) - && - same_ugid(tp, next, addr) - && - (next->p.remove_headers == addr->p.remove_headers || - (next->p.remove_headers != NULL && - addr->p.remove_headers != NULL && - Ustrcmp(next->p.remove_headers, addr->p.remove_headers) == 0))) + BOOL md; + if ( (multi_domain || Ustrcmp(next->domain, addr->domain) == 0) + && tp == next->transport + && same_hosts(next->host_list, addr->host_list) + && same_strings(next->prop.errors_address, addr->prop.errors_address) + && same_headers(next->prop.extra_headers, addr->prop.extra_headers) + && same_ugid(tp, next, addr) + && ( next->prop.remove_headers == addr->prop.remove_headers + || ( next->prop.remove_headers + && addr->prop.remove_headers + && Ustrcmp(next->prop.remove_headers, addr->prop.remove_headers) == 0 + ) ) + && ( !multi_domain + || ( ( + !tp->expand_multi_domain || (deliver_set_expansions(next), 1), + exp_bool(addr, + US"transport", next->transport->name, D_transport, + US"multi_domain", next->transport->multi_domain, + next->transport->expand_multi_domain, &md) == OK + ) + && md + ) ) ) { *anchor = next->next; next->next = NULL; @@ -3944,6 +4009,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) address_count++; } else anchor = &(next->next); + deliver_set_expansions(NULL); } /* If we are acting as an MUA wrapper, all addresses must go in a single @@ -3966,11 +4032,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Compute the return path, expanding a new one if required. The old one must be set first, as it might be referred to in the expansion. */ - if(addr->p.errors_address != NULL) - return_path = addr->p.errors_address; + if(addr->prop.errors_address != NULL) + return_path = addr->prop.errors_address; #ifdef EXPERIMENTAL_SRS - else if(addr->p.srs_sender != NULL) - return_path = addr->p.srs_sender; + else if(addr->prop.srs_sender != NULL) + return_path = addr->prop.srs_sender; #endif else return_path = sender_address; @@ -4349,11 +4415,9 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, 'P', '0', NULL, 0); #endif -#ifdef EXPERIMENTAL_DSN memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware)); rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware)); DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); -#endif /* Retry information: for most success cases this will be null. */ @@ -4415,6 +4479,18 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer); } + /* Local interface address/port */ + if (LOGGING(incoming_interface) && sending_ip_address) + { + uschar * ptr = big_buffer; + sprintf(CS ptr, "%.128s", sending_ip_address); + while(*ptr++); + sprintf(CS ptr, "%d", sending_port); + while(*ptr++); + + rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer); + } + /* Add termination flag, close the pipe, and that's it. The character after 'Z' indicates whether continue_transport is now NULL or not. A change from non-NULL to NULL indicates a problem with a continuing @@ -4541,7 +4617,7 @@ if (percent_hack_domains != NULL) deliver_domain = addr->domain; /* set $domain */ - while ((rc = match_isinlist(deliver_domain, &percent_hack_domains, 0, + while ((rc = match_isinlist(deliver_domain, (const uschar **)&percent_hack_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) == OK && (t = Ustrrchr(local_part, '%')) != NULL) @@ -4764,15 +4840,12 @@ print_address_error(address_item *addr, FILE *f, uschar *t) int count = Ustrlen(t); uschar *s = testflag(addr, af_pass_message)? addr->message : NULL; -if (s == NULL) - { - if (addr->user_message != NULL) s = addr->user_message; else return; - } +if (!s && !(s = addr->user_message)) + return; fprintf(f, "\n %s", t); -while (*s != 0) - { +while (*s) if (*s == '\\' && s[1] == 'n') { fprintf(f, "\n "); @@ -4789,12 +4862,59 @@ while (*s != 0) count = 0; } } - } } +/*********************************************************** +* Print Diagnostic-Code for an address * +************************************************************/ + +/* This function is called to print the error information out of an address for +a bounce or a warning message. It tries to format the message reasonably as +required by RFC 3461 by adding a space after each newline + +it uses the same logic as print_address_error() above. if af_pass_message is true +and addr->message is set it uses the remote host answer. if not addr->user_message +is used instead if available. + +Arguments: + addr the address + f the FILE to print on + +Returns: nothing +*/ + +static void +print_dsn_diagnostic_code(const address_item *addr, FILE *f) +{ +uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL; + +/* af_pass_message and addr->message set ? print remote host answer */ +if (s) + { + DEBUG(D_deliver) + debug_printf("DSN Diagnostic-Code: addr->message = %s\n", addr->message); + + /* search first ": ". we assume to find the remote-MTA answer there */ + if (!(s = Ustrstr(addr->message, ": "))) + return; /* not found, bail out */ + s += 2; /* skip ": " */ + fprintf(f, "Diagnostic-Code: smtp; "); + } +/* no message available. do nothing */ +else return; +while (*s) + if (*s == '\\' && s[1] == 'n') + { + fputs("\n ", f); /* as defined in RFC 3461 */ + s += 2; + } + else + fputc(*s++, f); +fputc('\n', f); +} /************************************************* @@ -5503,18 +5623,28 @@ if (process_recipients != RECIP_IGNORE) { recipient_item *r = recipients_list + i; address_item *new = deliver_make_addr(r->address, FALSE); - new->p.errors_address = r->errors_to; + new->prop.errors_address = r->errors_to; +#ifdef EXPERIMENTAL_INTERNATIONAL + if ((new->prop.utf8_msg = message_smtputf8)) + { + new->prop.utf8_downcvt = message_utf8_downconvert == 1; + new->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + DEBUG(D_deliver) debug_printf("utf8, downconvert %s\n", + new->prop.utf8_downcvt ? "yes" + : new->prop.utf8_downcvt_maybe ? "ifneeded" + : "no"); + } +#endif if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; -#ifdef EXPERIMENTAL_DSN /* If DSN support is enabled, set the dsn flags and the original receipt to be passed on to other DSN enabled MTAs */ 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); -#endif + DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", + new->dsn_orcpt, new->dsn_flags); switch (process_recipients) { @@ -5585,18 +5715,18 @@ if (process_recipients != RECIP_IGNORE) break; } -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT if (process_recipients != RECIP_ACCEPT) { uschar * save_local = deliver_localpart; - uschar * save_domain = deliver_domain; + const uschar * save_domain = deliver_domain; deliver_localpart = expand_string( string_sprintf("${local_part:%s}", new->address)); deliver_domain = expand_string( string_sprintf("${domain:%s}", new->address)); - (void) tpda_raise_event(delivery_event_action, + (void) event_raise(event_action, US"msg:fail:internal", new->message); deliver_localpart = save_local; @@ -5854,7 +5984,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ deliver_domain = addr->domain; /* set $domain */ if (!forced && hold_domains != NULL && - (rc = match_isinlist(addr->domain, &hold_domains, 0, + (rc = match_isinlist(addr->domain, (const uschar **)&hold_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) != FAIL) { @@ -6061,7 +6191,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ addr_route = addr->next; deliver_domain = addr->domain; /* set $domain */ - if ((rc = match_isinlist(addr->domain, &queue_domains, 0, + if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) != OK) { @@ -6094,15 +6224,15 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ { int rc; address_item *addr = addr_route; - uschar *old_domain = addr->domain; + const uschar *old_domain = addr->domain; uschar *old_unique = addr->unique; addr_route = addr->next; addr->next = NULL; /* Just in case some router parameter refers to it. */ - return_path = (addr->p.errors_address != NULL)? - addr->p.errors_address : sender_address; + return_path = (addr->prop.errors_address != NULL)? + addr->prop.errors_address : sender_address; /* If a router defers an address, add a retry item. Whether or not to use the local part in the key is a property of the router. */ @@ -6172,8 +6302,8 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ if (addr_remote == addr && addr->router->same_domain_copy_routing && - addr->p.extra_headers == NULL && - addr->p.remove_headers == NULL && + addr->prop.extra_headers == NULL && + addr->prop.remove_headers == NULL && old_domain == addr->domain) { address_item **chain = &addr_route; @@ -6200,7 +6330,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ addr2->transport = addr->transport; addr2->host_list = addr->host_list; addr2->fallback_hosts = addr->fallback_hosts; - addr2->p.errors_address = addr->p.errors_address; + addr2->prop.errors_address = addr->prop.errors_address; copyflag(addr2, addr, af_hide_child | af_local_host_removed); DEBUG(D_deliver|D_route) @@ -6459,31 +6589,7 @@ if (addr_remote != NULL) /* Precompile some regex that are used to recognize parameters in response to an EHLO command, if they aren't already compiled. */ - if (regex_PIPELINING == NULL) regex_PIPELINING = - regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE); - - if (regex_SIZE == NULL) regex_SIZE = - regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE); - - if (regex_AUTH == NULL) regex_AUTH = - regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", - FALSE, TRUE); - -#ifdef SUPPORT_TLS - if (regex_STARTTLS == NULL) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); -#endif - -#ifndef DISABLE_PRDR - if (regex_PRDR == NULL) regex_PRDR = - regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); -#endif - -#ifdef EXPERIMENTAL_DSN - /* Set the regex to check for DSN support on remote MTA */ - if (regex_DSN == NULL) regex_DSN = - regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); -#endif + deliver_init(); /* Now sort the addresses if required, and do the deliveries. The yield of do_remote_deliveries is FALSE when mua_wrapper is set and all addresses @@ -6592,34 +6698,40 @@ prevents actual delivery. */ else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); -#ifdef EXPERIMENTAL_DSN /* Send DSN for successful messages */ addr_dsntmp = addr_succeed; addr_senddsn = NULL; -while(addr_dsntmp != NULL) +while(addr_dsntmp) { - DEBUG(D_deliver) - debug_printf("DSN: processing router : %s\n", addr_dsntmp->router->name); - - DEBUG(D_deliver) - debug_printf("DSN: processing successful delivery address: %s\n", addr_dsntmp->address); - /* af_ignore_error not honored here. it's not an error */ - - DEBUG(D_deliver) debug_printf("DSN: Sender_address: %s\n", sender_address); - DEBUG(D_deliver) debug_printf("DSN: orcpt: %s flags: %d\n", addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags); - DEBUG(D_deliver) debug_printf("DSN: envid: %s ret: %d\n", dsn_envid, dsn_ret); - DEBUG(D_deliver) debug_printf("DSN: Final recipient: %s\n", addr_dsntmp->address); - DEBUG(D_deliver) debug_printf("DSN: Remote SMTP server supports DSN: %d\n", addr_dsntmp->dsn_aware); + DEBUG(D_deliver) + { + debug_printf("DSN: processing router : %s\n" + "DSN: processing successful delivery address: %s\n" + "DSN: Sender_address: %s\n" + "DSN: orcpt: %s flags: %d\n" + "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->address, + sender_address, + addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags, + dsn_envid, dsn_ret, + addr_dsntmp->address, + addr_dsntmp->dsn_aware + ); + } /* send report if next hop not DSN aware or a router flagged "last DSN hop" and a report was requested */ - if (((addr_dsntmp->dsn_aware != dsn_support_yes) || - ((addr_dsntmp->dsn_flags & rf_dsnlasthop) != 0)) - && - (((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) && - ((addr_dsntmp->dsn_flags & rf_notify_success) != 0))) + 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 + ) { /* copy and relink address_item and send report with all of them at once later */ address_item *addr_next; @@ -6629,14 +6741,12 @@ while(addr_dsntmp != NULL) addr_senddsn->next = addr_next; } else - { - DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); - } + DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); addr_dsntmp = addr_dsntmp->next; } -if (addr_senddsn != NULL) +if (addr_senddsn) { pid_t pid; int fd; @@ -6652,22 +6762,21 @@ if (addr_senddsn != NULL) "create child process to send failure message: %s", getpid(), getppid(), strerror(errno)); - DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); - + DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); } else /* Creation of child succeeded */ { FILE *f = fdopen(fd, "wb"); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ int topt = topt_add_return_path | topt_no_body; - uschar boundaryStr[64]; + uschar * bound; - DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address); + DEBUG(D_deliver) + debug_printf("sending error message to: %s\n", sender_address); /* build unique id for MIME boundary */ - snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d", - time(NULL), rand()); - DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr); + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound); if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); @@ -6684,11 +6793,10 @@ if (addr_senddsn != NULL) "This message was created automatically by mail delivery software.\n" " ----- The following addresses had successful delivery notifications -----\n", - qualify_domain_sender, sender_address, boundaryStr, boundaryStr); + qualify_domain_sender, sender_address, bound, bound); - addr_dsntmp = addr_senddsn; - while(addr_dsntmp) - { + for (addr_dsntmp = addr_senddsn; addr_dsntmp; + addr_dsntmp = addr_dsntmp->next) fprintf(f, "<%s> (relayed %s)\n\n", addr_dsntmp->address, (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 @@ -6697,15 +6805,14 @@ if (addr_senddsn != NULL) ? "to non-DSN-aware mailer" : "via non \"Remote SMTP\" router" ); - addr_dsntmp = addr_dsntmp->next; - } + fprintf(f, "--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", - boundaryStr, smtp_active_hostname); + bound, smtp_active_hostname); - if (dsn_envid != NULL) { - /* must be decoded from xtext: see RFC 3461:6.3a */ + if (dsn_envid) + { /* 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); @@ -6727,15 +6834,14 @@ if (addr_senddsn != NULL) addr_dsntmp->address); if (addr_dsntmp->host_used && addr_dsntmp->host_used->name) - fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n", + fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n", addr_dsntmp->host_used->name); else - fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n", + fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP"); - fputc('\n', f); } - fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr); + fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); fflush(f); transport_filter_argv = NULL; /* Just in case */ @@ -6745,15 +6851,13 @@ if (addr_senddsn != NULL) transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); fflush(f); - fprintf(f,"\n"); - fprintf(f,"--%s--\n", boundaryStr); + fprintf(f,"\n--%s--\n", bound); fflush(f); fclose(f); rc = child_close(pid, 0); /* Waits for child to close, no timeout */ } } -#endif /*EXPERIMENTAL_DSN*/ /* If any addresses failed, we must send a message to somebody, unless af_ignore_error is set, in which case no action is taken. It is possible for @@ -6797,10 +6901,10 @@ while (addr_failed != NULL) If neither of these cases obtains, something has gone wrong. Log the incident, but then ignore the error. */ - if (sender_address[0] == 0 && addr_failed->p.errors_address == NULL) + if (sender_address[0] == 0 && addr_failed->prop.errors_address == NULL) { - if (!testflag(addr_failed, af_retry_timedout) && - !testflag(addr_failed, af_ignore_error)) + if ( !testflag(addr_failed, af_retry_timedout) + && !testflag(addr_failed, af_ignore_error)) { log_write(0, LOG_MAIN|LOG_PANIC, "internal error: bounce message " "failure is neither frozen nor ignored (it's been ignored)"); @@ -6812,22 +6916,20 @@ while (addr_failed != NULL) it from the list, throw away any saved message file, log it, and mark the recipient done. */ - if (testflag(addr_failed, af_ignore_error) -#ifdef EXPERIMENTAL_DSN - || (((addr_failed->dsn_flags & rf_dsnflags) != 0) - && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure)) -#endif + if ( testflag(addr_failed, af_ignore_error) + || ( addr_failed->dsn_flags & rf_dsnflags + && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure) ) - { + { addr = addr_failed; addr_failed = addr->next; if (addr->return_filename != NULL) Uunlink(addr->return_filename); log_write(0, LOG_MAIN, "%s%s%s%s: error ignored", addr->address, - (addr->parent == NULL)? US"" : US" <", - (addr->parent == NULL)? US"" : addr->parent->address, - (addr->parent == NULL)? US"" : US">"); + !addr->parent ? US"" : US" <", + !addr->parent ? US"" : addr->parent->address, + !addr->parent ? US"" : US">"); address_done(addr, logtod); child_done(addr, logtod); @@ -6843,16 +6945,12 @@ while (addr_failed != NULL) else { - bounce_recipient = (addr_failed->p.errors_address == NULL)? - sender_address : addr_failed->p.errors_address; + bounce_recipient = addr_failed->prop.errors_address + ? addr_failed->prop.errors_address : sender_address; /* Make a subprocess to send a message */ - pid = child_open_exim(&fd); - - /* Creation of child failed */ - - if (pid < 0) + if ((pid = child_open_exim(&fd)) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " "create child process to send failure message: %s", getpid(), getppid(), strerror(errno)); @@ -6870,12 +6968,10 @@ while (addr_failed != 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; -#ifdef EXPERIMENTAL_DSN - uschar boundaryStr[64]; + uschar * bound; uschar *dsnlimitmsg; uschar *dsnnotifyhdr; int topt; -#endif DEBUG(D_deliver) debug_printf("sending error message to: %s\n", bounce_recipient); @@ -6885,20 +6981,16 @@ while (addr_failed != NULL) paddr = &addr_failed; for (addr = addr_failed; addr != NULL; addr = *paddr) - { - if (Ustrcmp(bounce_recipient, (addr->p.errors_address == NULL)? - sender_address : addr->p.errors_address) != 0) - { - paddr = &(addr->next); /* Not the same; skip */ - } - else /* The same - dechain */ - { + if (Ustrcmp(bounce_recipient, addr->prop.errors_address + ? addr->prop.errors_address : sender_address) == 0) + { /* The same - dechain */ *paddr = addr->next; *pmsgchain = addr; addr->next = NULL; pmsgchain = &(addr->next); } - } + else + paddr = &addr->next; /* Not the same; skip */ /* Include X-Failed-Recipients: for automatic interpretation, but do not let any one header line get too long. We do this by starting a @@ -6923,22 +7015,19 @@ while (addr_failed != NULL) /* Output the standard headers */ - if (errors_reply_to != NULL) + 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); -#ifdef EXPERIMENTAL_DSN /* generate boundary string and output MIME-Headers */ - snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d", - time(NULL), rand()); + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); fprintf(f, "Content-Type: multipart/report;" " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n", - boundaryStr); -#endif + bound); /* Open a template file if one is provided. Log failure to open, but carry on - default texts will be used. */ @@ -6963,12 +7052,10 @@ while (addr_failed != NULL) fprintf(f, "Subject: Mail delivery failed%s\n\n", to_sender? ": returning message to sender" : ""); -#ifdef EXPERIMENTAL_DSN /* output human readable part as text/plain section */ fprintf(f, "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n", - boundaryStr); -#endif + bound); if ((emf_text = next_emf(emf, US"intro"))) fprintf(f, "%s", CS emf_text); @@ -7095,12 +7182,19 @@ wording. */ fputc('\n', f); } -#ifdef EXPERIMENTAL_DSN /* output machine readable part */ - fprintf(f, "--%s\n" - "Content-type: message/delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - boundaryStr, smtp_active_hostname); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (message_smtputf8) + fprintf(f, "--%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" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); if (dsn_envid) { @@ -7120,10 +7214,13 @@ wording. */ "Status: 5.0.0\n", addr->address); if (addr->host_used && addr->host_used->name) - fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", - addr->host_used->name, addr->basic_errno); + { + fprintf(f, "Remote-MTA: dns; %s\n", + addr->host_used->name); + print_dsn_diagnostic_code(addr, f); + } + fputc('\n', f); } -#endif /* Now copy the message, trying to give an intelligible comment if it is too long for it all to be copied. The limit isn't strictly @@ -7132,65 +7229,6 @@ wording. */ emf_text = next_emf(emf, US"copy"); -#ifndef EXPERIMENTAL_DSN - if (bounce_return_message) - { - int topt = topt_add_return_path; - if (!bounce_return_body) topt |= topt_no_body; - - if (emf_text) - fprintf(f, "%s", CS emf_text); - else - { - if (bounce_return_body) fprintf(f, -"------ This is a copy of the message, including all the headers. ------\n"); - else fprintf(f, -"------ This is a copy of the message's headers. ------\n"); - } - - /* While reading the "truncated" message, set return_size_limit to - the actual max testing value, rounded. We need to read the message - whether we are going to use it or not. */ - - { - int temp = bounce_return_size_limit; - bounce_return_size_limit = (max/1000)*1000; - emf_text = next_emf(emf, US"truncated"); - bounce_return_size_limit = temp; - } - - if (bounce_return_body && bounce_return_size_limit > 0) - { - struct stat statbuf; - if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max) - { - if (emf_text) - fprintf(f, "%s", CS emf_text); - else - fprintf(f, -"------ The body of the message is " OFF_T_FMT " characters long; only the first\n" -"------ %d or so are included here.\n", statbuf.st_size, max); - } - } - - fputc('\n', f); - fflush(f); - - transport_filter_argv = NULL; /* Just in case */ - return_path = sender_address; /* In case not previously set */ - transport_write_message(NULL, fileno(f), topt, - bounce_return_size_limit, NULL, NULL, NULL, NULL, NULL, 0); - } - - /* Write final text and close the template file if one is open */ - - if (emf) - { - if ((emf_text = next_emf(emf, US"final"))) - fprintf(f, "%s", CS emf_text); - (void)fclose(emf); - } -#else /* add message body we ignore the intro text from template and add the text for bounce_return_size_limit at the end. @@ -7202,7 +7240,7 @@ wording. */ bounce_return_size_limit is always honored. */ - fprintf(f, "\n--%s\n", boundaryStr); + fprintf(f, "--%s\n", bound); dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; dsnnotifyhdr = NULL; @@ -7231,10 +7269,16 @@ wording. */ } } - if (topt & topt_no_body) - fprintf(f,"Content-type: text/rfc822-headers\n\n"); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (message_smtputf8) + fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" + : "Content-type: message/global\n\n", + f); else - fprintf(f,"Content-type: message/rfc822\n\n"); +#endif + fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" + : "Content-type: message/rfc822\n\n", + f); fflush(f); transport_filter_argv = NULL; /* Just in case */ @@ -7247,8 +7291,7 @@ wording. */ if (emf) (void)fclose(emf); - fprintf(f, "\n--%s--\n", boundaryStr); -#endif /*EXPERIMENTAL_DSN*/ + fprintf(f, "\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. */ @@ -7335,11 +7378,9 @@ if (addr_defer == NULL) "msglog.OLD directory", spoolname); } else - { if (Uunlink(spoolname) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", spoolname, strerror(errno)); - } } /* Remove the two message files. */ @@ -7355,7 +7396,7 @@ if (addr_defer == NULL) /* Log the end of this message, with queue time if requested. */ - if ((log_extra_selector & LX_queue_time_overall) != 0) + if (LOGGING(queue_time_overall)) log_write(0, LOG_MAIN, "Completed QT=%s", readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); else @@ -7364,8 +7405,8 @@ if (addr_defer == NULL) /* Unset deliver_freeze so that we won't try to move the spool files further down */ deliver_freeze = FALSE; -#ifdef EXPERIMENTAL_TPDA - (void) tpda_raise_event(delivery_event_action, US"msg:complete", NULL); +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(event_action, US"msg:complete", NULL); #endif } @@ -7413,7 +7454,8 @@ else if (addr_defer != (address_item *)(+1)) if (deliver_domain != NULL) { - uschar *d = (testflag(addr, af_pfr))? addr->parent->domain : addr->domain; + const uschar *d = testflag(addr, af_pfr) + ? addr->parent->domain : addr->domain; /* The domain may be unset for an address that has never been routed because the system filter froze the message. */ @@ -7451,7 +7493,7 @@ else if (addr_defer != (address_item *)(+1)) DEBUG(D_deliver) debug_printf("one_time: adding %s in place of %s\n", otaddr->address, otaddr->parent->address); receive_add_recipient(otaddr->address, t); - recipients_list[recipients_count-1].errors_to = otaddr->p.errors_address; + recipients_list[recipients_count-1].errors_to = otaddr->prop.errors_address; tree_add_nonrecipient(otaddr->parent->address); update_spool = TRUE; } @@ -7463,7 +7505,7 @@ else if (addr_defer != (address_item *)(+1)) if (sender_address[0] != 0) { - if (addr->p.errors_address == NULL) + if (addr->prop.errors_address == NULL) { if (Ustrstr(recipients, sender_address) == NULL) recipients = string_sprintf("%s%s%s", recipients, @@ -7471,9 +7513,9 @@ else if (addr_defer != (address_item *)(+1)) } else { - if (Ustrstr(recipients, addr->p.errors_address) == NULL) + if (Ustrstr(recipients, addr->prop.errors_address) == NULL) recipients = string_sprintf("%s%s%s", recipients, - (recipients[0] == 0)? "" : ",", addr->p.errors_address); + (recipients[0] == 0)? "" : ",", addr->prop.errors_address); } } } @@ -7483,15 +7525,18 @@ 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 && delivery_attempted && -#ifdef EXPERIMENTAL_DSN - (((addr_defer->dsn_flags & rf_dsnflags) == 0) || - (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay) && -#endif - delay_warning[1] > 0 && sender_address[0] != 0 && - (delay_warning_condition == NULL || - expand_check_condition(delay_warning_condition, - US"delay_warning", US"option"))) + if ( !queue_2stage + && delivery_attempted + && ( ((addr_defer->dsn_flags & rf_dsnflags) == 0) + || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay + ) + && delay_warning[1] > 0 + && sender_address[0] != 0 + && ( delay_warning_condition == NULL + || expand_check_condition(delay_warning_condition, + US"delay_warning", US"option") + ) + ) { int count; int show_time; @@ -7552,9 +7597,7 @@ else if (addr_defer != (address_item *)(+1)) uschar *wmf_text; FILE *wmf = NULL; FILE *f = fdopen(fd, "wb"); -#ifdef EXPERIMENTAL_DSN - uschar boundaryStr[64]; -#endif + uschar * bound; if (warn_message_file) { @@ -7575,16 +7618,13 @@ else if (addr_defer != (address_item *)(+1)) moan_write_from(f); fprintf(f, "To: %s\n", recipients); -#ifdef EXPERIMENTAL_DSN /* generated boundary string and output MIME-Headers */ - snprintf(boundaryStr, sizeof(boundaryStr)-1, - TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); fprintf(f, "Content-Type: multipart/report;" " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n", - boundaryStr); -#endif + bound); if ((wmf_text = next_emf(wmf, US"header"))) fprintf(f, "%s\n", wmf_text); @@ -7592,12 +7632,10 @@ else if (addr_defer != (address_item *)(+1)) fprintf(f, "Subject: Warning: message %s delayed %s\n\n", message_id, warnmsg_delay); -#ifdef EXPERIMENTAL_DSN /* output human readable part as text/plain section */ fprintf(f, "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n", - boundaryStr); -#endif + bound); if ((wmf_text = next_emf(wmf, US"intro"))) fprintf(f, "%s", CS wmf_text); @@ -7636,10 +7674,8 @@ else if (addr_defer != (address_item *)(+1)) /* List the addresses, with error information if allowed */ -#ifdef EXPERIMENTAL_DSN /* store addr_defer for machine readable part */ address_item *addr_dsndefer = addr_defer; -#endif fputc('\n', f); while (addr_defer) { @@ -7668,12 +7704,11 @@ else if (addr_defer != (address_item *)(+1)) "and when that happens, the message will be returned to you.\n"); } -#ifdef EXPERIMENTAL_DSN /* output machine readable part */ fprintf(f, "\n--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", - boundaryStr, + bound, smtp_active_hostname); @@ -7688,23 +7723,27 @@ else if (addr_defer != (address_item *)(+1)) } fputc('\n', f); - while (addr_dsndefer) + for ( ; addr_dsndefer; addr_dsndefer = addr_dsndefer->next) { if (addr_dsndefer->dsn_orcpt) - fprintf(f,"Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); + fprintf(f, "Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); - fprintf(f,"Action: delayed\n"); - fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsndefer->address); - fprintf(f,"Status: 4.0.0\n"); + fprintf(f, "Action: delayed\n" + "Final-Recipient: rfc822;%s\n" + "Status: 4.0.0\n", + addr_dsndefer->address); if (addr_dsndefer->host_used && addr_dsndefer->host_used->name) - fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", - addr_dsndefer->host_used->name, addr_dsndefer->basic_errno); - addr_dsndefer = addr_dsndefer->next; + { + fprintf(f, "Remote-MTA: dns; %s\n", + addr_dsndefer->host_used->name); + print_dsn_diagnostic_code(addr_dsndefer, f); + } + fputc('\n', f); } - fprintf(f, "\n--%s\n" + fprintf(f, "--%s\n" "Content-type: text/rfc822-headers\n\n", - boundaryStr); + bound); fflush(f); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ @@ -7715,10 +7754,9 @@ else if (addr_defer != (address_item *)(+1)) transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); fflush(f); - fprintf(f,"\n--%s--\n", boundaryStr); + fprintf(f,"\n--%s--\n", bound); fflush(f); -#endif /*EXPERIMENTAL_DSN*/ /* Close and wait for child process to complete, without a timeout. If there's an error, don't update the count. */ @@ -7856,6 +7894,79 @@ acl_where = ACL_WHERE_UNKNOWN; return final_yield; } + + +void +deliver_init(void) +{ +if (!regex_PIPELINING) regex_PIPELINING = + regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE); + +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); + +#ifdef SUPPORT_TLS +if (!regex_STARTTLS) regex_STARTTLS = + regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); +#endif + +#ifndef DISABLE_PRDR +if (!regex_PRDR) regex_PRDR = + regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); +#endif + +#ifdef EXPERIMENTAL_INTERNATIONAL +if (!regex_UTF8) regex_UTF8 = + regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE); +#endif + +if (!regex_DSN) regex_DSN = + regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); + +if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = + regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); +} + + +uschar * +deliver_get_sender_address (uschar * id) +{ +int rc; +uschar * new_sender_address, + * save_sender_address; + +if (!spool_open_datafile(id)) + return NULL; + +/* Save and restore the global sender_address. I'm not sure if we should +not save/restore all the other global variables too, because +spool_read_header() may change all of them. But OTOH, when this +deliver_get_sender_address() gets called, the current message is done +already and nobody needs the globals anymore. (HS12, 2015-08-21) */ + +sprintf(CS spoolname, "%s-H", id); +save_sender_address = sender_address; + +rc = spool_read_header(spoolname, TRUE, TRUE); + +new_sender_address = sender_address; +sender_address = save_sender_address; + +if (rc != spool_read_OK) + return NULL; + +assert(new_sender_address); + +(void)close(deliver_datafile); +deliver_datafile = -1; + +return new_sender_address; +} + /* vi: aw ai sw=2 */ /* End of deliver.c */