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