Multiple headers_add/remove options per router/transport - fixes bug 337
[exim.git] / src / src / route.c
index c5594b27c6bdd4ddb8a99dc1eb79e4ced49ba8c1..32dbd60abd04ea207d7f988f56f61dd21bf207d5 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/route.c,v 1.8 2006/02/07 11:19:00 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. */
 
 /* Functions concerned with routing, and the list of generic router options. */
@@ -50,7 +48,7 @@ optionlist optionlist_routers[] = {
                  (void *)(offsetof(router_instance, caseful_local_part)) },
   { "check_local_user",   opt_bool | opt_public,
                  (void *)(offsetof(router_instance, check_local_user)) },
-  { "condition",          opt_stringptr|opt_public,
+  { "condition",          opt_stringptr|opt_public|opt_rep_con,
                  (void *)offsetof(router_instance, condition) },
   { "debug_print",        opt_stringptr | opt_public,
                  (void *)offsetof(router_instance, debug_string) },
@@ -74,9 +72,9 @@ optionlist optionlist_routers[] = {
                  (void *)offsetof(router_instance, fallback_hosts) },
   { "group",              opt_expand_gid | opt_public,
                  (void *)(offsetof(router_instance, gid)) },
-  { "headers_add",        opt_stringptr|opt_public,
+  { "headers_add",        opt_stringptr|opt_public|opt_rep_str,
                  (void *)offsetof(router_instance, extra_headers) },
-  { "headers_remove",     opt_stringptr|opt_public,
+  { "headers_remove",     opt_stringptr|opt_public|opt_rep_str,
                  (void *)offsetof(router_instance, remove_headers) },
   { "ignore_target_hosts",opt_stringptr|opt_public,
                  (void *)offsetof(router_instance, ignore_target_hosts) },
@@ -825,6 +823,7 @@ check_router_conditions(router_instance *r, address_item *addr, int verify,
 {
 int rc;
 uschar *check_local_part;
+unsigned int *localpart_cache;
 
 /* Reset variables to hold a home directory and data from lookup of a domain or
 local part, and ensure search_find_defer is unset, in case there aren't any
@@ -885,12 +884,17 @@ if ((rc = route_check_dls(r->name, US"domains", r->domains, &domainlist_anchor,
 caseful local part, so that +caseful can restore it, even if this router is
 handling local parts caselessly. However, we can't just pass cc_local_part,
 because that doesn't have the prefix or suffix stripped. A bit of massaging is
-required. */
+required. Also, we only use the match cache for local parts that have not had
+a prefix or suffix stripped. */
 
 if (addr->prefix == NULL && addr->suffix == NULL)
+  {
+  localpart_cache = addr->localpart_cache;
   check_local_part = addr->cc_local_part;
+  }
 else
   {
+  localpart_cache = NULL;
   check_local_part = string_copy(addr->cc_local_part);
   if (addr->prefix != NULL)
     check_local_part += Ustrlen(addr->prefix);
@@ -899,7 +903,7 @@ else
   }
 
 if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts,
-       &localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART,
+       &localpartlist_anchor, localpart_cache, MCL_LOCALPART,
        check_local_part, &deliver_localpart_data, !r->caseful_local_part,
        perror)) != OK)
   return rc;
@@ -1077,7 +1081,12 @@ static uschar lastshell[128];
 BOOL
 route_finduser(uschar *s, struct passwd **pw, uid_t *return_uid)
 {
-if (Ustrcmp(lastname, s) != 0)
+BOOL cache_set = (Ustrcmp(lastname, s) == 0);
+
+DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s,
+  cache_set? "using cached result" : "cache not available");
+
+if (!cache_set)
   {
   int i = 0;
 
@@ -1104,6 +1113,7 @@ if (Ustrcmp(lastname, s) != 0)
 
   else for (;;)
     {
+    errno = 0;
     if ((lastpw = getpwnam(CS s)) != NULL) break;
     if (++i > finduser_retries) break;
     sleep(1);
@@ -1122,14 +1132,25 @@ if (Ustrcmp(lastname, s) != 0)
     pwcopy.pw_shell = CS lastshell;
     lastpw = &pwcopy;
     }
+
+  else DEBUG(D_uid)
+    {
+    if (errno != 0) debug_printf("getpwnam(%s) failed: %s\n", s,
+      strerror(errno));
+    }
+  }
+
+if (lastpw == NULL)
+  {
+  DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
+  return FALSE;
   }
 else
   {
-  DEBUG(D_uid) debug_printf("finduser used cached passwd data for %s\n", s);
+  DEBUG(D_uid) debug_printf("getpwnam() succeeded uid=%d gid=%d\n",
+    lastpw->pw_uid, lastpw->pw_gid);
   }
 
-if (lastpw == NULL) return FALSE;
-
 if (return_uid != NULL) *return_uid = lastpw->pw_uid;
 if (pw != NULL) *pw = lastpw;
 
@@ -1867,7 +1888,7 @@ if (r->translate_ip_address != NULL)
     h->mx = MX_NONE;
 
     store_pool = POOL_PERM;
-    rc = host_find_byname(h, NULL, NULL, TRUE);
+    rc = host_find_byname(h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE);
     store_pool = old_pool;
 
     if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN)
@@ -1929,6 +1950,22 @@ if (unseen && r->next != NULL)
 /* Unset the address expansions, and return the final result. */
 
 ROUTE_EXIT:
+if (yield == DEFER) {
+  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
+    )
+  ) {
+    addr->message = string_sprintf("Temporary internal error");
+  }
+}
+
 deliver_set_expansions(NULL);
 disable_logging = FALSE;
 return yield;