X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/f6c332bd03c89f108c7fe531156cb18d7888ba35..333eea9c862f2368e61fee5ce7231011c19f04ec:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index 81df0e083..55a27b023 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/deliver.c,v 1.37 2006/10/30 16:41:04 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* The main code for delivering a message. */ @@ -624,7 +622,7 @@ for (dup = addr_duplicate; dup != NULL; dup = dup->next) { if (Ustrcmp(addr->unique, dup->unique) == 0) { - tree_add_nonrecipient(dup->address); + tree_add_nonrecipient(dup->unique); child_done(dup, now); } } @@ -675,6 +673,150 @@ while (addr->parent != NULL) +/* If msg is NULL this is a delivery log and logchar is used. Otherwise +this is a nonstandard call; no two-characher delivery flag is written +but sender-host and sender are prefixed and "msg" is inserted in the log line. + +Arguments: + flags passed to log_write() +*/ +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. */ + + +/* 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. */ + +s = reset_point = store_get(size); + +log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE); +if (msg) + s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address); +else + { + s[ptr++] = logchar; + 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">"); + +#ifdef EXPERIMENTAL_SRS +if(addr->p.srs_sender) + s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->p.srs_sender, US">"); +#endif + +/* You might think that the return path must always be set for a successful +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) + s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); + +if (msg) + s = string_append(s, &size, &ptr, 2, US" ", msg); + +/* For a delivery from a system filter, there may not be a router */ +if (addr->router != NULL) + s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); + +s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); + +if ((log_extra_selector & LX_delivery_size) != 0) + s = string_append(s, &size, &ptr, 2, US" S=", + string_sprintf("%d", transport_count)); + +/* Local delivery */ + +if (addr->transport->info->local) + { + if (addr->host_list != NULL) + s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); + if (addr->shadow_message != NULL) + s = string_cat(s, &size, &ptr, addr->shadow_message, + Ustrlen(addr->shadow_message)); + } + +/* Remote delivery */ + +else + { + if (addr->host_used != NULL) + { + s = string_append(s, &size, &ptr, 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, &size, &ptr, 2, US":", string_sprintf("%d", + addr->host_used->port)); + if (continue_sequence > 1) + s = string_cat(s, &size, &ptr, US"*", 1); + } + + #ifdef SUPPORT_TLS + if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL) + s = string_append(s, &size, &ptr, 2, US" X=", addr->cipher); + if ((log_extra_selector & LX_tls_certificate_verified) != 0 && + addr->cipher != NULL) + s = string_append(s, &size, &ptr, 2, US" CV=", + testflag(addr, af_cert_verified)? "yes":"no"); + if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL) + s = string_append(s, &size, &ptr, 3, US" DN=\"", + string_printing(addr->peerdn), US"\""); + #endif + + if ((log_extra_selector & LX_smtp_confirmation) != 0 && + addr->message != NULL) + { + int i; + uschar *p = big_buffer; + uschar *ss = addr->message; + *p++ = '\"'; + for (i = 0; i < 100 && ss[i] != 0; i++) + { + if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; + *p++ = ss[i]; + } + *p++ = '\"'; + *p = 0; + s = string_append(s, &size, &ptr, 2, US" C=", big_buffer); + } + } + +/* Time on queue and actual time taken to deliver */ + +if ((log_extra_selector & LX_queue_time) != 0) + { + s = string_append(s, &size, &ptr, 2, US" QT=", + readconf_printtime(time(NULL) - received_time)); + } + +if ((log_extra_selector & LX_deliver_time) != 0) + { + s = string_append(s, &size, &ptr, 2, US" DT=", + readconf_printtime(addr->more_errno)); + } + +/* string_cat() always leaves room for the terminator. Release the +store we used to build the line after writing it. */ + +s[ptr] = 0; +log_write(0, flags, "%s", s); +store_reset(reset_point); +return; +} + + + /************************************************* * Actions at the end of handling an address * *************************************************/ @@ -744,17 +886,15 @@ malformed, it won't ever have gone near LDAP.) */ if (addr->message != NULL) { addr->message = string_printing(addr->message); - if (Ustrstr(addr->message, "failed to expand") != NULL && - (Ustrstr(addr->message, "ldap:") != NULL || + if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && + (Ustrstr(addr->message, "mysql") != NULL || + Ustrstr(addr->message, "pgsql") != NULL || + Ustrstr(addr->message, "sqlite") != NULL || + Ustrstr(addr->message, "ldap:") != NULL || Ustrstr(addr->message, "ldapdn:") != NULL || Ustrstr(addr->message, "ldapm:") != NULL)) { - uschar *p = Ustrstr(addr->message, "pass="); - if (p != NULL) - { - p += 5; - while (*p != 0 && !isspace(*p)) *p++ = 'x'; - } + addr->message = string_sprintf("Temporary internal error"); } } @@ -774,7 +914,7 @@ if (addr->return_file >= 0 && addr->return_filename != NULL) { BOOL return_output = FALSE; struct stat statbuf; - fsync(addr->return_file); + (void)EXIMfsync(addr->return_file); /* If there is no output, do nothing. */ @@ -839,12 +979,6 @@ if (addr->return_file >= 0 && addr->return_filename != NULL) (void)close(addr->return_file); } -/* 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); - /* The sucess case happens only after delivery by a transport. */ if (result == OK) @@ -872,119 +1006,7 @@ if (result == OK) child_done(addr, now); } - /* 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. */ - - s = reset_point = store_get(size); - s[ptr++] = logchar; - - s = string_append(s, &size, &ptr, 2, US"> ", log_address); - - if ((log_extra_selector & LX_sender_on_delivery) != 0) - s = string_append(s, &size, &ptr, 3, US" F=<", 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">"); - #endif - - /* You might think that the return path must always be set for a successful - 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) - s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); - - /* For a delivery from a system filter, there may not be a router */ - - if (addr->router != NULL) - s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); - - s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); - - if ((log_extra_selector & LX_delivery_size) != 0) - s = string_append(s, &size, &ptr, 2, US" S=", - string_sprintf("%d", transport_count)); - - /* Local delivery */ - - if (addr->transport->info->local) - { - if (addr->host_list != NULL) - s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); - if (addr->shadow_message != NULL) - s = string_cat(s, &size, &ptr, addr->shadow_message, - Ustrlen(addr->shadow_message)); - } - - /* Remote delivery */ - - else - { - if (addr->host_used != NULL) - { - s = string_append(s, &size, &ptr, 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, &size, &ptr, 2, US":", string_sprintf("%d", - addr->host_used->port)); - if (continue_sequence > 1) - s = string_cat(s, &size, &ptr, US"*", 1); - } - - #ifdef SUPPORT_TLS - if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL) - s = string_append(s, &size, &ptr, 2, US" X=", addr->cipher); - if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - addr->cipher != NULL) - s = string_append(s, &size, &ptr, 2, US" CV=", - testflag(addr, af_cert_verified)? "yes":"no"); - if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL) - s = string_append(s, &size, &ptr, 3, US" DN=\"", addr->peerdn, US"\""); - #endif - - if ((log_extra_selector & LX_smtp_confirmation) != 0 && - addr->message != NULL) - { - int i; - uschar *p = big_buffer; - uschar *ss = addr->message; - *p++ = '\"'; - for (i = 0; i < 100 && ss[i] != 0; i++) - { - if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; - *p++ = ss[i]; - } - *p++ = '\"'; - *p = 0; - s = string_append(s, &size, &ptr, 2, US" C=", big_buffer); - } - } - - /* Time on queue and actual time taken to deliver */ - - if ((log_extra_selector & LX_queue_time) != 0) - { - s = string_append(s, &size, &ptr, 2, US" QT=", - readconf_printtime(time(NULL) - received_time)); - } - - if ((log_extra_selector & LX_deliver_time) != 0) - { - s = string_append(s, &size, &ptr, 2, US" DT=", - readconf_printtime(addr->more_errno)); - } - - /* string_cat() always leaves room for the terminator. Release the - store we used to build the line after writing it. */ - - s[ptr] = 0; - log_write(0, LOG_MAIN, "%s", s); - store_reset(reset_point); + delivery_log(LOG_MAIN, addr, logchar, NULL); } @@ -1032,6 +1054,13 @@ else if (result == DEFER || result == PANIC) 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, + (log_write_selector & L_all_parents) != 0, result == OK); + s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); /* Either driver_name contains something and driver_kind contains @@ -1132,6 +1161,13 @@ else /* 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, + (log_write_selector & L_all_parents) != 0, result == OK); + s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); if ((log_extra_selector & LX_sender_on_delivery) != 0) @@ -1216,7 +1252,7 @@ if (format != NULL) va_start(ap, format); if (!string_vformat(buffer, sizeof(buffer), CS format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "common_error expansion was longer than %d", sizeof(buffer)); + "common_error expansion was longer than " SIZE_T_FMT, sizeof(buffer)); va_end(ap); addr->message = string_copy(buffer); } @@ -1726,7 +1762,20 @@ if ((pid = fork()) == 0) HP-UX doesn't have RLIMIT_CORE; I don't know how to do this in that system. Some experimental/developing systems (e.g. GNU/Hurd) may define RLIMIT_CORE but not support it in setrlimit(). For such systems, do not - complain if the error is "not supported". */ + complain if the error is "not supported". + + There are two scenarios where changing the max limit has an effect. In one, + the user is using a .forward and invoking a command of their choice via pipe; + for these, we do need the max limit to be 0 unless the admin chooses to + permit an increased limit. In the other, the command is invoked directly by + the transport and is under administrator control, thus being able to raise + the limit aids in debugging. So there's no general always-right answer. + + Thus we inhibit core-dumps completely but let individual transports, while + still root, re-raise the limits back up to aid debugging. We make the + default be no core-dumps -- few enough people can use core dumps in + diagnosis that it's reasonable to make them something that has to be explicitly requested. + */ #ifdef RLIMIT_CORE struct rlimit rl; @@ -1979,7 +2028,7 @@ if (!shadowing) /* Ensure the journal file is pushed out to disk. */ - if (fsync(journal_fd) < 0) + if (EXIMfsync(journal_fd) < 0) log_write(0, LOG_MAIN|LOG_PANIC, "failed to fsync journal: %s", strerror(errno)); } @@ -2043,9 +2092,7 @@ if (addr->special_action == SPECIAL_WARN && !contains_header(US"Reply-To", warn_message)) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); - if (!contains_header(US"From", warn_message)) - fprintf(f, "From: Mail Delivery System \n", - qualify_domain_sender); + if (!contains_header(US"From", warn_message)) moan_write_from(f); fprintf(f, "%s", CS warn_message); /* Close and wait for child process to complete, without a timeout. */ @@ -3915,7 +3962,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* The certificate verification status goes into the flags */ - if (tls_certificate_verified) setflag(addr, af_cert_verified); + if (tls_out.certificate_verified) setflag(addr, af_cert_verified); /* Use an X item only if there's something to send */ @@ -4808,6 +4855,7 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) RDO_REWRITE, NULL, /* No :include: restriction (not used in filter) */ NULL, /* No sieve vacation directory (not sieve!) */ + NULL, /* No sieve enotify mailto owner (not sieve!) */ NULL, /* No sieve user address (not sieve!) */ NULL, /* No sieve subaddress (not sieve!) */ &ugid, /* uid/gid data */ @@ -4941,6 +4989,9 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) while (p != NULL) { + if (parent->child_count == SHRT_MAX) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "system filter generated more " + "than %d delivery addresses", SHRT_MAX); parent->child_count++; p->parent = parent; @@ -5452,8 +5503,10 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ } /* Get the routing retry status, saving the two retry keys (with and - without the local part) for subsequent use. Ignore retry records that - are too old. */ + without the local part) for subsequent use. If there is no retry record for + the standard address routing retry key, we look for the same key with the + sender attached, because this form is used by the smtp transport after a + 4xx response to RCPT when address_retry_include_sender is true. */ addr->domain_retry_key = string_sprintf("R:%s", addr->domain); addr->address_retry_key = string_sprintf("R:%s@%s", addr->local_part, @@ -5466,12 +5519,22 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key); if (domain_retry_record != NULL && now - domain_retry_record->time_stamp > retry_data_expire) - domain_retry_record = NULL; + domain_retry_record = NULL; /* Ignore if too old */ address_retry_record = dbfn_read(dbm_file, addr->address_retry_key); if (address_retry_record != NULL && now - address_retry_record->time_stamp > retry_data_expire) - address_retry_record = NULL; + address_retry_record = NULL; /* Ignore if too old */ + + if (address_retry_record == NULL) + { + uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, + sender_address); + address_retry_record = dbfn_read(dbm_file, altkey); + if (address_retry_record != NULL && + now - address_retry_record->time_stamp > retry_data_expire) + address_retry_record = NULL; /* Ignore if too old */ + } } DEBUG(D_deliver|D_retry) @@ -5624,12 +5687,16 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ string_sprintf("R:%s", addr->domain), 0); /* Otherwise, if there is an existing retry record in the database, add - retry items to delete both forms. Since the domain might have been - rewritten (expanded to fully qualified) as a result of routing, ensure - that the rewritten form is also deleted. */ + retry items to delete both forms. We must also allow for the possibility + of a routing retry that includes the sender address. Since the domain might + have been rewritten (expanded to fully qualified) as a result of routing, + ensure that the rewritten form is also deleted. */ else if (testflag(addr, af_dr_retry_exists)) { + uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, + sender_address); + retry_add_item(addr, altkey, rf_delete); retry_add_item(addr, addr->address_retry_key, rf_delete); retry_add_item(addr, addr->domain_retry_key, rf_delete); if (Ustrcmp(addr->domain, old_domain) != 0) @@ -6233,8 +6300,7 @@ while (addr_failed != NULL) if (errors_reply_to != NULL) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); - fprintf(f, "From: Mail Delivery System \n", - qualify_domain_sender); + moan_write_from(f); fprintf(f, "To: %s\n", bounce_recipient); /* Open a template file if one is provided. Log failure to open, but @@ -6537,7 +6603,8 @@ if (addr_defer == NULL) else { if (Uunlink(spoolname) < 0) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", + spoolname, strerror(errno)); } } @@ -6545,10 +6612,12 @@ if (addr_defer == NULL) sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id); if (Uunlink(spoolname) < 0) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname); + 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) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", + spoolname, strerror(errno)); /* Log the end of this message, with queue time if requested. */ @@ -6557,6 +6626,9 @@ if (addr_defer == NULL) readconf_printtime(time(NULL) - received_time)); else log_write(0, LOG_MAIN, "Completed"); + + /* Unset deliver_freeze so that we won't try to move the spool files further down */ + deliver_freeze = FALSE; } /* If there are deferred addresses, we are keeping this message because it is @@ -6755,8 +6827,7 @@ else if (addr_defer != (address_item *)(+1)) if (errors_reply_to != NULL) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); - fprintf(f, "From: Mail Delivery System \n", - qualify_domain_sender); + moan_write_from(f); fprintf(f, "To: %s\n", recipients); wmf_text = next_emf(wmf, US"header");