> On Tue, Feb 21, 2017 at 6:32 PM, Yann Ylavic <[email protected]> wrote:
>>
>> Header set Client-SAN "expr=%{PeerExtList:2.5.29.17}"

This currently fails because list functions are not recognized in a
string context.

For now, lists can be either expressed with the syntax "{ <word>,
<word>, ... }", with <word> being itself something powerful, or
obtained from the mod_ssl's PeerExtList() function (grab anything from
a peer certificate).

For the latter case (or for future functions), it could be useful to
be able to work on such strings (e.g. with extracting regexes).
So I wonder if we could return the string elements separated by
something in the case of lists evaluated in a string context.

For example, the attached patch uses the seprator ", " (quite HTTP
field inspired), but it could be a json string or whatever...
We could also have an explicit tostring/tojson() function which would
stringify anything as argument.

Or yet more operators on lists, like list_empty(), list_first(),
list_last(), list_nth(), list_match(<list>, <regex>) (returning
another list of matching entries), ... you name it.

Working on anything from a certificates looks very useful at least.

WDYT?
Index: server/util_expr_eval.c
===================================================================
--- server/util_expr_eval.c	(revision 1783852)
+++ server/util_expr_eval.c	(working copy)
@@ -50,6 +50,9 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_
 static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
                                             const ap_expr_t *info,
                                             const ap_expr_t *args);
+static apr_array_header_t *ap_expr_eval_list_func(ap_expr_eval_ctx_t *ctx,
+                                            const ap_expr_t *info,
+                                            const ap_expr_t *args);
 static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx,
                                            unsigned int n);
 static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
@@ -80,6 +83,8 @@ static int inc_rec(ap_expr_eval_ctx_t *ctx)
     return 1;
 }
 
+#define AP_EXPR_MAX_LIST_STRINGS 500
+
 static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
                                      const ap_expr_t *node)
 {
@@ -161,6 +166,35 @@ static const char *ap_expr_eval_word(ap_expr_eval_
         result = ap_expr_eval_string_func(ctx, info, args);
         break;
     }
+    case op_ListFuncCall: {
+        const ap_expr_t *info = node->node_arg1;
+        const ap_expr_t *args = node->node_arg2;
+        apr_array_header_t *array = ap_expr_eval_list_func(ctx, info, args);
+        if (array && array->nelts > 0) {
+            struct iovec *vec;
+            int n = array->nelts, i = 0;
+            /* sanity check */
+            if (n > AP_EXPR_MAX_LIST_STRINGS) {
+                n = AP_EXPR_MAX_LIST_STRINGS;
+            }
+            /* all entries (but last) separated by ", " */
+            n = (n * 2) - 1;
+            vec = apr_palloc(ctx->p, n * sizeof(struct iovec));
+            for (;;) {
+                const char *s = APR_ARRAY_IDX(array, i, const char *);
+                vec[i].iov_base = (void *)s;
+                vec[i].iov_len = strlen(s);
+                if (++i >= n) {
+                    break;
+                }
+                vec[i].iov_base = (void *)", ";
+                vec[i].iov_len = 2;
+                ++i;
+            }
+            result = apr_pstrcatv(ctx->p, vec, n, NULL);
+        }
+        break;
+    }
     case op_RegexBackref: {
         const unsigned int *np = node->node_arg1;
         result = ap_expr_eval_re_backref(ctx, *np);
@@ -213,6 +247,19 @@ static const char *ap_expr_eval_string_func(ap_exp
     return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
 }
 
+static apr_array_header_t *ap_expr_eval_list_func(ap_expr_eval_ctx_t *ctx,
+                                            const ap_expr_t *info,
+                                            const ap_expr_t *arg)
+{
+    ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
+    const void *data = info->node_arg2;
+
+    AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
+    AP_DEBUG_ASSERT(func != NULL);
+    AP_DEBUG_ASSERT(data != NULL);
+    return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
+}
+
 static int intstrcmp(const char *s1, const char *s2)
 {
     apr_int64_t i1 = apr_atoi64(s1);
@@ -268,13 +315,8 @@ static int ap_expr_eval_comp(ap_expr_eval_ctx_t *c
             }
             else if (e2->node_op == op_ListFuncCall) {
                 const ap_expr_t *info = e2->node_arg1;
-                const ap_expr_t *arg = e2->node_arg2;
-                ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
-                apr_array_header_t *haystack;
-
-                AP_DEBUG_ASSERT(func != NULL);
-                AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
-                haystack = (*func)(ctx, info->node_arg2, ap_expr_eval_word(ctx, arg));
+                const ap_expr_t *args = e2->node_arg2;
+                apr_array_header_t *haystack = ap_expr_eval_list_func(ctx, info, args);
                 if (haystack == NULL) {
                     return 0;
                 }
@@ -474,8 +516,19 @@ ap_expr_t *ap_expr_str_func_make(const char *name,
                                ap_expr_parse_ctx_t *ctx)
 {
     ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx, arg);
-    if (!info)
-        return NULL;
+    if (!info) {
+        /* We know how to make a string result from a list, so if no string
+         * function is found, try to find a list function.
+         */
+        const char *saved_error = ctx->error2;
+        ctx->error2 = NULL;
+        info = ap_expr_list_func_make(name, arg, ctx);
+        if (!info) {
+            /* Didn't work, restore previous error */
+            ctx->error2 = saved_error;
+        }
+        return info;
+    }
 
     info->node_op = op_StringFuncInfo;
     return ap_expr_make(op_StringFuncCall, info, arg, ctx);

Reply via email to