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 #include "apr_strings.h"
21 #define APR_WANT_STRFUNC
22 #define APR_WANT_MEMFUNC
31 * +-------------------------------------------------------+
33 * | Debugging Utilities
35 * +-------------------------------------------------------+
40 #define TYPE_TOKEN(token, ttype) do { \
41 (token)->type = ttype; \
42 (token)->s = #ttype; \
45 #define CREATE_NODE(pool,name) do { \
46 (name) = apr_pcalloc(pool, sizeof(*(name)));
49 static void debug_printf(request_rec *r, const char *fmt, ...)
55 debug__str = apr_pvsprintf(r->pool, fmt, ap);
58 APR_BRIGADE_INSERT_TAIL(ctx->intern->debug.bb, apr_bucket_pool_create(
59 debug__str, strlen(debug__str), ctx->pool,
60 ctx->intern->debug.f->c->bucket_alloc));
64 #define DUMP__CHILD(ctx, is, node, child) if (1) { \
65 ap_parse_node_t *d__c = node->child; \
67 if (!d__c->dump_done) { \
68 if (d__c->parent != node) { \
69 debug_printf(ctx, "!!! Parse tree is not consistent !!!\n"); \
70 if (!d__c->parent) { \
71 debug_printf(ctx, "Parent of " #child " child node is " \
75 debug_printf(ctx, "Parent of " #child " child node " \
76 "points to another node (of type %s)!\n", \
77 d__c->parent->token.s); \
86 debug_printf(ctx, "%s(missing)\n", is); \
90 static void debug_dump_tree(include_ctx_t *ctx, ap_parse_node_t *root)
92 ap_parse_node_t *current;
96 debug_printf(ctx, " -- Parse Tree empty --\n\n");
100 debug_printf(ctx, " ----- Parse Tree -----\n");
105 switch (current->token.type) {
108 debug_printf(ctx, "%s%s (%s)\n", is, current->token.s,
109 current->token.value);
110 current->dump_done = 1;
111 current = current->parent;
118 if (!current->dump_done) {
119 debug_printf(ctx, "%s%s\n", is, current->token.s);
120 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
121 current->dump_done = 1;
124 DUMP__CHILD(ctx, is, current, right)
126 if (!current->right || current->right->dump_done) {
127 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
128 if (current->right) current->right->dump_done = 0;
129 current = current->parent;
134 if (!current->dump_done) {
135 debug_printf(ctx, "%s%s\n", is, current->token.s);
136 is = apr_pstrcat(ctx->dpool, is, " ", NULL);
137 current->dump_done = 1;
140 DUMP__CHILD(ctx, is, current, left)
141 DUMP__CHILD(ctx, is, current, right)
143 if ((!current->left || current->left->dump_done) &&
144 (!current->right || current->right->dump_done)) {
146 is = apr_pstrmemdup(ctx->dpool, is, strlen(is) - 4);
147 if (current->left) current->left->dump_done = 0;
148 if (current->right) current->right->dump_done = 0;
149 current = current->parent;
155 /* it is possible to call this function within the parser loop, to see
156 * how the tree is built. That way, we must cleanup after us to dump
157 * always the whole tree
160 if (root->left) root->left->dump_done = 0;
161 if (root->right) root->right->dump_done = 0;
163 debug_printf(ctx, " --- End Parse Tree ---\n\n");
168 #define DEBUG_INIT(ctx, filter, brigade) do { \
169 (ctx)->intern->debug.f = filter; \
170 (ctx)->intern->debug.bb = brigade; \
173 #define DEBUG_PRINTF(arg) debug_printf arg
175 #define DEBUG_DUMP_TOKEN(ctx, token) do { \
176 token_t *d__t = (token); \
178 if (d__t->type == TOKEN_STRING || d__t->type == TOKEN_RE) { \
179 DEBUG_PRINTF(((ctx), " Found: %s (%s)\n", d__t->s, d__t->value)); \
182 DEBUG_PRINTF((ctx, " Found: %s\n", d__t->s)); \
186 #define DEBUG_DUMP_EVAL(r, node) do { \
188 switch ((node)->token.type) { \
190 debug_printf((r), " Evaluate: %s (%s) -> %c\n", (node)->token.s,\
191 (node)->token.value, ((node)->value) ? '1':'0'); \
195 debug_printf((r), " Evaluate: %s (Left: %s; Right: %s) -> %c\n",\
197 (((node)->left->done) ? ((node)->left->value ?"1":"0") \
198 : "short circuited"), \
199 (((node)->right->done) ? ((node)->right->value?"1":"0") \
200 : "short circuited"), \
201 (node)->value ? '1' : '0'); \
209 if ((node)->right->token.type == TOKEN_RE) c = '/'; \
210 debug_printf((r), " Compare: %s (\"%s\" with %c%s%c) -> %c\n", \
212 (node)->left->token.value, \
213 c, (node)->right->token.value, c, \
214 (node)->value ? '1' : '0'); \
217 debug_printf((r), " Evaluate: %s -> %c\n", (node)->token.s, \
218 (node)->value ? '1' : '0'); \
223 #define DEBUG_DUMP_UNMATCHED(r, unmatched) do { \
225 DEBUG_PRINTF(((r), " Unmatched %c\n", (char)(unmatched))); \
229 #define DEBUG_DUMP_COND(ctx, text) \
230 DEBUG_PRINTF(((ctx), "**** %s cond status=\"%c\"\n", (text), \
231 ((ctx)->flags & SSI_FLAG_COND_TRUE) ? '1' : '0'))
233 #define DEBUG_DUMP_TREE(ctx, root) debug_dump_tree(ctx, root)
235 #else /* DEBUG_INCLUDE */
237 #define TYPE_TOKEN(token, ttype) (token)->type = ttype
239 #define CREATE_NODE(pool,name) do { \
240 (name) = apr_pcalloc(pool, sizeof(*(name))); \
243 #define DEBUG_INIT(ctx, f, bb)
244 #define DEBUG_PRINTF(arg)
245 #define DEBUG_DUMP_TOKEN(ctx, token)
246 #define DEBUG_DUMP_EVAL(ctx, node)
247 #define DEBUG_DUMP_UNMATCHED(ctx, unmatched)
248 #define DEBUG_DUMP_COND(ctx, text)
249 #define DEBUG_DUMP_TREE(ctx, root)
251 #endif /* !DEBUG_INCLUDE */
257 * +-------------------------------------------------------+
259 * | Conditional Expression Parser
261 * +-------------------------------------------------------+
263 static APR_INLINE int re_check(request_rec *r, const char *string,
264 const char *rexp, backref_t **reptr)
266 ap_regex_t *compiled;
267 backref_t *re = reptr ? *reptr : NULL;
270 compiled = ap_pregcomp(r->pool, rexp, AP_REG_EXTENDED);
272 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to "
273 "compile pattern \"%s\"", rexp);
278 re = apr_palloc(r->pool, sizeof(*re));
284 re->source = apr_pstrdup(r->pool, string);
285 re->rexp = apr_pstrdup(r->pool, rexp);
286 re->nsub = compiled->re_nsub;
287 rc = !ap_regexec(compiled, string, AP_MAX_REG_MATCH, re->match, 0);
289 ap_pregfree(r->pool, compiled);
293 static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token,
306 /* Skip leading white space */
307 while (apr_isspace(**parse)) {
316 TYPE_TOKEN(token, TOKEN_STRING); /* the default type */
320 switch (*(*parse)++) {
322 TYPE_TOKEN(token, TOKEN_LBRACE);
325 TYPE_TOKEN(token, TOKEN_RBRACE);
328 if (**parse == '=') ++*parse;
329 TYPE_TOKEN(token, TOKEN_EQ);
332 if (**parse == '=') {
333 TYPE_TOKEN(token, TOKEN_NE);
337 TYPE_TOKEN(token, TOKEN_NOT);
343 /* if last token was ACCESS, this token is STRING */
344 if (previous != NULL && TOKEN_ACCESS == previous->type) {
347 TYPE_TOKEN(token, TOKEN_RE);
351 if (**parse == '|') {
352 TYPE_TOKEN(token, TOKEN_OR);
358 if (**parse == '&') {
359 TYPE_TOKEN(token, TOKEN_AND);
365 if (**parse == '=') {
366 TYPE_TOKEN(token, TOKEN_GE);
370 TYPE_TOKEN(token, TOKEN_GT);
373 if (**parse == '=') {
374 TYPE_TOKEN(token, TOKEN_LE);
378 TYPE_TOKEN(token, TOKEN_LT);
381 if (apr_isalnum(**parse) && apr_isspace((*parse)[1])) {
382 TYPE_TOKEN(token, TOKEN_ACCESS);
383 token->value = *parse;
390 /* It's a string or regex token
391 * Now search for the next token, which finishes this string
394 p = *parse = token->value = unmatched ? *parse : p;
396 for (; **parse; p = ++*parse) {
397 if (**parse == '\\') {
407 if (**parse == unmatched) {
413 else if (apr_isspace(**parse)) {
431 if ((*parse)[1] == **parse) {
445 token->value = apr_pstrdup(pool, "");
448 apr_size_t len = p - token->value - shift;
449 char *c = apr_palloc(pool, len + 1);
455 const char *e = ap_strchr_c(p, '\\');
473 /* This is what we export. We can split it in two. */
474 AP_DECLARE(ap_parse_node_t*) ap_expr_parse(apr_pool_t* pool, const char *expr,
477 ap_parse_node_t *new, *root = NULL, *current = NULL;
478 const char *error = "Invalid expression \"%s\" in file %s";
479 const char *parse = expr;
480 int was_unmatched = 0;
489 /* Create Parse Tree */
491 /* uncomment this to see how the tree a built:
493 * DEBUG_DUMP_TREE(ctx, root);
495 CREATE_NODE(pool, new);
497 was_unmatched = get_ptoken(pool, &parse, &new->token,
498 (current != NULL ? ¤t->token : NULL));
503 DEBUG_DUMP_UNMATCHED(ctx, was_unmatched);
504 DEBUG_DUMP_TOKEN(ctx, &new->token);
507 switch (new->token.type) {
512 root = current = new;
521 switch (new->token.type) {
523 switch (current->token.type) {
525 current->token.value =
526 apr_pstrcat(pool, current->token.value,
527 *current->token.value ? " " : "",
528 new->token.value, NULL);
537 new->parent = current;
538 current = current->right = new;
544 switch (current->token.type) {
547 new->parent = current;
548 current = current->right = new;
559 switch (current->token.type) {
563 current = current->parent;
566 switch (current->token.type) {
573 current = current->parent;
582 current = root = new;
586 new->left = current->right;
587 new->left->parent = new;
588 new->parent = current;
589 current = current->right = new;
603 if (current->token.type == TOKEN_STRING) {
604 current = current->parent;
609 current = root = new;
613 switch (current->token.type) {
617 new->left = current->right;
618 new->left->parent = new;
619 new->parent = current;
620 current = current->right = new;
630 while (current && current->token.type != TOKEN_LBRACE) {
631 current = current->parent;
635 TYPE_TOKEN(¤t->token, TOKEN_GROUP);
639 error = "Unmatched ')' in \"%s\" in file %s";
645 switch (current->token.type) {
653 current->right = new;
654 new->parent = current;
668 DEBUG_DUMP_TREE(ctx, root);
672 static ap_parse_node_t *ap_expr_clone_tree(apr_pool_t *pool,
673 ap_parse_node_t *pnode,
674 ap_parse_node_t *parent)
676 ap_parse_node_t *ret;
677 ret = apr_pmemdup(pool, pnode, sizeof(ap_parse_node_t));
679 ret->left = ap_expr_clone_tree(pool, pnode->left, ret);
682 ret->right = ap_expr_clone_tree(pool, pnode->right, ret);
684 ret->parent = parent;
688 #define PARSE_STRING(r,s) (string_func ? string_func((r),(s)) : (s))
689 static int expr_eval(request_rec *r, ap_parse_node_t *root,
690 int *was_error, backref_t **reptr,
691 string_func_t string_func, opt_func_t eval_func)
693 ap_parse_node_t *current = root;
694 const char *error = NULL;
695 unsigned int regex = 0;
700 /* Evaluate Parse Tree */
702 switch (current->token.type) {
704 val = PARSE_STRING(r, current->token.value);
705 current->value = !!*val;
710 if (!current->left || !current->right) {
711 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
712 "Invalid expression in file %s", r->filename);
717 if (!current->left->done) {
718 switch (current->left->token.type) {
720 lval = PARSE_STRING(r, current->left->token.value);
721 current->left->value = !!*lval;
722 DEBUG_DUMP_EVAL(ctx, current->left);
723 current->left->done = 1;
727 current = current->left;
732 /* short circuit evaluation */
733 if (!current->right->done && !regex &&
734 ((current->token.type == TOKEN_AND && !current->left->value) ||
735 (current->token.type == TOKEN_OR && current->left->value))) {
736 current->value = current->left->value;
739 if (!current->right->done) {
740 switch (current->right->token.type) {
742 rval = PARSE_STRING(r,current->right->token.value);
743 current->right->value = !!*rval;
744 DEBUG_DUMP_EVAL(r, current->right);
745 current->right->done = 1;
749 current = current->right;
754 if (current->token.type == TOKEN_AND) {
755 current->value = current->left->value &&
756 current->right->value;
759 current->value = current->left->value ||
760 current->right->value;
767 if (!current->left || !current->right ||
768 current->left->token.type != TOKEN_STRING ||
769 (current->right->token.type != TOKEN_STRING &&
770 current->right->token.type != TOKEN_RE)) {
771 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
772 "Invalid expression in file %s", r->filename);
776 lval = PARSE_STRING(r, current->left->token.value);
777 rval = PARSE_STRING(r, current->right->token.value);
779 if (current->right->token.type == TOKEN_RE) {
780 current->value = re_check(r, lval, rval, reptr);
784 current->value = !strcmp(lval, rval);
787 if (current->token.type == TOKEN_NE) {
788 current->value = !current->value;
796 if (!current->left || !current->right ||
797 current->left->token.type != TOKEN_STRING ||
798 current->right->token.type != TOKEN_STRING) {
799 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
800 "Invalid expression in file %s", r->filename);
805 lval = PARSE_STRING(r, current->left->token.value);
806 rval = PARSE_STRING(r, current->right->token.value);
808 current->value = strcmp(lval, rval);
810 switch (current->token.type) {
811 case TOKEN_GE: current->value = current->value >= 0; break;
812 case TOKEN_GT: current->value = current->value > 0; break;
813 case TOKEN_LE: current->value = current->value <= 0; break;
814 case TOKEN_LT: current->value = current->value < 0; break;
815 default: current->value = 0; break; /* should not happen */
821 if (current->right) {
822 if (!current->right->done) {
823 current = current->right;
826 current->value = current->right->value;
832 if (current->token.type == TOKEN_NOT) {
833 current->value = !current->value;
838 *was_error = eval_func(r, current, string_func);
847 error = "No operator before regex in file %s";
851 error = "Unmatched '(' in file %s";
855 error = "internal parser error in file %s";
858 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error, r->filename);
863 DEBUG_DUMP_EVAL(r, current);
865 current = current->parent;
868 return (root ? root->value : 0);
870 AP_DECLARE(int) ap_expr_eval(request_rec *r, ap_parse_node_t *root,
871 int *was_error, backref_t **reptr,
872 string_func_t string_func, opt_func_t eval_func)
874 ap_parse_node_t *clone;
875 if (root == NULL) { /* no condition == unconditional */
878 clone = ap_expr_clone_tree(r->pool, root, NULL);
879 return expr_eval(r, clone, was_error, reptr, string_func, eval_func);
881 AP_DECLARE(int) ap_expr_evalstring(request_rec *r, const char *expr,
882 int *was_error, backref_t **reptr,
883 string_func_t string_func,
884 opt_func_t eval_func)
886 ap_parse_node_t *root = ap_expr_parse(r->pool, expr, was_error);
887 if (*was_error || !root) {
888 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
889 "Error parsing expression in %s", r->filename);
892 return expr_eval(r, root, was_error, reptr, string_func, eval_func);
896 static ap_regex_t *isvar = NULL;
897 AP_DECLARE(const char*) ap_expr_string(request_rec *r, const char *str)
899 /* a default string evaluator: support headers and env */
900 const char *ret = str;
901 ap_regmatch_t match[3];
902 ap_assert(isvar != NULL);
903 if (ap_regexec(isvar, str, 3, match, 0) == 0) {
904 apr_table_t *table = NULL;
905 int len = match[1].rm_eo-match[1].rm_so;
906 const char *name = str+match[1].rm_so;
907 if (!strncasecmp("req", name, len)) {
908 table = r->headers_in;
910 else if (!strncasecmp("resp", name, len)) {
911 table = r->headers_out;
913 else if (!strncasecmp("env", name, len)) {
914 table = r->subprocess_env;
917 char *key = apr_pstrndup(r->pool, str+match[2].rm_so,
918 match[2].rm_eo-match[2].rm_so);
919 ret = apr_table_get(table, key);
922 else if (str[0] == '$') {
923 if (!strcasecmp(str, "$handler")) {
926 else if (!strcasecmp(str, "$content-type")) {
927 ret = r->content_type;
930 /* TODO: provide a hook so modules can interpret other patterns */
931 /* OhBugger, where's the regexp for backreferences ? */
935 return ret; /* default - literal string as-is */
937 static apr_status_t ap_expr_term(void *expr)
945 AP_DECLARE(apr_status_t) ap_expr_init(apr_pool_t *pool)
947 static ap_regex_t var;
950 if (ap_regcomp(isvar, "\\$([A-Za-z0-9]+)\\{([^\\}]+)\\}", 0)) {
954 apr_pool_cleanup_register(pool, isvar, ap_expr_term,
955 apr_pool_cleanup_null);
958 return isvar ? APR_SUCCESS : APR_EGENERAL;