Blame modules/metadata/mod_version.c

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
                                      &current, &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
};