/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* _ _ * ap_expr_eval.c, based on ssl_expr_eval.c from mod_ssl */ #include "httpd.h" #include "http_log.h" #include "http_core.h" #include "http_protocol.h" #include "ap_provider.h" #include "util_expr_private.h" #include "apr_lib.h" APLOG_USE_MODULE(core); APR_HOOK_STRUCT( APR_HOOK_LINK(expr_lookup) ) AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_expr_lookup_parms *parms), (parms), DECLINED) static const char *ap_expr_eval_string_func(ap_expr_eval_ctx *ctx, const ap_expr *info, const ap_expr *args); static const char *ap_expr_eval_var(ap_expr_eval_ctx *ctx, const ap_expr_var_func_t *func, const void *data); static void expr_dump_tree(const ap_expr *e, const server_rec *s, int loglevel, int indent); static const char *ap_expr_eval_word(ap_expr_eval_ctx *ctx, const ap_expr *node) { const char *result = ""; switch (node->node_op) { case op_Digit: result = node->node_arg1; break; case op_String: result = node->node_arg1; break; case op_Var: result = ap_expr_eval_var(ctx, node->node_arg1, node->node_arg2); break; case op_StringFuncCall: { const ap_expr *info = node->node_arg1; const ap_expr *args = node->node_arg2; result = ap_expr_eval_string_func(ctx, info, args); break; } default: *ctx->err = "Internal evaluation error: Unknown expression node"; break; } if (!result) result = ""; return result; } static const char *ap_expr_eval_var(ap_expr_eval_ctx *ctx, const ap_expr_var_func_t *func, const void *data) { AP_DEBUG_ASSERT(func != NULL); AP_DEBUG_ASSERT(data != NULL); return (*func)(ctx, data); } static const char *ap_expr_eval_string_func(ap_expr_eval_ctx *ctx, const ap_expr *info, const ap_expr *arg) { ap_expr_string_func_t *func = info->node_arg1; const void *data = info->node_arg2; AP_DEBUG_ASSERT(info->node_op == op_StringFuncInfo); 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); apr_int64_t i2 = apr_atoi64(s2); if (i1 < i2) return -1; else if (i1 == i2) return 0; else return 1; } static int ap_expr_eval_comp(ap_expr_eval_ctx *ctx, const ap_expr *node) { switch (node->node_op) { case op_EQ: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0); } case op_NE: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0); } case op_LT: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0); } case op_LE: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0); } case op_GT: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0); } case op_GE: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0); } case op_STR_EQ: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0); } case op_STR_NE: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0); } case op_STR_LT: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0); } case op_STR_LE: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0); } case op_STR_GT: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0); } case op_STR_GE: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0); } case op_IN: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; const char *needle = ap_expr_eval_word(ctx, e1); if (e2->node_op == op_ListElement) { do { const ap_expr *val = e2->node_arg1; AP_DEBUG_ASSERT(e2->node_op == op_ListElement); if (strcmp(needle, ap_expr_eval_word(ctx, val)) == 0) { return 1; break; } e2 = e2->node_arg2; } while (e2 != NULL); } else if (e2->node_op == op_ListFuncCall) { const ap_expr *info = e2->node_arg1; const ap_expr *arg = e2->node_arg2; ap_expr_list_func_t *func = info->node_arg1; apr_array_header_t *haystack; int i = 0; 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)); if (haystack == NULL) return 0; for (; i < haystack->nelts; i++) { if (strcmp(needle, APR_ARRAY_IDX(haystack,i,char *)) == 0) return 1; } } return 0; } case op_REG: { const ap_expr *e1; const ap_expr *e2; const char *word; const ap_regex_t *regex; e1 = node->node_arg1; e2 = node->node_arg2; word = ap_expr_eval_word(ctx, e1); regex = e2->node_arg1; return (ap_regexec(regex, word, 0, NULL, 0) == 0); } case op_NRE: { const ap_expr *e1; const ap_expr *e2; const char *word; const ap_regex_t *regex; e1 = node->node_arg1; e2 = node->node_arg2; word = ap_expr_eval_word(ctx, e1); regex = e2->node_arg1; return !(ap_regexec(regex, word, 0, NULL, 0) == 0); } default: { *ctx->err = "Internal evaluation error: Unknown expression node"; return -1; } } } /* combined string/int comparison for compatibility with ssl_expr */ static int strcmplex(const char *str1, const char *str2) { int i, n1, n2; if (str1 == NULL) return -1; if (str2 == NULL) return +1; n1 = strlen(str1); n2 = strlen(str2); if (n1 > n2) return 1; if (n1 < n2) return -1; for (i = 0; i < n1; i++) { if (str1[i] > str2[i]) return 1; if (str1[i] < str2[i]) return -1; } return 0; } static int ssl_expr_eval_comp(ap_expr_eval_ctx *ctx, const ap_expr *node) { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; switch (node->node_op) { case op_EQ: case op_STR_EQ: return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0); case op_NE: case op_STR_NE: return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0); case op_LT: case op_STR_LT: return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0); case op_LE: case op_STR_LE: return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0); case op_GT: case op_STR_GT: return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0); case op_GE: case op_STR_GE: return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0); default: return ap_expr_eval_comp(ctx, node); } } AP_DECLARE(const char *) ap_expr_parse(apr_pool_t *pool, apr_pool_t *ptemp, ap_expr_info_t *info, const char *expr, ap_expr_lookup_fn *lookup_fn) { ap_expr_parse_ctx ctx; int rc; ctx.pool = pool; ctx.ptemp = ptemp; ctx.inputbuf = expr; ctx.inputlen = strlen(expr); ctx.inputptr = ctx.inputbuf; ctx.expr = NULL; ctx.error = NULL; /* generic bison error message (usually not very useful) */ ctx.error2 = NULL; /* additional error message */ ctx.flags = info->flags; ctx.scan_del = '\0'; ctx.scan_buf[0] = '\0'; ctx.scan_ptr = ctx.scan_buf; ctx.lookup_fn = lookup_fn ? lookup_fn : ap_run_expr_lookup; ap_expr_yylex_init(&ctx.scanner); ap_expr_yyset_extra(&ctx, ctx.scanner); rc = ap_expr_yyparse(&ctx); ap_expr_yylex_destroy(ctx.scanner); if (ctx.error) { if (ctx.error2) return apr_psprintf(pool, "%s: %s", ctx.error, ctx.error2); else return ctx.error; } else if (ctx.error2) { return ctx.error2; } if (rc) /* XXX can this happen? */ return "syntax error"; /* XXX Make this properly depend on the loglevel, which requires * XXX having a server_rec */ /* if (ctx.expr) expr_dump_tree(ctx.expr, NULL, APLOG_NOTICE, 2); */ info->root_node = ctx.expr; return NULL; } AP_DECLARE(ap_expr_info_t*) ap_expr_parse_cmd(const cmd_parms *cmd, const char *expr, const char **err, ap_expr_lookup_fn *lookup_fn) { ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t)); info->filename = cmd->directive->filename; info->line_number = cmd->directive->line_num; *err = ap_expr_parse(cmd->pool, cmd->temp_pool, info, expr, lookup_fn); if (*err) return NULL; return info; } ap_expr *ap_expr_make(ap_expr_node_op op, const void *a1, const void *a2, ap_expr_parse_ctx *ctx) { ap_expr *node = apr_palloc(ctx->pool, sizeof(ap_expr)); node->node_op = op; node->node_arg1 = a1; node->node_arg2 = a2; return node; } static ap_expr *ap_expr_info_make(int type, const char *name, ap_expr_parse_ctx *ctx) { ap_expr *info = apr_palloc(ctx->pool, sizeof(ap_expr)); ap_expr_lookup_parms parms; parms.type = type; parms.flags = 0; parms.pool = ctx->pool; parms.ptemp = ctx->ptemp; parms.name = name; parms.func = &info->node_arg1; parms.data = &info->node_arg2; parms.err = &ctx->error2; if (ctx->lookup_fn(&parms) != OK) return NULL; return info; } ap_expr *ap_expr_str_func_make(const char *name, const ap_expr *arg, ap_expr_parse_ctx *ctx) { ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx); if (!info) return NULL; info->node_op = op_StringFuncInfo; return ap_expr_make(op_StringFuncCall, info, arg, ctx); } ap_expr *ap_expr_list_func_make(const char *name, const ap_expr *arg, ap_expr_parse_ctx *ctx) { ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_LIST, name, ctx); if (!info) return NULL; info->node_op = op_ListFuncInfo; return ap_expr_make(op_ListFuncCall, info, arg, ctx); } ap_expr *ap_expr_unary_op_make(const char *name, const ap_expr *arg, ap_expr_parse_ctx *ctx) { ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx); if (!info) return NULL; info->node_op = op_UnaryOpInfo; return ap_expr_make(op_UnaryOpCall, info, arg, ctx); } ap_expr *ap_expr_binary_op_make(const char *name, const ap_expr *arg1, const ap_expr *arg2, ap_expr_parse_ctx *ctx) { ap_expr *args; ap_expr *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx); if (!info) return NULL; info->node_op = op_BinaryOpInfo; args = ap_expr_make(op_BinaryOpArgs, arg1, arg2, ctx); return ap_expr_make(op_BinaryOpCall, info, args, ctx); } ap_expr *ap_expr_var_make(const char *name, ap_expr_parse_ctx *ctx) { ap_expr *node = ap_expr_info_make(AP_EXPR_FUNC_VAR, name, ctx); if (!node) return NULL; node->node_op = op_Var; return node; } #define MARK APLOG_MARK,loglevel,0,s #define DUMP_E_E(op, e1, e2) \ do { ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, e1, e2); \ if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \ if (e2) expr_dump_tree(e2, s, loglevel, indent + 2); \ } while (0) #define DUMP_S_E(op, s1, e1) \ do { ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, e1); \ if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \ } while (0) #define DUMP_S_P(op, s1, p1) \ ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, p1); #define DUMP_P_P(op, p1, p2) \ ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, p1, p2); #define DUMP_S_S(op, s1, s2) \ ap_log_error(MARK,"%*s%s: '%s' '%s'", indent, " ", op, (char *)s1, (char *)s2) #define DUMP_P(op, p1) \ ap_log_error(MARK,"%*s%s: %pp", indent, " ", op, p1); #define DUMP_S(op, s1) \ ap_log_error(MARK,"%*s%s: '%s'", indent, " ", op, (char *)s1) #define CASE_OP(op) case op: name = #op ; break; static void expr_dump_tree(const ap_expr *e, const server_rec *s, int loglevel, int indent) { switch (e->node_op) { /* no arg */ case op_NOP: case op_True: case op_False: { char *name; switch (e->node_op) { CASE_OP(op_NOP); CASE_OP(op_True); CASE_OP(op_False); default: ap_assert(0); } ap_log_error(MARK, "%*s%s", indent, " ", name); } break; /* arg1: string, arg2: expr */ case op_UnaryOpCall: case op_BinaryOpCall: case op_BinaryOpArgs: { char *name; switch (e->node_op) { CASE_OP(op_BinaryOpCall); CASE_OP(op_UnaryOpCall); CASE_OP(op_BinaryOpArgs); default: ap_assert(0); } DUMP_S_E(name, e->node_arg1, e->node_arg2); } break; /* arg1: expr, arg2: expr */ case op_Comp: case op_Not: case op_Or: case op_And: case op_EQ: case op_NE: case op_LT: case op_LE: case op_GT: case op_GE: case op_STR_EQ: case op_STR_NE: case op_STR_LT: case op_STR_LE: case op_STR_GT: case op_STR_GE: case op_IN: case op_REG: case op_NRE: case op_Concat: case op_StringFuncCall: case op_ListFuncCall: case op_ListElement: { char *name; switch (e->node_op) { CASE_OP(op_Comp); CASE_OP(op_Not); CASE_OP(op_Or); CASE_OP(op_And); CASE_OP(op_EQ); CASE_OP(op_NE); CASE_OP(op_LT); CASE_OP(op_LE); CASE_OP(op_GT); CASE_OP(op_GE); CASE_OP(op_STR_EQ); CASE_OP(op_STR_NE); CASE_OP(op_STR_LT); CASE_OP(op_STR_LE); CASE_OP(op_STR_GT); CASE_OP(op_STR_GE); CASE_OP(op_IN); CASE_OP(op_REG); CASE_OP(op_NRE); CASE_OP(op_Concat); CASE_OP(op_StringFuncCall); CASE_OP(op_ListFuncCall); CASE_OP(op_ListElement); default: ap_assert(0); } DUMP_E_E(name, e->node_arg1, e->node_arg2); } break; /* arg1: string */ case op_Digit: case op_String: { char *name; switch (e->node_op) { CASE_OP(op_Digit); CASE_OP(op_String); default: ap_assert(0); } DUMP_S(name, e->node_arg1); } break; /* arg1: pointer, arg2: pointer */ case op_Var: case op_StringFuncInfo: case op_UnaryOpInfo: case op_BinaryOpInfo: case op_ListFuncInfo: { char *name; switch (e->node_op) { CASE_OP(op_Var); CASE_OP(op_StringFuncInfo); CASE_OP(op_UnaryOpInfo); CASE_OP(op_BinaryOpInfo); CASE_OP(op_ListFuncInfo); default: ap_assert(0); } DUMP_P_P(name, e->node_arg1, e->node_arg2); } break; /* arg1: pointer */ case op_Regex: DUMP_P("op_Regex", e->node_arg1); break; default: ap_log_error(MARK, "%*sERROR: INVALID OP %d", indent, " ", e->node_op); break; } } static int ap_expr_eval_unary_op(ap_expr_eval_ctx *ctx, const ap_expr *info, const ap_expr *arg) { const ap_expr_op_unary_t *op_func = info->node_arg1; const void *data = info->node_arg2; AP_DEBUG_ASSERT(info->node_op == op_UnaryOpInfo); AP_DEBUG_ASSERT(op_func != NULL); AP_DEBUG_ASSERT(data != NULL); return (*op_func)(ctx, data, ap_expr_eval_word(ctx, arg)); } static int ap_expr_eval_binary_op(ap_expr_eval_ctx *ctx, const ap_expr *info, const ap_expr *args) { const ap_expr_op_binary_t *op_func = info->node_arg1; const void *data = info->node_arg2; const ap_expr *a1 = args->node_arg1; const ap_expr *a2 = args->node_arg2; AP_DEBUG_ASSERT(info->node_op == op_BinaryOpInfo); AP_DEBUG_ASSERT(args->node_op == op_BinaryOpArgs); AP_DEBUG_ASSERT(op_func != NULL); AP_DEBUG_ASSERT(data != NULL); return (*op_func)(ctx, data, ap_expr_eval_word(ctx, a1), ap_expr_eval_word(ctx, a2)); } static int ap_expr_eval(ap_expr_eval_ctx *ctx, const ap_expr *node) { switch (node->node_op) { case op_True: { return 1; } case op_False: { return 0; } case op_Not: { const ap_expr *e = node->node_arg1; return (!ap_expr_eval(ctx, e)); } case op_Or: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (ap_expr_eval(ctx, e1) || ap_expr_eval(ctx, e2)); } case op_And: { const ap_expr *e1 = node->node_arg1; const ap_expr *e2 = node->node_arg2; return (ap_expr_eval(ctx, e1) && ap_expr_eval(ctx, e2)); } case op_UnaryOpCall: { const ap_expr *info = node->node_arg1; const ap_expr *args = node->node_arg2; return ap_expr_eval_unary_op(ctx, info, args); } case op_BinaryOpCall: { const ap_expr *info = node->node_arg1; const ap_expr *args = node->node_arg2; return ap_expr_eval_binary_op(ctx, info, args); } case op_Comp: { const ap_expr *e = node->node_arg1; if (ctx->info->flags & AP_EXPR_FLAGS_SSL_EXPR_COMPAT) return ssl_expr_eval_comp(ctx, e); else return ap_expr_eval_comp(ctx, e); } default: { *ctx->err = "Internal evaluation error: Unknown expression node"; return FALSE; } } } AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *info, const char **err) { ap_expr_eval_ctx ctx; int rc; ctx.r = r; ctx.c = r->connection; ctx.s = r->server; ctx.p = r->pool; ctx.err = err; ctx.info = info; *err = NULL; rc = ap_expr_eval(&ctx, info->root_node); if (*err != NULL) return (-1); else return (rc ? 1 : 0); } static const char *req_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { if (ctx->r) return apr_table_get(ctx->r->headers_in, arg); else return ""; } static const char *resp_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { if (ctx->r) return apr_table_get(ctx->r->headers_out, arg); else return ""; } static const char *env_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { if (ctx->r) return apr_table_get(ctx->r->subprocess_env, arg); else return ""; } static const char *osenv_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { return getenv(arg); } static const char *tolower_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { char *result = apr_pstrdup(ctx->p, arg); ap_str_tolower(result); return result; } static const char *toupper_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { char *p; char *result = apr_pstrdup(ctx->p, arg); for (p = result; *p; ++p) { *p = apr_toupper(*p); } return result; } static const char *escape_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { return ap_escape_uri(ctx->p, arg); } static const char *unescape_func(ap_expr_eval_ctx *ctx, const char *name, const char *arg) { char *result = apr_pstrdup(ctx->p, arg); if (ap_unescape_url(result)) return ""; else return result; } static const char *request_var_names[] = { "REQUEST_METHOD", /* 0 */ "REQUEST_SCHEME", /* 1 */ "REQUEST_URI", /* 2 */ "REQUEST_FILENAME", /* 3 */ "REMOTE_HOST", /* 4 */ "REMOTE_IDENT", /* 5 */ "REMOTE_USER", /* 6 */ "SERVER_ADMIN", /* 7 */ "SERVER_NAME", /* 8 */ "SERVER_PORT", /* 9 */ "SERVER_PROTOCOL", /* 10 */ "SCRIPT_FILENAME", /* 11 */ "PATH_INFO", /* 12 */ "QUERY_STRING", /* 13 */ "IS_SUBREQ", /* 14 */ "DOCUMENT_ROOT", /* 15 */ "AUTH_TYPE", /* 16 */ "THE_REQUEST", /* 17 */ "REMOTE_ADDR", /* 18 */ "CONTENT_TYPE", /* 19 */ NULL }; static const char *request_var_fn(ap_expr_eval_ctx *ctx, const void *data) { int index = ((const char **)data - request_var_names); request_rec *r = ctx->r; if (!r) return ""; switch (index) { case 0: return r->method; case 1: return ap_http_scheme(r); case 2: return r->uri; case 3: return r->filename; case 4: return ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL); case 5: return ap_get_remote_logname(r); case 6: return r->user; case 7: return r->server->server_admin; case 8: return ap_get_server_name(r); case 9: return apr_psprintf(ctx->p, "%u", ap_get_server_port(r)); case 10: return r->protocol; case 11: return r->filename; case 12: return r->path_info; case 13: return r->args; case 14: return (r->main != NULL ? "true" : "false"); case 15: return ap_document_root(r); case 16: return r->ap_auth_type; case 17: return r->the_request; case 18: return ctx->c->remote_ip; case 19: return r->content_type; default: ap_assert(0); return NULL; } } static const char *req_header_var_names[] = { "HTTP_USER_AGENT", /* 0 */ "HTTP_PROXY_CONNECTION", /* 1 */ "HTTP_REFERER", "HTTP_COOKIE", "HTTP_FORWARDED", "HTTP_HOST", "HTTP_ACCEPT", NULL }; static const char *req_header_var_fn(ap_expr_eval_ctx *ctx, const void *data) { const char **name = (const char **)data; int index = (name - req_header_var_names); if (!ctx->r) return ""; switch (index) { case 0: return apr_table_get(ctx->r->headers_in, "User-Agent"); case 1: return apr_table_get(ctx->r->headers_in, "Proxy-Connection"); default: /* apr_table_get is case insensitive, just skip leading "HTTP_" */ return apr_table_get(ctx->r->headers_in, *name + 5); } } static const char *misc_var_names[] = { "TIME_YEAR", /* 0 */ "TIME_MON", /* 1 */ "TIME_DAY", /* 2 */ "TIME_HOUR", /* 3 */ "TIME_MIN", /* 4 */ "TIME_SEC", /* 5 */ "TIME_WDAY", /* 6 */ "TIME", /* 7 */ "SERVER_SOFTWARE", /* 8 */ "API_VERSION", /* 9 */ NULL }; static const char *misc_var_fn(ap_expr_eval_ctx *ctx, const void *data) { apr_time_exp_t tm; int index = ((const char **)data - misc_var_names); apr_time_exp_lt(&tm, apr_time_now()); switch (index) { case 0: return apr_psprintf(ctx->p, "%02d%02d", (tm.tm_year / 100) + 19, tm.tm_year % 100); case 1: return apr_psprintf(ctx->p, "%02d", tm.tm_mon+1); case 2: return apr_psprintf(ctx->p, "%02d", tm.tm_mday); case 3: return apr_psprintf(ctx->p, "%02d", tm.tm_hour); case 4: return apr_psprintf(ctx->p, "%02d", tm.tm_min); case 5: return apr_psprintf(ctx->p, "%02d", tm.tm_sec); case 6: return apr_psprintf(ctx->p, "%d", tm.tm_wday); case 7: return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d", (tm.tm_year / 100) + 19, (tm.tm_year % 100), tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); case 8: return ap_get_server_banner(); case 9: return apr_itoa(ctx->p, MODULE_MAGIC_NUMBER); default: ap_assert(0); } return NULL; } struct expr_provider_single { const void *func; const char *name; }; struct expr_provider_multi { const void *func; const char **names; }; static const struct expr_provider_multi var_providers[] = { { misc_var_fn, misc_var_names }, { req_header_var_fn, req_header_var_names }, { request_var_fn, request_var_names }, { NULL, NULL } }; static const struct expr_provider_single string_func_providers[] = { { osenv_func, "osenv" }, { env_func, "env" }, { resp_func, "resp" }, { req_func, "req" }, /* 'http' as alias for 'req' for compatibility with ssl_expr */ { req_func, "http" }, { tolower_func, "tolower" }, { toupper_func, "toupper" }, { escape_func, "escape" }, { unescape_func, "unescape" }, { NULL, NULL} }; /* XXX: base64 encode/decode ? */ static int core_expr_lookup(ap_expr_lookup_parms *parms) { switch (parms->type) { case AP_EXPR_FUNC_VAR: { const struct expr_provider_multi *prov = var_providers; while (prov->func) { const char **name = prov->names; while (*name) { if (strcasecmp(*name, parms->name) == 0) { *parms->func = prov->func; *parms->data = name; return OK; } name++; } prov++; } break; } case AP_EXPR_FUNC_STRING: { const struct expr_provider_single *prov = string_func_providers; while (prov->func) { if (strcasecmp(prov->name, parms->name) == 0) { *parms->func = prov->func; *parms->data = prov->name; return OK; } prov++; } break; } default: break; } return DECLINED; } static int expr_lookup_not_found(ap_expr_lookup_parms *parms) { const char *type; switch (parms->type) { case AP_EXPR_FUNC_VAR: type = "Variable"; break; case AP_EXPR_FUNC_STRING: type = "Function"; break; case AP_EXPR_FUNC_LIST: type = "List-returning function"; break; case AP_EXPR_FUNC_OP_UNARY: type = "Unary operator"; break; case AP_EXPR_FUNC_OP_BINARY: type = "Binary operator"; break; default: *parms->err = "Inavalid expression type in expr_lookup"; return !OK; } *parms->err = apr_psprintf(parms->ptemp, "%s '%s' does not exist", type, parms->name); return !OK; } void ap_expr_init(apr_pool_t *p) { ap_hook_expr_lookup(core_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_expr_lookup(expr_lookup_not_found, NULL, NULL, APR_HOOK_REALLY_LAST); }