Routers: make retry_use_local_part default true when any non-domain condition is...
[exim.git] / src / src / route.c
index 74466733e0cab1dc800d0243e088b6c5b60fa144..c6119eed0e68c1bfbd93723f2b73be1b1a3f7379 100644 (file)
@@ -281,7 +281,8 @@ for (router_instance * r = routers; r; r = r->next)
   TRUE; otherwise its default is FALSE. */
 
   if (r->retry_use_local_part == TRUE_UNSET)
-    r->retry_use_local_part = r->check_local_user;
+    r->retry_use_local_part =
+      r->check_local_user || r->local_parts || r->condition || r->prefix || r->suffix || r->senders || r->require_files;
 
   /* Build a host list if fallback hosts is set. */
 
@@ -737,21 +738,20 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
       {
       exim_setugid(uid, gid, TRUE,
         string_sprintf("require_files check, file=%s", ss));
-      if (route_check_access(ss, uid, gid, 4)) _exit(0);
+      if (route_check_access(ss, uid, gid, 4))
+       exim_underbar_exit(0);
       DEBUG(D_route) debug_printf("route_check_access() failed\n");
-      _exit(1);
+      exim_underbar_exit(1);
       }
 
     /* In the parent, wait for the child to finish */
 
     while (waitpid(pid, &status, 0) < 0)
-     {
      if (errno != EINTR)  /* unexpected error, interpret as failure */
        {
        status = 1;
        break;
        }
-     }
 
     signal(SIGCHLD, oldsignal);   /* restore */
     if ((status == 0) == invert) return SKIP;
@@ -1101,7 +1101,7 @@ route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid)
 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");
+  cache_set ? "using cached result" : "cache not available");
 
 if (!cache_set)
   {
@@ -1115,7 +1115,7 @@ if (!cache_set)
     return TRUE;
     }
 
-  (void)string_format(lastname, sizeof(lastname), "%s", s);
+  string_format_nt(lastname, sizeof(lastname), "%s", s);
 
   /* Force failure if string length is greater than given maximum */
 
@@ -1363,7 +1363,8 @@ new->prop.errors_address = parent->prop.errors_address;
 
 new->prop.ignore_error = addr->prop.ignore_error;
 new->prop.address_data = addr->prop.address_data;
-new->prop.set  = addr->prop.set;
+new->prop.variables = NULL;
+tree_dup((tree_node **)&new->prop.variables, addr->prop.variables);
 new->dsn_flags = addr->dsn_flags;
 new->dsn_orcpt = addr->dsn_orcpt;
 
@@ -1406,6 +1407,90 @@ if (addr->transport && tree_search(tree_nonrecipients, addr->unique))
 
 
 
+/************************************************/
+/* Add router-assigned variables
+Return OK/DEFER/FAIL/PASS */
+
+static int
+set_router_vars(address_item * addr, const router_instance * r)
+{
+const uschar * varlist = r->set;
+tree_node ** root = (tree_node **) &addr->prop.variables;
+int sep = ';';
+
+if (!varlist) return OK;
+
+/* Walk the varlist, creating variables */
+
+for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); )
+  {
+  const uschar * assignment = ele;
+  int esep = '=';
+  uschar * name = string_nextinlist(&assignment, &esep, NULL, 0);
+  uschar * val;
+  tree_node * node;
+
+  /* Variable name must exist and start "r_". */
+
+  if (!name || name[0] != 'r' || name[1] != '_' || !name[2])
+    {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+       "bad router variable name '%s' in router '%s'\n", name, r->name);
+    return FAIL;
+    }
+  name += 2;
+
+  while (isspace(*assignment)) assignment++;
+
+  if (!(val = expand_string(US assignment)))
+    if (f.expand_string_forcedfail)
+      {
+      int yield;
+      BOOL more;
+      DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
+         "(router variable): decline action taken\n", ele);
+
+      /* Expand "more" if necessary; DEFER => an expansion failed */
+
+      yield = exp_bool(addr, US"router", r->name, D_route,
+                     US"more", r->more, r->expand_more, &more);
+      if (yield != OK) return yield;
+
+      if (!more)
+       {
+       DEBUG(D_route)
+         debug_printf("\"more\"=false: skipping remaining routers\n");
+       router_name = NULL;
+       r = NULL;
+       return FAIL;
+       }
+      return PASS;
+      }
+    else
+      {
+      addr->message = string_sprintf("expansion of \"%s\" failed "
+       "in %s router: %s", ele, r->name, expand_string_message);
+      return DEFER;
+      }
+
+  if (!(node = tree_search(*root, name)))
+    {                          /* name should never be tainted */
+    node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE);
+    Ustrcpy(node->name, name);
+    (void)tree_insertnode(root, node);
+    }
+  node->data.ptr = US val;
+  DEBUG(D_route) debug_printf("set r_%s%s = '%s'%s\n",
+                   name, is_tainted(name)?" (tainted)":"",
+                   val, is_tainted(val)?" (tainted)":"");
+
+  /* All expansions after this point need visibility of that variable */
+  router_var = *root;
+  }
+return OK;
+}
+
+
 /*************************************************
 *                 Route one address              *
 *************************************************/
