#define AP_EXPR_H
#include "httpd.h"
+#include "ap_regex.h"
#ifdef __cplusplus
extern "C" {
#ifdef DEBUG_INCLUDE
int dump_done;
#endif
-} parse_node_t;
+} ap_parse_node_t;
typedef struct {
const char *source;
ap_regmatch_t match[AP_MAX_REG_MATCH];
} backref_t;
-typedef char *(*string_func_t)(request_rec*, const char*);
-typedef int (*opt_func_t)(request_rec*, parse_node_t*, string_func_t);
+typedef const char *(*string_func_t)(request_rec*, const char*);
+typedef int (*opt_func_t)(request_rec*, ap_parse_node_t*, string_func_t);
/**
* Parse an expression into a parse tree
* @param was_error On return, set to zero if parse successful, nonzero on error
* @return The parse tree
*/
-AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t *pool, const char *expr,
- int *was_error);
+AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t *pool, const char *expr,
+ int *was_error);
/**
* Evaluate a parse tree
* @param r The current request
* @param was_error On return, set to zero if parse successful, nonzero on error
* @param reptr Regular expression memory for backreferencing if a regexp was parsed
* @param string_func String parser function - perform variable substitutions
+ * Use ap_expr_string where applicable
* @param eval_func Option evaluation function (e.g. -A filename)
* @return the value the expression parsed to
*/
-AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
+AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
int *was_error, backref_t **reptr,
string_func_t string_func, opt_func_t eval_func);
/**
* @param was_error On return, set to zero if parse successful, nonzero on error
* @param reptr Regular expression memory for backreferencing if a regexp was parsed
* @param string_func String parser function - perform variable substitutions
+ * Use ap_expr_string where applicable
* @param eval_func Option evaluation function (e.g. -A filename)
* @return the value the expression parsed to
*/
string_func_t string_func,
opt_func_t eval_func);
+/**
+ * Internal initialisation of ap_expr (for httpd)
+ * @param pool Pool
+ * @return APR_SUCCESS or error
+ */
+AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool);
+
+/**
+ * Default string evaluation function for passing to ap_expr_eval and
+ * ap_expr_evalstring. Use this (and update as necessary) to offer
+ * a consistent expression syntax across different modules.
+ * Supports the following:
+ * $req{foo} - request header "foo"
+ * $resp{foo} - response header "foo"
+ * $env{foo} - environment variable "foo"
+ * $handler - r->handler
+ * $content-type - r->content_type
+ * Other strings are returned unmodified.
+ * @param r The current request
+ * @param str The string to evaluate
+ * @return The evaluated string
+ */
+AP_DECLARE(const char*) ap_expr_string(request_rec *r, const char *str);
+
+/**
+ * Clone a parse tree. This is required if you create a parse tree
+ * using ap_expr_parse, and wish to re-use it many times in ap_expr_eval.
+ * It is not required if you need to use it just once.
+ * @param pool Pool
+ * @param node The parse tree to clone
+ * @param parent Parent node (for internal use when recursing - pass in NULL)
+ * @return The cloned tree
+ */
+AP_DECLARE(ap_parse_node_t*) ap_expr_clone_tree(apr_pool_t *pool,
+ ap_parse_node_t *node,
+ ap_parse_node_t *parent);
+
#ifdef __cplusplus
}
#endif
#include "http_log.h"
#include "ap_expr.h"
+#include <assert.h>
#if 1
/*
* +-------------------------------------------------------+
} while(0)
#define CREATE_NODE(pool,name) do { \
- (name) = apr_palloc(pool, sizeof(*(name))); \
- (name)->parent = (name)->left = (name)->right = NULL; \
- (name)->done = 0; \
- (name)->dump_done = 0; \
+ (name) = apr_pcalloc(pool, sizeof(*(name)));
} while(0)
static void debug_printf(request_rec *r, const char *fmt, ...)
}
#define DUMP__CHILD(ctx, is, node, child) if (1) { \
- parse_node_t *d__c = node->child; \
+ ap_parse_node_t *d__c = node->child; \
if (d__c) { \
if (!d__c->dump_done) { \
if (d__c->parent != node) { \
} \
}
-static void debug_dump_tree(include_ctx_t *ctx, parse_node_t *root)
+static void debug_dump_tree(include_ctx_t *ctx, ap_parse_node_t *root)
{
- parse_node_t *current;
+ ap_parse_node_t *current;
char *is;
if (!root) {
#define TYPE_TOKEN(token, ttype) (token)->type = ttype
#define CREATE_NODE(pool,name) do { \
- (name) = apr_palloc(pool, sizeof(*(name))); \
- (name)->parent = (name)->left = (name)->right = NULL; \
- (name)->done = 0; \
+ (name) = apr_pcalloc(pool, sizeof(*(name))); \
} while(0)
#define DEBUG_INIT(ctx, f, bb)
const char *rexp, backref_t **reptr)
{
ap_regex_t *compiled;
- backref_t *re = *reptr;
+ backref_t *re = reptr ? *reptr : NULL;
int rc;
compiled = ap_pregcomp(r->pool, rexp, AP_REG_EXTENDED);
}
if (!re) {
- re = *reptr = apr_palloc(r->pool, sizeof(*re));
+ re = apr_palloc(r->pool, sizeof(*re));
+ if (reptr) {
+ *reptr = re;
+ }
}
re->source = apr_pstrdup(r->pool, string);
}
/* This is what we export. We can split it in two. */
-AP_DECLARE(parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
- int *was_error)
+AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
+ int *was_error)
{
- parse_node_t *new, *root = NULL, *current = NULL;
+ ap_parse_node_t *new, *root = NULL, *current = NULL;
const char *error = "Invalid expression \"%s\" in file %s";
const char *parse = expr;
int was_unmatched = 0;
return root;
}
+AP_DECLARE(ap_parse_node_t*) ap_expr_clone_tree(apr_pool_t *pool,
+ ap_parse_node_t *pnode,
+ ap_parse_node_t *parent)
+{
+ ap_parse_node_t *ret;
+ ret = apr_pmemdup(pool, pnode, sizeof(ap_parse_node_t));
+ if (pnode->left) {
+ ret->left = ap_expr_clone_tree(pool, pnode->left, ret);
+ }
+ if (pnode->right) {
+ ret->right = ap_expr_clone_tree(pool, pnode->right, ret);
+ }
+ ret->parent = parent;
+ return ret;
+}
+
#define PARSE_STRING(r,s) (string_func ? string_func((r),(s)) : (s))
-AP_DECLARE(int) ap_expr_eval(request_rec *r, parse_node_t *root,
+AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
int *was_error, backref_t **reptr,
string_func_t string_func, opt_func_t eval_func)
{
- parse_node_t *current = root;
+ ap_parse_node_t *current = root;
const char *error = NULL;
unsigned int regex = 0;
+ const char *val;
+ const char *lval;
+ const char *rval;
/* Evaluate Parse Tree */
while (current) {
switch (current->token.type) {
case TOKEN_STRING:
- current->token.value = PARSE_STRING(r, current->token.value);
- current->value = !!*current->token.value;
+ val = PARSE_STRING(r, current->token.value);
+ current->value = !!*val;
break;
case TOKEN_AND:
if (!current->left->done) {
switch (current->left->token.type) {
case TOKEN_STRING:
- current->left->token.value =
- PARSE_STRING(r, current->left->token.value);
- current->left->value = !!*current->left->token.value;
+ lval = PARSE_STRING(r, current->left->token.value);
+ current->left->value = !!*lval;
DEBUG_DUMP_EVAL(ctx, current->left);
current->left->done = 1;
break;
if (!current->right->done) {
switch (current->right->token.type) {
case TOKEN_STRING:
- current->right->token.value =
- PARSE_STRING(r,current->right->token.value);
- current->right->value = !!*current->right->token.value;
+ rval = PARSE_STRING(r,current->right->token.value);
+ current->right->value = !!*rval;
DEBUG_DUMP_EVAL(r, current->right);
current->right->done = 1;
break;
*was_error = 1;
return 0;
}
- current->left->token.value =
- PARSE_STRING(r, current->left->token.value);
- current->right->token.value =
- PARSE_STRING(r, current->right->token.value);
+ lval = PARSE_STRING(r, current->left->token.value);
+ rval = PARSE_STRING(r, current->right->token.value);
if (current->right->token.type == TOKEN_RE) {
- current->value = re_check(r, current->left->token.value,
- current->right->token.value, reptr);
+ current->value = re_check(r, lval, rval, reptr);
--regex;
}
else {
- current->value = !strcmp(current->left->token.value,
- current->right->token.value);
+ current->value = !strcmp(lval, rval);
}
if (current->token.type == TOKEN_NE) {
return 0;
}
- current->left->token.value =
- PARSE_STRING(r, current->left->token.value);
- current->right->token.value =
- PARSE_STRING(r, current->right->token.value);
+ lval = PARSE_STRING(r, current->left->token.value);
+ rval = PARSE_STRING(r, current->right->token.value);
- current->value = strcmp(current->left->token.value,
- current->right->token.value);
+ current->value = strcmp(lval, rval);
switch (current->token.type) {
case TOKEN_GE: current->value = current->value >= 0; break;
string_func_t string_func,
opt_func_t eval_func)
{
- parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
+ ap_parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
if (*was_error || !root) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Error parsing expression in %s", r->filename);
}
return ap_expr_eval(r, root, was_error, reptr, string_func, eval_func);
}
+
+
+static ap_regex_t *isvar = NULL;
+AP_DECLARE(const char*) ap_expr_string(request_rec *r, const char *str)
+{
+ /* a default string evaluator: support headers and env */
+ ap_regmatch_t match[3];
+ assert(isvar != NULL);
+ if (ap_regexec(isvar, str, 3, match, 0) == 0) {
+ apr_table_t *table = NULL;
+ int len = match[1].rm_eo-match[1].rm_so;
+ const char *name = str+match[1].rm_so;
+ if (!strncasecmp("req", name, len)) {
+ table = r->headers_in;
+ }
+ else if (!strncasecmp("resp", name, len)) {
+ table = r->headers_out;
+ }
+ else if (!strncasecmp("env", name, len)) {
+ table = r->subprocess_env;
+ }
+ if (table != NULL) {
+ char *key = apr_pstrndup(r->pool, str+match[2].rm_so,
+ match[2].rm_eo-match[2].rm_so);
+ return apr_table_get(table, key);
+ }
+ }
+ else if (str[0] == '$') {
+ if (!strcasecmp(str, "$handler")) {
+ return r->handler;
+ }
+ else if (!strcasecmp(str, "$content-type")) {
+ return r->content_type;
+ }
+ }
+ /* TODO: provide a hook so modules can interpret other patterns */
+ /* OhBugger, where's the regexp for backreferences ? */
+ return str; /* default - literal string as-is */
+}
+static apr_status_t ap_expr_term(void *expr)
+{
+ if (isvar) {
+ ap_regfree(isvar);
+ isvar = NULL;
+ }
+ return APR_SUCCESS;
+}
+AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool)
+{
+ static ap_regex_t var;
+ if (!isvar) {
+ isvar = &var;
+ if (ap_regcomp(isvar, "\\$([A-Za-z0-9]+)\\{([^\\}]+)\\}", 0)) {
+ isvar = NULL;
+ }
+ else {
+ apr_pool_cleanup_register(pool, isvar, ap_expr_term,
+ apr_pool_cleanup_null);
+ }
+ }
+ return isvar ? APR_SUCCESS : APR_EGENERAL;
+}