From: Stefan Fritsch Date: Sat, 28 May 2011 11:47:55 +0000 (+0000) Subject: Use the new "ap_expr" expression parser. X-Git-Tag: 2.3.13~101 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3dd90a26b710c4935728a3c1a076dbe61eeb2c83;p=thirdparty%2Fapache%2Fhttpd.git Use the new "ap_expr" expression parser. The old parser can still be used by setting the new directive SSILegacyExprParser git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1128614 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 9883c78cd97..626ecc31027 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.3.13 + *) mod_include: Make the "#if expr" element use the new "ap_expr" expression + parser. The old parser can still be used by setting the new directive + SSILegacyExprParser. [Stefan Fritsch] + *) core: Add some features to ap_expr for use by mod_include: a restricted mode that does not allow to bypass request access restrictions; new variables DOCUMENT_URI (alias for REQUEST_URI), LAST_MODIFIED; -A as an diff --git a/STATUS b/STATUS index 4d313990081..182289049c6 100644 --- a/STATUS +++ b/STATUS @@ -96,8 +96,6 @@ RELEASE SHOWSTOPPERS: jim sez: Why a blocker?, pgollucci +1 jim wrowe asks: what's the API change required? - * mod_includes aught to use ap_expr - * Clarify/potentially change the meaning of MaxConnections for Event MPM with respect to accepting new connections and keep alive requests for the docs and example config. This shouldn't change after users and diff --git a/docs/manual/expr.xml b/docs/manual/expr.xml index 03bf655ab34..bc86b793ab1 100644 --- a/docs/manual/expr.xml +++ b/docs/manual/expr.xml @@ -354,48 +354,50 @@ listfunction ::= listfuncname "(" word ")" - + + True if the file exists and is a directory + True if the file (or dir or special) exists + True if the file exists and is regular file + True if the file exists and is symlink + (same as -L) + impact your server's performance! + impact your server's performance! - + - + - + + True otherwise. +
NameDescription
NameDescriptionRestricted
-d The argument is treated as a filename. - True if the file exists and is a directory
yes
-e The argument is treated as a filename. - True if the file (or dir or special) exists
yes
-f The argument is treated as a filename. - True if the file exists and is regular file
yes
-L The argument is treated as a filename. - True if the file exists and is symlink
yes
-h The argument is treated as a filename. True if the file exists and is symlink - (same as -L)
yes
-F True if string is a valid file, accessible via all the server's currently-configured access controls for that path. This uses an internal subrequest to do the check, so use it with care - it can - impact your server's performance!
-U True if string is a valid URL, accessible via all the server's currently-configured access controls for that path. This uses an internal subrequest to do the check, so use it with care - it can - impact your server's performance!
-AAlias for -U
Alias for -U
-nTrue if string is not empty
True if string is not empty
-zTrue if string is empty
True if string is empty
-T False if string is empty, "0", "off", "false", or "no" (case insensitive). - True otherwise.
-R Same as "%{REMOTE_ADDR} -ipmatch ...", but more efficient -
+

The operators marked as "restricted" are not available in some modules + like mod_include.

@@ -408,35 +410,40 @@ listfunction ::= listfuncname "(" word ")" - + - + - + - + - + - + + osenv - + - + - + - + - + - +
NameDescription
NameDescriptionRestricted
req, httpGet HTTP request header
Get HTTP request header
respGet HTTP response header
Get HTTP response header
reqenvLookup request environment variable
Lookup request environment variable
osenvLookup operating system environment variable
Lookup operating system environment variable
noteLookup request note
Lookup request note
env Return first match of note, reqenv, - osenv
tolowerConvert string to lower case
Convert string to lower case
toupperConvert string to uppser case
Convert string to uppser case
escapeEscape special characters in %hex encoding
Escape special characters in %hex encoding
unescapeUnescape %hex encoded string, leaving URL-special characters encoded (XXX: describe better)
Unescape %hex encoded string, leaving URL-special characters + encoded (XXX: describe better)
fileRead contents from a file
Read contents from a fileyes
filesizeReturn size of a file (or 0 if file does not exist or is not regular file)
Return size of a file (or 0 if file does not exist or is not + regular file)yes
+

The functions marked as "restricted" are not available in some modules + like mod_include.

