upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / metadata / mod_version.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * mod_version.c
19  * Allow conditional configuration depending on the httpd version
20  * 
21  * AndrĂ© Malo (nd/perlig.de), January 2004
22  *
23  * Some stuff coded here is heavily based on the core <IfModule>
24  * containers.
25  *
26  * The module makes the following confgurations possible:
27  *
28  * <IfVersion op major.minor.patch>
29  *     # conditional config here ...
30  *</IfVersion>
31  *
32  * where "op" is one of:
33  * = / ==       equal
34  * >            greater than
35  * >=           greater or equal
36  * <            less than
37  * <=           less or equal
38  *
39  * If minor version and patch level are omitted they are assumed to be 0.
40  *
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:
43  *
44  * <IfVersion op regex>
45  *     # conditional config here ...
46  *</IfVersion>
47  *
48  * where "op" is one of:
49  * = / ==       match; regex must be surrounded by slashes
50  * ~            match; regex MAY NOT be surrounded by slashes
51  *
52  * Note that all operators may be preceeded by an exclamation mark
53  * (without spaces) in order to reverse their meaning.
54  *
55  */
56
57 #include "apr.h"
58 #include "apr_strings.h"
59 #include "apr_lib.h"
60
61 #include "httpd.h"
62 #include "http_config.h"
63 #include "http_log.h"
64
65
66 /* module structure */
67 module AP_MODULE_DECLARE_DATA version_module;
68
69 /* queried httpd version */
70 static ap_version_t httpd_version;
71
72
73 /*
74  * compare the supplied version with the core one
75  */
76 static int compare_version(char *version_string, const char **error)
77 {
78     char *p = version_string, *ep;
79     int version[3] = {0, 0, 0};
80     int c = 0;
81
82     *error = "Version appears to be invalid. It must have the format "
83              "major[.minor[.patch]] where major, minor and patch are "
84              "numbers.";
85
86     if (!apr_isdigit(*p)) {
87         return 0;
88     }
89
90     /* parse supplied version */
91     ep = version_string + strlen(version_string);
92     while (p <= ep && c < 3) {
93         if (*p == '.') {
94             *p = '\0';
95         }
96
97         if (!*p) {
98             version[c++] = atoi(version_string);
99             version_string = ++p;
100             continue;
101         }
102
103         if (!apr_isdigit(*p)) {
104             break;
105         }
106
107         ++p;
108     }
109
110     if (p < ep) { /* syntax error */
111         return 0;
112     }
113
114     *error = NULL;
115
116     if      (httpd_version.major > version[0]) {
117         return 1;
118     }
119     else if (httpd_version.major < version[0]) {
120         return -1;
121     }
122     else if (httpd_version.minor > version[1]) {
123         return 1;
124     }
125     else if (httpd_version.minor < version[1]) {
126         return -1;
127     }
128     else if (httpd_version.patch > version[2]) {
129         return 1;
130     }
131     else if (httpd_version.patch < version[2]) {
132         return -1;
133     }
134
135     /* seems to be the same */
136     return 0;
137 }
138
139 /*
140  * match version against a regular expression
141  */
142 static int match_version(apr_pool_t *pool, char *version_string,
143                          const char **error)
144 {
145     regex_t *compiled;
146     const char *to_match;
147     int rc;
148
149     compiled = ap_pregcomp(pool, version_string, REG_EXTENDED);
150     if (!compiled) {
151         *error = "Unable to compile regular expression";
152         return 0;
153     }
154
155     *error = NULL;
156
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);
162
163     rc = !ap_regexec(compiled, to_match, 0, NULL, 0);
164
165     ap_pregfree(pool, compiled);
166     return rc;    
167 }
168
169 /*
170  * Implements the <IfVersion> container
171  */
172 static const char *start_ifversion(cmd_parms *cmd, void *mconfig,
173                                    const char *arg1, const char *arg2,
174                                    const char *arg3)
175 {
176     const char *endp;
177     int reverse = 0, done = 0, match = 0, compare;
178     const char *p, *error;
179     char c;
180
181     /* supplying one argument is possible, we assume an equality check then */
182     if (!arg2) {
183         arg2 = arg1;
184         arg1 = "=";
185     }
186
187     /* surrounding quotes without operator */
188     if (!arg3 && *arg2 == '>' && !arg2[1]) {
189         arg3 = ">";
190         arg2 = arg1;
191         arg1 = "=";
192     }
193
194     /* the third argument makes version surrounding quotes plus operator
195      * possible.
196      */
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);
202     }
203
204     p = arg1;
205     if (*p == '!') {
206         reverse = 1;
207         if (p[1]) {
208             ++p;
209         }
210     }
211
212     c = *p++;
213     if (!*p || (*p == '=' && !p[1] && c != '~')) {
214         if (!httpd_version.major) {
215             ap_get_server_revision(&httpd_version);
216         }
217
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;
228                 }
229
230                 match = !compare;
231                 break;
232             }
233
234             /* regexp otherwise */
235             if (endp == ++arg2 || *--endp != '/') {
236                 return "Missing delimiting / of regular expression.";
237             }
238
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;
246             }
247             break;
248
249         case '<':
250             compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
251                                                      endp-arg2),
252                                       &error);
253             if (error) {
254                 return error;
255             }
256
257             match = ((-1 == compare) || (*p && !compare));
258             break;
259
260         case '>':
261             compare = compare_version(apr_pstrmemdup(cmd->pool, arg2,
262                                                      endp-arg2),
263                                       &error);
264             if (error) {
265                 return error;
266             }
267
268             match = ((1 == compare) || (*p && !compare));
269             break;
270
271         default:
272             done = 0;
273             break;
274         }
275     }
276
277     if (!done) {
278         return apr_pstrcat(cmd->pool, "unrecognized operator '", arg1, "'",
279                            NULL);
280     }
281
282     if ((!reverse && match) || (reverse && !match)) {
283         ap_directive_t *parent = NULL;
284         ap_directive_t *current = NULL;
285         const char *retval;
286
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;
291     }
292
293     *(ap_directive_t **)mconfig = NULL;
294     return ap_soak_end_container(cmd, "<IfVersion");
295 }
296
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 }
301 };
302
303 module AP_MODULE_DECLARE_DATA version_module =
304 {
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 */
312 };