For cases like HttpProtocolOptions where a new directive is introduced 
to multiple active branches simultaneously, it gets awkward to use 
<IfVersion> to write conf files which use the new directive but are 
compatible across multiple versions.

Triggered by a conversation with a user, but also e.g. see current test 
suite t/conf/extra.conf.in which breaks for 2.4 releases older than 
2.4.25 with:

  <IfVersion >= 2.2.32>
    <VirtualHost _default_:http_strict>
      DocumentRoot @SERVERROOT@/htdocs/
      HttpProtocolOptions Strict Require1.0 RegisteredMethods

Any reason <IfDirective> is a bad idea, so we can do that more cleanly 
(... in a couple of decades time)?

Regards, Joe
Index: server/config.c
===================================================================
--- server/config.c     (revision 1783943)
+++ server/config.c     (working copy)
@@ -2668,6 +2668,16 @@
         printf("  %s\n", ap_loaded_modules[n]->name);
 }
 
+AP_DECLARE(int) ap_exists_directive(apr_pool_t *p, const char *name)
+{
+    char *lname = apr_pstrdup(p, name);
+
+    ap_str_tolower(lname);
+    
+    return ap_config_hash &&
+        apr_hash_get(ap_config_hash, lname, APR_HASH_KEY_STRING) != NULL;
+}
+
 AP_DECLARE(void *) ap_retained_data_get(const char *key)
 {
     void *retained;
Index: include/http_config.h
===================================================================
--- include/http_config.h       (revision 1783943)
+++ include/http_config.h       (working copy)
@@ -992,6 +992,12 @@
 AP_DECLARE(void) ap_show_directives(void);
 
 /**
+ * Returns non-zero if a configuration directive of the given name has
+ * been registered by a module at the time of calling.
+ */
+AP_DECLARE(int) ap_exists_directive(apr_pool_t *p, const char *name);
+
+/**
  * Show the preloaded module names.  Used for httpd -l.
  */
 AP_DECLARE(void) ap_show_modules(void);
Index: server/core.c
===================================================================
--- server/core.c       (revision 1783943)
+++ server/core.c       (working copy)
@@ -2894,6 +2894,46 @@
     }
 }
 
+
+static const char *start_ifdirective(cmd_parms *cmd, void *dummy, const char 
*arg)
+{
+    const char *endp;
+    int defined;
+    int not = 0;
+
+    endp = ap_strrchr_c(arg, '>');
+    if (endp == NULL) {
+        return unclosed_directive(cmd);
+    }
+
+    arg = apr_pstrmemdup(cmd->temp_pool, arg, endp - arg);
+
+    if (arg[0] == '!') {
+        not = 1;
+        arg++;
+    }
+
+    if (!arg[0]) {
+        return missing_container_arg(cmd);
+    }
+
+    defined = ap_exists_directive(cmd->temp_pool, arg);
+    if ((!not && defined) || (not && !defined)) {
+        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, "<IfDirective");
+        *(ap_directive_t **)dummy = current;
+        return retval;
+    }
+    else {
+        *(ap_directive_t **)dummy = NULL;
+        return ap_soak_end_container(cmd, "<IfDirective");
+    }
+}
+
 /* httpd.conf commands... beginning with the <VirtualHost> business */
 
 static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
@@ -4509,6 +4549,8 @@
   "Container for directives based on existence of command line defines"),
 AP_INIT_TAKE1("<IfFile", start_iffile, NULL, EXEC_ON_READ | OR_ALL,
   "Container for directives based on existence of files on disk"),
+AP_INIT_TAKE1("<IfDirective", start_ifdirective, NULL, EXEC_ON_READ | OR_ALL,
+  "Container for directives based on existence of named directive"),
 AP_INIT_RAW_ARGS("<DirectoryMatch", dirsection, (void*)1, RSRC_CONF,
   "Container for directives affecting resources located in the "
   "specified directories"),

Reply via email to