+

In addition to string-valued functions, there are also list-valued functions which take one string as argument and return a wordlist, i.e. a list of strings. The wordlist can be used with the special -in operator. diff --git a/docs/manual/howto/ssi.xml b/docs/manual/howto/ssi.xml index b40dafc8fa9..577c5155a41 100644 --- a/docs/manual/howto/ssi.xml +++ b/docs/manual/howto/ssi.xml @@ -448,7 +448,8 @@ modified?

Then, in your SSI-enabled document, you might do the following:

- <!--#if expr="${Mac} && ${InternetExplorer}" -->
+ <!--#if expr="-T reqenv('Mac') && + -T reqenv('InternetExplorer')" -->
Apologetic text goes here
<!--#else -->
Cool JavaScript code goes here
diff --git a/docs/manual/mod/mod_include.xml b/docs/manual/mod/mod_include.xml index 9828384a72d..4dd0ac70b90 100644 --- a/docs/manual/mod/mod_include.xml +++ b/docs/manual/mod/mod_include.xml @@ -504,12 +504,14 @@ directive. This includes the config, exec, flastmod, fsize, include, echo, and set - directives, as well as the arguments to conditional operators. + directives. If SSILegacyExprParser is set to on, + substitution also occures in the arguments to conditional operators. You can insert a literal dollar sign into the string using backslash quoting:

- <!--#if expr="$a = \$test" --> + <!--#set var="cur" value="\$test" -->

If a variable reference needs to be substituted in the @@ -526,26 +528,6 @@ to "X_Y" if REMOTE_HOST is "X" and REQUEST_METHOD is "Y".

- -

The below example will print "in foo" if the - DOCUMENT_URI is /foo/file.html, "in bar" - if it is /bar/file.html and "in neither" otherwise:

- - - <!--#if expr='"$DOCUMENT_URI" = "/foo/file.html"' -->
- - in foo
-
- <!--#elif expr='"$DOCUMENT_URI" = "/bar/file.html"' -->
- - in bar
-
- <!--#else -->
- - in neither
-
- <!--#endif --> -
@@ -573,7 +555,55 @@

The endif element ends the if element and is required.

-

test_condition is one of the following:

+

test_condition is a boolean expression tha follows the + ap_expr syntax. The syntax can be changed + to be compatible with Apache HTTPD 2.2.x using SSILegacyExprParser.

+ +

The SSI variables set with the var element are exported + into the request environment and can be accessed with the + reqenv function. As a short-cut, the function name + v is also available inside mod_include.

+ +

The below example will print "from local net" if client IP address + belongs to the 10.0.0.0/8 subnet.

+ + + <!--#if expr='-R "10.0.0.0/8"' -->
+ + from local net
+
+ <!--#else -->
+ + from somewhere else
+
+ <!--#endif --> +
+ +

The below example will print "foo is bar" if the variable + foo is set to the value "bar".

+ + + <!--#if expr='v("foo") = "bar"' -->
+ + foo is bar
+
+ <!--#endif --> +
+ + Reference Documentation +

See also: Expressions in Apache HTTP Server, + for a complete reference and examples. The restricted functions + are not available inside mod_include

+
+
+ +
+ Legacy expression syntax + +

This section describes the syntax of the #if expr + element if SSILegacyExprParser + is set to on.

string
@@ -713,12 +743,6 @@ be escaped. This is regardless of their meaning to the regex engine.

-
@@ -856,14 +880,20 @@ displayed SSIAccessEnable -Enable the -A flag during conditional flow control processing. +Enable the -A flag in legacy conditional expressions. SSIAccessEnable on|off SSIAccessEnable off directory.htaccess + SSIAccessEnable has no effect unless + SSILegacyExprParser is set to + on. + +

The SSIAccessEnable directive controls whether - the -A test is enabled during conditional flow control processing. + the -A test is enabled during conditional flow control processing when + using the 2.2.x compatible expression parser. SSIAccessEnable can take on the following values:

@@ -961,7 +991,25 @@ server. - + + +SSILegacyExprParser +Enable compatibility mode for conditional expressions. +SSILegacyExprParser on|off +SSILegacyExprParser off +directory.htaccess +Available in version 2.3.13 and later. + + +

