-/* $Cambridge: exim/src/src/deliver.c,v 1.26 2006/02/07 11:19:00 ph10 Exp $ */
+/* $Cambridge: exim/src/src/deliver.c,v 1.31 2006/04/20 14:11:29 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
}
/* For multiple addresses, don't set local part, and leave the domain and
-self_hostname set only if it is the same for all of them. */
+self_hostname set only if it is the same for all of them. It is possible to
+have multiple pipe and file addresses, but only when all addresses have routed
+to the same pipe or file. */
else
{
address_item *addr2;
+ if (testflag(addr, af_pfr))
+ {
+ if (testflag(addr, af_file)) address_file = addr->local_part;
+ else if (addr->local_part[0] == '|') address_pipe = addr->local_part;
+ }
for (addr2 = addr->next; addr2 != NULL; addr2 = addr2->next)
{
if (deliver_domain != NULL &&
+/******************************************************
+* Check for a given header in a header string *
+******************************************************/
+
+/* This function is used when generating quota warnings. The configuration may
+specify any header lines it likes in quota_warn_message. If certain of them are
+missing, defaults are inserted, so we need to be able to test for the presence
+of a given header.
+
+Arguments:
+ hdr the required header name
+ hstring the header string
+
+Returns: TRUE the header is in the string
+ FALSE the header is not in the string
+*/
+
+static BOOL
+contains_header(uschar *hdr, uschar *hstring)
+{
+int len = Ustrlen(hdr);
+uschar *p = hstring;
+while (*p != 0)
+ {
+ if (strncmpic(p, hdr, len) == 0)
+ {
+ p += len;
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == ':') return TRUE;
+ }
+ while (*p != 0 && *p != '\n') p++;
+ if (*p == '\n') p++;
+ }
+return FALSE;
+}
+
+
+
/*************************************************
* Perform a local delivery *
if (addr->transport->setup != NULL)
{
- switch((addr->transport->setup)(addr->transport, addr, NULL,
+ switch((addr->transport->setup)(addr->transport, addr, NULL, uid, gid,
&(addr->message)))
{
case DEFER:
if (pid > 0)
{
FILE *f = fdopen(fd, "wb");
-
- if (errors_reply_to != NULL)
+ if (errors_reply_to != NULL &&
+ !contains_header(US"Reply-To", warn_message))
fprintf(f, "Reply-To: %s\n", errors_reply_to);
fprintf(f, "Auto-Submitted: auto-replied\n");
- fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
- qualify_domain_sender);
+ if (!contains_header(US"From", warn_message))
+ fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
+ qualify_domain_sender);
fprintf(f, "%s", CS warn_message);
/* Close and wait for child process to complete, without a timeout. */
disable_logging = tp->disable_logging;
- /* Check for batched addresses and possible amalgamation. File deliveries can
- never be batched. Skip all the work if either batch_max <= 1 or there aren't
- any other addresses for local delivery. */
+ /* Check for batched addresses and possible amalgamation. Skip all the work
+ if either batch_max <= 1 or there aren't any other addresses for local
+ delivery. */
- if (!testflag(addr, af_file) && tp->batch_max > 1 && addr_local != NULL)
+ if (tp->batch_max > 1 && addr_local != NULL)
{
int batch_count = 1;
BOOL uses_dom = readconf_depends((driver_instance *)tp, US"domain");
- BOOL uses_lp = readconf_depends((driver_instance *)tp, US"local_part");
+ BOOL uses_lp = (testflag(addr, af_pfr) &&
+ (testflag(addr, af_file) || addr->local_part[0] == '|')) ||
+ readconf_depends((driver_instance *)tp, US"local_part");
uschar *batch_id = NULL;
address_item **anchor = &addr_local;
address_item *last = addr;
same transport
not previously delivered (see comment about 50 lines above)
same local part if the transport's configuration contains $local_part
+ or if this is a file or pipe delivery from a redirection
same domain if the transport's configuration contains $domain
same errors address
same additional headers
BOOL ok =
tp == next->transport &&
!previously_transported(next, TRUE) &&
+ (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) &&
DEBUG(D_retry)
{
- debug_printf("retry record exists: age=%d (max=%d)\n",
- (int)(now - retry_record->time_stamp), retry_data_expire);
- debug_printf(" time to retry = %d expired = %d\n",
- (int)(now - retry_record->next_try), retry_record->expired);
+ debug_printf("retry record exists: age=%s ",
+ readconf_printtime(now - retry_record->time_stamp));
+ debug_printf("(max %s)\n", readconf_printtime(retry_data_expire));
+ debug_printf(" time to retry = %s expired = %d\n",
+ readconf_printtime(retry_record->next_try - now),
+ retry_record->expired);
}
if (queue_running && !deliver_force)
for (last_rule = retry->rules;
last_rule->next != NULL;
last_rule = last_rule->next);
+ DEBUG(D_deliver|D_retry)
+ debug_printf("now=%d received_time=%d diff=%d timeout=%d\n",
+ (int)now, received_time, (int)now - received_time,
+ last_rule->timeout);
if (now - received_time > last_rule->timeout) ok = TRUE;
}
- else ok = TRUE; /* No rule => timed out */
+ else
+ {
+ DEBUG(D_deliver|D_retry)
+ debug_printf("no retry rule found: assume timed out\n");
+ ok = TRUE; /* No rule => timed out */
+ }
DEBUG(D_deliver|D_retry)
{
else return_path = new_return_path;
}
+ /* Find the uid, gid, and use_initgroups setting for this transport. Failure
+ logs and sets up error messages, so we just post-process and continue with
+ the next address. */
+
+ if (!findugid(addr, tp, &uid, &gid, &use_initgroups))
+ {
+ remote_post_process(addr, LOG_MAIN|LOG_PANIC, NULL, fallback);
+ continue;
+ }
+
/* If this transport has a setup function, call it now so that it gets
run in this process and not in any subprocess. That way, the results of
- any setup that are retained by the transport can be reusable. */
+ any setup that are retained by the transport can be reusable. One of the
+ things the setup does is to set the fallback host lists in the addresses.
+ That is why it is called at this point, before the continue delivery
+ processing, because that might use the fallback hosts. */
if (tp->setup != NULL)
- (void)((tp->setup)(addr->transport, addr, NULL, NULL));
+ (void)((tp->setup)(addr->transport, addr, NULL, uid, gid, NULL));
/* If this is a run to continue delivery down an already-established
channel, check that this set of addresses matches the transport and
transport_filter_argv = NULL;
- /* Find the uid, gid, and use_initgroups setting for this transport. Failure
- logs and sets up error messages, so we just post-process and continue with
- the next address. */
-
- if (!findugid(addr, tp, &uid, &gid, &use_initgroups))
- {
- remote_post_process(addr, LOG_MAIN|LOG_PANIC, NULL, fallback);
- continue;
- }
-
/* Create the pipe for inter-process communication. If pipe creation
fails, it is probably because the value of remote_max_parallel is so
large that too many file descriptors for pipes have been created. Arrange
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.
+to cases such as LMTP/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 the address
f the FILE to print on
- s some leading text
+ t some leading text
Returns: nothing
*/
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;
+uschar *s = testflag(addr, af_pass_message)? addr->message : NULL;
-if (addr->user_message != NULL)
- s = addr->user_message;
-else
+if (s == NULL)
{
- if (!testflag(addr, af_pass_message) || addr->message == NULL) return;
- s = addr->message;
+ if (addr->user_message != NULL) s = addr->user_message; else return;
}
fprintf(f, "\n %s", t);