]> granicus.if.org Git - apache/commitdiff
Use the new "ap_expr" expression parser.
authorStefan Fritsch <sf@apache.org>
Sat, 28 May 2011 11:47:55 +0000 (11:47 +0000)
committerStefan Fritsch <sf@apache.org>
Sat, 28 May 2011 11:47:55 +0000 (11:47 +0000)
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

CHANGES
STATUS
docs/manual/expr.xml
docs/manual/howto/ssi.xml
docs/manual/mod/mod_include.xml
docs/manual/upgrading.xml
modules/filters/mod_include.c

diff --git a/CHANGES b/CHANGES
index 9883c78cd978fe72c0edfb42737f1ac53fcbba03..626ecc3102700028a82c3d5c68042d7c87616a7d 100644 (file)
--- 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 4d313990081e33206f1c882fcce1cd014b624ac3..182289049c69fafe8c50a197aa105f8868578927 100644 (file)
--- 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 
index 03bf655ab34dd8499d0c1b658499fe4fc11b336b..bc86b793ab13afca65d21f8d82cd0e8e43f04ee0 100644 (file)
@@ -354,48 +354,50 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
     <table border="1" style="zebra">
     <columnspec><column width=".2"/><column width=".2"/><column width=".6"/></columnspec>
 
-    <tr><th>Name</th><th>Description</th></tr>
+    <tr><th>Name</th><th>Description</th><th>Restricted</th></tr>
     <tr><td><code>-d</code></td>
         <td>The argument is treated as a filename.
-            True if the file exists and is a directory</td></tr>
+            True if the file exists and is a directory</td><td>yes</td></tr>
     <tr><td><code>-e</code></td>
         <td>The argument is treated as a filename.
-            True if the file (or dir or special) exists</td></tr>
+            True if the file (or dir or special) exists</td><td>yes</td></tr>
     <tr><td><code>-f</code></td>
         <td>The argument is treated as a filename.
-            True if the file exists and is regular file</td></tr>
+            True if the file exists and is regular file</td><td>yes</td></tr>
     <tr><td><code>-L</code></td>
         <td>The argument is treated as a filename.
-            True if the file exists and is symlink</td></tr>
+            True if the file exists and is symlink</td><td>yes</td></tr>
     <tr><td><code>-h</code></td>
         <td>The argument is treated as a filename.
             True if the file exists and is symlink
-            (same as <code>-L</code>)</td></tr>
+            (same as <code>-L</code>)</td><td>yes</td></tr>
     <tr><td><code>-F</code></td>
         <td>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!</td></tr>
+            impact your server's performance!</td><td></td></tr>
     <tr><td><code>-U</code></td>
         <td>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!</td></tr>
+            impact your server's performance!</td><td></td></tr>
     <tr><td><code>-A</code></td>
-        <td>Alias for <code>-U</code></td></tr>
+        <td>Alias for <code>-U</code></td><td></td></tr>
     <tr><td><code>-n</code></td>
-        <td>True if string is not empty</td></tr>
+        <td>True if string is not empty</td><td></td></tr>
     <tr><td><code>-z</code></td>
-        <td>True if string is empty</td></tr>
+        <td>True if string is empty</td><td></td></tr>
     <tr><td><code>-T</code></td>
         <td>False if string is empty, "<code>0</code>", "<code>off</code>",
             "<code>false</code>", or "<code>no</code>" (case insensitive).
-            True otherwise.</td></tr>
+            True otherwise.</td><td></td></tr>
     <tr><td><code>-R</code></td>
         <td>Same as "<code>%{REMOTE_ADDR} -ipmatch ...</code>", but more efficient
-        </td></tr>
+        </td><td></td></tr>
     </table>
 
+    <p>The operators marked as "restricted" are not available in some modules
+    like <module>mod_include</module>.</p>
 </section>
 
 <section id="functions">
@@ -408,35 +410,40 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
     <table border="1" style="zebra">
     <columnspec><column width=".2"/><column width=".8"/></columnspec>
 
-    <tr><th>Name</th><th>Description</th></tr>
+    <tr><th>Name</th><th>Description</th><th>Restricted</th></tr>
     <tr><td><code>req</code>, <code>http</code></td>
-        <td>Get HTTP request header</td></tr>
+        <td>Get HTTP request header</td><td></td></tr>
     <tr><td><code>resp</code></td>
-        <td>Get HTTP response header</td></tr>
+        <td>Get HTTP response header</td><td></td></tr>
     <tr><td><code>reqenv</code></td>
-        <td>Lookup request environment variable</td></tr>
+        <td>Lookup request environment variable</td><td></td></tr>
     <tr><td><code>osenv</code></td>
-        <td>Lookup operating system environment variable</td></tr>
+        <td>Lookup operating system environment variable</td><td></td></tr>
     <tr><td><code>note</code></td>