As of version 2.3.13, mod_include has switched to the + new ap_expr syntax for conditional expressions + in #if flow control elements. This directive allows to + switch to the old syntax which is compatible + with Apache HTTPD version 2.2.x and earlier. +

+
+
+ XBitHack Parse SSI directives in files with the execute bit diff --git a/docs/manual/upgrading.xml b/docs/manual/upgrading.xml index 9c95f5e8178..3a2423006a9 100644 --- a/docs/manual/upgrading.xml +++ b/docs/manual/upgrading.xml @@ -226,9 +226,17 @@ now uses a boolean expression to determine if a filter is applied. -
  • mod_include: An SSI* config directive in directory - scope no longer causes all other per-directory SSI* directives to be - reset to their default values. +
  • mod_include: +
      +
    • The #if expr element now uses the new expression parser. The old syntax can be + restored with the new directive SSILegacyExprParser. +
    • +
    • An SSI* config directive in directory scope no longer causes + all other per-directory SSI* directives to be reset to their + default values.
    • +
  • diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c index d520785570e..def1994af2d 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -39,6 +39,7 @@ #include "util_script.h" #include "http_core.h" #include "mod_include.h" +#include "ap_expr.h" /* helper for Latin1 <-> entity encoding */ #if APR_CHARSET_EBCDIC @@ -119,6 +120,7 @@ typedef struct { signed char accessenable; signed char lastmodified; signed char etag; + signed char legacy_expr; } include_dir_config; typedef struct { @@ -195,8 +197,13 @@ struct ssi_internal_ctx { const char *undefined_echo; apr_size_t undefined_echo_len; - int accessenable; /* is using the access tests allowed? */ + char accessenable; /* is using the access tests allowed? */ + char legacy_expr; /* use ap_expr or legacy mod_include + expression parser? */ + ap_expr_eval_ctx_t *expr_eval_ctx; /* NULL if there wasn't an ap_expr yet */ + const char *expr_vary_this; /* for use by ap_expr_eval_ctx */ + const char *expr_err; /* for use by ap_expr_eval_ctx */ #ifdef DEBUG_INCLUDE struct { ap_filter_t *f; @@ -454,7 +461,7 @@ static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register; /* Sentinel value to store in subprocess_env for items that * shouldn't be evaluated until/unless they're actually used */ -static const char lazy_eval_sentinel; +static const char lazy_eval_sentinel = '\0'; #define LAZY_VALUE (&lazy_eval_sentinel) /* default values */ @@ -689,6 +696,48 @@ static const char *get_include_var(const char *var, include_ctx_t *ctx) return val; } +static const char *include_expr_var_fn(ap_expr_eval_ctx_t *eval_ctx, + const void *data, + const char *arg) +{ + const char *res, *name = data; + include_ctx_t *ctx = eval_ctx->data; + if (name[0] == 'e') { + /* keep legacy "env" semantics */ + if ((res = apr_table_get(ctx->r->notes, arg)) != NULL) + return res; + else if ((res = get_include_var(arg, ctx)) != NULL) + return res; + else + return getenv(arg); + } + else { + return get_include_var(arg, ctx); + } +} + +static int include_expr_lookup(ap_expr_lookup_parms *parms) +{ + switch (parms->type) { + case AP_EXPR_FUNC_STRING: + if (strcasecmp(parms->name, "v") == 0 || + strcasecmp(parms->name, "reqenv") == 0 || + strcasecmp(parms->name, "env") == 0) { + *parms->func = include_expr_var_fn; + *parms->data = parms->name; + return OK; + } + break; + /* + * We could also make the SSI vars available as %{...} style variables + * (AP_EXPR_FUNC_VAR), but this would create problems if we ever want + * to cache parsed expressions for performance reasons. + */ + } + return ap_run_expr_lookup(parms); +} + + /* * Do variable substitution on strings * @@ -1536,6 +1585,69 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) return (root ? root->value : 0); } +/* same as above, but use common ap_expr syntax / API */ +static int parse_ap_expr(include_ctx_t *ctx, const char *expr, int *was_error) +{ + ap_expr_info_t expr_info; + const char *err; + int ret; + backref_t *re = ctx->intern->re; + ap_expr_eval_ctx_t *eval_ctx = ctx->intern->expr_eval_ctx; + + expr_info.filename = ctx->r->filename; + expr_info.line_number = 0; + expr_info.module_index = APLOG_MODULE_INDEX; + expr_info.flags = AP_EXPR_FLAGS_RESTRICTED; + err = ap_expr_parse(ctx->r->pool, ctx->r->pool, &expr_info, expr, + include_expr_lookup); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, + "Could not parse expr \"%s\" in %s: %s", expr, + ctx->r->filename, err); + *was_error = 1; + return 0; + } + + if (!re) { + ctx->intern->re = re = apr_pcalloc(ctx->pool, sizeof(*re)); + } + else { + /* ap_expr_exec_ctx() does not care about re->have_match but only about + * re->source + */ + if (!re->have_match) + re->source = NULL; + } + + if (!eval_ctx) { + eval_ctx = apr_pcalloc(ctx->pool, sizeof(*eval_ctx)); + ctx->intern->expr_eval_ctx = eval_ctx; + eval_ctx->r = ctx->r; + eval_ctx->c = ctx->r->connection; + eval_ctx->s = ctx->r->server; + eval_ctx->p = ctx->r->pool; + eval_ctx->data = ctx; + eval_ctx->err = &ctx->intern->expr_err; + eval_ctx->vary_this = &ctx->intern->expr_vary_this; + eval_ctx->re_nmatch = AP_MAX_REG_MATCH; + eval_ctx->re_pmatch = re->match; + eval_ctx->re_source = &re->source; + } + + eval_ctx->info = &expr_info; + ret = ap_expr_exec_ctx(eval_ctx); + if (ret < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, + "Could not evaluate expr \"%s\" in %s: %s", expr, + ctx->r->filename, ctx->intern->expr_err); + *was_error = 1; + return 0; + } + *was_error = 0; + if (re->source) + re->have_match = 1; + return ret; +} /* * +-------------------------------------------------------+ @@ -2211,7 +2323,10 @@ static apr_status_t handle_if(include_ctx_t *ctx, ap_filter_t *f, DEBUG_PRINTF((ctx, "**** if expr=\"%s\"\n", expr)); - expr_ret = parse_expr(ctx, expr, &was_error); + if (ctx->intern->legacy_expr) + expr_ret = parse_expr(ctx, expr, &was_error); + else + expr_ret = parse_ap_expr(ctx, expr, &was_error); if (was_error) { SSI_CREATE_ERROR_BUCKET(ctx, f, bb); @@ -3731,6 +3846,10 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) ctx->flags |= SSI_FLAG_NO_EXEC; } intern->accessenable = (conf->accessenable > 0); + intern->legacy_expr = (conf->legacy_expr > 0); + intern->expr_eval_ctx = NULL; + intern->expr_err = NULL; + intern->expr_vary_this = NULL; ctx->if_nesting_level = 0; intern->re = NULL; @@ -3890,6 +4009,7 @@ static void *create_includes_dir_config(apr_pool_t *p, char *dummy) result->accessenable = UNSET; result->lastmodified = UNSET; result->etag = UNSET; + result->accessenable = UNSET; return result; } @@ -3907,6 +4027,7 @@ static void *merge_includes_dir_config(apr_pool_t *p, void *basev, void *overrid MERGE(base, over, new, accessenable, UNSET); MERGE(base, over, new, lastmodified, UNSET); MERGE(base, over, new, etag, UNSET); + MERGE(base, over, new, legacy_expr, UNSET); return new; } @@ -4058,6 +4179,11 @@ static const command_rec includes_cmds[] = AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot_char, (void *)APR_OFFSETOF(include_dir_config, accessenable), OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"), + AP_INIT_FLAG("SSILegacyExprParser", ap_set_flag_slot_char, + (void *)APR_OFFSETOF(include_dir_config, legacy_expr), + OR_LIMIT, + "Whether to use the legacy expression parser compatible " + "with <= 2.2.x. Limited to 'on' or 'off'"), AP_INIT_FLAG("SSILastModified", ap_set_flag_slot_char, (void *)APR_OFFSETOF(include_dir_config, lastmodified), OR_LIMIT, "Whether to set the last modified header or respect "