@@ -1605,50 +1690,17 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
 
   search_error_message = NULL;
 
-  /* Add any variable-settings that are on the router, to the list on the
+  /* Add any variable-settings that are on the router, to the set on the
   addr. Expansion is done here and not later when the addr is used.  There may
   be multiple settings, gathered during readconf; this code gathers them during
-  router traversal. */
+  router traversal.  On the addr string they are held as a variable tree, so
+  as to maintain the post-expansion taints separate. */
 
-  if (r->set)
+  switch (set_router_vars(addr, r))
     {
-    const uschar * list = r->set;
-    int sep = 0;
-    for (uschar * ele; (ele = string_nextinlist(&list, &sep, NULL, 0)); )
-      {
-      uschar * ee;
-      if (!(ee = expand_string(ele)))
-       if (f.expand_string_forcedfail)
-         {
-         DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
-             "(router variable): decline action taken\n", ele);
-
-         /* Expand "more" if necessary; DEFER => an expansion failed */
-
-         yield = exp_bool(addr, US"router", r->name, D_route,
-                         US"more", r->more, r->expand_more, &more);
-         if (yield != OK) goto ROUTE_EXIT;
-
-         if (!more)
-           {
-           DEBUG(D_route)
-             debug_printf("\"more\"=false: skipping remaining routers\n");
-           router_name = NULL;
-           r = NULL;
-           break;
-           }
-         else continue;    /* With next router */
-         }
-       else
-         {
-         addr->message = string_sprintf("expansion of \"%s\" failed "
-           "in %s router: %s", ele, r->name, expand_string_message);
-         yield = DEFER;
-         goto ROUTE_EXIT;
-         }
-
-      addr->prop.set = string_append_listele(addr->prop.set, ':', ee);
-      }
+    case OK:   break;
+    case PASS: continue;               /* with next router */
+    default:    goto ROUTE_EXIT;
     }
 
   /* Finally, expand the address_data field in the router. Forced failure
@@ -1739,9 +1791,10 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   /* If succeeded while verifying but fail_verify is set, convert into
   a failure, and take it off the local or remote delivery list. */
 
-  if (((verify == v_sender && r->fail_verify_sender) ||
-       (verify == v_recipient && r->fail_verify_recipient)) &&
-      (yield == OK || yield == PASS))
+  if (  (  verify == v_sender && r->fail_verify_sender
+       || verify == v_recipient && r->fail_verify_recipient
+       )
+     && (yield == OK || yield == PASS))
     {
     addr->message = string_sprintf("%s router forced verify failure", r->name);
     if (*paddr_remote == addr) *paddr_remote = addr->next;
@@ -1758,7 +1811,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr)
   HDEBUG(D_route)
     {
     debug_printf("%s router %s for %s\n", r->name,
-      (yield == PASS)? "passed" : "declined", addr->address);
+      yield == PASS ? "passed" : "declined", addr->address);
     if (Ustrcmp(old_domain, addr->domain) != 0)
       debug_printf("domain %s rewritten\n", old_domain);
     }
@@ -1822,12 +1875,8 @@ if (!r)
 
 if (yield == DEFER)
   {
-  HDEBUG(D_route)
-    {
-    debug_printf("%s router: defer for %s\n", r->name, addr->address);
-    debug_printf("  message: %s\n", (addr->message == NULL)?
-      US"<none>" : addr->message);
-    }
+  HDEBUG(D_route) debug_printf("%s router: defer for %s\n  message: %s\n",
+      r->name, addr->address, addr->message ? addr->message : US"<none>");
   goto ROUTE_EXIT;
   }