X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/94431adbd61d7706fe6df3a19bcae043fec950bf..44bc8f0c2f3576b46bd6df1b818cb29eaf84df5b:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index 5c4b22712..eae675753 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 - 2015 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* The main code for delivering a message. */ @@ -76,8 +76,6 @@ static int return_count; static uschar *frozen_info = US""; static uschar *used_return_path = NULL; -static uschar spoolname[PATH_MAX]; - /************************************************* @@ -285,10 +283,9 @@ int fd = Uopen(filename, O_WRONLY|O_APPEND|O_CREAT, mode); if (fd < 0 && errno == ENOENT) { - uschar temp[16]; - sprintf(CS temp, "msglog/%s", message_subdir); - if (message_subdir[0] == 0) temp[6] = 0; - (void)directory_make(spool_directory, temp, MSGLOG_DIRECTORY_MODE, TRUE); + (void)directory_make(spool_directory, + spool_sname(US"msglog", message_subdir), + MSGLOG_DIRECTORY_MODE, TRUE); fd = Uopen(filename, O_WRONLY|O_APPEND|O_CREAT, mode); } @@ -703,7 +700,7 @@ if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) s = LOGGING(outgoing_port) ? string_append(s, sizep, ptrp, 2, US"]:", string_sprintf("%d", sending_port)) - : string_cat(s, sizep, ptrp, "]", 1); + : string_catn(s, sizep, ptrp, US"]", 1); } return s; } @@ -711,18 +708,37 @@ return s; static uschar * -d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr) +d_hostlog(uschar * s, int * sp, int * pp, address_item * addr) { -s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, - US" [", addr->host_used->address, US"]"); +host_item * h = addr->host_used; + +s = string_append(s, sp, pp, 2, US" H=", h->name); + +if (LOGGING(dnssec) && h->dnssec == DS_YES) + s = string_catn(s, sp, pp, US" DS", 3); + +s = string_append(s, sp, pp, 3, US" [", h->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); + s = string_append(s, sp, pp, 2, US":", string_sprintf("%d", h->port)); + +#ifdef SUPPORT_SOCKS +if (LOGGING(proxy) && proxy_local_address) + { + s = string_append(s, sp, pp, 3, US" PRX=[", proxy_local_address, US"]"); + if (LOGGING(outgoing_port)) + s = string_append(s, sp, pp, 2, US":", string_sprintf("%d", + proxy_local_port)); + } +#endif + +return d_log_interface(s, sp, pp); } + + #ifdef SUPPORT_TLS static uschar * d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) @@ -750,7 +766,7 @@ return s; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT uschar * event_raise(uschar * action, const uschar * event, uschar * ev_data) { @@ -785,12 +801,14 @@ if (action) return NULL; } -static void +void msg_event_raise(const uschar * event, const address_item * addr) { const uschar * save_domain = deliver_domain; uschar * save_local = deliver_localpart; const uschar * save_host = deliver_host; +const uschar * save_address = deliver_host_address; +const int save_port = deliver_host_port; if (!addr->transport) return; @@ -802,15 +820,197 @@ 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, "lmtp") == 0 - ? addr->message : NULL); + addr->host_used + || Ustrcmp(addr->transport->driver_name, "smtp") == 0 + || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + ? addr->message : NULL); +deliver_host_port = save_port; +deliver_host_address = save_address; deliver_host = save_host; deliver_localpart = save_local; deliver_domain = save_domain; router_name = transport_name = NULL; } -#endif /*EXPERIMENTAL_EVENT*/ +#endif /*DISABLE_EVENT*/ + + + +/******************************************************************************/ + + +/************************************************* +* Generate local prt for logging * +*************************************************/ + +/* This function is a subroutine for use in string_log_address() below. + +Arguments: + addr the address being logged + yield the current dynamic buffer pointer + sizeptr points to current size + ptrptr points to current insert pointer + +Returns: the new value of the buffer pointer +*/ + +static uschar * +string_get_localpart(address_item *addr, uschar *yield, int *sizeptr, + int *ptrptr) +{ +uschar * s; + +s = addr->prefix; +if (testflag(addr, af_include_affixes) && s) + { +#ifdef SUPPORT_I18N + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, sizeptr, ptrptr, s); + } + +s = addr->local_part; +#ifdef SUPPORT_I18N +if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif +yield = string_cat(yield, sizeptr, ptrptr, s); + +s = addr->suffix; +if (testflag(addr, af_include_affixes) && s) + { +#ifdef SUPPORT_I18N + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, sizeptr, ptrptr, s); + } + +return yield; +} + + +/************************************************* +* Generate log address list * +*************************************************/ + +/* This function generates a list consisting of an address and its parents, for +use in logging lines. For saved onetime aliased addresses, the onetime parent +field is used. If the address was delivered by a transport with rcpt_include_ +affixes set, the af_include_affixes bit will be set in the address. In that +case, we include the affixes here too. + +Arguments: + str points to start of growing string, or NULL + size points to current allocation for string + ptr points to offset for append point; updated on exit + addr bottom (ultimate) address + all_parents if TRUE, include all parents + success TRUE for successful delivery + +Returns: a growable string in dynamic store +*/ + +static uschar * +string_log_address(uschar * str, int * size, int * ptr, + address_item *addr, BOOL all_parents, BOOL success) +{ +BOOL add_topaddr = TRUE; +address_item *topaddr; + +/* Find the ultimate parent */ + +for (topaddr = addr; topaddr->parent; topaddr = topaddr->parent) ; + +/* We start with just the local part for pipe, file, and reply deliveries, and +for successful local deliveries from routers that have the log_as_local flag +set. File deliveries from filters can be specified as non-absolute paths in +cases where the transport is goin to complete the path. If there is an error +before this happens (expansion failure) the local part will not be updated, and +so won't necessarily look like a path. Add extra text for this case. */ + +if ( testflag(addr, af_pfr) + || ( success + && addr->router && addr->router->log_as_local + && addr->transport && addr->transport->info->local + ) ) + { + if (testflag(addr, af_file) && addr->local_part[0] != '/') + str = string_catn(str, size, ptr, CUS"save ", 5); + str = string_get_localpart(addr, str, size, ptr); + } + +/* Other deliveries start with the full address. It we have split it into local +part and domain, use those fields. Some early failures can happen before the +splitting is done; in those cases use the original field. */ + +else + { + uschar * cmp = str + *ptr; + + if (addr->local_part) + { + const uschar * s; + str = string_get_localpart(addr, str, size, ptr); + str = string_catn(str, size, ptr, US"@", 1); + s = addr->domain; +#ifdef SUPPORT_I18N + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + str = string_cat(str, size, ptr, s); + } + else + str = string_cat(str, size, ptr, addr->address); + + /* If the address we are going to print is the same as the top address, + 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. */ + + str[*ptr] = 0; + if ( strcmpic(cmp, topaddr->address) == 0 + && Ustrncmp(cmp, topaddr->address, Ustrchr(cmp, '@') - cmp) == 0 + && !addr->onetime_parent + && (!all_parents || !addr->parent || addr->parent == topaddr) + ) + add_topaddr = FALSE; + } + +/* If all parents are requested, or this is a local pipe/file/reply, and +there is at least one intermediate parent, show it in brackets, and continue +with all of them if all are wanted. */ + +if ( (all_parents || testflag(addr, af_pfr)) + && addr->parent + && addr->parent != topaddr) + { + uschar *s = US" ("; + address_item *addr2; + for (addr2 = addr->parent; addr2 != topaddr; addr2 = addr2->parent) + { + str = string_catn(str, size, ptr, s, 2); + str = string_cat (str, size, ptr, addr2->address); + if (!all_parents) break; + s = US", "; + } + str = string_catn(str, size, ptr, US")", 1); + } + +/* Add the top address if it is required */ + +if (add_topaddr) + str = string_append(str, size, ptr, 3, + US" <", + addr->onetime_parent ? addr->onetime_parent : topaddr->address, + US">"); + +return str; +} + + +/******************************************************************************/ @@ -824,36 +1024,35 @@ Arguments: void delivery_log(int flags, address_item * addr, int logchar, uschar * msg) { -uschar *log_address; int size = 256; /* Used for a temporary, */ int ptr = 0; /* expanding buffer, for */ -uschar *s; /* building log lines; */ -void *reset_point; /* released afterwards. */ +uschar * s; /* building log lines; */ +void * reset_point; /* released afterwards. */ /* Log the delivery on the main log. We use an extensible string to build up 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_EVENT +#ifndef DISABLE_EVENT /* presume no successful remote delivery */ lookup_dnssec_authenticated = NULL; #endif s = reset_point = store_get(size); -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); + s = string_append(s, &size, &ptr, 2, host_and_ident(TRUE), US" "); else { s[ptr++] = logchar; - s = string_append(s, &size, &ptr, 2, US"> ", log_address); + s = string_catn(s, &size, &ptr, US"> ", 2); } +s = string_log_address(s, &size, &ptr, addr, LOGGING(all_parents), TRUE); if (LOGGING(sender_on_delivery) || msg) s = string_append(s, &size, &ptr, 3, US" F=<", -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N testflag(addr, af_utf8_downcvt) ? string_address_utf8_to_alabel(sender_address, NULL) : @@ -861,6 +1060,9 @@ if (LOGGING(sender_on_delivery) || msg) sender_address, US">"); +if (*queue_name) + s = string_append(s, &size, &ptr, 2, US" Q=", queue_name); + #ifdef EXPERIMENTAL_SRS if(addr->prop.srs_sender) s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->prop.srs_sender, US">"); @@ -895,8 +1097,7 @@ if (addr->transport->info->local) s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); s = d_log_interface(s, &size, &ptr); if (addr->shadow_message) - s = string_cat(s, &size, &ptr, addr->shadow_message, - Ustrlen(addr->shadow_message)); + s = string_cat(s, &size, &ptr, addr->shadow_message); } /* Remote delivery */ @@ -907,9 +1108,9 @@ else { s = d_hostlog(s, &size, &ptr, addr); if (continue_sequence > 1) - s = string_cat(s, &size, &ptr, US"*", 1); + s = string_catn(s, &size, &ptr, US"*", 1); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT deliver_host_address = addr->host_used->address; deliver_host_port = addr->host_used->port; deliver_host = addr->host_used->name; @@ -938,8 +1139,11 @@ else #ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) - s = string_append(s, &size, &ptr, 1, US" PRDR"); + s = string_catn(s, &size, &ptr, US" PRDR", 5); #endif + + if (addr->flags & af_chunking_used) + s = string_catn(s, &size, &ptr, US" K", 2); } /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ @@ -980,7 +1184,7 @@ store we used to build the line after writing it. */ s[ptr] = 0; log_write(0, flags, "%s", s); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (!msg) msg_event_raise(US"msg:delivery", addr); #endif @@ -990,6 +1194,163 @@ return; +static void +deferral_log(address_item * addr, uschar * now, + int logflags, uschar * driver_name, uschar * driver_kind) +{ +int size = 256; /* Used for a temporary, */ +int ptr = 0; /* expanding buffer, for */ +uschar * s; /* building log lines; */ +void * reset_point; /* released afterwards. */ + +uschar ss[32]; + +/* Build up the line that is used for both the message log and the main +log. */ + +s = reset_point = store_get(size); + +/* 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. */ + +s = string_log_address(s, &size, &ptr, addr, LOGGING(all_parents), FALSE); + +if (*queue_name) + s = string_append(s, &size, &ptr, 2, US" Q=", queue_name); + +/* Either driver_name contains something and driver_kind contains +" router" or " transport" (note the leading space), or driver_name is +a null string and driver_kind contains "routing" without the leading +space, if all routing has been deferred. When a domain has been held, +so nothing has been done at all, both variables contain null strings. */ + +if (driver_name) + { + if (driver_kind[1] == 't' && addr->router) + s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); + Ustrcpy(ss, " ?="); + ss[1] = toupper(driver_kind[1]); + s = string_append(s, &size, &ptr, 2, ss, driver_name); + } +else if (driver_kind) + s = string_append(s, &size, &ptr, 2, US" ", driver_kind); + +/*XXX need an s+s+p sprintf */ +sprintf(CS ss, " defer (%d)", addr->basic_errno); +s = string_cat(s, &size, &ptr, ss); + +if (addr->basic_errno > 0) + s = string_append(s, &size, &ptr, 2, US": ", + US strerror(addr->basic_errno)); + +if (addr->host_used) + { + s = string_append(s, &size, &ptr, 5, + US" H=", addr->host_used->name, + US" [", addr->host_used->address, US"]"); + if (LOGGING(outgoing_port)) + { + int port = addr->host_used->port; + s = string_append(s, &size, &ptr, 2, + US":", port == PORT_NONE ? US"25" : string_sprintf("%d", port)); + } + } + +if (addr->message) + s = string_append(s, &size, &ptr, 2, US": ", addr->message); + +s[ptr] = 0; + +/* 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) + deliver_msglog("%s %s\n", now, s); + +/* Write the main log and reset the store. +For errors of the type "retry time not reached" (also remotes skipped +on queue run), logging is controlled by L_retry_defer. Note that this kind +of error number is negative, and all the retry ones are less than any +others. */ + + +log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags, + "== %s", s); + +store_reset(reset_point); +return; +} + + + +static void +failure_log(address_item * addr, uschar * driver_kind, uschar * now) +{ +int size = 256; /* Used for a temporary, */ +int ptr = 0; /* expanding buffer, for */ +uschar * s; /* building log lines; */ +void * reset_point; /* released afterwards. */ + +/* Build up the log line for the message and main logs */ + +s = reset_point = store_get(size); + +/* 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. */ + +s = string_log_address(s, &size, &ptr, addr, LOGGING(all_parents), FALSE); + +if (LOGGING(sender_on_delivery)) + s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); + +if (*queue_name) + s = string_append(s, &size, &ptr, 2, US" Q=", queue_name); + +/* Return path may not be set if no delivery actually happened */ + +if (used_return_path && LOGGING(return_path_on_delivery)) + s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); + +if (addr->router) + s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); +if (addr->transport) + s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); + +if (addr->host_used) + s = d_hostlog(s, &size, &ptr, addr); + +#ifdef SUPPORT_TLS +s = d_tlslog(s, &size, &ptr, addr); +#endif + +if (addr->basic_errno > 0) + s = string_append(s, &size, &ptr, 2, US": ", US strerror(addr->basic_errno)); + +if (addr->message) + s = string_append(s, &size, &ptr, 2, US": ", addr->message); + +s[ptr] = 0; + +/* Do the logging. For the message log, "routing failed" for those cases, +just to make it clearer. */ + +if (driver_kind) + deliver_msglog("%s %s failed for %s\n", now, driver_kind, s); +else + deliver_msglog("%s %s\n", now, s); + +log_write(0, LOG_MAIN, "** %s", s); + +#ifndef DISABLE_EVENT +msg_event_raise(US"msg:fail:delivery", addr); +#endif + +store_reset(reset_point); +return; +} + + + /************************************************* * Actions at the end of handling an address * *************************************************/ @@ -1015,12 +1376,6 @@ post_process_one(address_item *addr, int result, int logflags, int driver_type, uschar *now = tod_stamp(tod_log); uschar *driver_kind = NULL; uschar *driver_name = NULL; -uschar *log_address; - -int size = 256; /* Used for a temporary, */ -int ptr = 0; /* expanding buffer, for */ -uschar *s; /* building log lines; */ -void *reset_point; /* released afterwards. */ DEBUG(D_deliver) debug_printf("post-process %s (%d)\n", addr->address, result); @@ -1058,23 +1413,9 @@ malformed, it won't ever have gone near LDAP.) */ if (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(s, "failed to expand") != NULL - || Ustrstr(s, "expansion of ") != NULL - ) - && ( Ustrstr(s, "mysql") != NULL - || Ustrstr(s, "pgsql") != NULL -#ifdef EXPERIMENTAL_REDIS - || Ustrstr(s, "redis") != NULL -#endif - || Ustrstr(s, "sqlite") != NULL - || Ustrstr(s, "ldap:") != NULL - || Ustrstr(s, "ldapdn:") != NULL - || Ustrstr(s, "ldapm:") != NULL - ) ) - addr->message = string_sprintf("Temporary internal error"); + + /* deconst cast ok as string_printing known to have alloc'n'copied */ + addr->message = expand_hide_passwords(US s); } /* If we used a transport that has one of the "return_output" options set, and @@ -1240,74 +1581,7 @@ else if (result == DEFER || result == PANIC) log or the main log for SMTP defers. */ if (!queue_2stage || addr->basic_errno != 0) - { - uschar ss[32]; - - /* For errors of the type "retry time not reached" (also remotes skipped - on queue run), logging is controlled by L_retry_defer. Note that this kind - of error number is negative, and all the retry ones are less than any - others. */ - - unsigned int use_log_selector = addr->basic_errno <= ERRNO_RETRY_BASE - ? L_retry_defer : 0; - - /* Build up the line that is used for both the message log and the main - log. */ - - s = reset_point = store_get(size); - - /* 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, LOGGING(all_parents), result == OK); - - s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); - - /* Either driver_name contains something and driver_kind contains - " router" or " transport" (note the leading space), or driver_name is - a null string and driver_kind contains "routing" without the leading - space, if all routing has been deferred. When a domain has been held, - so nothing has been done at all, both variables contain null strings. */ - - if (driver_name) - { - if (driver_kind[1] == 't' && addr->router) - s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); - Ustrcpy(ss, " ?="); - ss[1] = toupper(driver_kind[1]); - s = string_append(s, &size, &ptr, 2, ss, driver_name); - } - else if (driver_kind) - s = string_append(s, &size, &ptr, 2, US" ", driver_kind); - - sprintf(CS ss, " defer (%d)", addr->basic_errno); - s = string_cat(s, &size, &ptr, ss, Ustrlen(ss)); - - if (addr->basic_errno > 0) - s = string_append(s, &size, &ptr, 2, US": ", - US strerror(addr->basic_errno)); - - if (addr->host_used) - s = string_append(s, &size, &ptr, 5, - US" H=", addr->host_used->name, - US" [", addr->host_used->address, US"]"); - - if (addr->message) - s = string_append(s, &size, &ptr, 2, US": ", addr->message); - - s[ptr] = 0; - - /* 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) - deliver_msglog("%s %s\n", now, s); - - /* Write the main log and reset the store */ - - log_write(use_log_selector, logflags, "== %s", s); - store_reset(reset_point); - } + deferral_log(addr, now, logflags, driver_name, driver_kind); } @@ -1362,61 +1636,7 @@ else addr_failed = addr; } - /* Build up the log line for the message and main logs */ - - s = reset_point = store_get(size); - - /* 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, LOGGING(all_parents), result == OK); - - s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); - - 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 && LOGGING(return_path_on_delivery)) - s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); - - if (addr->router) - s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); - if (addr->transport) - s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); - - if (addr->host_used) - s = d_hostlog(s, &size, &ptr, addr); - -#ifdef SUPPORT_TLS - s = d_tlslog(s, &size, &ptr, addr); -#endif - - if (addr->basic_errno > 0) - s = string_append(s, &size, &ptr, 2, US": ", - US strerror(addr->basic_errno)); - - if (addr->message) - s = string_append(s, &size, &ptr, 2, US": ", addr->message); - - s[ptr] = 0; - - /* Do the logging. For the message log, "routing failed" for those cases, - just to make it clearer. */ - - if (driver_name) - deliver_msglog("%s %s\n", now, s); - else - deliver_msglog("%s %s failed for %s\n", now, driver_kind, s); - - log_write(0, LOG_MAIN, "** %s", s); - -#ifdef EXPERIMENTAL_EVENT - msg_event_raise(US"msg:fail:delivery", addr); -#endif - - store_reset(reset_point); + failure_log(addr, driver_name ? NULL : driver_kind, now); } /* Ensure logging is turned on again in all cases */ @@ -1932,12 +2152,13 @@ if ( !shadowing || tp->log_output || tp->log_fail_output || tp->log_defer_output ) ) { - uschar *error; + uschar * error; + addr->return_filename = - string_sprintf("%s/msglog/%s/%s-%d-%d", spool_directory, message_subdir, - message_id, getpid(), return_count++); - addr->return_file = open_msglog_file(addr->return_filename, 0400, &error); - if (addr->return_file < 0) + spool_fname(US"msglog", message_subdir, message_id, + string_sprintf("-%d-%d", getpid(), return_count++)); + + if ((addr->return_file = open_msglog_file(addr->return_filename, 0400, &error)) < 0) { common_error(TRUE, addr, errno, US"Unable to %s file for %s transport " "to return message: %s", error, tp->name, strerror(errno)); @@ -2195,6 +2416,7 @@ for (addr2 = addr; addr2; addr2 = addr2->next) if (message_length > 0) { len = read(pfd[pipe_read], big_buffer, message_length); + big_buffer[big_buffer_size-1] = '\0'; /* guard byte */ if (len > 0) *sptr = string_copy(big_buffer); } } @@ -3263,6 +3485,10 @@ while (!done) break; #endif + case 'K': + addr->flags |= af_chunking_used; + break; + case 'D': if (!addr) goto ADDR_MISMATCH; memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); @@ -3283,6 +3509,21 @@ while (!done) switch (subid) { +#ifdef SUPPORT_SOCKS + case '2': /* proxy information; must arrive before A0 and applies to that addr XXX oops*/ + proxy_session = TRUE; /*XXX shouod this be cleared somewhere? */ + if (*ptr == 0) + ptr++; + else + { + proxy_local_address = string_copy(ptr); + while(*ptr++); + memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port)); + ptr += sizeof(proxy_local_port); + } + break; +#endif + #ifdef EXPERIMENTAL_DSN_INFO case '1': /* must arrive before A0, and applies to that addr */ /* Two strings: smtp_greeting and helo_response */ @@ -3815,7 +4056,8 @@ static void rmt_dlv_checked_write(int fd, char id, char subid, void * buf, int size) { uschar writebuffer[PIPE_HEADER_SIZE + BIG_BUFFER_SIZE]; -int header_length; +int header_length; +int ret; /* we assume that size can't get larger then BIG_BUFFER_SIZE which currently is set to 16k */ /* complain to log if someone tries with buffer sizes we can't handle*/ @@ -3845,8 +4087,7 @@ if (buf && size > 0) memcpy(writebuffer + PIPE_HEADER_SIZE, buf, size); size += PIPE_HEADER_SIZE; -int ret = write(fd, writebuffer, size); -if(ret != size) +if ((ret = write(fd, writebuffer, size)) != size) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed writing transport result to pipe: %s\n", ret == -1 ? strerror(errno) : "short write"); } @@ -4203,7 +4444,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) addr_fallback = addr; } - else + else if (next) { while (next->next) next = next->next; next->next = addr_defer; @@ -4344,13 +4585,13 @@ for (delivery_count = 0; addr_remote; delivery_count++) a dup-with-new-file-pointer. */ (void)close(deliver_datafile); - sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, - message_id); - deliver_datafile = Uopen(spoolname, O_RDWR | O_APPEND, 0); + { + uschar * fname = spool_fname(US"input", message_subdir, message_id, US"-D"); - if (deliver_datafile < 0) + if ((deliver_datafile = Uopen(fname, O_RDWR | O_APPEND, 0)) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed to reopen %s for remote " - "parallel delivery: %s", spoolname, strerror(errno)); + "parallel delivery: %s", fname, strerror(errno)); + } /* Set the close-on-exec flag */ @@ -4427,15 +4668,13 @@ for (delivery_count = 0; addr_remote; delivery_count++) #ifdef SUPPORT_TLS if (addr->cipher) { - ptr = big_buffer; - sprintf(CS ptr, "%.128s", addr->cipher); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1; if (!addr->peerdn) *ptr++ = 0; else { - sprintf(CS ptr, "%.512s", addr->peerdn); - while(*ptr++); + ptr += sprintf(CS ptr, "%.512s", addr->peerdn); + ptr++; } rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer); @@ -4461,9 +4700,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) # ifndef DISABLE_OCSP if (addr->ocsp > OCSP_NOT_REQ) { - ptr = big_buffer; - sprintf(CS ptr, "%c", addr->ocsp + '0'); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%c", addr->ocsp + '0') + 1; rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer); } # endif @@ -4471,23 +4708,17 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (client_authenticator) { - ptr = big_buffer; - sprintf(CS big_buffer, "%.64s", client_authenticator); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticator) + 1; rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer); } if (client_authenticated_id) { - ptr = big_buffer; - sprintf(CS big_buffer, "%.64s", client_authenticated_id); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_id) + 1; rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer); } if (client_authenticated_sender) { - ptr = big_buffer; - sprintf(CS big_buffer, "%.64s", client_authenticated_sender); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_sender) + 1; rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer); } @@ -4496,6 +4727,9 @@ for (delivery_count = 0; addr_remote; delivery_count++) rmt_dlv_checked_write(fd, 'P', '0', NULL, 0); #endif + if (addr->flags & af_chunking_used) + rmt_dlv_checked_write(fd, 'K', '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)); DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); @@ -4518,19 +4752,34 @@ for (delivery_count = 0; addr_remote; delivery_count++) rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } +#ifdef SUPPORT_SOCKS + if (LOGGING(proxy) && proxy_session) + { + ptr = big_buffer; + if (proxy_local_address) + { + DEBUG(D_deliver) debug_printf("proxy_local_address '%s'\n", proxy_local_address); + ptr = big_buffer + sprintf(CS ptr, "%.128s", proxy_local_address) + 1; + DEBUG(D_deliver) debug_printf("proxy_local_port %d\n", proxy_local_port); + memcpy(ptr, &proxy_local_port, sizeof(proxy_local_port)); + ptr += sizeof(proxy_local_port); + } + else + *ptr++ = '\0'; + rmt_dlv_checked_write(fd, 'A', '2', big_buffer, ptr - big_buffer); + } +#endif + #ifdef EXPERIMENTAL_DSN_INFO /*um, are they really per-addr? Other per-conn stuff is not (auth, tls). But host_used is! */ if (addr->smtp_greeting) { - ptr = big_buffer; DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting); - sprintf(CS ptr, "%.128s", addr->smtp_greeting); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->smtp_greeting) + 1; if (addr->helo_response) { DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response); - sprintf(CS ptr, "%.128s", addr->helo_response); - while(*ptr++); + ptr += sprintf(CS ptr, "%.128s", addr->helo_response) + 1; } else *ptr++ = '\0'; @@ -4540,8 +4789,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) /* The rest of the information goes in an 'A0' item. */ - sprintf(CS big_buffer, "%c%c", addr->transport_return, - addr->special_action); + sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action); ptr = big_buffer + 2; memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno)); ptr += sizeof(addr->basic_errno); @@ -4551,23 +4799,15 @@ for (delivery_count = 0; addr_remote; delivery_count++) ptr += sizeof(addr->flags); if (!addr->message) *ptr++ = 0; else - { - sprintf(CS ptr, "%.1024s", addr->message); - while(*ptr++); - } + ptr += sprintf(CS ptr, "%.1024s", addr->message) + 1; if (!addr->user_message) *ptr++ = 0; else - { - sprintf(CS ptr, "%.1024s", addr->user_message); - while(*ptr++); - } + ptr += sprintf(CS ptr, "%.1024s", addr->user_message) + 1; if (!addr->host_used) *ptr++ = 0; else { - sprintf(CS ptr, "%.256s", addr->host_used->name); - while(*ptr++); - sprintf(CS ptr, "%.64s", addr->host_used->address); - while(*ptr++); + ptr += sprintf(CS ptr, "%.256s", addr->host_used->name) + 1; + ptr += sprintf(CS ptr, "%.64s", addr->host_used->address) + 1; memcpy(ptr, &(addr->host_used->port), sizeof(addr->host_used->port)); ptr += sizeof(addr->host_used->port); @@ -4586,12 +4826,9 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (LOGGING(incoming_interface) && sending_ip_address) #endif { - uschar * ptr = big_buffer; - sprintf(CS ptr, "%.128s", sending_ip_address); - while(*ptr++); - sprintf(CS ptr, "%d", sending_port); - while(*ptr++); - + uschar * ptr; + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", sending_ip_address) + 1; + ptr += sprintf(CS ptr, "%d", sending_port) + 1; rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer); } @@ -4688,13 +4925,17 @@ Returns: OK */ int -deliver_split_address(address_item *addr) +deliver_split_address(address_item * addr) { -uschar *address = addr->address; -uschar *domain = Ustrrchr(address, '@'); -uschar *t; -int len = domain - address; +uschar * address = addr->address; +uschar * domain; +uschar * t; +int len; + +if (!(domain = Ustrrchr(address, '@'))) + return DEFER; /* should always have a domain, but just in case... */ +len = domain - address; addr->domain = string_copylc(domain+1); /* Domains are always caseless */ /* The implication in the RFCs (though I can't say I've seen it spelled out @@ -4706,7 +4947,7 @@ removing quoting backslashes and any unquoted doublequotes. */ t = addr->cc_local_part = store_get(len+1); while(len-- > 0) { - register int c = *address++; + int c = *address++; if (c == '\"') continue; if (c == '\\') { @@ -4798,7 +5039,7 @@ if (!Ufgets(buffer, sizeof(buffer), f) || Ustrcmp(buffer, "****\n") == 0) para = store_get(size); for (;;) { - para = string_cat(para, &size, &ptr, buffer, Ustrlen(buffer)); + para = string_cat(para, &size, &ptr, buffer); if (!Ufgets(buffer, sizeof(buffer), f) || Ustrcmp(buffer, "****\n") == 0) break; } @@ -5193,7 +5434,7 @@ Any failures cause messages to be written to the log, except for missing files while queue running - another process probably completed delivery. As part of opening the data file, message_subdir gets set. */ -if (!spool_open_datafile(id)) +if ((deliver_datafile = spool_open_datafile(id)) < 0) return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ /* The value of message_size at this point has been set to the data length, @@ -5204,53 +5445,51 @@ store, and also the list of recipients and the tree of non-recipients and assorted flags. It updates message_size. If there is a reading or format error, give up; if the message has been around for sufficiently long, remove it. */ -sprintf(CS spoolname, "%s-H", id); -if ((rc = spool_read_header(spoolname, TRUE, TRUE)) != spool_read_OK) { - if (errno == ERRNO_SPOOLFORMAT) + uschar * spoolname = string_sprintf("%s-H", id); + if ((rc = spool_read_header(spoolname, TRUE, TRUE)) != spool_read_OK) { - struct stat statbuf; - sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir, - spoolname); - if (Ustat(big_buffer, &statbuf) == 0) - log_write(0, LOG_MAIN, "Format error in spool file %s: " - "size=" OFF_T_FMT, spoolname, statbuf.st_size); - else log_write(0, LOG_MAIN, "Format error in spool file %s", spoolname); - } - else - log_write(0, LOG_MAIN, "Error reading spool file %s: %s", spoolname, - strerror(errno)); + if (errno == ERRNO_SPOOLFORMAT) + { + struct stat statbuf; + if (Ustat(spool_fname(US"input", message_subdir, spoolname, US""), + &statbuf) == 0) + log_write(0, LOG_MAIN, "Format error in spool file %s: " + "size=" OFF_T_FMT, spoolname, statbuf.st_size); + else + log_write(0, LOG_MAIN, "Format error in spool file %s", spoolname); + } + else + log_write(0, LOG_MAIN, "Error reading spool file %s: %s", spoolname, + strerror(errno)); - /* If we managed to read the envelope data, received_time contains the - time the message was received. Otherwise, we can calculate it from the - message id. */ + /* If we managed to read the envelope data, received_time contains the + time the message was received. Otherwise, we can calculate it from the + message id. */ - if (rc != spool_read_hdrerror) - { - received_time = 0; - for (i = 0; i < 6; i++) - received_time = received_time * BASE_62 + tab62[id[i] - '0']; - } + if (rc != spool_read_hdrerror) + { + received_time = 0; + for (i = 0; i < 6; i++) + received_time = received_time * BASE_62 + tab62[id[i] - '0']; + } - /* If we've had this malformed message too long, sling it. */ + /* If we've had this malformed message too long, sling it. */ - if (now - received_time > keep_malformed) - { - sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir, id); - Uunlink(spoolname); - sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id); - Uunlink(spoolname); - sprintf(CS spoolname, "%s/input/%s/%s-H", spool_directory, message_subdir, id); - Uunlink(spoolname); - sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id); - Uunlink(spoolname); - log_write(0, LOG_MAIN, "Message removed because older than %s", - readconf_printtime(keep_malformed)); - } + if (now - received_time > keep_malformed) + { + Uunlink(spool_fname(US"msglog", message_subdir, id, US"")); + Uunlink(spool_fname(US"input", message_subdir, id, US"-D")); + Uunlink(spool_fname(US"input", message_subdir, id, US"-H")); + Uunlink(spool_fname(US"input", message_subdir, id, US"-J")); + log_write(0, LOG_MAIN, "Message removed because older than %s", + readconf_printtime(keep_malformed)); + } - (void)close(deliver_datafile); - deliver_datafile = -1; - return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ + (void)close(deliver_datafile); + deliver_datafile = -1; + return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ + } } /* The spool header file has been read. Look to see if there is an existing @@ -5262,37 +5501,39 @@ existence, as it will get further successful deliveries added to it in this run, and it will be deleted if this function gets to its end successfully. Otherwise it might be needed again. */ -sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id); -jread = Ufopen(spoolname, "rb"); -if (jread) { - while (Ufgets(big_buffer, big_buffer_size, jread)) + uschar * fname = spool_fname(US"input", message_subdir, id, US"-J"); + + if ((jread = Ufopen(fname, "rb"))) + { + while (Ufgets(big_buffer, big_buffer_size, jread)) + { + int n = Ustrlen(big_buffer); + big_buffer[n-1] = 0; + tree_add_nonrecipient(big_buffer); + DEBUG(D_deliver) debug_printf("Previously delivered address %s taken from " + "journal file\n", big_buffer); + } + (void)fclose(jread); + /* Panic-dies on error */ + (void)spool_write_header(message_id, SW_DELIVERING, NULL); + } + else if (errno != ENOENT) { - int n = Ustrlen(big_buffer); - big_buffer[n-1] = 0; - tree_add_nonrecipient(big_buffer); - DEBUG(D_deliver) debug_printf("Previously delivered address %s taken from " - "journal file\n", big_buffer); + log_write(0, LOG_MAIN|LOG_PANIC, "attempt to open journal for reading gave: " + "%s", strerror(errno)); + return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ } - (void)fclose(jread); - /* Panic-dies on error */ - (void)spool_write_header(message_id, SW_DELIVERING, NULL); - } -else if (errno != ENOENT) - { - log_write(0, LOG_MAIN|LOG_PANIC, "attempt to open journal for reading gave: " - "%s", strerror(errno)); - return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ - } -/* A null recipients list indicates some kind of disaster. */ + /* A null recipients list indicates some kind of disaster. */ -if (!recipients_list) - { - (void)close(deliver_datafile); - deliver_datafile = -1; - log_write(0, LOG_MAIN, "Spool error: no recipients for %s", spoolname); - return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ + if (!recipients_list) + { + (void)close(deliver_datafile); + deliver_datafile = -1; + log_write(0, LOG_MAIN, "Spool error: no recipients for %s", fname); + return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ + } } @@ -5380,16 +5621,14 @@ done by rewriting the header spool file. */ if (message_logs) { - uschar *error; + uschar * fname = spool_fname(US"msglog", message_subdir, id, US""); + uschar * error; int fd; - sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir, id); - fd = open_msglog_file(spoolname, SPOOL_MODE, &error); - - if (fd < 0) + if ((fd = open_msglog_file(fname, SPOOL_MODE, &error)) < 0) { log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't %s message log %s: %s", error, - spoolname, strerror(errno)); + fname, strerror(errno)); return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ } @@ -5398,7 +5637,7 @@ if (message_logs) if (!(message_log = fdopen(fd, "a"))) { log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't fdopen message log %s: %s", - spoolname, strerror(errno)); + fname, strerror(errno)); return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ } } @@ -5736,7 +5975,7 @@ if (process_recipients != RECIP_IGNORE) recipient_item *r = recipients_list + i; address_item *new = deliver_make_addr(r->address, FALSE); new->prop.errors_address = r->errors_to; -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N if ((new->prop.utf8_msg = message_smtputf8)) { new->prop.utf8_downcvt = message_utf8_downconvert == 1; @@ -5827,7 +6066,7 @@ if (process_recipients != RECIP_IGNORE) break; } -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (process_recipients != RECIP_ACCEPT) { uschar * save_local = deliver_localpart; @@ -6617,13 +6856,12 @@ therein are added to the non-recipients. */ if (addr_local || addr_remote) { - sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id); - journal_fd = Uopen(spoolname, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE); - - if (journal_fd < 0) + uschar * fname = spool_fname(US"input", message_subdir, id, US"-J"); + + if ((journal_fd = Uopen(fname, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE)) <0) { log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't open journal file %s: %s", - spoolname, strerror(errno)); + fname, strerror(errno)); return DELIVER_NOT_ATTEMPTED; } @@ -6636,12 +6874,12 @@ if (addr_local || addr_remote) || fchmod(journal_fd, SPOOL_MODE) ) { - int ret = Uunlink(spoolname); + int ret = Uunlink(fname); log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't set perms on journal file %s: %s", - spoolname, strerror(errno)); + fname, strerror(errno)); if(ret && errno != ENOENT) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", - spoolname, strerror(errno)); + fname, strerror(errno)); return DELIVER_NOT_ATTEMPTED; } } @@ -6807,16 +7045,13 @@ prevents actual delivery. */ else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); -/* Send DSN for successful messages */ -addr_dsntmp = addr_succeed; +/* Send DSN for successful messages if requested */ addr_senddsn = NULL; -while(addr_dsntmp) +for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next) { /* af_ignore_error not honored here. it's not an error */ - DEBUG(D_deliver) - { - debug_printf("DSN: processing router : %s\n" + 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" @@ -6831,7 +7066,6 @@ while(addr_dsntmp) 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 */ @@ -6851,8 +7085,6 @@ while(addr_dsntmp) } else DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); - - addr_dsntmp = addr_dsntmp->next; } if (addr_senddsn) @@ -6877,8 +7109,8 @@ if (addr_senddsn) { 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 * bound; + transport_ctx tctx = {0}; DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address); @@ -6926,7 +7158,7 @@ if (addr_senddsn) if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); } fputc('\n', f); @@ -6957,7 +7189,9 @@ if (addr_senddsn) return_path = sender_address; /* In case not previously set */ /* Write the original email out */ - transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); + + tctx.options = topt_add_return_path | topt_no_body; + transport_write_message(fileno(f), &tctx, 0); fflush(f); fprintf(f,"\n--%s--\n", bound); @@ -7292,7 +7526,7 @@ wording. */ } /* output machine readable part */ -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N if (message_smtputf8) fprintf(f, "--%s\n" "Content-type: message/global-delivery-status\n\n" @@ -7312,7 +7546,7 @@ wording. */ if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); } fputc('\n', f); @@ -7326,8 +7560,7 @@ wording. */ if ((hu = addr->host_used) && hu->name) { const uschar * s; - fprintf(f, "Remote-MTA: dns; %s\n", - hu->name); + fprintf(f, "Remote-MTA: dns; %s\n", hu->name); #ifdef EXPERIMENTAL_DSN_INFO if (hu->address) { @@ -7375,6 +7608,9 @@ wording. */ if (dsn_ret == dsn_ret_hdrs) topt |= topt_no_body; else + { + struct stat statbuf; + /* no full body return at all? */ if (!bounce_return_body) { @@ -7383,18 +7619,20 @@ wording. */ if (dsn_ret == dsn_ret_full) dsnnotifyhdr = dsnlimitmsg; } + /* line length limited... return headers only if oversize */ /* size limited ... return headers only if limit reached */ - else if (bounce_return_size_limit > 0) - { - struct stat statbuf; - if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max) - { - topt |= topt_no_body; - dsnnotifyhdr = dsnlimitmsg; - } + else if ( max_received_linelength > bounce_return_linesize_limit + || ( bounce_return_size_limit > 0 + && fstat(deliver_datafile, &statbuf) == 0 + && statbuf.st_size > max + ) ) + { + topt |= topt_no_body; + dsnnotifyhdr = dsnlimitmsg; } + } -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N if (message_smtputf8) fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" : "Content-type: message/global\n\n", @@ -7408,8 +7646,16 @@ wording. */ 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, - 0, dsnnotifyhdr, NULL, NULL, NULL, NULL, 0); + { /* Dummy transport for headers add */ + transport_ctx tctx = {0}; + transport_instance tb = {0}; + + tctx.tblock = &tb; + tctx.options = topt; + tb.add_headers = dsnnotifyhdr; + + transport_write_message(fileno(f), &tctx, 0); + } fflush(f); /* we never add the final text. close the file */ @@ -7484,40 +7730,43 @@ Then delete the message itself. */ if (!addr_defer) { + uschar * fname; + if (message_logs) { - sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir, - id); + fname = spool_fname(US"msglog", message_subdir, id, US""); if (preserve_message_logs) { int rc; - sprintf(CS big_buffer, "%s/msglog.OLD/%s", spool_directory, id); - if ((rc = Urename(spoolname, big_buffer)) < 0) + uschar * moname = spool_fname(US"msglog.OLD", US"", id, US""); + + if ((rc = Urename(fname, moname)) < 0) { - (void)directory_make(spool_directory, US"msglog.OLD", - MSGLOG_DIRECTORY_MODE, TRUE); - rc = Urename(spoolname, big_buffer); + (void)directory_make(spool_directory, + spool_sname(US"msglog.OLD", US""), + MSGLOG_DIRECTORY_MODE, TRUE); + rc = Urename(fname, moname); } if (rc < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to move %s to the " - "msglog.OLD directory", spoolname); + "msglog.OLD directory", fname); } else - if (Uunlink(spoolname) < 0) + if (Uunlink(fname) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", - spoolname, strerror(errno)); + fname, strerror(errno)); } /* Remove the two message files. */ - sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id); - if (Uunlink(spoolname) < 0) + fname = spool_fname(US"input", message_subdir, id, US"-D"); + if (Uunlink(fname) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", - spoolname, strerror(errno)); - sprintf(CS spoolname, "%s/input/%s/%s-H", spool_directory, message_subdir, id); - if (Uunlink(spoolname) < 0) + fname, strerror(errno)); + fname = spool_fname(US"input", message_subdir, id, US"-H"); + if (Uunlink(fname) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", - spoolname, strerror(errno)); + fname, strerror(errno)); /* Log the end of this message, with queue time if requested. */ @@ -7530,7 +7779,7 @@ if (!addr_defer) /* Unset deliver_freeze so that we won't try to move the spool files further down */ deliver_freeze = FALSE; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT (void) event_raise(event_action, US"msg:complete", NULL); #endif } @@ -7722,6 +7971,7 @@ else if (addr_defer != (address_item *)(+1)) FILE *wmf = NULL; FILE *f = fdopen(fd, "wb"); uschar * bound; + transport_ctx tctx = {0}; if (warn_message_file) if (!(wmf = Ufopen(warn_message_file, "rb"))) @@ -7840,7 +8090,7 @@ else if (addr_defer != (address_item *)(+1)) if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); + fprintf(f,"X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); } fputc('\n', f); @@ -7868,11 +8118,12 @@ else if (addr_defer != (address_item *)(+1)) fflush(f); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ - int topt = topt_add_return_path | topt_no_body; + tctx.options = topt_add_return_path | topt_no_body; transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ + /* Write the original email out */ - transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); + transport_write_message(fileno(f), &tctx, 0); fflush(f); fprintf(f,"\n--%s--\n", bound); @@ -7983,9 +8234,10 @@ if (journal_fd >= 0) (void)close(journal_fd); if (remove_journal) { - sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id); - if (Uunlink(spoolname) < 0 && errno != ENOENT) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", spoolname, + uschar * fname = spool_fname(US"input", message_subdir, id, US"-J"); + + if (Uunlink(fname) < 0 && errno != ENOENT) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", fname, strerror(errno)); /* Move the message off the spool if reqested */ @@ -8035,12 +8287,15 @@ if (!regex_STARTTLS) regex_STARTTLS = regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); #endif +if (!regex_CHUNKING) regex_CHUNKING = + regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE); + #ifndef DISABLE_PRDR if (!regex_PRDR) regex_PRDR = regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); #endif -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N if (!regex_UTF8) regex_UTF8 = regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE); #endif @@ -8059,8 +8314,18 @@ deliver_get_sender_address (uschar * id) int rc; uschar * new_sender_address, * save_sender_address; +BOOL save_qr = queue_running; +uschar * spoolname; + +/* make spool_open_datafile non-noisy on fail */ + +queue_running = TRUE; + +/* Side effect: message_subdir is set for the (possibly split) spool directory */ -if (!spool_open_datafile(id)) +deliver_datafile = spool_open_datafile(id); +queue_running = save_qr; +if (deliver_datafile < 0) return NULL; /* Save and restore the global sender_address. I'm not sure if we should @@ -8069,7 +8334,7 @@ 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); +spoolname = string_sprintf("%s-H", id); save_sender_address = sender_address; rc = spool_read_header(spoolname, TRUE, TRUE);