From: Graham Leggett Date: Sun, 21 Mar 2010 18:36:59 +0000 (+0000) Subject: core: Support wildcards in both the directory and file components of X-Git-Tag: 2.2.16~100 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f8786b674414ad16fae37ba85b3fc788c4a3c66f;p=thirdparty%2Fapache%2Fhttpd.git core: Support wildcards in both the directory and file components of the path specified by the Include directive. Submitted by: minfrin, poirier Reviewed by: minfrin, jim, poirier git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@925858 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 419b9379d68..3fd21667a34 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.2.16 + *) core: Support wildcards in both the directory and file components of + the path specified by the Include directive. [Graham Leggett, Dan + Poirier] Changes with Apache 2.2.15 diff --git a/STATUS b/STATUS index 29826f3cd8c..13cc760f270 100644 --- a/STATUS +++ b/STATUS @@ -86,15 +86,6 @@ RELEASE SHOWSTOPPERS: PATCHES ACCEPTED TO BACKPORT FROM TRUNK: [ start all new proposals below, under PATCHES PROPOSED. ] - * core: Support wildcards in both the directory and file components of - the path specified by the Include directive. - Trunk patch: http://svn.apache.org/viewvc?rev=909878&view=rev - http://svn.apache.org/viewvc?rev=917735&view=rev - http://svn.apache.org/viewvc?rev=917759&view=rev - 2.2.x patch: http://people.apache.org/~minfrin/httpd-wildcard+docs2.patch - Submitted by: minfrin, poirier - +1: minfrin, jim, poirier - PATCHES PROPOSED TO BACKPORT FROM TRUNK: [ New proposals should be added at the end of the list ] diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml index 12c37e2dad1..3a1d8dccde1 100644 --- a/docs/manual/mod/core.xml +++ b/docs/manual/mod/core.xml @@ -1513,20 +1513,33 @@ the server configuration files server configvirtual host directory -Wildcard matching available in 2.0.41 and later +Wildcard matching available in 2.0.41 and later, directory +wildcard matching available in 2.3.6 and later

This directive allows inclusion of other configuration files from within the server configuration files.

-

Shell-style (fnmatch()) wildcard characters can be used to - include several files at once, in alphabetical order. In - addition, if Include points to a directory, - rather than a file, Apache will read all files in that directory - and any subdirectory. But including entire directories is not - recommended, because it is easy to accidentally leave temporary - files in a directory that can cause httpd to - fail.

+

Shell-style (fnmatch()) wildcard characters can be used + in the filename or directory parts of the path to include several files + at once, in alphabetical order. In addition, if + Include points to a directory, rather than a file, + Apache will read all files in that directory and any subdirectory. + However, including entire directories is not recommended, because it is + easy to accidentally leave temporary files in a directory that can cause + httpd to fail. Instead, we encourage you to use the + wildcard syntax shown below, to include files that match a particular + pattern, such as *.conf, for example.

+ +

When a wildcard is specified for a file or directory component of the + path, and no file or directory matches the wildcard, the + Include directive will be + silently ignored. When a directory or file component of the path is + specified exactly, and that directory or file does not exist, + Include directive will fail with an + error saying the file or directory cannot be found. This removes the need + for placeholder files to exist so that at least one file or directory is + found by the wildcard.

The file path specified may be an absolute path, or may be relative to the ServerRoot directory.

