#define AP_NOTE_DIRECTORY_WALK 0
#define AP_NOTE_LOCATION_WALK 1
#define AP_NOTE_FILE_WALK 2
-#define AP_NUM_STD_NOTES 3
+#define AP_NOTE_IF_WALK 3
+#define AP_NUM_STD_NOTES 4
/**
* Reserve an element in the core_request_config->notes array
/* Access control */
apr_array_header_t *sec_file;
+ apr_array_header_t *sec_if;
ap_regex_t *r;
const char *mime_type; /* forced with ForceType */
AP_CORE_DECLARE(void) ap_add_per_dir_conf(server_rec *s, void *dir_config);
AP_CORE_DECLARE(void) ap_add_per_url_conf(server_rec *s, void *url_config);
AP_CORE_DECLARE(void) ap_add_file_conf(core_dir_config *conf, void *url_config);
+AP_CORE_DECLARE(void) ap_add_if_conf(core_dir_config *conf, void *url_config);
AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy, const char *arg);
/* Core filters; not exported. */
conf->limit_req_body = AP_LIMIT_REQ_BODY_UNSET;
conf->limit_xml_body = AP_LIMIT_UNSET;
conf->sec_file = apr_array_make(a, 2, sizeof(ap_conf_vector_t *));
+ conf->sec_if = apr_array_make(a, 2, sizeof(ap_conf_vector_t *));
conf->server_signature = srv_sig_unset;
/* Otherwise we simply use the base->sec_file array
*/
+ if (!conf->sec_if) {
+ conf->sec_if = new->sec_if;
+ }
+ else if (new->sec_if) {
+ /* If we merge, the merge-result must have it's own array
+ */
+ conf->sec_if = apr_array_append(a, base->sec_if, new->sec_if);
+ }
+ /* Otherwise we simply use the base->sec_if array
+ */
+
if (new->server_signature != srv_sig_unset) {
conf->server_signature = new->server_signature;
}
*new_space = url_config;
}
+AP_CORE_DECLARE(void) ap_add_if_conf(core_dir_config *conf, void *url_config)
+{
+ void **new_space = (void **)apr_array_push(conf->sec_if);
+
+ *new_space = url_config;
+}
+
+
/* We need to do a stable sort, qsort isn't stable. So to make it stable
* we'll be maintaining the original index into the list, and using it
* as the minor key during sorting. The major key is the number of
core_dir_config *conf;
const command_rec *thiscmd = cmd->cmd;
ap_conf_vector_t *new_file_conf = ap_create_per_dir_config(cmd->pool);
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_LOCATION | NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
const char *condition;
const char *expr_err;
conf->d_is_fnmatch = 0;
conf->r = NULL;
- ap_add_file_conf((core_dir_config *)mconfig, new_file_conf);
+ ap_add_if_conf((core_dir_config *)mconfig, new_file_conf);
if (*arg != '\0') {
return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
if ((access_status = ap_location_walk(r))) {
return access_status;
}
+ if ((access_status = ap_if_walk(r))) {
+ return access_status;
+ }
d = ap_get_module_config(r->per_dir_config, &core_module);
if (d->log) {
if ((access_status = ap_location_walk(r))) {
return access_status;
}
+ if ((access_status = ap_if_walk(r))) {
+ return access_status;
+ }
d = ap_get_module_config(r->per_dir_config, &core_module);
if (d->log) {
* really try them with the most general first.
*/
for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
- const char *err = NULL;
core_dir_config *entry_core;
entry_core = ap_get_module_config(sec_ent[sec_idx], &core_module);
- if (entry_core->condition) {
- /* XXX: error handling */
- if (ap_expr_exec(r, entry_core->condition, &err) <= 0) {
- continue;
- }
- }
- else {
- if (entry_core->r
- ? ap_regexec(entry_core->r, cache->cached , 0, NULL, 0)
- : (entry_core->d_is_fnmatch
- ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
- : strcmp(entry_core->d, cache->cached))) {
- continue;
- }
+ if (entry_core->r
+ ? ap_regexec(entry_core->r, cache->cached , 0, NULL, 0)
+ : (entry_core->d_is_fnmatch
+ ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
+ : strcmp(entry_core->d, cache->cached))) {
+ continue;
}
/* If we merged this same section last time, reuse it
return OK;
}
+AP_DECLARE(int) ap_if_walk(request_rec *r)
+{
+ ap_conf_vector_t *now_merged = NULL;
+ core_dir_config *dconf = ap_get_module_config(r->per_dir_config,
+ &core_module);
+ ap_conf_vector_t **sec_ent = (ap_conf_vector_t **)dconf->sec_if->elts;
+ int num_sec = dconf->sec_if->nelts;
+ walk_cache_t *cache;
+ int cached;
+ int sec_idx;
+ int matches;
+ int cached_matches;
+ walk_walked_t *last_walk;
+
+ /* No tricks here, there are just no <If > to parse in this context.
+ * We won't destroy the cache, just in case _this_ redirect is later
+ * redirected again to a context containing the same or similar <If >.
+ */
+ if (!num_sec) {
+ return OK;
+ }
+
+ cache = prep_walk_cache(AP_NOTE_IF_WALK, r);
+ cached = (cache->cached != NULL);
+ cache->cached = (void *)1;
+ matches = cache->walked->nelts;
+ cached_matches = matches;
+ last_walk = (walk_walked_t*)cache->walked->elts;
+
+ cached &= auth_internal_per_conf;
+
+ /* Go through the if entries, and check for matches */
+ for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
+ const char *err = NULL;
+ core_dir_config *entry_core;
+ int rc;
+ entry_core = ap_get_module_config(sec_ent[sec_idx], &core_module);
+
+ rc = ap_expr_exec(r, entry_core->condition, &err);
+ if (rc <= 0) {
+ if (rc < 0)
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Failed to evaluate <If > condition: %s",
+ err);
+ continue;
+ }
+
+ /* If we merged this same section last time, reuse it
+ */
+ if (matches) {
+ if (last_walk->matched == sec_ent[sec_idx]) {
+ now_merged = last_walk->merged;
+ ++last_walk;
+ --matches;
+ continue;
+ }
+
+ /* We fell out of sync. This is our own copy of walked,
+ * so truncate the remaining matches and reset remaining.
+ */
+ cache->walked->nelts -= matches;
+ matches = 0;
+ cached = 0;
+ }
+
+ if (now_merged) {
+ now_merged = ap_merge_per_dir_configs(r->pool,
+ now_merged,
+ sec_ent[sec_idx]);
+ }
+ else {
+ now_merged = sec_ent[sec_idx];
+ }
+
+ last_walk = (walk_walked_t*)apr_array_push(cache->walked);
+ last_walk->matched = sec_ent[sec_idx];
+ last_walk->merged = now_merged;
+ }
+
+ /* Everything matched in sequence, but it may be that the original
+ * walk found some additional matches (which we need to truncate), or
+ * this walk found some additional matches.
+ */
+ if (matches) {
+ cache->walked->nelts -= matches;
+ cached = 0;
+ }
+ else if (cache->walked->nelts > cached_matches) {
+ cached = 0;
+ }
+
+ if (cached
+ && r->per_dir_config == cache->dir_conf_merged) {
+ r->per_dir_config = cache->per_dir_result;
+ return OK;
+ }
+
+ cache->dir_conf_tested = sec_ent;
+ cache->dir_conf_merged = r->per_dir_config;
+
+ /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs,
+ * and note the end result to (potentially) skip this step next time.
+ */
+ if (now_merged) {
+ r->per_dir_config = ap_merge_per_dir_configs(r->pool,
+ r->per_dir_config,
+ now_merged);
+ }
+ cache->per_dir_result = r->per_dir_config;
+
+ return OK;
+}
+
/*****************************************************************
*
* The sub_request mechanism.