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,
145
ap_regex_t *compiled;
146
const char *to_match;
149
compiled = ap_pregcomp(pool, version_string, AP_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 */