1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * ap_expr_eval.c, based on ssl_expr_eval.c from mod_ssl
23 #include "http_core.h"
24 #include "http_protocol.h"
25 #include "http_request.h"
26 #include "ap_provider.h"
27 #include "util_expr_private.h"
31 #include "apr_fnmatch.h"
32 #include "apr_base64.h"
35 #include <limits.h> /* for INT_MAX */
37 /* we know core's module_index is 0 */
38 #undef APLOG_MODULE_INDEX
39 #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
42 APR_HOOK_LINK(expr_lookup)
45 AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_expr_lookup_parms *parms),
48 #define LOG_MARK(info) __FILE__, __LINE__, (info)->module_index
50 static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
51 const ap_expr_t *info,
52 const ap_expr_t *args);
53 static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx,
55 static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
56 ap_expr_var_func_t *func,
59 /* define AP_EXPR_DEBUG to log the parse tree when parsing an expression */
61 static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
62 int loglevel, int indent);
66 * To reduce counting overhead, we only count calls to
67 * ap_expr_eval_word() and ap_expr_eval(). The max number of
68 * stack frames is larger by some factor.
70 #define AP_EXPR_MAX_RECURSION 20
71 static int inc_rec(ap_expr_eval_ctx_t *ctx)
73 if (ctx->reclvl < AP_EXPR_MAX_RECURSION) {
77 *ctx->err = "Recursion limit reached";
78 /* short circuit further evaluation */
79 ctx->reclvl = INT_MAX;
83 static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
84 const ap_expr_t *node)
86 const char *result = "";
89 switch (node->node_op) {
92 result = node->node_arg1;
95 result = ap_expr_eval_var(ctx, (ap_expr_var_func_t *)node->node_arg1,
99 if (((ap_expr_t *)node->node_arg2)->node_op != op_Concat &&
100 ((ap_expr_t *)node->node_arg1)->node_op != op_Concat) {
101 const char *s1 = ap_expr_eval_word(ctx, node->node_arg1);
102 const char *s2 = ap_expr_eval_word(ctx, node->node_arg2);
108 result = apr_pstrcat(ctx->p, s1, s2, NULL);
110 else if (((ap_expr_t *)node->node_arg1)->node_op == op_Concat) {
111 const ap_expr_t *nodep = node;
116 nodep = nodep->node_arg1;
118 } while (nodep->node_op == op_Concat);
119 vec = apr_palloc(ctx->p, i * sizeof(struct iovec));
124 vec[i].iov_base = (void *)ap_expr_eval_word(ctx,
126 vec[i].iov_len = strlen(vec[i].iov_base);
128 nodep = nodep->node_arg1;
129 } while (nodep->node_op == op_Concat);
130 vec[i].iov_base = (void *)ap_expr_eval_word(ctx, nodep);
131 vec[i].iov_len = strlen(vec[i].iov_base);
132 result = apr_pstrcatv(ctx->p, vec, n, NULL);
135 const ap_expr_t *nodep = node;
139 nodep = nodep->node_arg2;
141 } while (nodep->node_op == op_Concat);
142 vec = apr_palloc(ctx->p, i * sizeof(struct iovec));
146 vec[i].iov_base = (void *)ap_expr_eval_word(ctx,
148 vec[i].iov_len = strlen(vec[i].iov_base);
150 nodep = nodep->node_arg2;
151 } while (nodep->node_op == op_Concat);
152 vec[i].iov_base = (void *)ap_expr_eval_word(ctx, nodep);
153 vec[i].iov_len = strlen(vec[i].iov_base);
155 result = apr_pstrcatv(ctx->p, vec, i, NULL);
158 case op_StringFuncCall: {
159 const ap_expr_t *info = node->node_arg1;
160 const ap_expr_t *args = node->node_arg2;
161 result = ap_expr_eval_string_func(ctx, info, args);
164 case op_RegexBackref: {
165 const unsigned int *np = node->node_arg1;
166 result = ap_expr_eval_re_backref(ctx, *np);
170 *ctx->err = "Internal evaluation error: Unknown word expression node";
179 static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
180 ap_expr_var_func_t *func,
183 AP_DEBUG_ASSERT(func != NULL);
184 AP_DEBUG_ASSERT(data != NULL);
185 return (*func)(ctx, data);
188 static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx, unsigned int n)
192 if (!ctx->re_pmatch || !ctx->re_source || *ctx->re_source == '\0' ||
193 ctx->re_nmatch < n + 1)
196 len = ctx->re_pmatch[n].rm_eo - ctx->re_pmatch[n].rm_so;
200 return apr_pstrndup(ctx->p, *ctx->re_source + ctx->re_pmatch[n].rm_so, len);
203 static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
204 const ap_expr_t *info,
205 const ap_expr_t *arg)
207 ap_expr_string_func_t *func = (ap_expr_string_func_t *)info->node_arg1;
208 const void *data = info->node_arg2;
210 AP_DEBUG_ASSERT(info->node_op == op_StringFuncInfo);
211 AP_DEBUG_ASSERT(func != NULL);
212 AP_DEBUG_ASSERT(data != NULL);
213 return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
216 static int intstrcmp(const char *s1, const char *s2)
218 apr_int64_t i1 = apr_atoi64(s1);
219 apr_int64_t i2 = apr_atoi64(s2);
229 static int ap_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
231 const ap_expr_t *e1 = node->node_arg1;
232 const ap_expr_t *e2 = node->node_arg2;
233 switch (node->node_op) {
235 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
237 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
239 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
241 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
243 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
245 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
247 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
249 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
251 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
253 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
255 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
257 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
259 const char *needle = ap_expr_eval_word(ctx, e1);
260 if (e2->node_op == op_ListElement) {
262 const ap_expr_t *val = e2->node_arg1;
263 AP_DEBUG_ASSERT(e2->node_op == op_ListElement);
264 if (strcmp(needle, ap_expr_eval_word(ctx, val)) == 0)
267 } while (e2 != NULL);
269 else if (e2->node_op == op_ListFuncCall) {
270 const ap_expr_t *info = e2->node_arg1;
271 const ap_expr_t *arg = e2->node_arg2;
272 ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
273 apr_array_header_t *haystack;
275 AP_DEBUG_ASSERT(func != NULL);
276 AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
277 haystack = (*func)(ctx, info->node_arg2, ap_expr_eval_word(ctx, arg));
278 if (haystack == NULL) {
281 if (ap_array_str_contains(haystack, needle)) {
289 const char *word = ap_expr_eval_word(ctx, e1);
290 const ap_regex_t *regex = e2->node_arg1;
294 * $0 ... $9 may contain stuff the user wants to keep. Therefore
295 * we only set them if there are capturing parens in the regex.
297 if (regex->re_nsub > 0) {
298 result = (0 == ap_regexec(regex, word, ctx->re_nmatch,
300 *ctx->re_source = result ? word : NULL;
303 result = (0 == ap_regexec(regex, word, 0, NULL, 0));
306 if (node->node_op == op_REG)
312 *ctx->err = "Internal evaluation error: Unknown comp expression node";
317 /* combined string/int comparison for compatibility with ssl_expr */
318 static int strcmplex(const char *str1, const char *str2)
332 for (i = 0; i < n1; i++) {
333 if (str1[i] > str2[i])
335 if (str1[i] < str2[i])
341 static int ssl_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
343 const ap_expr_t *e1 = node->node_arg1;
344 const ap_expr_t *e2 = node->node_arg2;
345 switch (node->node_op) {
348 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
351 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
354 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
357 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
360 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
363 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
365 return ap_expr_eval_comp(ctx, node);
369 AP_DECLARE_NONSTD(int) ap_expr_lookup_default(ap_expr_lookup_parms *parms)
371 return ap_run_expr_lookup(parms);
374 AP_DECLARE(const char *) ap_expr_parse(apr_pool_t *pool, apr_pool_t *ptemp,
375 ap_expr_info_t *info, const char *expr,
376 ap_expr_lookup_fn_t *lookup_fn)
378 ap_expr_parse_ctx_t ctx;
384 ctx.inputlen = strlen(expr);
385 ctx.inputptr = ctx.inputbuf;
387 ctx.error = NULL; /* generic bison error message (XXX: usually not very useful, should be axed) */
388 ctx.error2 = NULL; /* additional error message */
389 ctx.flags = info->flags;
391 ctx.scan_buf[0] = '\0';
392 ctx.scan_ptr = ctx.scan_buf;
393 ctx.lookup_fn = lookup_fn ? lookup_fn : ap_expr_lookup_default;
396 ap_expr_yylex_init(&ctx.scanner);
397 ap_expr_yyset_extra(&ctx, ctx.scanner);
398 rc = ap_expr_yyparse(&ctx);
399 ap_expr_yylex_destroy(ctx.scanner);
402 return apr_psprintf(pool, "%s: %s", ctx.error, ctx.error2);
406 else if (ctx.error2) {
410 if (rc) /* XXX can this happen? */
411 return "syntax error";
415 expr_dump_tree(ctx.expr, NULL, APLOG_NOTICE, 2);
418 info->root_node = ctx.expr;
423 AP_DECLARE(ap_expr_info_t*) ap_expr_parse_cmd_mi(const cmd_parms *cmd,
427 ap_expr_lookup_fn_t *lookup_fn,
430 ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t));
431 info->filename = cmd->directive->filename;
432 info->line_number = cmd->directive->line_num;
434 info->module_index = module_index;
435 *err = ap_expr_parse(cmd->pool, cmd->temp_pool, info, expr, lookup_fn);
443 ap_expr_t *ap_expr_make(ap_expr_node_op_e op, const void *a1, const void *a2,
444 ap_expr_parse_ctx_t *ctx)
446 ap_expr_t *node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
448 node->node_arg1 = a1;
449 node->node_arg2 = a2;
453 static ap_expr_t *ap_expr_info_make(int type, const char *name,
454 ap_expr_parse_ctx_t *ctx,
455 const ap_expr_t *arg)
457 ap_expr_t *info = apr_palloc(ctx->pool, sizeof(ap_expr_t));
458 ap_expr_lookup_parms parms;
460 parms.flags = ctx->flags;
461 parms.pool = ctx->pool;
462 parms.ptemp = ctx->ptemp;
464 parms.func = &info->node_arg1;
465 parms.data = &info->node_arg2;
466 parms.err = &ctx->error2;
467 parms.arg = (arg && arg->node_op == op_String) ? arg->node_arg1 : NULL;
468 if (ctx->lookup_fn(&parms) != OK)
473 ap_expr_t *ap_expr_str_func_make(const char *name, const ap_expr_t *arg,
474 ap_expr_parse_ctx_t *ctx)
476 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx, arg);
480 info->node_op = op_StringFuncInfo;
481 return ap_expr_make(op_StringFuncCall, info, arg, ctx);
484 ap_expr_t *ap_expr_list_func_make(const char *name, const ap_expr_t *arg,
485 ap_expr_parse_ctx_t *ctx)
487 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_LIST, name, ctx, arg);
491 info->node_op = op_ListFuncInfo;
492 return ap_expr_make(op_ListFuncCall, info, arg, ctx);
495 ap_expr_t *ap_expr_unary_op_make(const char *name, const ap_expr_t *arg,
496 ap_expr_parse_ctx_t *ctx)
498 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx, arg);
502 info->node_op = op_UnaryOpInfo;
503 return ap_expr_make(op_UnaryOpCall, info, arg, ctx);
506 ap_expr_t *ap_expr_binary_op_make(const char *name, const ap_expr_t *arg1,
507 const ap_expr_t *arg2, ap_expr_parse_ctx_t *ctx)
510 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_OP_BINARY, name, ctx,
515 info->node_op = op_BinaryOpInfo;
516 args = ap_expr_make(op_BinaryOpArgs, arg1, arg2, ctx);
517 return ap_expr_make(op_BinaryOpCall, info, args, ctx);
521 ap_expr_t *ap_expr_var_make(const char *name, ap_expr_parse_ctx_t *ctx)
523 ap_expr_t *node = ap_expr_info_make(AP_EXPR_FUNC_VAR, name, ctx, NULL);
527 node->node_op = op_Var;
533 #define MARK APLOG_MARK,loglevel,0,s
534 #define DUMP_E_E(op, e1, e2) \
535 do { ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, e1, e2); \
536 if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \
537 if (e2) expr_dump_tree(e2, s, loglevel, indent + 2); \
539 #define DUMP_S_E(op, s1, e1) \
540 do { ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, e1); \
541 if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \
543 #define DUMP_S_P(op, s1, p1) \
544 ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, p1);
545 #define DUMP_P_P(op, p1, p2) \
546 ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, p1, p2);
547 #define DUMP_S_S(op, s1, s2) \
548 ap_log_error(MARK,"%*s%s: '%s' '%s'", indent, " ", op, (char *)s1, (char *)s2)
549 #define DUMP_P(op, p1) \
550 ap_log_error(MARK,"%*s%s: %pp", indent, " ", op, p1);
551 #define DUMP_IP(op, p1) \
552 ap_log_error(MARK,"%*s%s: %d", indent, " ", op, *(int *)p1);
553 #define DUMP_S(op, s1) \
554 ap_log_error(MARK,"%*s%s: '%s'", indent, " ", op, (char *)s1)
556 #define CASE_OP(op) case op: name = #op ; break;
558 static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
559 int loglevel, int indent)
561 switch (e->node_op) {
568 switch (e->node_op) {
575 ap_log_error(MARK, "%*s%s", indent, " ", name);
579 /* arg1: string, arg2: expr */
581 case op_BinaryOpCall:
582 case op_BinaryOpArgs:
585 switch (e->node_op) {
586 CASE_OP(op_BinaryOpCall);
587 CASE_OP(op_UnaryOpCall);
588 CASE_OP(op_BinaryOpArgs);
592 DUMP_S_E(name, e->node_arg1, e->node_arg2);
596 /* arg1: expr, arg2: expr */
617 case op_StringFuncCall:
618 case op_ListFuncCall:
622 switch (e->node_op) {
643 CASE_OP(op_StringFuncCall);
644 CASE_OP(op_ListFuncCall);
645 CASE_OP(op_ListElement);
649 DUMP_E_E(name, e->node_arg1, e->node_arg2);
657 switch (e->node_op) {
663 DUMP_S(name, e->node_arg1);
666 /* arg1: pointer, arg2: pointer */
668 case op_StringFuncInfo:
670 case op_BinaryOpInfo:
671 case op_ListFuncInfo:
674 switch (e->node_op) {
676 CASE_OP(op_StringFuncInfo);
677 CASE_OP(op_UnaryOpInfo);
678 CASE_OP(op_BinaryOpInfo);
679 CASE_OP(op_ListFuncInfo);
683 DUMP_P_P(name, e->node_arg1, e->node_arg2);
688 DUMP_P("op_Regex", e->node_arg1);
690 /* arg1: pointer to int */
691 case op_RegexBackref:
692 DUMP_IP("op_RegexBackref", e->node_arg1);
695 ap_log_error(MARK, "%*sERROR: INVALID OP %d", indent, " ", e->node_op);
699 #endif /* AP_EXPR_DEBUG */
701 static int ap_expr_eval_unary_op(ap_expr_eval_ctx_t *ctx, const ap_expr_t *info,
702 const ap_expr_t *arg)
704 ap_expr_op_unary_t *op_func = (ap_expr_op_unary_t *)info->node_arg1;
705 const void *data = info->node_arg2;
707 AP_DEBUG_ASSERT(info->node_op == op_UnaryOpInfo);
708 AP_DEBUG_ASSERT(op_func != NULL);
709 AP_DEBUG_ASSERT(data != NULL);
710 return (*op_func)(ctx, data, ap_expr_eval_word(ctx, arg));
713 static int ap_expr_eval_binary_op(ap_expr_eval_ctx_t *ctx,
714 const ap_expr_t *info,
715 const ap_expr_t *args)
717 ap_expr_op_binary_t *op_func = (ap_expr_op_binary_t *)info->node_arg1;
718 const void *data = info->node_arg2;
719 const ap_expr_t *a1 = args->node_arg1;
720 const ap_expr_t *a2 = args->node_arg2;
722 AP_DEBUG_ASSERT(info->node_op == op_BinaryOpInfo);
723 AP_DEBUG_ASSERT(args->node_op == op_BinaryOpArgs);
724 AP_DEBUG_ASSERT(op_func != NULL);
725 AP_DEBUG_ASSERT(data != NULL);
726 return (*op_func)(ctx, data, ap_expr_eval_word(ctx, a1),
727 ap_expr_eval_word(ctx, a2));
731 static int ap_expr_eval(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
733 const ap_expr_t *e1 = node->node_arg1;
734 const ap_expr_t *e2 = node->node_arg2;
739 switch (node->node_op) {
752 if (e1->node_op == op_Not) {
753 if (!ap_expr_eval(ctx, e1->node_arg1)) {
759 if (ap_expr_eval(ctx, e1)) {
764 node = node->node_arg2;
765 e1 = node->node_arg1;
766 } while (node->node_op == op_Or);
770 if (e1->node_op == op_Not) {
771 if (ap_expr_eval(ctx, e1->node_arg1)) {
777 if (!ap_expr_eval(ctx, e1)) {
782 node = node->node_arg2;
783 e1 = node->node_arg1;
784 } while (node->node_op == op_And);
787 result ^= ap_expr_eval_unary_op(ctx, e1, e2);
789 case op_BinaryOpCall:
790 result ^= ap_expr_eval_binary_op(ctx, e1, e2);
793 if (ctx->info->flags & AP_EXPR_FLAG_SSL_EXPR_COMPAT)
794 result ^= ssl_expr_eval_comp(ctx, e1);
796 result ^= ap_expr_eval_comp(ctx, e1);
799 *ctx->err = "Internal evaluation error: Unknown expression node";
802 e1 = node->node_arg1;
803 e2 = node->node_arg2;
810 AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *info,
813 return ap_expr_exec_re(r, info, 0, NULL, NULL, err);
816 AP_DECLARE(int) ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx)
820 AP_DEBUG_ASSERT(ctx->p != NULL);
821 /* XXX: allow r, c == NULL */
822 AP_DEBUG_ASSERT(ctx->r != NULL);
823 AP_DEBUG_ASSERT(ctx->c != NULL);
824 AP_DEBUG_ASSERT(ctx->s != NULL);
825 AP_DEBUG_ASSERT(ctx->err != NULL);
826 AP_DEBUG_ASSERT(ctx->info != NULL);
827 if (ctx->re_pmatch) {
828 AP_DEBUG_ASSERT(ctx->re_source != NULL);
829 AP_DEBUG_ASSERT(ctx->re_nmatch > 0);
834 if (ctx->info->flags & AP_EXPR_FLAG_STRING_RESULT) {
835 *ctx->result_string = ap_expr_eval_word(ctx, ctx->info->root_node);
836 if (*ctx->err != NULL) {
837 ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
838 "Evaluation of expression from %s:%d failed: %s",
839 ctx->info->filename, ctx->info->line_number, *ctx->err);
842 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
843 "Evaluation of string expression from %s:%d gave: %s",
844 ctx->info->filename, ctx->info->line_number,
845 *ctx->result_string);
850 rc = ap_expr_eval(ctx, ctx->info->root_node);
851 if (*ctx->err != NULL) {
852 ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
853 "Evaluation of expression from %s:%d failed: %s",
854 ctx->info->filename, ctx->info->line_number, *ctx->err);
858 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
859 "Evaluation of expression from %s:%d gave: %d",
860 ctx->info->filename, ctx->info->line_number, rc);
862 if (ctx->vary_this && *ctx->vary_this)
863 apr_table_merge(ctx->r->headers_out, "Vary", *ctx->vary_this);
870 AP_DECLARE(int) ap_expr_exec_re(request_rec *r, const ap_expr_info_t *info,
871 apr_size_t nmatch, ap_regmatch_t *pmatch,
872 const char **source, const char **err)
874 ap_expr_eval_ctx_t ctx;
875 int dont_vary = (info->flags & AP_EXPR_FLAG_DONT_VARY);
876 const char *tmp_source = NULL, *vary_this = NULL;
877 ap_regmatch_t tmp_pmatch[AP_MAX_REG_MATCH];
879 AP_DEBUG_ASSERT((info->flags & AP_EXPR_FLAG_STRING_RESULT) == 0);
882 ctx.c = r->connection;
887 ctx.re_nmatch = nmatch;
888 ctx.re_pmatch = pmatch;
889 ctx.re_source = source;
890 ctx.vary_this = dont_vary ? NULL : &vary_this;
894 ctx.re_nmatch = AP_MAX_REG_MATCH;
895 ctx.re_pmatch = tmp_pmatch;
896 ctx.re_source = &tmp_source;
899 return ap_expr_exec_ctx(&ctx);
902 AP_DECLARE(const char *) ap_expr_str_exec_re(request_rec *r,
903 const ap_expr_info_t *info,
905 ap_regmatch_t *pmatch,
909 ap_expr_eval_ctx_t ctx;
911 const char *tmp_source, *vary_this;
912 ap_regmatch_t tmp_pmatch[AP_MAX_REG_MATCH];
915 AP_DEBUG_ASSERT(info->flags & AP_EXPR_FLAG_STRING_RESULT);
917 if (info->root_node->node_op == op_String) {
918 /* short-cut for constant strings */
920 return (const char *)info->root_node->node_arg1;
926 dont_vary = (info->flags & AP_EXPR_FLAG_DONT_VARY);
929 ctx.c = r->connection;
934 ctx.re_nmatch = nmatch;
935 ctx.re_pmatch = pmatch;
936 ctx.re_source = source;
937 ctx.vary_this = dont_vary ? NULL : &vary_this;
939 ctx.result_string = &result;
942 ctx.re_nmatch = AP_MAX_REG_MATCH;
943 ctx.re_pmatch = tmp_pmatch;
944 ctx.re_source = &tmp_source;
947 rc = ap_expr_exec_ctx(&ctx);
958 AP_DECLARE(const char *) ap_expr_str_exec(request_rec *r,
959 const ap_expr_info_t *info,
962 return ap_expr_str_exec_re(r, info, 0, NULL, NULL, err);
966 static void add_vary(ap_expr_eval_ctx_t *ctx, const char *name)
971 if (*ctx->vary_this) {
972 *ctx->vary_this = apr_pstrcat(ctx->p, *ctx->vary_this, ", ", name,
976 *ctx->vary_this = name;
980 static const char *req_table_func(ap_expr_eval_ctx_t *ctx, const void *data,
983 const char *name = (const char *)data;
988 if (name[2] == 's') { /* resp */
989 /* Try r->headers_out first, fall back on err_headers_out. */
990 const char *v = apr_table_get(ctx->r->headers_out, arg);
994 t = ctx->r->err_headers_out;
996 else if (name[0] == 'n') /* notes */
998 else if (name[3] == 'e') /* reqenv */
999 t = ctx->r->subprocess_env;
1000 else if (name[3] == '_') /* req_novary */
1001 t = ctx->r->headers_in;
1002 else { /* req, http */
1003 t = ctx->r->headers_in;
1006 return apr_table_get(t, arg);
1009 static const char *env_func(ap_expr_eval_ctx_t *ctx, const void *data,
1013 /* this order is for ssl_expr compatibility */
1015 if ((res = apr_table_get(ctx->r->notes, arg)) != NULL)
1017 else if ((res = apr_table_get(ctx->r->subprocess_env, arg)) != NULL)
1023 static const char *osenv_func(ap_expr_eval_ctx_t *ctx, const void *data,
1029 static const char *tolower_func(ap_expr_eval_ctx_t *ctx, const void *data,
1032 char *result = apr_pstrdup(ctx->p, arg);
1033 ap_str_tolower(result);
1037 static const char *toupper_func(ap_expr_eval_ctx_t *ctx, const void *data,
1040 char *result = apr_pstrdup(ctx->p, arg);
1041 ap_str_toupper(result);
1045 static const char *escape_func(ap_expr_eval_ctx_t *ctx, const void *data,
1048 return ap_escape_uri(ctx->p, arg);
1051 static const char *base64_func(ap_expr_eval_ctx_t *ctx, const void *data,
1054 return ap_pbase64encode(ctx->p, (char *)arg);
1057 static const char *unbase64_func(ap_expr_eval_ctx_t *ctx, const void *data,
1060 return ap_pbase64decode(ctx->p, arg);
1063 static const char *sha1_func(ap_expr_eval_ctx_t *ctx, const void *data,
1066 apr_sha1_ctx_t context;
1067 apr_byte_t sha1[APR_SHA1_DIGESTSIZE];
1070 out = apr_palloc(ctx->p, APR_SHA1_DIGESTSIZE*2+1);
1072 apr_sha1_init(&context);
1073 apr_sha1_update(&context, arg, strlen(arg));
1074 apr_sha1_final(sha1, &context);
1076 ap_bin2hex(sha1, APR_SHA1_DIGESTSIZE, out);
1081 static const char *md5_func(ap_expr_eval_ctx_t *ctx, const void *data,
1084 return ap_md5(ctx->p, (const unsigned char *)arg);
1088 #define MAX_FILE_SIZE 10*1024*1024
1089 static const char *file_func(ap_expr_eval_ctx_t *ctx, const void *data,
1098 if (apr_file_open(&fp, arg, APR_READ|APR_BUFFERED,
1099 APR_OS_DEFAULT, ctx->p) != APR_SUCCESS) {
1100 *ctx->err = apr_psprintf(ctx->p, "Cannot open file %s", arg);
1103 apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
1104 if (finfo.size > MAX_FILE_SIZE) {
1105 *ctx->err = apr_psprintf(ctx->p, "File %s too large", arg);
1109 len = (apr_size_t)finfo.size;
1115 if ((buf = (char *)apr_palloc(ctx->p, sizeof(char)*(len+1))) == NULL) {
1116 *ctx->err = "Cannot allocate memory";
1121 apr_file_seek(fp, APR_SET, &offset);
1122 if (apr_file_read(fp, buf, &len) != APR_SUCCESS) {
1123 *ctx->err = apr_psprintf(ctx->p, "Cannot read from file %s", arg);
1133 static const char *filesize_func(ap_expr_eval_ctx_t *ctx, const void *data,
1137 if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) == APR_SUCCESS
1138 && sb.filetype == APR_REG && sb.size > 0)
1139 return apr_psprintf(ctx->p, "%" APR_OFF_T_FMT, sb.size);
1144 static const char *unescape_func(ap_expr_eval_ctx_t *ctx, const void *data,
1147 char *result = apr_pstrdup(ctx->p, arg);
1148 int ret = ap_unescape_url_keep2f(result, 0);
1151 ap_log_rerror(LOG_MARK(ctx->info), APLOG_DEBUG, 0, ctx->r, APLOGNO(00538)
1152 "%s %% escape in unescape('%s') at %s:%d",
1153 ret == HTTP_BAD_REQUEST ? "Bad" : "Forbidden", arg,
1154 ctx->info->filename, ctx->info->line_number);
1158 static int op_nz(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1160 const char *name = (const char *)data;
1162 return (arg[0] == '\0');
1164 return (arg[0] != '\0');
1167 static int op_file_min(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1170 const char *name = (const char *)data;
1171 if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) != APR_SUCCESS)
1175 return (sb.filetype == APR_DIR);
1179 return (sb.filetype == APR_REG);
1181 return (sb.filetype == APR_REG && sb.size > 0);
1188 static int op_file_link(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1192 if (apr_stat(&sb, arg, APR_FINFO_MIN | APR_FINFO_LINK, ctx->p) == APR_SUCCESS
1193 && sb.filetype == APR_LNK) {
1200 static int op_file_xbit(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1203 if (apr_stat(&sb, arg, APR_FINFO_PROT| APR_FINFO_LINK, ctx->p) == APR_SUCCESS
1204 && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) {
1210 static int op_url_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1213 request_rec *rsub, *r = ctx->r;
1216 /* avoid some infinite recursions */
1217 if (r->main && r->main->uri && r->uri && strcmp(r->main->uri, r->uri) == 0)
1220 rsub = ap_sub_req_lookup_uri(arg, r, NULL);
1221 if (rsub->status < 400) {
1224 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
1225 "Subrequest for -U %s at %s:%d gave status: %d",
1226 arg, ctx->info->filename, ctx->info->line_number,
1228 ap_destroy_sub_req(rsub);
1232 static int op_file_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1236 request_rec *rsub, *r = ctx->r;
1239 rsub = ap_sub_req_lookup_file(arg, r, NULL);
1240 if (rsub->status < 300 &&
1241 /* double-check that file exists since default result is 200 */
1242 apr_stat(&sb, rsub->filename, APR_FINFO_MIN, ctx->p) == APR_SUCCESS) {
1245 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
1246 "Subrequest for -F %s at %s:%d gave status: %d",
1247 arg, ctx->info->filename, ctx->info->line_number,
1249 ap_destroy_sub_req(rsub);
1254 APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
1255 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *is_https = NULL;
1257 APR_DECLARE_OPTIONAL_FN(int, http2_is_h2, (conn_rec *));
1258 static APR_OPTIONAL_FN_TYPE(http2_is_h2) *is_http2 = NULL;
1260 static const char *conn_var_names[] = {
1263 "CONN_LOG_ID", /* 2 */
1264 "CONN_REMOTE_ADDR", /* 3 */
1269 static const char *conn_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1271 int index = ((const char **)data - conn_var_names);
1272 conn_rec *c = ctx->c;
1278 if (is_https && is_https(c))
1285 apr_sockaddr_t *addr = c->client_addr;
1286 if (addr->family == AF_INET6
1287 && !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr))
1298 return c->client_ip;
1300 if (is_http2 && is_http2(c))
1310 static const char *request_var_names[] = {
1311 "REQUEST_METHOD", /* 0 */
1312 "REQUEST_SCHEME", /* 1 */
1313 "REQUEST_URI", /* 2 */
1314 "REQUEST_FILENAME", /* 3 */
1315 "REMOTE_HOST", /* 4 */
1316 "REMOTE_IDENT", /* 5 */
1317 "REMOTE_USER", /* 6 */
1318 "SERVER_ADMIN", /* 7 */
1319 "SERVER_NAME", /* 8 */
1320 "SERVER_PORT", /* 9 */
1321 "SERVER_PROTOCOL", /* 10 */
1322 "SCRIPT_FILENAME", /* 11 */
1323 "PATH_INFO", /* 12 */
1324 "QUERY_STRING", /* 13 */
1325 "IS_SUBREQ", /* 14 */
1326 "DOCUMENT_ROOT", /* 15 */
1327 "AUTH_TYPE", /* 16 */
1328 "THE_REQUEST", /* 17 */
1329 "CONTENT_TYPE", /* 18 */
1331 "REQUEST_LOG_ID", /* 20 */
1332 "SCRIPT_USER", /* 21 */
1333 "SCRIPT_GROUP", /* 22 */
1334 "DOCUMENT_URI", /* 23 */
1335 "LAST_MODIFIED", /* 24 */
1336 "CONTEXT_PREFIX", /* 25 */
1337 "CONTEXT_DOCUMENT_ROOT", /* 26 */
1338 "REQUEST_STATUS", /* 27 */
1339 "REMOTE_ADDR", /* 28 */
1343 static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1345 int index = ((const char **)data - request_var_names);
1346 request_rec *r = ctx->r;
1354 return ap_http_scheme(r);
1360 return ap_get_useragent_host(r, REMOTE_NAME, NULL);
1362 return ap_get_remote_logname(r);
1366 return r->server->server_admin;
1368 return ap_get_server_name_for_url(r);
1370 return apr_psprintf(ctx->p, "%u", ap_get_server_port(r));
1376 return r->path_info;
1380 return (r->main != NULL ? "true" : "false");
1382 return ap_document_root(r);
1384 return r->ap_auth_type;
1386 return r->the_request;
1388 return r->content_type;
1396 if (r->finfo.valid & APR_FINFO_USER)
1397 apr_uid_name_get(&result, r->finfo.user, ctx->p);
1403 if (r->finfo.valid & APR_FINFO_USER)
1404 apr_gid_name_get(&result, r->finfo.group, ctx->p);
1412 apr_time_exp_lt(&tm, r->mtime);
1413 return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
1414 (tm.tm_year / 100) + 19, (tm.tm_year % 100),
1415 tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
1419 return ap_context_prefix(r);
1421 return ap_context_document_root(r);
1423 return r->status ? apr_psprintf(ctx->p, "%d", r->status) : "";
1425 return r->useragent_ip;
1432 static const char *req_header_var_names[] = {
1433 "HTTP_USER_AGENT", /* 0 */
1434 "HTTP_PROXY_CONNECTION", /* 1 */
1435 "HTTP_REFERER", /* 2 */
1436 "HTTP_COOKIE", /* 3 */
1437 "HTTP_FORWARDED", /* 4 */
1438 "HTTP_HOST", /* 5 */
1439 "HTTP_ACCEPT", /* 6 */
1443 static const char *req_header_header_names[] = {
1453 static const char *req_header_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1455 const char **varname = (const char **)data;
1456 int index = (varname - req_header_var_names);
1459 AP_DEBUG_ASSERT(index < 7);
1463 name = req_header_header_names[index];
1464 add_vary(ctx, name);
1465 return apr_table_get(ctx->r->headers_in, name);
1468 static const char *misc_var_names[] = {
1469 "TIME_YEAR", /* 0 */
1472 "TIME_HOUR", /* 3 */
1475 "TIME_WDAY", /* 6 */
1477 "SERVER_SOFTWARE", /* 8 */
1478 "API_VERSION", /* 9 */
1482 static const char *misc_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1485 int index = ((const char **)data - misc_var_names);
1486 apr_time_exp_lt(&tm, apr_time_now());
1490 return apr_psprintf(ctx->p, "%02d%02d", (tm.tm_year / 100) + 19,
1493 return apr_psprintf(ctx->p, "%02d", tm.tm_mon+1);
1495 return apr_psprintf(ctx->p, "%02d", tm.tm_mday);
1497 return apr_psprintf(ctx->p, "%02d", tm.tm_hour);
1499 return apr_psprintf(ctx->p, "%02d", tm.tm_min);
1501 return apr_psprintf(ctx->p, "%02d", tm.tm_sec);
1503 return apr_psprintf(ctx->p, "%d", tm.tm_wday);
1505 return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
1506 (tm.tm_year / 100) + 19, (tm.tm_year % 100),
1507 tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
1510 return ap_get_server_banner();
1512 return apr_itoa(ctx->p, MODULE_MAGIC_NUMBER_MAJOR);
1520 static int subnet_parse_arg(ap_expr_lookup_parms *parms)
1522 apr_ipsubnet_t *subnet;
1523 const char *addr = parms->arg;
1528 *parms->err = apr_psprintf(parms->ptemp,
1529 "-%s requires subnet/netmask as constant argument",
1534 mask = ap_strchr_c(addr, '/');
1536 addr = apr_pstrmemdup(parms->ptemp, addr, mask - addr);
1540 ret = apr_ipsubnet_create(&subnet, addr, mask, parms->pool);
1541 if (ret != APR_SUCCESS) {
1542 *parms->err = "parsing of subnet/netmask failed";
1546 *parms->data = subnet;
1550 static int op_ipmatch(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg1,
1553 apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data;
1554 apr_sockaddr_t *saddr;
1556 AP_DEBUG_ASSERT(subnet != NULL);
1558 /* maybe log an error if this goes wrong? */
1559 if (apr_sockaddr_info_get(&saddr, arg1, APR_UNSPEC, 0, 0, ctx->p) != APR_SUCCESS)
1562 return apr_ipsubnet_test(subnet, saddr);
1565 static int op_R(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg1)
1567 apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data;
1569 AP_DEBUG_ASSERT(subnet != NULL);
1574 return apr_ipsubnet_test(subnet, ctx->r->useragent_addr);
1577 static int op_T(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1584 return strcasecmp(arg, "off") == 0 ? FALSE : TRUE;
1587 return strcasecmp(arg, "no") == 0 ? FALSE : TRUE;
1590 return strcasecmp(arg, "false") == 0 ? FALSE : TRUE;
1592 return arg[1] == '\0' ? FALSE : TRUE;
1598 static int op_fnmatch(ap_expr_eval_ctx_t *ctx, const void *data,
1599 const char *arg1, const char *arg2)
1601 return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_PATHNAME));
1604 static int op_strmatch(ap_expr_eval_ctx_t *ctx, const void *data,
1605 const char *arg1, const char *arg2)
1607 return (APR_SUCCESS == apr_fnmatch(arg2, arg1, 0));
1610 static int op_strcmatch(ap_expr_eval_ctx_t *ctx, const void *data,
1611 const char *arg1, const char *arg2)
1613 return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_CASE_BLIND));
1616 struct expr_provider_single {
1619 ap_expr_lookup_fn_t *arg_parsing_func;
1623 struct expr_provider_multi {
1628 static const struct expr_provider_multi var_providers[] = {
1629 { misc_var_fn, misc_var_names },
1630 { req_header_var_fn, req_header_var_names },
1631 { request_var_fn, request_var_names },
1632 { conn_var_fn, conn_var_names },
1636 static const struct expr_provider_single string_func_providers[] = {
1637 { osenv_func, "osenv", NULL, 0 },
1638 { env_func, "env", NULL, 0 },
1639 { req_table_func, "resp", NULL, 0 },
1640 { req_table_func, "req", NULL, 0 },
1641 /* 'http' as alias for 'req' for compatibility with ssl_expr */
1642 { req_table_func, "http", NULL, 0 },
1643 { req_table_func, "note", NULL, 0 },
1644 { req_table_func, "reqenv", NULL, 0 },
1645 { req_table_func, "req_novary", NULL, 0 },
1646 { tolower_func, "tolower", NULL, 0 },
1647 { toupper_func, "toupper", NULL, 0 },
1648 { escape_func, "escape", NULL, 0 },
1649 { unescape_func, "unescape", NULL, 0 },
1650 { file_func, "file", NULL, 1 },
1651 { filesize_func, "filesize", NULL, 1 },
1652 { base64_func, "base64", NULL, 0 },
1653 { unbase64_func, "unbase64", NULL, 0 },
1654 { sha1_func, "sha1", NULL, 0 },
1655 { md5_func, "md5", NULL, 0 },
1659 static const struct expr_provider_single unary_op_providers[] = {
1660 { op_nz, "n", NULL, 0 },
1661 { op_nz, "z", NULL, 0 },
1662 { op_R, "R", subnet_parse_arg, 0 },
1663 { op_T, "T", NULL, 0 },
1664 { op_file_min, "d", NULL, 1 },
1665 { op_file_min, "e", NULL, 1 },
1666 { op_file_min, "f", NULL, 1 },
1667 { op_file_min, "s", NULL, 1 },
1668 { op_file_link, "L", NULL, 1 },
1669 { op_file_link, "h", NULL, 1 },
1670 { op_file_xbit, "x", NULL, 1 },
1671 { op_file_subr, "F", NULL, 0 },
1672 { op_url_subr, "U", NULL, 0 },
1673 { op_url_subr, "A", NULL, 0 },
1674 { NULL, NULL, NULL }
1677 static const struct expr_provider_single binary_op_providers[] = {
1678 { op_ipmatch, "ipmatch", subnet_parse_arg, 0 },
1679 { op_fnmatch, "fnmatch", NULL, 0 },
1680 { op_strmatch, "strmatch", NULL, 0 },
1681 { op_strcmatch, "strcmatch", NULL, 0 },
1682 { NULL, NULL, NULL }
1685 static int core_expr_lookup(ap_expr_lookup_parms *parms)
1687 switch (parms->type) {
1688 case AP_EXPR_FUNC_VAR: {
1689 const struct expr_provider_multi *prov = var_providers;
1690 while (prov->func) {
1691 const char **name = prov->names;
1693 if (strcasecmp(*name, parms->name) == 0) {
1694 *parms->func = prov->func;
1695 *parms->data = name;
1704 case AP_EXPR_FUNC_STRING:
1705 case AP_EXPR_FUNC_OP_UNARY:
1706 case AP_EXPR_FUNC_OP_BINARY: {
1707 const struct expr_provider_single *prov;
1708 switch (parms->type) {
1709 case AP_EXPR_FUNC_STRING:
1710 prov = string_func_providers;
1712 case AP_EXPR_FUNC_OP_UNARY:
1713 prov = unary_op_providers;
1715 case AP_EXPR_FUNC_OP_BINARY:
1716 prov = binary_op_providers;
1721 while (prov->func) {
1723 if (parms->type == AP_EXPR_FUNC_OP_UNARY)
1724 match = !strcmp(prov->name, parms->name);
1726 match = !strcasecmp(prov->name, parms->name);
1728 if ((parms->flags & AP_EXPR_FLAG_RESTRICTED)
1729 && prov->restricted) {
1731 apr_psprintf(parms->ptemp,
1732 "%s%s not available in restricted context",
1733 (parms->type == AP_EXPR_FUNC_STRING) ? "" : "-",
1737 *parms->func = prov->func;
1738 if (prov->arg_parsing_func) {
1739 return prov->arg_parsing_func(parms);
1742 *parms->data = prov->name;
1756 static int expr_lookup_not_found(ap_expr_lookup_parms *parms)
1759 const char *prefix = "";
1761 switch (parms->type) {
1762 case AP_EXPR_FUNC_VAR:
1765 case AP_EXPR_FUNC_STRING:
1768 case AP_EXPR_FUNC_LIST:
1769 type = "List-returning function";
1771 case AP_EXPR_FUNC_OP_UNARY:
1772 type = "Unary operator";
1774 case AP_EXPR_FUNC_OP_BINARY:
1775 type = "Binary operator";
1778 *parms->err = "Inavalid expression type in expr_lookup";
1781 if ( parms->type == AP_EXPR_FUNC_OP_UNARY
1782 || parms->type == AP_EXPR_FUNC_OP_BINARY) {
1785 *parms->err = apr_psprintf(parms->ptemp, "%s '%s%s' does not exist", type,
1786 prefix, parms->name);
1790 static int ap_expr_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1791 apr_pool_t *ptemp, server_rec *s)
1793 is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
1794 is_http2 = APR_RETRIEVE_OPTIONAL_FN(http2_is_h2);
1795 apr_pool_cleanup_register(pconf, &is_https, ap_pool_cleanup_set_null,
1796 apr_pool_cleanup_null);
1800 void ap_expr_init(apr_pool_t *p)
1802 ap_hook_expr_lookup(core_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
1803 ap_hook_expr_lookup(expr_lookup_not_found, NULL, NULL, APR_HOOK_REALLY_LAST);
1804 ap_hook_post_config(ap_expr_post_config, NULL, NULL, APR_HOOK_MIDDLE);