-/* $Cambridge: exim/src/src/deliver.c,v 1.7 2005/02/17 11:58:25 ph10 Exp $ */
+/* $Cambridge: exim/src/src/deliver.c,v 1.16 2005/06/07 15:20:56 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
/* If there's an error message set, ensure that it contains only printing
characters - it should, but occasionally things slip in and this at least
-stops the log format from getting wrecked. */
+stops the log format from getting wrecked. We also scan the message for an LDAP
+expansion item that has a password setting, and flatten the password. This is a
+fudge, but I don't know a cleaner way of doing this. (If the item is badly
+malformed, it won't ever have gone near LDAP.) */
-if (addr->message != NULL) addr->message = string_printing(addr->message);
+if (addr->message != NULL)
+ {
+ addr->message = string_printing(addr->message);
+ if (Ustrstr(addr->message, "failed to expand") != 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';
+ }
+ }
+ }
/* If we used a transport that has one of the "return_output" options set, and
if it did in fact generate some output, then for return_output we treat the
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
*************************************************/
/* Check that this base address hasn't previously been delivered to its routed
-transport. The check is necessary at delivery time in order to handle homonymic
-addresses correctly in cases where the pattern of redirection changes between
-delivery attempts (so the unique fields change). Non-homonymic previous
-delivery is detected earlier, at routing time (which saves unnecessary
-routing).
+transport. If it has been delivered, mark it done. The check is necessary at
+delivery time in order to handle homonymic addresses correctly in cases where
+the pattern of redirection changes between delivery attempts (so the unique
+fields change). Non-homonymic previous delivery is detected earlier, at routing
+time (which saves unnecessary routing).
+
+Arguments:
+ addr the address item
+ testing TRUE if testing wanted only, without side effects
-Argument: the address item
Returns: TRUE if previously delivered by the transport
*/
static BOOL
-previously_transported(address_item *addr)
+previously_transported(address_item *addr, BOOL testing)
{
(void)string_format(big_buffer, big_buffer_size, "%s/%s",
addr->unique + (testflag(addr, af_homonym)? 3:0), addr->transport->name);
DEBUG(D_deliver|D_route|D_transport)
debug_printf("%s was previously delivered (%s transport): discarded\n",
addr->address, addr->transport->name);
- child_done(addr, tod_stamp(tod_log));
+ if (!testing) child_done(addr, tod_stamp(tod_log));
return TRUE;
}
/* 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. */
-return_path = (addr->p.errors_address != NULL)?
- addr->p.errors_address : sender_address;
+if(addr->p.errors_address != NULL)
+ return_path = addr->p.errors_address;
+#ifdef EXPERIMENTAL_SRS
+else if(addr->p.srs_sender != NULL)
+ return_path = addr->p.srs_sender;
+#endif
+else
+ return_path = sender_address;
if (tp->return_path != NULL)
{
attempts. Non-homonymic previous delivery is detected earlier, at routing
time. */
- if (previously_transported(addr)) continue;
+ if (previously_transported(addr, FALSE)) continue;
/* There are weird cases where logging is disabled */
same characteristics. These are:
same transport
+ not previously delivered (see comment about 50 lines above)
same local part if the transport's configuration contains $local_part
same domain if the transport's configuration contains $domain
same errors address
{
BOOL ok =
tp == next->transport &&
+ !previously_transported(next, TRUE) &&
(!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) &&
attempts. Non-homonymic previous delivery is detected earlier, at routing
time. */
- if (previously_transported(addr)) continue;
+ if (previously_transported(addr, FALSE)) continue;
/* Force failure if the message is too big. */
/* 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. */
- return_path = (addr->p.errors_address != NULL)?
- addr->p.errors_address : sender_address;
+ if(addr->p.errors_address != NULL)
+ return_path = addr->p.errors_address;
+#ifdef EXPERIMENTAL_SRS
+ else if(addr->p.srs_sender != NULL)
+ return_path = addr->p.srs_sender;
+#endif
+ else
+ return_path = sender_address;
if (tp->return_path != NULL)
{
printed = US"an undisclosed address";
yield = FALSE;
}
-
else if (!testflag(addr, af_pfr) || addr->parent == NULL)
printed = addr->address;
-
/*************************************************
* Print error for an address *
*************************************************/
introducing newlines. All lines are indented by 4; the initial printing
position must be set before calling.
+This function used always to print the error. Nowadays we want to restrict it
+to cases such as SMTP errors from a remote host, and errors from :fail: and
+filter "fail". We no longer pass other information willy-nilly in bounce and
+warning messages. Text in user_message is always output; text in message only
+if the af_pass_message flag is set.
+
Arguments:
- addr points to the address
+ addr the address
f the FILE to print on
+ s some leading text
Returns: nothing
*/
static void
-print_address_error(address_item *addr, FILE *f)
+print_address_error(address_item *addr, FILE *f, uschar *t)
{
+int count = Ustrlen(t);
uschar *s = (addr->user_message != NULL)? addr->user_message : addr->message;
-if (addr->basic_errno > 0)
- {
- fprintf(f, "%s%s", strerror(addr->basic_errno),
- (s == NULL)? "" : ":\n ");
- }
-if (s == NULL)
+
+if (addr->user_message != NULL)
+ s = addr->user_message;
+else
{
- if (addr->basic_errno <= 0) fprintf(f, "unknown error");
+ if (!testflag(addr, af_pass_message) || addr->message == NULL) return;
+ s = addr->message;
}
-else
+
+fprintf(f, "\n %s", t);
+
+while (*s != 0)
{
- int count = 0;
- while (*s != 0)
+ if (*s == '\\' && s[1] == 'n')
{
- if (*s == '\\' && s[1] == 'n')
+ fprintf(f, "\n ");
+ s += 2;
+ count = 0;
+ }
+ else
+ {
+ fputc(*s, f);
+ count++;
+ if (*s++ == ':' && isspace(*s) && count > 45)
{
- fprintf(f, "\n ");
- s += 2;
+ fprintf(f, "\n "); /* sic (because space follows) */
count = 0;
}
- else
- {
- fputc(*s, f);
- count++;
- if (*s++ == ':' && isspace(*s) && count > 45)
- {
- fprintf(f, "\n "); /* sic (because space follows) */
- count = 0;
- }
- }
+ }
+ }
+}
+
+
+
+
+
+
+/*************************************************
+* Check list of addresses for duplication *
+*************************************************/
+
+/* This function was introduced when the test for duplicate addresses that are
+not pipes, files, or autoreplies was moved from the middle of routing to when
+routing was complete. That was to fix obscure cases when the routing history
+affects the subsequent routing of identical addresses. If that change has to be
+reversed, this function is no longer needed. For a while, the old code that was
+affected by this change is commented with !!!OLD-DE-DUP!!! so it can be found
+easily.
+
+This function is called after routing, to check that the final routed addresses
+are not duplicates. If we detect a duplicate, we remember what it is a
+duplicate of. Note that pipe, file, and autoreply de-duplication is handled
+during routing, so we must leave such "addresses" alone here, as otherwise they
+will incorrectly be discarded.
+
+Argument: address of list anchor
+Returns: nothing
+*/
+
+static void
+do_duplicate_check(address_item **anchor)
+{
+address_item *addr;
+while ((addr = *anchor) != NULL)
+ {
+ tree_node *tnode;
+ if (testflag(addr, af_pfr))
+ {
+ anchor = &(addr->next);
+ }
+ else if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL)
+ {
+ DEBUG(D_deliver|D_route)
+ debug_printf("%s is a duplicate address: discarded\n", addr->unique);
+ *anchor = addr->next;
+ addr->dupof = tnode->data.ptr;
+ addr->next = addr_duplicate;
+ addr_duplicate = addr;
+ }
+ else
+ {
+ tree_add_duplicate(addr->unique, addr);
+ anchor = &(addr->next);
}
}
}
sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
spoolname);
if (Ustat(big_buffer, &statbuf) == 0)
- {
- int size = statbuf.st_size; /* Because might be a long */
- log_write(0, LOG_MAIN, "Format error in spool file %s: size=%d",
- spoolname, size);
- }
+ log_write(0, LOG_MAIN, "Format error in spool file %s: size=%.30g",
+ spoolname, (double)statbuf.st_size);
else log_write(0, LOG_MAIN, "Format error in spool file %s", spoolname);
}
else
RDO_REWRITE,
NULL, /* No :include: restriction (not used in filter) */
NULL, /* No sieve vacation directory (not sieve!) */
+ NULL, /* No sieve user address (not sieve!) */
+ NULL, /* No sieve subaddress (not sieve!) */
&ugid, /* uid/gid data */
&addr_new, /* Where to hang generated addresses */
&filter_message, /* Where to put error message */
case RECIP_FAIL_FILTER:
new->message =
(filter_message == NULL)? US"delivery cancelled" : filter_message;
+ setflag(new, af_pass_message);
goto RECIP_QUEUE_FAILED; /* below */
continue;
}
+
+ /* !!!OLD-DE-DUP!!! We used to test for duplicates at this point, in order
+ to save effort on routing duplicate addresses. However, facilities have
+ been added to Exim so that now two identical addresses that are children of
+ other addresses may be routed differently as a result of their previous
+ routing history. For example, different redirect routers may have given
+ them different redirect_router values, but there are other cases too.
+ Therefore, tests for duplicates now take place when routing is complete.
+ This is the old code, kept for a while for the record, and in case this
+ radical change has to be backed out for some reason. */
+
+ #ifdef NEVER
/* If it's a duplicate, remember what it's a duplicate of */
if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL)
/* Record this address, so subsequent duplicates get picked up. */
tree_add_duplicate(addr->unique, addr);
+ #endif
+
+
/* Get the routing retry status, saving the two retry keys (with and
without the local part) for subsequent use. Ignore retry records that
local_user_gid = (gid_t)(-1);
local_user_uid = (uid_t)(-1);
+
+/* !!!OLD-DE-DUP!!! The next two statement were introduced when checking for
+duplicates was moved from within routing to afterwards. If that change has to
+be backed out, they should be removed. */
+
+/* Check for any duplicate addresses. This check is delayed until after
+routing, because the flexibility of the routing configuration means that
+identical addresses with different parentage may end up being redirected to
+different addresses. Checking for duplicates too early (as we previously used
+to) makes this kind of thing not work. */
+
+do_duplicate_check(&addr_local);
+do_duplicate_check(&addr_remote);
+
+
/* When acting as an MUA wrapper, we proceed only if all addresses route to a
remote transport. The check that they all end up in one transaction happens in
the do_remote_deliveries() function. */
/* Process the addresses, leaving them on the msgchain if they have a
file name for a return message. (There has already been a check in
- post_process_one() for the existence of data in the message file.) */
+ post_process_one() for the existence of data in the message file.) A TRUE
+ return from print_address_information() means that the address is not
+ hidden. */
paddr = &msgchain;
for (addr = msgchain; addr != NULL; addr = *paddr)
{
if (print_address_information(addr, f, US" ", US"\n ", US""))
- {
- /* A TRUE return from print_address_information() means that the
- address is not hidden. If there is a return file, it has already
- been checked to ensure it is not empty. Omit the bland "return
- message generated" error, but otherwise include error information. */
-
- if (addr->return_file < 0 ||
- addr->message == NULL ||
- Ustrcmp(addr->message, "return message generated") != 0)
- {
- fprintf(f, "\n ");
- print_address_error(addr, f);
- }
- }
+ print_address_error(addr, f, US"");
/* End the final line for the address */
if (emf_text != NULL) fprintf(f, "%s", CS emf_text); else
{
fprintf(f,
-"------ The body of the message is %d characters long; only the first\n"
-"------ %d or so are included here.\n", (int)statbuf.st_size, max);
+"------ The body of the message is %.30g characters long; only the first\n"
+"------ %d or so are included here.\n", (double)statbuf.st_size, max);
}
}
}
(addr_defer->next == NULL)? "is": "are");
}
- /* List the addresses. For any that are hidden, don't give the delay
- reason, because it might expose that which is hidden. Also, do not give
- "retry time not reached" because that isn't helpful. */
+ /* List the addresses, with error information if allowed */
fprintf(f, "\n");
while (addr_defer != NULL)
{
address_item *addr = addr_defer;
addr_defer = addr->next;
- if (print_address_information(addr, f, US" ", US"\n ", US"") &&
- addr->basic_errno > ERRNO_RETRY_BASE)
- {
- fprintf(f, "\n Delay reason: ");
- print_address_error(addr, f);
- }
+ if (print_address_information(addr, f, US" ", US"\n ", US""))
+ print_address_error(addr, f, US"Delay reason: ");
fprintf(f, "\n");
}
fprintf(f, "\n");