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 */