|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* mod_version.c
|
|
Packit |
90a5c9 |
* Allow conditional configuration depending on the httpd version
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* André Malo (nd/perlig.de), January 2004
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Some stuff coded here is heavily based on the core <IfModule>
|
|
Packit |
90a5c9 |
* containers.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* The module makes the following confgurations possible:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* <IfVersion op major.minor.patch>
|
|
Packit |
90a5c9 |
* # conditional config here ...
|
|
Packit |
90a5c9 |
*</IfVersion>
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* where "op" is one of:
|
|
Packit |
90a5c9 |
* = / == equal
|
|
Packit |
90a5c9 |
* > greater than
|
|
Packit |
90a5c9 |
* >= greater or equal
|
|
Packit |
90a5c9 |
* < less than
|
|
Packit |
90a5c9 |
* <= less or equal
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If minor version and patch level are omitted they are assumed to be 0.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Alternatively you can match the whole version (including some vendor-added
|
|
Packit |
90a5c9 |
* string of the CORE version, see ap_release.h) against a regular expression:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* <IfVersion op regex>
|
|
Packit |
90a5c9 |
* # conditional config here ...
|
|
Packit |
90a5c9 |
*</IfVersion>
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* where "op" is one of:
|
|
Packit |
90a5c9 |
* = / == match; regex must be surrounded by slashes
|
|
Packit |
90a5c9 |
* ~ match; regex MAY NOT be surrounded by slashes
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Note that all operators may be preceded by an exclamation mark
|
|
Packit |
90a5c9 |
* (without spaces) in order to reverse their meaning.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr.h"
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_lib.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_log.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* module structure */
|
|
Packit |
90a5c9 |
module AP_MODULE_DECLARE_DATA version_module;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* queried httpd version */
|
|
Packit |
90a5c9 |
static ap_version_t httpd_version;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* compare the supplied version with the core one
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int compare_version(char *version_string, const char **error)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *p = version_string, *ep;
|
|
Packit |
90a5c9 |
int version[3] = {0, 0, 0};
|
|
Packit |
90a5c9 |
int c = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*error = "Version appears to be invalid. It must have the format "
|
|
Packit |
90a5c9 |
"major[.minor[.patch]] where major, minor and patch are "
|
|
Packit |
90a5c9 |
"numbers.";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!apr_isdigit(*p)) {
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* parse supplied version */
|
|
Packit |
90a5c9 |
ep = version_string + strlen(version_string);
|
|
Packit |
90a5c9 |
while (p <= ep && c < 3) {
|
|
Packit |
90a5c9 |
if (*p == '.') {
|
|
Packit |
90a5c9 |
*p = '\0';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!*p) {
|
|
Packit |
90a5c9 |
version[c++] = atoi(version_string);
|
|
Packit |
90a5c9 |
version_string = ++p;
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!apr_isdigit(*p)) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
++p;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (p < ep) { /* syntax error */
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*error = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (httpd_version.major > version[0]) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (httpd_version.major < version[0]) {
|
|
Packit |
90a5c9 |
return -1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (httpd_version.minor > version[1]) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (httpd_version.minor < version[1]) {
|
|
Packit |
90a5c9 |
return -1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (httpd_version.patch > version[2]) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (httpd_version.patch < version[2]) {
|
|
Packit |
90a5c9 |
return -1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* seems to be the same */
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* match version against a regular expression
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int match_version(apr_pool_t *pool, char *version_string,
|
|
Packit |
90a5c9 |
const char **error)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_regex_t *compiled;
|
|
Packit |
90a5c9 |
const char *to_match;
|
|
Packit |
90a5c9 |
int rc;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
compiled = ap_pregcomp(pool, version_string, AP_REG_EXTENDED);
|
|
Packit |
90a5c9 |
if (!compiled) {
|
|
Packit |
90a5c9 |
*error = "Unable to compile regular expression";
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*error = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
to_match = apr_psprintf(pool, "%d.%d.%d%s",
|
|
Packit |
90a5c9 |
httpd_version.major,
|
|
Packit |
90a5c9 |
httpd_version.minor,
|
|
Packit |
90a5c9 |
httpd_version.patch,
|
|
Packit |
90a5c9 |
httpd_version.add_string);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rc = !ap_regexec(compiled, to_match, 0, NULL, 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_pregfree(pool, compiled);
|
|
Packit |
90a5c9 |
return rc;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Implements the <IfVersion> container
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static const char *start_ifversion(cmd_parms *cmd, void *mconfig,
|
|
Packit |
90a5c9 |
const char *arg1, const char *arg2,
|
|
Packit |
90a5c9 |
const char *arg3)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *endp;
|
|
Packit |
90a5c9 |
int reverse = 0, done = 0, match = 0, compare;
|
|
Packit |
90a5c9 |
const char *p, *error;
|
|
Packit |
90a5c9 |
char c;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* supplying one argument is possible, we assume an equality check then */
|
|
Packit |
90a5c9 |
if (!arg2) {
|
|
Packit |
90a5c9 |
arg2 = arg1;
|
|
Packit |
90a5c9 |
arg1 = "=";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* surrounding quotes without operator */
|
|
Packit |
90a5c9 |
if (!arg3 && *arg2 == '>' && !arg2[1]) {
|
|
Packit |
90a5c9 |
arg3 = ">";
|
|
Packit |
90a5c9 |
arg2 = arg1;
|
|
Packit |
90a5c9 |
arg1 = "=";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* the third argument makes version surrounding quotes plus operator
|
|
Packit |
90a5c9 |
* possible.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
endp = arg2 + strlen(arg2);
|
|
Packit |
90a5c9 |
if ( endp == arg2
|
|
Packit |
90a5c9 |
|| (!(arg3 && *arg3 == '>' && !arg3[1]) && *--endp != '>')) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->pool, cmd->cmd->name,
|
|
Packit |
90a5c9 |
"> directive missing closing '>'", NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
p = arg1;
|
|
Packit |
90a5c9 |
if (*p == '!') {
|
|
Packit |
90a5c9 |
reverse = 1;
|
|
Packit |
90a5c9 |
if (p[1]) {
|
|
Packit |
90a5c9 |
++p;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
c = *p++;
|
|
Packit |
90a5c9 |
if (!*p || (*p == '=' && !p[1] && c != '~')) {
|
|
Packit |
90a5c9 |
if (!httpd_version.major) {
|
|
Packit |
90a5c9 |
ap_get_server_revision(&httpd_version);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
done = 1;
|
|
Packit |
90a5c9 |
switch (c) {
|
|
Packit |
90a5c9 |
case '=':
|
|
Packit |
90a5c9 |
/* normal comparison */
|
|
Packit |
90a5c9 |
if (*arg2 != '/') {
|
|
Packit |
90a5c9 |
compare = compare_version(apr_pstrmemdup(cmd->temp_pool, arg2,
|
|
Packit |
90a5c9 |
endp-arg2),
|
|
Packit |
90a5c9 |
&error);
|
|
Packit |
90a5c9 |
if (error) {
|
|
Packit |
90a5c9 |
return error;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
match = !compare;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* regexp otherwise */
|
|
Packit |
90a5c9 |
if (endp == ++arg2 || *--endp != '/') {
|
|
Packit |
90a5c9 |
return "Missing delimiting / of regular expression.";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '~':
|
|
Packit |
90a5c9 |
/* regular expression */
|
|
Packit |
90a5c9 |
match = match_version(cmd->temp_pool,
|
|
Packit |
90a5c9 |
apr_pstrmemdup(cmd->temp_pool, arg2,
|
|
Packit |
90a5c9 |
endp-arg2),
|
|
Packit |
90a5c9 |
&error);
|
|
Packit |
90a5c9 |
if (error) {
|
|
Packit |
90a5c9 |
return error;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '<':
|
|
Packit |
90a5c9 |
compare = compare_version(apr_pstrmemdup(cmd->temp_pool, arg2,
|
|
Packit |
90a5c9 |
endp-arg2),
|
|
Packit |
90a5c9 |
&error);
|
|
Packit |
90a5c9 |
if (error) {
|
|
Packit |
90a5c9 |
return error;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
match = ((-1 == compare) || (*p && !compare));
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '>':
|
|
Packit |
90a5c9 |
compare = compare_version(apr_pstrmemdup(cmd->temp_pool, arg2,
|
|
Packit |
90a5c9 |
endp-arg2),
|
|
Packit |
90a5c9 |
&error);
|
|
Packit |
90a5c9 |
if (error) {
|
|
Packit |
90a5c9 |
return error;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
match = ((1 == compare) || (*p && !compare));
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
done = 0;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!done) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->pool, "unrecognized operator '", arg1, "'",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((!reverse && match) || (reverse && !match)) {
|
|
Packit |
90a5c9 |
ap_directive_t *parent = NULL;
|
|
Packit |
90a5c9 |
ap_directive_t *current = NULL;
|
|
Packit |
90a5c9 |
const char *retval;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
|
|
Packit |
90a5c9 |
¤t, &parent, "
|
|
Packit |
90a5c9 |
*(ap_directive_t **)mconfig = current;
|
|
Packit |
90a5c9 |
return retval;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*(ap_directive_t **)mconfig = NULL;
|
|
Packit |
90a5c9 |
return ap_soak_end_container(cmd, "
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const command_rec version_cmds[] = {
|
|
Packit |
90a5c9 |
AP_INIT_TAKE123("
|
|
Packit |
90a5c9 |
"a comparison operator, a version (and a delimiter)"),
|
|
Packit |
90a5c9 |
{ NULL }
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_MODULE(version) =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
STANDARD20_MODULE_STUFF,
|
|
Packit |
90a5c9 |
NULL, /* dir config creater */
|
|
Packit |
90a5c9 |
NULL, /* dir merger --- default is to override */
|
|
Packit |
90a5c9 |
NULL, /* server config */
|
|
Packit |
90a5c9 |
NULL, /* merge server configs */
|
|
Packit |
90a5c9 |
version_cmds, /* command apr_table_t */
|
|
Packit |
90a5c9 |
NULL, /* register hooks */
|
|
Packit |
90a5c9 |
};
|