diff --git a/server/config.c b/server/config.c index 101d0e4ed06..00a0d031dcf 100644 --- a/server/config.c +++ b/server/config.c @@ -1528,7 +1528,7 @@ static const char *process_command_config(server_rec *s, } typedef struct { - char *fname; + const char *fname; } fnames; static int fname_alphasort(const void *fn1, const void *fn2) @@ -1639,6 +1639,107 @@ static const char *process_resource_config_nofnmatch(server_rec *s, return NULL; } +static const char *process_resource_config_fnmatch(server_rec *s, + const char *path, + const char *fname, + ap_directive_t **conftree, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth) +{ + char *rest; + apr_status_t rv; + apr_dir_t *dirp; + apr_finfo_t dirent; + apr_array_header_t *candidates = NULL; + fnames *fnew; + int current; + + /* find the first part of the filename */ + rest = ap_strchr(fname, '/'); + if (rest) { + fname = apr_pstrndup(ptemp, fname, rest - fname); + rest++; + } + + /* optimisation - if the filename isn't a wildcard, process it directly */ + if (!apr_fnmatch_test(fname)) { + path = ap_make_full_path(ptemp, path, fname); + if (!rest) { + return process_resource_config_nofnmatch(s, path, + conftree, p, + ptemp, 0); + } + else { + return process_resource_config_fnmatch(s, path, rest, + conftree, p, + ptemp, 0); + } + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..") + && (apr_fnmatch(fname, dirent.name, + APR_FNM_PERIOD) == APR_SUCCESS)) { + const char *full_path = ap_make_full_path(ptemp, path, dirent.name); + /* If matching internal to path, and we happen to match something + * other than a directory, skip it + */ + if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) { + continue; + } + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = full_path; + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + const char *error; + + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + if (!rest) { + error = process_resource_config_nofnmatch(s, fnew->fname, + conftree, p, + ptemp, 0); + } + else { + error = process_resource_config_fnmatch(s, fnew->fname, rest, + conftree, p, + ptemp, 0); + } + if (error) { + return error; + } + } + } + + return NULL; +} + AP_DECLARE(const char *) ap_process_resource_config(server_rec *s, const char *fname, ap_directive_t **conftree, @@ -1663,80 +1764,24 @@ AP_DECLARE(const char *) ap_process_resource_config(server_rec *s, 0); } else { - apr_dir_t *dirp; - apr_finfo_t dirent; - int current; - apr_array_header_t *candidates = NULL; - fnames *fnew; - apr_status_t rv; - char *path = apr_pstrdup(p, fname), *pattern = NULL; - - pattern = ap_strrchr(path, '/'); + apr_status_t status; + const char *rootpath, *filepath = fname; - AP_DEBUG_ASSERT(pattern != NULL); /* path must be absolute. */ + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, ptemp); - *pattern++ = '\0'; - - if (apr_fnmatch_test(path)) { - return apr_pstrcat(p, "Wildcard patterns not allowed in Include ", - fname, NULL); + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL); } - - if (!ap_is_directory(p, path)){ - return apr_pstrcat(p, "Include directory '", path, "' not found", - NULL); - } - - if (!apr_fnmatch_test(pattern)) { - return apr_pstrcat(p, "Must include a wildcard pattern for " - "Include ", fname, NULL); - } - - /* - * first course of business is to grok all the directory - * entries here and store 'em away. Recall we need full pathnames - * for this. - */ - rv = apr_dir_open(&dirp, path, p); - if (rv != APR_SUCCESS) { - char errmsg[120]; - return apr_psprintf(p, "Could not open config directory %s: %s", - path, apr_strerror(rv, errmsg, sizeof errmsg)); - } - - candidates = apr_array_make(p, 1, sizeof(fnames)); - while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { - /* strip out '.' and '..' */ - if (strcmp(dirent.name, ".") - && strcmp(dirent.name, "..") - && (apr_fnmatch(pattern, dirent.name, - APR_FNM_PERIOD) == APR_SUCCESS)) { - fnew = (fnames *) apr_array_push(candidates); - fnew->fname = ap_make_full_path(p, path, dirent.name); - } + else if (APR_EBADPATH == status) { + return apr_pstrcat(p, "Include has a bad path, ", fname, NULL); } - apr_dir_close(dirp); - if (candidates->nelts != 0) { - const char *error; - - qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); + /* walk the filepath */ + return process_resource_config_fnmatch(s, rootpath, filepath, conftree, p, ptemp, + 0); - /* - * Now recurse these... we handle errors and subdirectories - * via the recursion, which is nice - */ - for (current = 0; current < candidates->nelts; ++current) { - fnew = &((fnames *) candidates->elts)[current]; - error = process_resource_config_nofnmatch(s, fnew->fname, - conftree, p, - ptemp, 0); - if (error) { - return error; - } - } - } } return NULL;