Use custom variables for ACL args, up to nine. Add an arg-count variable.
[users/jgh/exim.git] / src / src / expand.c
index 2a9e6b4fc3e567e586e62aa9bb676cba91b86b8b..913a808b88d52be279c96ee0a9592d4a2dcf5f39 100644 (file)
@@ -102,6 +102,7 @@ bcrypt ({CRYPT}$2a$).
 alphabetical order. */
 
 static uschar *item_table[] = {
+  US"acl",
   US"dlfunc",
   US"extract",
   US"filter",
@@ -124,6 +125,7 @@ static uschar *item_table[] = {
   US"tr" };
 
 enum {
+  EITEM_ACL,
   EITEM_DLFUNC,
   EITEM_EXTRACT,
   EITEM_FILTER,
@@ -182,12 +184,12 @@ static uschar *op_table_main[] = {
   US"l",
   US"lc",
   US"length",
-  US"list",
+  US"listcount",
+  US"listnamed",
   US"mask",
   US"md5",
   US"nh",
   US"nhash",
-  US"nlist",
   US"quote",
   US"randint",
   US"rfc2047",
@@ -217,12 +219,12 @@ enum {
   EOP_L,
   EOP_LC,
   EOP_LENGTH,
-  EOP_LIST,
+  EOP_LISTCOUNT,
+  EOP_LISTNAMED,
   EOP_MASK,
   EOP_MD5,
   EOP_NH,
   EOP_NHASH,
-  EOP_NLIST,
   EOP_QUOTE,
   EOP_RANDINT,
   EOP_RFC2047,
@@ -390,6 +392,16 @@ enum {
 static var_entry var_table[] = {
   /* WARNING: Do not invent variables whose names start acl_c or acl_m because
      they will be confused with user-creatable ACL variables. */
+  { "acl_arg1",            vtype_stringptr,   &acl_arg[0] },
+  { "acl_arg2",            vtype_stringptr,   &acl_arg[1] },
+  { "acl_arg3",            vtype_stringptr,   &acl_arg[2] },
+  { "acl_arg4",            vtype_stringptr,   &acl_arg[3] },
+  { "acl_arg5",            vtype_stringptr,   &acl_arg[4] },
+  { "acl_arg6",            vtype_stringptr,   &acl_arg[5] },
+  { "acl_arg7",            vtype_stringptr,   &acl_arg[6] },
+  { "acl_arg8",            vtype_stringptr,   &acl_arg[7] },
+  { "acl_arg9",            vtype_stringptr,   &acl_arg[8] },
+  { "acl_narg",            vtype_int,         &acl_narg },
   { "acl_verify_message",  vtype_stringptr,   &acl_verify_message },
   { "address_data",        vtype_stringptr,   &deliver_address_data },
   { "address_file",        vtype_stringptr,   &address_file },
@@ -3641,6 +3653,53 @@ while (*s != 0)
 
   switch(item_type)
     {
+    /* Call an ACL from an expansion.  We feed data in via $acl_arg1 - $acl_arg9.
+    If the ACL returns acceptance we return content set by "message ="
+    There is currently no limit on recursion; this would have us call
+    acl_check_internal() directly and get a current level from somewhere.
+    */
+
+    case EITEM_ACL:
+      {
+      int rc;
+      uschar *sub[10]; /* name + arg1-arg9, must match number of acl_arg[] */
+      uschar *new_yield;
+      uschar *user_msg;
+      uschar *log_msg;
+      switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl"))
+        {
+        case 1: goto EXPAND_FAILED_CURLY;
+        case 2:
+        case 3: goto EXPAND_FAILED;
+        }
+      if (skipping) continue;
+
+      for (rc = 1; rc < sizeof(sub)/sizeof(*sub) && sub[rc]; rc++)
+        acl_arg[rc-1] = sub[rc];
+      acl_narg = rc-1;
+      while (rc < sizeof(sub)/sizeof(*sub))
+        acl_arg[rc++ - 1] = NULL;
+
+      DEBUG(D_expand)
+        debug_printf("expanding: acl: %s  arg: %s%s\n",
+         sub[0],
+         acl_narg>0 ? sub[1]   : US"<none>",
+         acl_narg>1 ? " +more" : "");
+
+      switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg))
+       {
+       case OK:
+         if (user_msg)
+            yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg));
+         continue;
+       case DEFER:
+         continue;
+       default:
+          expand_string_message = string_sprintf("acl \"%s\" did not accept", sub[0]);
+         goto EXPAND_FAILED;
+       }
+      }
+
     /* Handle conditionals - preserve the values of the numerical expansion
     variables in case they get changed by a regular expression match in the
     condition. If not, they retain their external settings. At the end
@@ -5474,10 +5533,25 @@ while (*s != 0)
         continue;
         }
 
+      /* count the number of list elements */
+
+      case EOP_LISTCOUNT:
+        {
+       int cnt = 0;
+       int sep = 0;
+       uschar * cp;
+       uschar buffer[256];
+
+       while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
+       cp = string_sprintf("%d", cnt);
+        yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
+        continue;
+        }
+
       /* expand a named list given the name */
-      /* handles nested named lists but will confuse the separators in the result */
+      /* handles nested named lists; requotes as colon-sep list */
 
-      case EOP_LIST:
+      case EOP_LISTNAMED:
        {
        tree_node *t = NULL;
        uschar * list;
@@ -5518,7 +5592,6 @@ while (*s != 0)
          goto EXPAND_FAILED;
          }
 
-       if (skipping) continue;
        list = ((namedlist_block *)(t->data.ptr))->string;
 
        while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
@@ -5531,7 +5604,7 @@ while (*s != 0)
 
          if (*item == '+')     /* list item is itself a named list */
            {
-           uschar * sub = string_sprintf("${list%s:%s}", suffix, item);
+           uschar * sub = string_sprintf("${listnamed%s:%s}", suffix, item);
            item = expand_string_internal(sub, FALSE, NULL, FALSE, TRUE);
            }
          else if (sep != ':')  /* item from non-colon-sep list, re-quote for colon list-separator */
@@ -5560,21 +5633,6 @@ while (*s != 0)
         continue;
        }
 
-      /* count the number of list elements */
-
-      case EOP_NLIST:
-        {
-       int cnt = 0;
-       int sep = 0;
-       uschar * cp;
-       uschar buffer[256];
-
-       while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
-       cp = string_sprintf("%d", cnt);
-        yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
-        continue;
-        }
-
       /* mask applies a mask to an IP address; for example the result of
       ${mask:131.111.10.206/28} is 131.111.10.192/28. */