-        <td>Lookup request note</td></tr>
+        <td>Lookup request note</td><td></td></tr>
     <tr><td><code>env</code></td>
         <td>Return first match of <code>note</code>, <code>reqenv</code>,
-            <code>osenv</code></td></tr>
+            <code>osenv</code></td><td></td></tr>
     <tr><td><code>tolower</code></td>
-        <td>Convert string to lower case</td></tr>
+        <td>Convert string to lower case</td><td></td></tr>
     <tr><td><code>toupper</code></td>
-        <td>Convert string to uppser case</td></tr>
+        <td>Convert string to uppser case</td><td></td></tr>
     <tr><td><code>escape</code></td>
-        <td>Escape special characters in %hex encoding</td></tr>
+        <td>Escape special characters in %hex encoding</td><td></td></tr>
     <tr><td><code>unescape</code></td>
-        <td>Unescape %hex encoded string, leaving URL-special characters encoded (XXX: describe better)</td></tr>
+        <td>Unescape %hex encoded string, leaving URL-special characters
+            encoded (XXX: describe better)</td><td></td></tr>
     <tr><td><code>file</code></td>
-        <td>Read contents from a file</td></tr>
+        <td>Read contents from a file</td><td>yes</td></tr>
     <tr><td><code>filesize</code></td>
-        <td>Return size of a file (or 0 if file does not exist or is not regular file)</td></tr>
+        <td>Return size of a file (or 0 if file does not exist or is not
+            regular file)</td><td>yes</td></tr>
 
     </table>
 
+    <p>The functions marked as "restricted" are not available in some modules
+    like <module>mod_include</module>.</p>
+
     <p>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 <code>-in</code> operator.
index b40dafc8fa918a3feb4642496f077b35b5ced0f7..577c5155a41f6abfe0e3f905cb3ee0edc053cbfa 100644 (file)
@@ -448,7 +448,8 @@ modified?</title>
     <p>Then, in your SSI-enabled document, you might do the
     following:</p>
 <example>
-        &lt;!--#if expr="${Mac} &amp;&amp; ${InternetExplorer}" --&gt;<br />
+        &lt;!--#if expr="-T reqenv('Mac') &amp;&amp;
+                         -T reqenv('InternetExplorer')" --&gt;<br />
         Apologetic text goes here<br />
         &lt;!--#else --&gt;<br />
         Cool JavaScript code goes here<br />
index 9828384a72d0a637cb755a0a495a5f8939aef89a..4dd0ac70b90f0fd15ccec93535d5da333b25725c 100644 (file)
     directive. This includes the <code>config</code>,
     <code>exec</code>, <code>flastmod</code>, <code>fsize</code>,
     <code>include</code>, <code>echo</code>, and <code>set</code>
-    directives, as well as the arguments to conditional operators.
+    directives. If <directive module="mod_include"
+    >SSILegacyExprParser</directive> is set to <code>on</code>,
+    substitution also occures in the arguments to conditional operators.
     You can insert a literal dollar sign into the string using backslash
     quoting:</p>
 
     <example>
-      &lt;!--#if expr="$a = \$test" --&gt;
+      &lt;!--#set var="cur" value="\$test" --&gt;
     </example>
 
     <p>If a variable reference needs to be substituted in the
     to "<code>X_Y</code>" if <code>REMOTE_HOST</code> is
     "<code>X</code>" and <code>REQUEST_METHOD</code> is
     "<code>Y</code>".</p>
-
-    <p>The below example will print "in foo" if the
-    <code>DOCUMENT_URI</code> is <code>/foo/file.html</code>, "in bar"
-    if it is <code>/bar/file.html</code> and "in neither" otherwise:</p>
-
-    <example>
-      &lt;!--#if expr='"$DOCUMENT_URI" = "/foo/file.html"' --&gt;<br />
-      <indent>
-        in foo<br />
-      </indent>
-      &lt;!--#elif expr='"$DOCUMENT_URI" = "/bar/file.html"' --&gt;<br />
-      <indent>
-        in bar<br />
-      </indent>
-      &lt;!--#else --&gt;<br />
-      <indent>
-        in neither<br />
-      </indent>
-      &lt;!--#endif --&gt;
-    </example>
 </section>
 
 <section id="flowctrl">
     <p>The <code>endif</code> element ends the <code>if</code> element
     and is required.</p>
 
