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.
19 * Allow conditional configuration depending on the httpd version
21 * André Malo (nd/perlig.de), January 2004
23 * Some stuff coded here is heavily based on the core <IfModule>
26 * The module makes the following confgurations possible:
28 * <IfVersion op major.minor.patch>
29 * # conditional config here ...
32 * where "op" is one of:
39 * If minor version and patch level are omitted they are assumed to be 0.
41 * Alternatively you can match the whole version (including some vendor-added
42 * string of the CORE version, see ap_release.h) against a regular expression:
44 * <IfVersion op regex>
45 * # conditional config here ...
48 * where "op" is one of:
49 * = / == match; regex must be surrounded by slashes
50 * ~ match; regex MAY NOT be surrounded by slashes
52 * Note that all operators may be preceeded by an exclamation mark
53 * (without spaces) in order to reverse their meaning.
58 #include "apr_strings.h"
62 #include "http_config.h"
66 /* module structure */
67 module AP_MODULE_DECLARE_DATA version_module;
69 /* queried httpd version */
70 static ap_version_t httpd_version;
74 * compare the supplied version with the core one
76 static int compare_version(char *version_string, const char **error)
78 char *p = version_string, *ep;
79 int version[3] = {0, 0, 0};
82 *error = "Version appears to be invalid. It must have the format "
83 "major[.minor[.patch]] where major, minor and patch are "
86 if (!apr_isdigit(*p)) {
90 /* parse supplied version */
91 ep = version_string + strlen(version_string);
92 while (p <= ep && c < 3) {
98 version[c++] = atoi(version_string);
103 if (!apr_isdigit(*p)) {
110 if (p < ep) { /* syntax error */
116 if (httpd_version.major > version[0]) {
119 else if (httpd_version.major < version[0]) {
122 else if (httpd_version.minor > version[1]) {
125 else if (httpd_version.minor < version[1]) {
128 else if (httpd_version.patch > version[2]) {
131 else if (httpd_version.patch < version[2]) {
135 /* seems to be the same */
140 * match version against a regular expression
142 static int match_version(apr_pool_t *pool, char *version_string,
146 const char *to_match;
149 compiled = ap_pregcomp(pool, version_string, REG_EXTENDED);
151 *error = "Unable to compile regular expression";
157 to_match = apr_psprintf(pool, "%d.%d.%d%s",
161 httpd_version.add_string);
163 rc = !ap_regexec(compiled, to_match, 0, NULL, 0);
165 ap_pregfree(pool, compiled);
170 * Implements the <IfVersion> container
172 static const char *start_ifversion(cmd_parms *cmd, void *mconfig,
173 const char *arg1, const char *arg2,
177 int reverse = 0, done = 0, match = 0, compare;
178 const char *p, *error;
181 /* supplying one argument is possible, we assume an equality check then */
187 /* surrounding quotes without operator */
188 if (!arg3 && *arg2 == '>' && !arg2[1]) {
194 /* the third argument makes version surrounding quotes plus operator
197 endp = arg2 + strlen(arg2);
199 || (!(arg3 && *arg3 == '>' && !arg3[1]) && *--endp != '>')) {
200 return apr_pstrcat(cmd->pool, cmd->cmd->name,
201 "> directive missing closing '>'", NULL);
213 if (!*p || (*p == '=' && !p[1] && c != '~')) {
214 if (!httpd_version.major) {
215 ap_get_server_revision(&httpd_version);
221 /* normal comparison */
223 compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
234 /* regexp otherwise */
235 if (endp == ++arg2 || *--endp != '/') {
236 return "Missing delimiting / of regular expression.";
240 /* regular expression */
241 match = match_version(cmd->pool, apr_pstrmemdup(cmd->pool, arg2,
250 compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
257 match = ((-1 == compare) || (*p && !compare));
261 compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
268 match = ((1 == compare) || (*p && !compare));
278 return apr_pstrcat(cmd->pool, "unrecognized operator '", arg1, "'",
282 if ((!reverse && match) || (reverse && !match)) {
283 ap_directive_t *parent = NULL;
284 ap_directive_t *current = NULL;
287 retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
288 ¤t, &parent, "<IfVersion");
289 *(ap_directive_t **)mconfig = current;
293 *(ap_directive_t **)mconfig = NULL;
294 return ap_soak_end_container(cmd, "<IfVersion");
297 static const command_rec version_cmds[] = {
298 AP_INIT_TAKE123("<IfVersion", start_ifversion, NULL, EXEC_ON_READ | OR_ALL,
299 "a comparison operator, a version (and a delimiter)"),
303 module AP_MODULE_DECLARE_DATA version_module =
305 STANDARD20_MODULE_STUFF,
306 NULL, /* dir config creater */
307 NULL, /* dir merger --- default is to override */
308 NULL, /* server config */
309 NULL, /* merge server configs */
310 version_cmds, /* command apr_table_t */
311 NULL, /* register hooks */