-/* $Cambridge: exim/src/src/deliver.c,v 1.20 2005/06/27 14:29:43 ph10 Exp $ */
+/* $Cambridge: exim/src/src/deliver.c,v 1.29 2006/02/21 16:24:19 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2006 */
/* See the file NOTICE for conditions of use and distribution. */
/* The main code for delivering a message. */
}
}
+/* If the transport did not set a group, see if the router did. */
+
+if (!gid_set && testflag(addr, af_gid_set))
+ {
+ *gidp = addr->gid;
+ gid_set = TRUE;
+ }
+
/* Pick up a uid from the transport if one is set. */
if (tp->uid_set) *uidp = tp->uid;
}
}
-/* Otherwise see if the address specifies the uid and if so, take its
-initgroups flag. The gid from the address is taken only if the transport hasn't
-set it. In other words, a gid on the transport overrides the gid on the
-address. */
+/* Otherwise see if the address specifies the uid and if so, take it and its
+initgroups flag. */
else if (testflag(addr, af_uid_set))
{
*uidp = addr->uid;
*igfp = testflag(addr, af_initgroups);
- if (!gid_set)
- {
- *gidp = addr->gid;
- gid_set = TRUE;
- }
}
/* Nothing has specified the uid - default to the Exim user, and group if the
}
}
-/* If no gid is set, it is a disaster. */
+/* If no gid is set, it is a disaster. We default to the Exim gid only if
+defaulting to the Exim uid. In other words, if the configuration has specified
+a uid, it must also provide a gid. */
if (!gid_set)
{
+/******************************************************
+* 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-generated\n");
- fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
- qualify_domain_sender);
+ fprintf(f, "Auto-Submitted: auto-replied\n");
+ 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. */
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
if (testflag(addr, af_pfr))
{
- int offset = testflag(addr->parent, af_homonym)? 3:0;
+ /* If an autoreply in a filter could not generate a syntactically valid
+ address, give up forthwith. Set af_ignore_error so that we don't try to
+ generate a bounce. */
+
+ if (testflag(addr, af_bad_reply))
+ {
+ addr->basic_errno = ERRNO_BADADDRESS2;
+ addr->local_part = addr->address;
+ addr->message =
+ US"filter autoreply generated syntactically invalid recipient";
+ setflag(addr, af_ignore_error);
+ (void)post_process_one(addr, FAIL, LOG_MAIN, DTYPE_ROUTER, 0);
+ continue; /* with the next new address */
+ }
/* If two different users specify delivery to the same pipe or file or
autoreply, there should be two different deliveries, so build a unique
duplicate testing and recording delivery, and also for retrying. */
addr->unique =
- string_sprintf("%s:%s", addr->address, addr->parent->unique + offset);
+ string_sprintf("%s:%s", addr->address, addr->parent->unique +
+ (testflag(addr->parent, af_homonym)? 3:0));
addr->address_retry_key = addr->domain_retry_key =
string_sprintf("T:%s", addr->unique);
handle fallback transports are figured out, this section can be put into a loop
for handling fallbacks, though the uid switching will have to be revised. */
+/* Precompile a regex that is used to recognize a parameter in response
+to an LHLO command, if is isn't already compiled. This may be used on both
+local and remote LMTP deliveries. */
+
+if (regex_IGNOREQUOTA == NULL) regex_IGNOREQUOTA =
+ regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+
+/* Handle local deliveries */
+
if (addr_local != NULL)
{
DEBUG(D_deliver|D_transport)
signal(SIGTERM, SIG_IGN);
/* When we are acting as an MUA wrapper, the smtp transport will either have
-succeeded for all addresses, or failed them all. We do not ever want to retry,
-nor do we want to send a bounce message. */
+succeeded for all addresses, or failed them all in normal cases. However, there
+are some setup situations (e.g. when a named port does not exist) that cause an
+immediate exit with deferral of all addresses. Convert those into failures. We
+do not ever want to retry, nor do we want to send a bounce message. */
if (mua_wrapper)
{
+ if (addr_defer != NULL)
+ {
+ address_item *addr, *nextaddr;
+ for (addr = addr_defer; addr != NULL; addr = nextaddr)
+ {
+ log_write(0, LOG_MAIN, "** %s mua_wrapper forced failure for deferred "
+ "delivery", addr->address);
+ nextaddr = addr->next;
+ addr->next = addr_failed;
+ addr_failed = addr;
+ }
+ addr_defer = NULL;
+ }
+
+ /* Now all should either have succeeded or failed. */
+
if (addr_failed == NULL) final_yield = DELIVER_MUA_SUCCEEDED; else
{
uschar *s = (addr_failed->user_message != NULL)?
if (errors_reply_to != NULL)
fprintf(f, "Reply-To: %s\n", errors_reply_to);
- fprintf(f, "Auto-Submitted: auto-generated\n");
+ fprintf(f, "Auto-Submitted: auto-replied\n");
fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
qualify_domain_sender);
fprintf(f, "To: %s\n", bounce_recipient);
if (errors_reply_to != NULL)
fprintf(f, "Reply-To: %s\n", errors_reply_to);
- fprintf(f, "Auto-Submitted: auto-generated\n");
+ fprintf(f, "Auto-Submitted: auto-replied\n");
fprintf(f, "From: Mail Delivery System <Mailer-Daemon@%s>\n",
qualify_domain_sender);
fprintf(f, "To: %s\n", recipients);