-    <p><var>test_condition</var> is one of the following:</p>
+    <p><var>test_condition</var> is a boolean expression tha follows the
+    <a href="../expr.html">ap_expr</a> syntax. The syntax can be changed
+    to be compatible with Apache HTTPD 2.2.x using <directive
+    module="mod_include">SSILegacyExprParser</directive>.</p>
+
+    <p>The SSI variables set with the <code>var</code> element are exported
+    into the request environment and can be accessed with the
+    <code>reqenv</code> function. As a short-cut, the function name
+    <code>v</code> is also available inside <module>mod_include</module>.</p>
+
+    <p>The below example will print "from local net" if client IP address
+    belongs to the 10.0.0.0/8 subnet.</p>
+
+    <example>
+      &lt;!--#if expr='-R "10.0.0.0/8"' --&gt;<br />
+      <indent>
+        from local net<br />
+      </indent>
+      &lt;!--#else --&gt;<br />
+      <indent>
+        from somewhere else<br />
+      </indent>
+      &lt;!--#endif --&gt;
+    </example>
+
+    <p>The below example will print "foo is bar" if the variable
+    <code>foo</code> is set to the value "bar".</p>
+
+    <example>
+      &lt;!--#if expr='v("foo") = "bar"' --&gt;<br />
+      <indent>
+        foo is bar<br />
+      </indent>
+      &lt;!--#endif --&gt;
+    </example>
+
+    <note><title>Reference Documentation</title>
+    <p>See also: <a href="../expr.html">Expressions in Apache HTTP Server</a>,
+    for a complete reference and examples. The <em>restricted</em> functions
+    are not available inside <module>mod_include</module></p>
+    </note>
+</section>
+
+<section id="legacyexpr">
+    <title>Legacy expression syntax</title>
+
+    <p>This section describes the syntax of the <code>#if expr</code>
+    element if <directive module="mod_include">SSILegacyExprParser</directive>
+    is set to <code>on</code>.</p>
 
     <dl>
       <dt><code><var>string</var></code></dt>
      be escaped.  This is regardless of their meaning to the regex engine.</p>
     </note>
 
-    <!-- mod_include does not use ap_expr, yet
-    <note><title>Reference Documentation</title>
-    <p>See also: <a href="../expr.html">Expressions in Apache HTTP Server</a>,
-    for a complete reference and examples.</p>
-    </note>
-    -->
 </section>
 
 <directivesynopsis>
@@ -856,14 +880,20 @@ displayed</description>
 
 <directivesynopsis>
 <name>SSIAccessEnable</name>
-<description>Enable the -A flag during conditional flow control processing.</description>
+<description>Enable the -A flag in legacy conditional expressions.</description>
 <syntax>SSIAccessEnable on|off</syntax>
 <default>SSIAccessEnable off</default>
 <contextlist><context>directory</context><context>.htaccess</context></contextlist>
 
 <usage>
+    <note><directive>SSIAccessEnable</directive> has no effect unless
+    <directive module="mod_include">SSILegacyExprParser</directive> is set to
+    <code>on</code>.
+    </note>
+
     <p>The <directive>SSIAccessEnable</directive> 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.
     <directive>SSIAccessEnable</directive> can take on the following values:</p>
 
     <dl>
@@ -961,7 +991,25 @@ server.</description>
       
 </usage>
 </directivesynopsis>
-  
+
+<directivesynopsis>
+<name>SSILegacyExprParser</name>
+<description>Enable compatibility mode for conditional expressions.</description>
+<syntax>SSILegacyExprParser on|off</syntax>
+<default>SSILegacyExprParser off</default>
+<contextlist><context>directory</context><context>.htaccess</context></contextlist>
+<compatibility>Available in version 2.3.13 and later.</compatibility>
+
+<usage>
+    <p>As of version 2.3.13, <module>mod_include</module> has switched to the
+    new <a href="../expr.html">ap_expr</a> syntax for conditional expressions
+    in <code>#if</code> flow control elements.  This directive allows to
+    switch to the <a href="#legacyexpr">old syntax</a> which is compatible
+    with Apache HTTPD version 2.2.x and earlier. 
+    </p>
+</usage>
+</directivesynopsis>
+
 <directivesynopsis>
 <name>XBitHack</name>
 <description>Parse SSI directives in files with the execute bit
index 9c95f5e81782e7a91dd32a2300580e74bae6c796..3a2423006a9a622ea4f23ca19f9f7ea1d2521095 100644 (file)
         now uses a boolean expression to determine if a filter is applied.
         </li>
 
-        <li><module>mod_include</module>: An SSI* config directive in directory
-        scope no longer causes all other per-directory SSI* directives to be
-        reset to their default values.
+        <li><module>mod_include</module>:
+            <ul>
+            <li>The <code>#if expr</code> element now uses the new <a
+            href="expr.html">expression parser</a>. The old syntax can be
+            restored with the new directive <directive module="include"
+            >SSILegacyExprParser</directive>.
+            </li>
+            <li>An SSI* config directive in directory scope no longer causes
+            all other per-directory SSI* directives to be reset to their
+            default values.</li>
+            </ul>
         </li>
       </ul>
     </section>
index d520785570e29a69cfcc713678ab28dfda6f7753..def1994af2d910a31eb50e17c139924a9d9846da 100644 (file)
@@ -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 "