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"
30 #include "apr_fnmatch.h"
32 /* we know core's module_index is 0 */
33 #undef APLOG_MODULE_INDEX
34 #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
37 APR_HOOK_LINK(expr_lookup)
40 AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_expr_lookup_parms *parms),
43 #define LOG_MARK(info) __FILE__, __LINE__, (info)->module_index
45 static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
46 const ap_expr_t *info,
47 const ap_expr_t *args);
48 static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx, int n);
49 static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
50 const ap_expr_var_func_t *func,
53 /* define AP_EXPR_DEBUG to log the parse tree when parsing an expression */
55 static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
56 int loglevel, int indent);
59 static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
60 const ap_expr_t *node)
62 const char *result = "";
63 switch (node->node_op) {
66 result = node->node_arg1;
69 result = ap_expr_eval_var(ctx, node->node_arg1, node->node_arg2);
72 const char *s1 = ap_expr_eval_word(ctx, node->node_arg1);
73 const char *s2 = ap_expr_eval_word(ctx, node->node_arg2);
79 result = apr_pstrcat(ctx->p, s1, s2, NULL);
82 case op_StringFuncCall: {
83 const ap_expr_t *info = node->node_arg1;
84 const ap_expr_t *args = node->node_arg2;
85 result = ap_expr_eval_string_func(ctx, info, args);
88 case op_RegexBackref: {
89 const int *np = node->node_arg1;
90 result = ap_expr_eval_re_backref(ctx, *np);
94 *ctx->err = "Internal evaluation error: Unknown word expression node";
102 static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
103 const ap_expr_var_func_t *func,
106 AP_DEBUG_ASSERT(func != NULL);
107 AP_DEBUG_ASSERT(data != NULL);
108 return (*func)(ctx, data);
111 static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx, int n)
115 if (!ctx->re_pmatch || !ctx->re_source || *ctx->re_source == '\0' ||
116 ctx->re_nmatch < n + 1)
119 len = ctx->re_pmatch[n].rm_eo - ctx->re_pmatch[n].rm_so;
123 return apr_pstrndup(ctx->p, *ctx->re_source + ctx->re_pmatch[n].rm_so, len);
126 static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
127 const ap_expr_t *info,
128 const ap_expr_t *arg)
130 ap_expr_string_func_t *func = (ap_expr_string_func_t *)info->node_arg1;
131 const void *data = info->node_arg2;
133 AP_DEBUG_ASSERT(info->node_op == op_StringFuncInfo);
134 AP_DEBUG_ASSERT(func != NULL);
135 AP_DEBUG_ASSERT(data != NULL);
136 return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
139 static int intstrcmp(const char *s1, const char *s2)
141 apr_int64_t i1 = apr_atoi64(s1);
142 apr_int64_t i2 = apr_atoi64(s2);
152 static int ap_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
154 const ap_expr_t *e1 = node->node_arg1;
155 const ap_expr_t *e2 = node->node_arg2;
156 switch (node->node_op) {
158 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
160 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
162 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
164 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
166 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
168 return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
170 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
172 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
174 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
176 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
178 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
180 return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
182 const char *needle = ap_expr_eval_word(ctx, e1);
183 if (e2->node_op == op_ListElement) {
185 const ap_expr_t *val = e2->node_arg1;
186 AP_DEBUG_ASSERT(e2->node_op == op_ListElement);
187 if (strcmp(needle, ap_expr_eval_word(ctx, val)) == 0) {
192 } while (e2 != NULL);
194 else if (e2->node_op == op_ListFuncCall) {
195 const ap_expr_t *info = e2->node_arg1;
196 const ap_expr_t *arg = e2->node_arg2;
197 ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
198 apr_array_header_t *haystack;
200 AP_DEBUG_ASSERT(func != NULL);
201 AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
202 haystack = (*func)(ctx, info->node_arg2, ap_expr_eval_word(ctx, arg));
203 if (haystack == NULL)
205 for (; i < haystack->nelts; i++) {
206 if (strcmp(needle, APR_ARRAY_IDX(haystack,i,char *)) == 0)
214 const char *word = ap_expr_eval_word(ctx, e1);
215 const ap_regex_t *regex = e2->node_arg1;
219 * $0 ... $9 may contain stuff the user wants to keep. Therefore
220 * we only set them if there are capturing parens in the regex.
222 if (regex->re_nsub > 0) {
223 result = (0 == ap_regexec(regex, word, ctx->re_nmatch,
225 *ctx->re_source = result ? word : NULL;
228 result = (0 == ap_regexec(regex, word, 0, NULL, 0));
231 if (node->node_op == op_REG)
237 *ctx->err = "Internal evaluation error: Unknown comp expression node";
242 /* combined string/int comparison for compatibility with ssl_expr */
243 static int strcmplex(const char *str1, const char *str2)
257 for (i = 0; i < n1; i++) {
258 if (str1[i] > str2[i])
260 if (str1[i] < str2[i])
266 static int ssl_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
268 const ap_expr_t *e1 = node->node_arg1;
269 const ap_expr_t *e2 = node->node_arg2;
270 switch (node->node_op) {
273 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
276 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
279 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
282 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
285 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
288 return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
290 return ap_expr_eval_comp(ctx, node);
294 AP_DECLARE_NONSTD(int) ap_expr_lookup_default(ap_expr_lookup_parms *parms)
296 return ap_run_expr_lookup(parms);
299 AP_DECLARE(const char *) ap_expr_parse(apr_pool_t *pool, apr_pool_t *ptemp,
300 ap_expr_info_t *info, const char *expr,
301 ap_expr_lookup_fn_t *lookup_fn)
303 ap_expr_parse_ctx_t ctx;
309 ctx.inputlen = strlen(expr);
310 ctx.inputptr = ctx.inputbuf;
312 ctx.error = NULL; /* generic bison error message (XXX: usually not very useful, should be axed) */
313 ctx.error2 = NULL; /* additional error message */
314 ctx.flags = info->flags;
316 ctx.scan_buf[0] = '\0';
317 ctx.scan_ptr = ctx.scan_buf;
318 ctx.lookup_fn = lookup_fn ? lookup_fn : ap_expr_lookup_default;
323 * Be sure to avoid overflows in the scanner. In practice the input length
324 * will be limited by the config file parser, anyway.
325 * XXX: The scanner really should do proper buffer overflow checks
327 if (ctx.inputlen >= MAX_STRING_LEN)
328 return "Expression too long";
330 ap_expr_yylex_init(&ctx.scanner);
331 ap_expr_yyset_extra(&ctx, ctx.scanner);
332 rc = ap_expr_yyparse(&ctx);
333 ap_expr_yylex_destroy(ctx.scanner);
336 return apr_psprintf(pool, "%s: %s", ctx.error, ctx.error2);
340 else if (ctx.error2) {
344 if (rc) /* XXX can this happen? */
345 return "syntax error";
349 expr_dump_tree(ctx.expr, NULL, APLOG_NOTICE, 2);
352 info->root_node = ctx.expr;
357 AP_DECLARE(ap_expr_info_t*) ap_expr_parse_cmd_mi(const cmd_parms *cmd,
361 ap_expr_lookup_fn_t *lookup_fn,
364 ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t));
365 info->filename = cmd->directive->filename;
366 info->line_number = cmd->directive->line_num;
368 info->module_index = module_index;
369 *err = ap_expr_parse(cmd->pool, cmd->temp_pool, info, expr, lookup_fn);
377 ap_expr_t *ap_expr_make(ap_expr_node_op_e op, const void *a1, const void *a2,
378 ap_expr_parse_ctx_t *ctx)
380 ap_expr_t *node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
382 node->node_arg1 = a1;
383 node->node_arg2 = a2;
387 static ap_expr_t *ap_expr_info_make(int type, const char *name,
388 ap_expr_parse_ctx_t *ctx,
389 const ap_expr_t *arg)
391 ap_expr_t *info = apr_palloc(ctx->pool, sizeof(ap_expr_t));
392 ap_expr_lookup_parms parms;
394 parms.flags = ctx->flags;
395 parms.pool = ctx->pool;
396 parms.ptemp = ctx->ptemp;
398 parms.func = &info->node_arg1;
399 parms.data = &info->node_arg2;
400 parms.err = &ctx->error2;
401 parms.arg = (arg && arg->node_op == op_String) ? arg->node_arg1 : NULL;
402 if (ctx->lookup_fn(&parms) != OK)
407 ap_expr_t *ap_expr_str_func_make(const char *name, const ap_expr_t *arg,
408 ap_expr_parse_ctx_t *ctx)
410 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx, arg);
414 info->node_op = op_StringFuncInfo;
415 return ap_expr_make(op_StringFuncCall, info, arg, ctx);
418 ap_expr_t *ap_expr_list_func_make(const char *name, const ap_expr_t *arg,
419 ap_expr_parse_ctx_t *ctx)
421 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_LIST, name, ctx, arg);
425 info->node_op = op_ListFuncInfo;
426 return ap_expr_make(op_ListFuncCall, info, arg, ctx);
429 ap_expr_t *ap_expr_unary_op_make(const char *name, const ap_expr_t *arg,
430 ap_expr_parse_ctx_t *ctx)
432 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx, arg);
436 info->node_op = op_UnaryOpInfo;
437 return ap_expr_make(op_UnaryOpCall, info, arg, ctx);
440 ap_expr_t *ap_expr_binary_op_make(const char *name, const ap_expr_t *arg1,
441 const ap_expr_t *arg2, ap_expr_parse_ctx_t *ctx)
444 ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_OP_BINARY, name, ctx,
449 info->node_op = op_BinaryOpInfo;
450 args = ap_expr_make(op_BinaryOpArgs, arg1, arg2, ctx);
451 return ap_expr_make(op_BinaryOpCall, info, args, ctx);
455 ap_expr_t *ap_expr_var_make(const char *name, ap_expr_parse_ctx_t *ctx)
457 ap_expr_t *node = ap_expr_info_make(AP_EXPR_FUNC_VAR, name, ctx, NULL);
461 node->node_op = op_Var;
467 #define MARK APLOG_MARK,loglevel,0,s
468 #define DUMP_E_E(op, e1, e2) \
469 do { ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, e1, e2); \
470 if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \
471 if (e2) expr_dump_tree(e2, s, loglevel, indent + 2); \
473 #define DUMP_S_E(op, s1, e1) \
474 do { ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, e1); \
475 if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \
477 #define DUMP_S_P(op, s1, p1) \
478 ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, p1);
479 #define DUMP_P_P(op, p1, p2) \
480 ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, p1, p2);
481 #define DUMP_S_S(op, s1, s2) \
482 ap_log_error(MARK,"%*s%s: '%s' '%s'", indent, " ", op, (char *)s1, (char *)s2)
483 #define DUMP_P(op, p1) \
484 ap_log_error(MARK,"%*s%s: %pp", indent, " ", op, p1);
485 #define DUMP_IP(op, p1) \
486 ap_log_error(MARK,"%*s%s: %d", indent, " ", op, *(int *)p1);
487 #define DUMP_S(op, s1) \
488 ap_log_error(MARK,"%*s%s: '%s'", indent, " ", op, (char *)s1)
490 #define CASE_OP(op) case op: name = #op ; break;
492 static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
493 int loglevel, int indent)
495 switch (e->node_op) {
502 switch (e->node_op) {
509 ap_log_error(MARK, "%*s%s", indent, " ", name);
513 /* arg1: string, arg2: expr */
515 case op_BinaryOpCall:
516 case op_BinaryOpArgs:
519 switch (e->node_op) {
520 CASE_OP(op_BinaryOpCall);
521 CASE_OP(op_UnaryOpCall);
522 CASE_OP(op_BinaryOpArgs);
526 DUMP_S_E(name, e->node_arg1, e->node_arg2);
530 /* arg1: expr, arg2: expr */
551 case op_StringFuncCall:
552 case op_ListFuncCall:
556 switch (e->node_op) {
577 CASE_OP(op_StringFuncCall);
578 CASE_OP(op_ListFuncCall);
579 CASE_OP(op_ListElement);
583 DUMP_E_E(name, e->node_arg1, e->node_arg2);
591 switch (e->node_op) {
597 DUMP_S(name, e->node_arg1);
600 /* arg1: pointer, arg2: pointer */
602 case op_StringFuncInfo:
604 case op_BinaryOpInfo:
605 case op_ListFuncInfo:
608 switch (e->node_op) {
610 CASE_OP(op_StringFuncInfo);
611 CASE_OP(op_UnaryOpInfo);
612 CASE_OP(op_BinaryOpInfo);
613 CASE_OP(op_ListFuncInfo);
617 DUMP_P_P(name, e->node_arg1, e->node_arg2);
622 DUMP_P("op_Regex", e->node_arg1);
624 /* arg1: pointer to int */
625 case op_RegexBackref:
626 DUMP_IP("op_RegexBackref", e->node_arg1);
629 ap_log_error(MARK, "%*sERROR: INVALID OP %d", indent, " ", e->node_op);
633 #endif /* AP_EXPR_DEBUG */
635 static int ap_expr_eval_unary_op(ap_expr_eval_ctx_t *ctx, const ap_expr_t *info,
636 const ap_expr_t *arg)
638 const ap_expr_op_unary_t *op_func = info->node_arg1;
639 const void *data = info->node_arg2;
641 AP_DEBUG_ASSERT(info->node_op == op_UnaryOpInfo);
642 AP_DEBUG_ASSERT(op_func != NULL);
643 AP_DEBUG_ASSERT(data != NULL);
644 return (*op_func)(ctx, data, ap_expr_eval_word(ctx, arg));
647 static int ap_expr_eval_binary_op(ap_expr_eval_ctx_t *ctx,
648 const ap_expr_t *info,
649 const ap_expr_t *args)
651 const ap_expr_op_binary_t *op_func = info->node_arg1;
652 const void *data = info->node_arg2;
653 const ap_expr_t *a1 = args->node_arg1;
654 const ap_expr_t *a2 = args->node_arg2;
656 AP_DEBUG_ASSERT(info->node_op == op_BinaryOpInfo);
657 AP_DEBUG_ASSERT(args->node_op == op_BinaryOpArgs);
658 AP_DEBUG_ASSERT(op_func != NULL);
659 AP_DEBUG_ASSERT(data != NULL);
660 return (*op_func)(ctx, data, ap_expr_eval_word(ctx, a1),
661 ap_expr_eval_word(ctx, a2));
665 static int ap_expr_eval(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
667 const ap_expr_t *e1 = node->node_arg1;
668 const ap_expr_t *e2 = node->node_arg2;
669 switch (node->node_op) {
675 return (!ap_expr_eval(ctx, e1));
677 return (ap_expr_eval(ctx, e1) || ap_expr_eval(ctx, e2));
679 return (ap_expr_eval(ctx, e1) && ap_expr_eval(ctx, e2));
681 return ap_expr_eval_unary_op(ctx, e1, e2);
682 case op_BinaryOpCall:
683 return ap_expr_eval_binary_op(ctx, e1, e2);
685 if (ctx->info->flags & AP_EXPR_FLAG_SSL_EXPR_COMPAT)
686 return ssl_expr_eval_comp(ctx, e1);
688 return ap_expr_eval_comp(ctx, e1);
690 *ctx->err = "Internal evaluation error: Unknown expression node";
695 AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *info,
698 return ap_expr_exec_re(r, info, 0, NULL, NULL, err);
701 AP_DECLARE(int) ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx)
705 AP_DEBUG_ASSERT(ctx->p != NULL);
706 /* XXX: allow r, c == NULL */
707 AP_DEBUG_ASSERT(ctx->r != NULL);
708 AP_DEBUG_ASSERT(ctx->c != NULL);
709 AP_DEBUG_ASSERT(ctx->s != NULL);
710 AP_DEBUG_ASSERT(ctx->err != NULL);
711 AP_DEBUG_ASSERT(ctx->info != NULL);
712 if (ctx->re_pmatch) {
713 AP_DEBUG_ASSERT(ctx->re_source != NULL);
714 AP_DEBUG_ASSERT(ctx->re_nmatch > 0);
718 if (ctx->info->flags & AP_EXPR_FLAG_STRING_RESULT) {
719 *ctx->result_string = ap_expr_eval_word(ctx, ctx->info->root_node);
720 if (*ctx->err != NULL) {
721 ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
722 "Evaluation of expression from %s:%d failed: %s",
723 ctx->info->filename, ctx->info->line_number, *ctx->err);
726 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
727 "Evaluation of string expression from %s:%d gave: %s",
728 ctx->info->filename, ctx->info->line_number,
729 *ctx->result_string);
734 rc = ap_expr_eval(ctx, ctx->info->root_node);
735 if (*ctx->err != NULL) {
736 ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
737 "Evaluation of expression from %s:%d failed: %s",
738 ctx->info->filename, ctx->info->line_number, *ctx->err);
742 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
743 "Evaluation of expression from %s:%d gave: %d",
744 ctx->info->filename, ctx->info->line_number, rc);
746 if (ctx->vary_this && *ctx->vary_this)
747 apr_table_merge(ctx->r->headers_out, "Vary", *ctx->vary_this);
754 AP_DECLARE(int) ap_expr_exec_re(request_rec *r, const ap_expr_info_t *info,
755 apr_size_t nmatch, ap_regmatch_t *pmatch,
756 const char **source, const char **err)
758 ap_expr_eval_ctx_t ctx;
759 int dont_vary = (info->flags & AP_EXPR_FLAG_DONT_VARY);
760 const char *tmp_source = NULL, *vary_this = NULL;
761 ap_regmatch_t tmp_pmatch[10];
763 AP_DEBUG_ASSERT((info->flags & AP_EXPR_FLAG_STRING_RESULT) == 0);
766 ctx.c = r->connection;
771 ctx.re_nmatch = nmatch;
772 ctx.re_pmatch = pmatch;
773 ctx.re_source = source;
774 ctx.vary_this = dont_vary ? NULL : &vary_this;
779 ctx.re_pmatch = tmp_pmatch;
780 ctx.re_source = &tmp_source;
783 return ap_expr_exec_ctx(&ctx);
786 AP_DECLARE(const char *) ap_expr_str_exec_re(request_rec *r,
787 const ap_expr_info_t *info,
789 ap_regmatch_t *pmatch,
793 ap_expr_eval_ctx_t ctx;
795 const char *tmp_source = NULL, *vary_this = NULL;
796 ap_regmatch_t tmp_pmatch[10];
799 AP_DEBUG_ASSERT(info->flags & AP_EXPR_FLAG_STRING_RESULT);
801 if (info->root_node->node_op == op_String) {
802 /* short-cut for constant strings */
804 return (const char *)info->root_node->node_arg1;
807 dont_vary = (info->flags & AP_EXPR_FLAG_DONT_VARY);
810 ctx.c = r->connection;
815 ctx.re_nmatch = nmatch;
816 ctx.re_pmatch = pmatch;
817 ctx.re_source = source;
818 ctx.vary_this = dont_vary ? NULL : &vary_this;
820 ctx.result_string = &result;
824 ctx.re_pmatch = tmp_pmatch;
825 ctx.re_source = &tmp_source;
828 rc = ap_expr_exec_ctx(&ctx);
837 AP_DECLARE(const char *) ap_expr_str_exec(request_rec *r,
838 const ap_expr_info_t *info,
841 return ap_expr_str_exec_re(r, info, 0, NULL, NULL, err);
845 static void add_vary(ap_expr_eval_ctx_t *ctx, const char *name)
850 if (*ctx->vary_this) {
851 *ctx->vary_this = apr_pstrcat(ctx->p, *ctx->vary_this, ", ", name,
855 *ctx->vary_this = name;
859 static const char *req_table_func(ap_expr_eval_ctx_t *ctx, const void *data,
862 const char *name = (const char *)data;
867 if (name[2] == 's') { /* resp */
868 /* Try r->headers_out first, fall back on err_headers_out. */
869 const char *v = apr_table_get(ctx->r->headers_out, arg);
873 t = ctx->r->err_headers_out;
875 else if (name[0] == 'n') /* notes */
877 else if (name[3] == 'e') /* reqenv */
878 t = ctx->r->subprocess_env;
879 else { /* req, http */
880 t = ctx->r->headers_in;
883 return apr_table_get(t, arg);
886 static const char *env_func(ap_expr_eval_ctx_t *ctx, const void *data,
890 /* this order is for ssl_expr compatibility */
892 if ((res = apr_table_get(ctx->r->notes, arg)) != NULL)
894 else if ((res = apr_table_get(ctx->r->subprocess_env, arg)) != NULL)
900 static const char *osenv_func(ap_expr_eval_ctx_t *ctx, const void *data,
906 static const char *tolower_func(ap_expr_eval_ctx_t *ctx, const void *data,
909 char *result = apr_pstrdup(ctx->p, arg);
910 ap_str_tolower(result);
914 static const char *toupper_func(ap_expr_eval_ctx_t *ctx, const void *data,
917 char *result = apr_pstrdup(ctx->p, arg);
918 ap_str_toupper(result);
922 static const char *escape_func(ap_expr_eval_ctx_t *ctx, const void *data,
925 return ap_escape_uri(ctx->p, arg);
928 #define MAX_FILE_SIZE 10*1024*1024
929 static const char *file_func(ap_expr_eval_ctx_t *ctx, const void *data,
938 if (apr_file_open(&fp, arg, APR_READ|APR_BUFFERED,
939 APR_OS_DEFAULT, ctx->p) != APR_SUCCESS) {
940 *ctx->err = apr_psprintf(ctx->p, "Cannot open file %s", arg);
943 apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
944 if (finfo.size > MAX_FILE_SIZE) {
945 *ctx->err = apr_psprintf(ctx->p, "File %s too large", arg);
949 len = (apr_size_t)finfo.size;
955 if ((buf = (char *)apr_palloc(ctx->p, sizeof(char)*(len+1))) == NULL) {
956 *ctx->err = "Cannot allocate memory";
961 apr_file_seek(fp, APR_SET, &offset);
962 if (apr_file_read(fp, buf, &len) != APR_SUCCESS) {
963 *ctx->err = apr_psprintf(ctx->p, "Cannot read from file %s", arg);
973 static const char *filesize_func(ap_expr_eval_ctx_t *ctx, const void *data,
977 if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) == APR_SUCCESS
978 && sb.filetype == APR_REG && sb.size > 0)
979 return apr_psprintf(ctx->p, "%" APR_OFF_T_FMT, sb.size);
984 static const char *unescape_func(ap_expr_eval_ctx_t *ctx, const void *data,
987 char *result = apr_pstrdup(ctx->p, arg);
988 if (ap_unescape_url(result))
995 static int op_nz(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
997 const char *name = (const char *)data;
999 return (arg[0] == '\0');
1001 return (arg[0] != '\0');
1004 static int op_file_min(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1007 const char *name = (const char *)data;
1008 if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) != APR_SUCCESS)
1012 return (sb.filetype == APR_DIR);
1016 return (sb.filetype == APR_REG);
1018 return (sb.filetype == APR_REG && sb.size > 0);
1025 static int op_file_link(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1029 if (apr_stat(&sb, arg, APR_FINFO_MIN | APR_FINFO_LINK, ctx->p) == APR_SUCCESS
1030 && sb.filetype == APR_LNK) {
1037 static int op_file_xbit(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1040 if (apr_stat(&sb, arg, APR_FINFO_PROT| APR_FINFO_LINK, ctx->p) == APR_SUCCESS
1041 && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) {
1047 static int op_url_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1050 request_rec *rsub, *r = ctx->r;
1053 /* avoid some infinite recursions */
1054 if (r->main && r->main->uri && r->uri && strcmp(r->main->uri, r->uri) == 0)
1057 rsub = ap_sub_req_lookup_uri(arg, r, NULL);
1058 if (rsub->status < 400) {
1061 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
1062 "Subrequest for -U %s at %s:%d gave status: %d",
1063 arg, ctx->info->filename, ctx->info->line_number,
1065 ap_destroy_sub_req(rsub);
1069 static int op_file_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1073 request_rec *rsub, *r = ctx->r;
1076 rsub = ap_sub_req_lookup_file(arg, r, NULL);
1077 if (rsub->status < 300 &&
1078 /* double-check that file exists since default result is 200 */
1079 apr_stat(&sb, rsub->filename, APR_FINFO_MIN, ctx->p) == APR_SUCCESS) {
1082 ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
1083 "Subrequest for -F %s at %s:%d gave status: %d",
1084 arg, ctx->info->filename, ctx->info->line_number,
1086 ap_destroy_sub_req(rsub);
1091 APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
1092 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *is_https = NULL;
1094 static const char *conn_var_names[] = {
1095 "REMOTE_ADDR", /* 0 */
1098 "CONN_LOG_ID", /* 3 */
1102 static const char *conn_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1104 int index = ((const char **)data - conn_var_names);
1105 conn_rec *c = ctx->c;
1111 return c->remote_ip;
1113 if (is_https && is_https(c))
1120 apr_sockaddr_t *addr = c->remote_addr;
1121 if (addr->family == AF_INET6
1122 && !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr))
1138 static const char *request_var_names[] = {
1139 "REQUEST_METHOD", /* 0 */
1140 "REQUEST_SCHEME", /* 1 */
1141 "REQUEST_URI", /* 2 */
1142 "REQUEST_FILENAME", /* 3 */
1143 "REMOTE_HOST", /* 4 */
1144 "REMOTE_IDENT", /* 5 */
1145 "REMOTE_USER", /* 6 */
1146 "SERVER_ADMIN", /* 7 */
1147 "SERVER_NAME", /* 8 */
1148 "SERVER_PORT", /* 9 */
1149 "SERVER_PROTOCOL", /* 10 */
1150 "SCRIPT_FILENAME", /* 11 */
1151 "PATH_INFO", /* 12 */
1152 "QUERY_STRING", /* 13 */
1153 "IS_SUBREQ", /* 14 */
1154 "DOCUMENT_ROOT", /* 15 */
1155 "AUTH_TYPE", /* 16 */
1156 "THE_REQUEST", /* 17 */
1157 "CONTENT_TYPE", /* 18 */
1159 "REQUEST_LOG_ID", /* 20 */
1160 "SCRIPT_USER", /* 21 */
1161 "SCRIPT_GROUP", /* 22 */
1162 "DOCUMENT_URI", /* 23 */
1163 "LAST_MODIFIED", /* 24 */
1164 "CONTEXT_PREFIX", /* 25 */
1165 "CONTEXT_DOCUMENT_ROOT", /* 26 */
1166 "REQUEST_STATUS", /* 27 */
1170 static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1172 int index = ((const char **)data - request_var_names);
1173 request_rec *r = ctx->r;
1181 return ap_http_scheme(r);
1187 return ap_get_remote_host(r->connection, r->per_dir_config,
1190 return ap_get_remote_logname(r);
1194 return r->server->server_admin;
1196 return ap_get_server_name_for_url(r);
1198 return apr_psprintf(ctx->p, "%u", ap_get_server_port(r));
1204 return r->path_info;
1208 return (r->main != NULL ? "true" : "false");
1210 return ap_document_root(r);
1212 return r->ap_auth_type;
1214 return r->the_request;
1216 return r->content_type;
1224 if (r->finfo.valid & APR_FINFO_USER)
1225 apr_uid_name_get(&result, r->finfo.user, ctx->p);
1231 if (r->finfo.valid & APR_FINFO_USER)
1232 apr_gid_name_get(&result, r->finfo.group, ctx->p);
1240 apr_time_exp_lt(&tm, r->mtime);
1241 return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
1242 (tm.tm_year / 100) + 19, (tm.tm_year % 100),
1243 tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
1247 return ap_context_prefix(r);
1249 return ap_context_document_root(r);
1251 return r->status ? apr_psprintf(ctx->p, "%d", r->status) : "";
1258 static const char *req_header_var_names[] = {
1260 "HTTP_PROXY_CONNECTION",
1269 static const char *req_header_header_names[] = {
1279 static const char *req_header_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1281 const char **varname = (const char **)data;
1282 int index = (varname - req_header_var_names);
1285 AP_DEBUG_ASSERT(index < 6);
1289 name = req_header_header_names[index];
1290 add_vary(ctx, name);
1291 return apr_table_get(ctx->r->headers_in, name);
1294 static const char *misc_var_names[] = {
1295 "TIME_YEAR", /* 0 */
1298 "TIME_HOUR", /* 3 */
1301 "TIME_WDAY", /* 6 */
1303 "SERVER_SOFTWARE", /* 8 */
1304 "API_VERSION", /* 9 */
1308 static const char *misc_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1311 int index = ((const char **)data - misc_var_names);
1312 apr_time_exp_lt(&tm, apr_time_now());
1316 return apr_psprintf(ctx->p, "%02d%02d", (tm.tm_year / 100) + 19,
1319 return apr_psprintf(ctx->p, "%02d", tm.tm_mon+1);
1321 return apr_psprintf(ctx->p, "%02d", tm.tm_mday);
1323 return apr_psprintf(ctx->p, "%02d", tm.tm_hour);
1325 return apr_psprintf(ctx->p, "%02d", tm.tm_min);
1327 return apr_psprintf(ctx->p, "%02d", tm.tm_sec);
1329 return apr_psprintf(ctx->p, "%d", tm.tm_wday);
1331 return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
1332 (tm.tm_year / 100) + 19, (tm.tm_year % 100),
1333 tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
1336 return ap_get_server_banner();
1338 return apr_itoa(ctx->p, MODULE_MAGIC_NUMBER);
1346 static int subnet_parse_arg(ap_expr_lookup_parms *parms)
1348 apr_ipsubnet_t *subnet;
1349 const char *addr = parms->arg;
1354 *parms->err = apr_psprintf(parms->ptemp,
1355 "-%s requires subnet/netmask as constant argument",
1360 mask = ap_strchr_c(addr, '/');
1362 addr = apr_pstrmemdup(parms->ptemp, addr, mask - addr);
1366 ret = apr_ipsubnet_create(&subnet, addr, mask, parms->pool);
1367 if (ret != APR_SUCCESS) {
1368 *parms->err = "parsing of subnet/netmask failed";
1372 *parms->data = subnet;
1376 static int op_ipmatch(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg1,
1379 apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data;
1380 apr_sockaddr_t *saddr;
1382 AP_DEBUG_ASSERT(subnet != NULL);
1384 /* maybe log an error if this goes wrong? */
1385 if (apr_sockaddr_info_get(&saddr, arg1, APR_UNSPEC, 0, 0, ctx->p) != APR_SUCCESS)
1388 return apr_ipsubnet_test(subnet, saddr);
1391 static int op_R(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg1)
1393 apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data;
1395 AP_DEBUG_ASSERT(subnet != NULL);
1400 return apr_ipsubnet_test(subnet, ctx->c->remote_addr);
1403 static int op_T(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
1410 return strcasecmp(arg, "off") == 0 ? FALSE : TRUE;
1413 return strcasecmp(arg, "no") == 0 ? FALSE : TRUE;
1416 return strcasecmp(arg, "false") == 0 ? FALSE : TRUE;
1418 return arg[1] == '\0' ? FALSE : TRUE;
1424 static int op_fnmatch(ap_expr_eval_ctx_t *ctx, const void *data,
1425 const char *arg1, const char *arg2)
1427 return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_PATHNAME));
1430 static int op_strmatch(ap_expr_eval_ctx_t *ctx, const void *data,
1431 const char *arg1, const char *arg2)
1433 return (APR_SUCCESS == apr_fnmatch(arg2, arg1, 0));
1436 static int op_strcmatch(ap_expr_eval_ctx_t *ctx, const void *data,
1437 const char *arg1, const char *arg2)
1439 return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_CASE_BLIND));
1442 struct expr_provider_single {
1445 ap_expr_lookup_fn_t *arg_parsing_func;
1449 struct expr_provider_multi {
1454 static const struct expr_provider_multi var_providers[] = {
1455 { misc_var_fn, misc_var_names },
1456 { req_header_var_fn, req_header_var_names },
1457 { request_var_fn, request_var_names },
1458 { conn_var_fn, conn_var_names },
1462 static const struct expr_provider_single string_func_providers[] = {
1463 { osenv_func, "osenv", NULL, 0 },
1464 { env_func, "env", NULL, 0 },
1465 { req_table_func, "resp", NULL, 0 },
1466 { req_table_func, "req", NULL, 0 },
1467 /* 'http' as alias for 'req' for compatibility with ssl_expr */
1468 { req_table_func, "http", NULL, 0 },
1469 { req_table_func, "note", NULL, 0 },
1470 { req_table_func, "reqenv", NULL, 0 },
1471 { tolower_func, "tolower", NULL, 0 },
1472 { toupper_func, "toupper", NULL, 0 },
1473 { escape_func, "escape", NULL, 0 },
1474 { unescape_func, "unescape", NULL, 0 },
1475 { file_func, "file", NULL, 1 },
1476 { filesize_func, "filesize", NULL, 1 },
1479 /* XXX: base64 encode/decode ? */
1481 static const struct expr_provider_single unary_op_providers[] = {
1482 { op_nz, "n", NULL, 0 },
1483 { op_nz, "z", NULL, 0 },
1484 { op_R, "R", subnet_parse_arg, 0 },
1485 { op_T, "T", NULL, 0 },
1486 { op_file_min, "d", NULL, 1 },
1487 { op_file_min, "e", NULL, 1 },
1488 { op_file_min, "f", NULL, 1 },
1489 { op_file_min, "s", NULL, 1 },
1490 { op_file_link, "L", NULL, 1 },
1491 { op_file_link, "h", NULL, 1 },
1492 { op_file_xbit, "x", NULL, 1 },
1493 { op_file_subr, "F", NULL, 0 },
1494 { op_url_subr, "U", NULL, 0 },
1495 { op_url_subr, "A", NULL, 0 },
1496 { NULL, NULL, NULL }
1499 static const struct expr_provider_single binary_op_providers[] = {
1500 { op_ipmatch, "ipmatch", subnet_parse_arg, 0 },
1501 { op_fnmatch, "fnmatch", NULL, 0 },
1502 { op_strmatch, "strmatch", NULL, 0 },
1503 { op_strcmatch, "strcmatch", NULL, 0 },
1504 { NULL, NULL, NULL }
1507 static int core_expr_lookup(ap_expr_lookup_parms *parms)
1509 switch (parms->type) {
1510 case AP_EXPR_FUNC_VAR: {
1511 const struct expr_provider_multi *prov = var_providers;
1512 while (prov->func) {
1513 const char **name = prov->names;
1515 if (strcasecmp(*name, parms->name) == 0) {
1516 *parms->func = prov->func;
1517 *parms->data = name;
1526 case AP_EXPR_FUNC_STRING:
1527 case AP_EXPR_FUNC_OP_UNARY:
1528 case AP_EXPR_FUNC_OP_BINARY: {
1529 const struct expr_provider_single *prov;
1530 switch (parms->type) {
1531 case AP_EXPR_FUNC_STRING:
1532 prov = string_func_providers;
1534 case AP_EXPR_FUNC_OP_UNARY:
1535 prov = unary_op_providers;
1537 case AP_EXPR_FUNC_OP_BINARY:
1538 prov = binary_op_providers;
1543 while (prov->func) {
1544 if (strcasecmp(prov->name, parms->name) == 0) {
1545 if ((parms->flags & AP_EXPR_FLAG_RESTRICTED)
1546 && prov->restricted) {
1548 apr_psprintf(parms->ptemp,
1549 "%s%s not available in restricted context",
1550 (parms->type == AP_EXPR_FUNC_STRING) ? "" : "-",
1554 *parms->func = prov->func;
1555 if (prov->arg_parsing_func) {
1556 return prov->arg_parsing_func(parms);
1559 *parms->data = prov->name;
1573 static int expr_lookup_not_found(ap_expr_lookup_parms *parms)
1577 switch (parms->type) {
1578 case AP_EXPR_FUNC_VAR:
1581 case AP_EXPR_FUNC_STRING:
1584 case AP_EXPR_FUNC_LIST:
1585 type = "List-returning function";
1587 case AP_EXPR_FUNC_OP_UNARY:
1588 type = "Unary operator";
1590 case AP_EXPR_FUNC_OP_BINARY:
1591 type = "Binary operator";
1594 *parms->err = "Inavalid expression type in expr_lookup";
1597 *parms->err = apr_psprintf(parms->ptemp, "%s '%s' does not exist", type,
1602 static int ap_expr_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1603 apr_pool_t *ptemp, server_rec *s)
1605 is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
1606 apr_pool_cleanup_register(pconf, &is_https, ap_pool_cleanup_set_null,
1607 apr_pool_cleanup_null);
1611 void ap_expr_init(apr_pool_t *p)
1613 ap_hook_expr_lookup(core_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
1614 ap_hook_expr_lookup(expr_lookup_not_found, NULL, NULL, APR_HOOK_REALLY_LAST);
1615 ap_hook_post_config(ap_expr_post_config, NULL, NULL, APR_HOOK_MIDDLE);