switch to a 60 bit hash
[httpd-crcsyncproxy.git] / modules / metadata / mod_version.c
blobd614cae067938860d8ad48d16755f19b418f594a
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.
18 * mod_version.c
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>
24 * containers.
26 * The module makes the following confgurations possible:
28 * <IfVersion op major.minor.patch>
29 * # conditional config here ...
30 *</IfVersion>
32 * where "op" is one of:
33 * = / == equal
34 * > greater than
35 * >= greater or equal
36 * < less than
37 * <= less or equal
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 ...
46 *</IfVersion>
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.
57 #include "apr.h"
58 #include "apr_strings.h"
59 #include "apr_lib.h"
61 #include "httpd.h"
62 #include "http_config.h"
63 #include "http_log.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};
80 int c = 0;
82 *error = "Version appears to be invalid. It must have the format "
83 "major[.minor[.patch]] where major, minor and patch are "
84 "numbers.";
86 if (!apr_isdigit(*p)) {
87 return 0;
90 /* parse supplied version */
91 ep = version_string + strlen(version_string);
92 while (p <= ep && c < 3) {
93 if (*p == '.') {
94 *p = '\0';
97 if (!*p) {
98 version[c++] = atoi(version_string);
99 version_string = ++p;
100 continue;
103 if (!apr_isdigit(*p)) {
104 break;
107 ++p;
110 if (p < ep) { /* syntax error */
111 return 0;
114 *error = NULL;
116 if (httpd_version.major > version[0]) {
117 return 1;
119 else if (httpd_version.major < version[0]) {
120 return -1;
122 else if (httpd_version.minor > version[1]) {
123 return 1;
125 else if (httpd_version.minor < version[1]) {
126 return -1;
128 else if (httpd_version.patch > version[2]) {
129 return 1;
131 else if (httpd_version.patch < version[2]) {
132 return -1;
135 /* seems to be the same */
136 return 0;
140 * match version against a regular expression
142 static int match_version(apr_pool_t *pool, char *version_string,
143 const char **error)
145 ap_regex_t *compiled;
146 const char *to_match;
147 int rc;
149 compiled = ap_pregcomp(pool, version_string, AP_REG_EXTENDED);
150 if (!compiled) {
151 *error = "Unable to compile regular expression";
152 return 0;
155 *error = NULL;
157 to_match = apr_psprintf(pool, "%d.%d.%d%s",
158 httpd_version.major,
159 httpd_version.minor,
160 httpd_version.patch,
161 httpd_version.add_string);
163 rc = !ap_regexec(compiled, to_match, 0, NULL, 0);
165 ap_pregfree(pool, compiled);
166 return rc;
170 * Implements the <IfVersion> container
172 static const char *start_ifversion(cmd_parms *cmd, void *mconfig,
173 const char *arg1, const char *arg2,
174 const char *arg3)
176 const char *endp;
177 int reverse = 0, done = 0, match = 0, compare;
178 const char *p, *error;
179 char c;
181 /* supplying one argument is possible, we assume an equality check then */
182 if (!arg2) {
183 arg2 = arg1;
184 arg1 = "=";
187 /* surrounding quotes without operator */
188 if (!arg3 && *arg2 == '>' && !arg2[1]) {
189 arg3 = ">";
190 arg2 = arg1;
191 arg1 = "=";
194 /* the third argument makes version surrounding quotes plus operator
195 * possible.
197 endp = arg2 + strlen(arg2);
198 if ( endp == arg2
199 || (!(arg3 && *arg3 == '>' && !arg3[1]) && *--endp != '>')) {
200 return apr_pstrcat(cmd->pool, cmd->cmd->name,
201 "> directive missing closing '>'", NULL);
204 p = arg1;
205 if (*p == '!') {
206 reverse = 1;
207 if (p[1]) {
208 ++p;
212 c = *p++;
213 if (!*p || (*p == '=' && !p[1] && c != '~')) {
214 if (!httpd_version.major) {
215 ap_get_server_revision(&httpd_version);
218 done = 1;
219 switch (c) {
220 case '=':
221 /* normal comparison */
222 if (*arg2 != '/') {
223 compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
224 endp-arg2),
225 &error);
226 if (error) {
227 return error;
230 match = !compare;
231 break;
234 /* regexp otherwise */
235 if (endp == ++arg2 || *--endp != '/') {
236 return "Missing delimiting / of regular expression.";
239 case '~':
240 /* regular expression */
241 match = match_version(cmd->pool, apr_pstrmemdup(cmd->pool, arg2,
242 endp-arg2),
243 &error);
244 if (error) {
245 return error;
247 break;
249 case '<':
250 compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
251 endp-arg2),
252 &error);
253 if (error) {
254 return error;
257 match = ((-1 == compare) || (*p && !compare));
258 break;
260 case '>':
261 compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
262 endp-arg2),
263 &error);
264 if (error) {
265 return error;
268 match = ((1 == compare) || (*p && !compare));
269 break;
271 default:
272 done = 0;
273 break;
277 if (!done) {
278 return apr_pstrcat(cmd->pool, "unrecognized operator '", arg1, "'",
279 NULL);
282 if ((!reverse && match) || (reverse && !match)) {
283 ap_directive_t *parent = NULL;
284 ap_directive_t *current = NULL;
285 const char *retval;
287 retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
288 &current, &parent, "<IfVersion");
289 *(ap_directive_t **)mconfig = current;
290 return retval;
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)"),
300 { NULL }
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 */