]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Another chunk of code from http to core. This should continue to build
authorRyan Bloom <rbb@apache.org>
Mon, 5 Mar 2001 04:43:56 +0000 (04:43 +0000)
committerRyan Bloom <rbb@apache.org>
Mon, 5 Mar 2001 04:43:56 +0000 (04:43 +0000)
on all platforms.  The next job is to shuffle functions back and forth
so that the server builds without mod_http.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88453 13f79535-47bb-0310-9956-ffa450edef68

include/http_request.h
libhttpd.dsp
modules/http/http_protocol.c
modules/http/http_request.c
server/Makefile.in
server/request.c [new file with mode: 0644]

index 867a3a98a2a3c24ce709d897f87bd637be5d3d22..6e9fb642f09e4a6ad0de760d8be9769b037eb0ef 100644 (file)
@@ -304,6 +304,10 @@ AP_DECLARE_HOOK(int,auth_checker,(request_rec *r))
  */
 AP_DECLARE_HOOK(void,insert_filter,(request_rec *r))
 
+AP_DECLARE(int) directory_walk(request_rec *r);
+AP_DECLARE(int) location_walk(request_rec *r);
+AP_DECLARE(int) file_walk(request_rec *r);
+
 #ifdef __cplusplus
 }
 #endif
index ec9ba73470109467c4b890153ca932a67b98a30d..ba0a6b78f43a6e7d4ea66f26673c41fa9a41076a 100644 (file)
@@ -358,6 +358,10 @@ SOURCE=.\server\core.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\server\request.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\server\protocol.c
 # End Source File
 # Begin Source File
index e6501ffaba96338c74c78b5df74843f7491c13ac..8abd5ca82aae52e8259431564660e515df38cc29 100644 (file)
@@ -853,176 +853,6 @@ static void terminate_header(apr_bucket_brigade *bb)
     apr_brigade_puts(bb, NULL, NULL, crlf);
 }
 
-/*
- * Create a new method list with the specified number of preallocated
- * extension slots.
- */
-AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
-{
-    ap_method_list_t *ml;
-
-    ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
-    ml->method_mask = 0;
-    ml->method_list = apr_array_make(p, sizeof(char *), nelts);
-    return ml;
-}
-
-/*
- * Make a copy of a method list (primarily for subrequests that may
- * subsequently change it; don't want them changing the parent's, too!).
- */
-AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
-                                    ap_method_list_t *src)
-{
-    int i;
-    char **imethods;
-    char **omethods;
-
-    dest->method_mask = src->method_mask;
-    imethods = (char **) src->method_list->elts;
-    for (i = 0; i < src->method_list->nelts; ++i) {
-       omethods = (char **) apr_array_push(dest->method_list);
-       *omethods = apr_pstrdup(dest->method_list->cont, imethods[i]);
-    }
-}
-
-/*
- * Invoke a callback routine for each method in the specified list.
- */
-AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec, const char *mname,
-                                                      int mnum),
-                                         void *rec,
-                                         const ap_method_list_t *ml, ...)
-{
-    va_list vp;
-    va_start(vp, ml);
-    ap_method_list_vdo(comp, rec, ml, vp);
-    va_end(vp);  
-}
-
-AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
-                                                const char *mname,
-                                                int mnum),
-                                   void *rec, const ap_method_list_t *ml,
-                                   va_list vp)
-{
-    
-}
-
-/*
- * Return true if the specified HTTP method is in the provided
- * method list.
- */
-AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
-{
-    int methnum;
-    int i;
-    char **methods;
-
-    /*
-     * If it's one of our known methods, use the shortcut and check the
-     * bitmask.
-     */
-    methnum = ap_method_number_of(method);
-    if (methnum != M_INVALID) {
-       return (l->method_mask & (1 << methnum));
-    }
-    /*
-     * Otherwise, see if the method name is in the array or string names
-     */
-    if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
-       return 0;
-    }
-    methods = (char **)l->method_list->elts;
-    for (i = 0; i < l->method_list->nelts; ++i) {
-       if (strcmp(method, methods[i]) == 0) {
-           return 1;
-       }
-    }
-    return 0;
-}
-
-/*
- * Add the specified method to a method list (if it isn't already there).
- */
-AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
-{
-    int methnum;
-    int i;
-    const char **xmethod;
-    char **methods;
-
-    /*
-     * If it's one of our known methods, use the shortcut and use the
-     * bitmask.
-     */
-    methnum = ap_method_number_of(method);
-    l->method_mask |= (1 << methnum);
-    if (methnum != M_INVALID) {
-       return;
-    }
-    /*
-     * Otherwise, see if the method name is in the array of string names.
-     */
-    if (l->method_list->nelts != 0) {
-        methods = (char **)l->method_list->elts;
-       for (i = 0; i < l->method_list->nelts; ++i) {
-           if (strcmp(method, methods[i]) == 0) {
-               return;
-           }
-       }
-    }
-    xmethod = (const char **) apr_array_push(l->method_list);
-    *xmethod = method;
-}
-    
-/*
- * Remove the specified method from a method list.
- */
-AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
-                                      const char *method)
-{
-    int methnum;
-    char **methods;
-
-    /*
-     * If it's one of our known methods, use the shortcut and use the
-     * bitmask.
-     */
-    methnum = ap_method_number_of(method);
-    l->method_mask |= ~(1 << methnum);
-    if (methnum != M_INVALID) {
-       return;
-    }
-    /*
-     * Otherwise, see if the method name is in the array of string names.
-     */
-    if (l->method_list->nelts != 0) {
-       register int i, j, k;
-        methods = (char **)l->method_list->elts;
-       for (i = 0; i < l->method_list->nelts; ) {
-           if (strcmp(method, methods[i]) == 0) {
-               for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
-                   methods[j] = methods[k];
-               }
-               --l->method_list->nelts;
-           }
-           else {
-               ++i;
-           }
-       }
-    }
-}
-
-/*
- * Reset a method list to be completely empty.
- */
-AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
-{
-    l->method_mask = 0;
-    l->method_list->nelts = 0;
-}
-
 /* Build the Allow field-value from the request handler method mask.
  * Note that we always allow TRACE, since it is handled below.
  */
@@ -2061,3 +1891,173 @@ AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
     ap_finalize_request_protocol(r);
 }
 
+/*
+ * Create a new method list with the specified number of preallocated
+ * extension slots.
+ */
+AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
+{
+    ap_method_list_t *ml;
+    ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
+    ml->method_mask = 0;
+    ml->method_list = apr_array_make(p, sizeof(char *), nelts);
+    return ml;
+}
+/*
+ * Make a copy of a method list (primarily for subrequests that may
+ * subsequently change it; don't want them changing the parent's, too!).
+ */
+AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
+                                     ap_method_list_t *src)
+{
+    int i;
+    char **imethods;
+    char **omethods;
+    dest->method_mask = src->method_mask;
+    imethods = (char **) src->method_list->elts;
+    for (i = 0; i < src->method_list->nelts; ++i) {
+        omethods = (char **) apr_array_push(dest->method_list);
+        *omethods = apr_pstrdup(dest->method_list->cont, imethods[i]);
+    }
+}
+/*
+ * Invoke a callback routine for each method in the specified list.
+ */
+AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec, const char *mname,
+                                                       int mnum),
+                                          void *rec, 
+                                          const ap_method_list_t *ml, ...)
+{
+    va_list vp;
+    va_start(vp, ml);
+    ap_method_list_vdo(comp, rec, ml, vp);
+    va_end(vp);
+}
+AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
+                                                 const char *mname,
+                                                 int mnum),
+                                    void *rec, const ap_method_list_t *ml,
+                                    va_list vp)
+{
+}
+/*
+ * Return true if the specified HTTP method is in the provided
+ * method list.
+ */
+AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
+{
+    int methnum;
+    int i;
+    char **methods;
+    /*
+     * If it's one of our known methods, use the shortcut and check the
+     * bitmask.
+     */
+    methnum = ap_method_number_of(method);
+    if (methnum != M_INVALID) {
+        return (l->method_mask & (1 << methnum));
+    }
+    /*
+     * Otherwise, see if the method name is in the array or string names
+     */ 
+    if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
+        return 0;
+    }
+    methods = (char **)l->method_list->elts;
+    for (i = 0; i < l->method_list->nelts; ++i) {
+        if (strcmp(method, methods[i]) == 0) {
+            return 1;
+        }
+    }
+    return 0;
+}
+/*
+ * Add the specified method to a method list (if it isn't already there).
+ */
+AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
+{
+    int methnum;
+    int i;
+    const char **xmethod;
+    char **methods;
+    /*
+     * If it's one of our known methods, use the shortcut and use the
+     * bitmask.
+     */
+    methnum = ap_method_number_of(method);
+    l->method_mask |= (1 << methnum);
+    if (methnum != M_INVALID) {
+        return;
+    }
+    /*
+     * Otherwise, see if the method name is in the array of string names.
+     */
+    if (l->method_list->nelts != 0) {
+        methods = (char **)l->method_list->elts;
+        for (i = 0; i < l->method_list->nelts; ++i) {
+            if (strcmp(method, methods[i]) == 0) { 
+                return;
+            }
+        }
+    }
+    xmethod = (const char **) apr_array_push(l->method_list);
+    *xmethod = method;
+}
+/*
+ * Remove the specified method from a method list.
+ */
+AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
+                                       const char *method)
+{
+    int methnum;
+    char **methods;
+    /*
+     * If it's one of our known methods, use the shortcut and use the
+     * bitmask.
+     */
+    methnum = ap_method_number_of(method);
+    l->method_mask |= ~(1 << methnum);
+    if (methnum != M_INVALID) {
+        return;
+    }
+    /*
+     * Otherwise, see if the method name is in the array of string names.
+     */
+    if (l->method_list->nelts != 0) {
+        register int i, j, k;
+        methods = (char **)l->method_list->elts;
+        for (i = 0; i < l->method_list->nelts; ) {
+            if (strcmp(method, methods[i]) == 0) {
+                for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
+                    methods[j] = methods[k];
+                }
+                --l->method_list->nelts; 
+            }
+            else {
+                ++i;
+            }
+        }
+    }
+}
+/*
+ * Reset a method list to be completely empty.
+ */
+AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
+{
+    l->method_mask = 0;
+    l->method_list->nelts = 0;
+} 
+
index 83913d49eef81d080a116de71a0a2e86c13848db..1c7018bad58a22982dc2e2a483f522f2c290c138 100644 (file)
 #include <stdarg.h>
 #endif
 
-APR_HOOK_STRUCT(
-           APR_HOOK_LINK(translate_name)
-           APR_HOOK_LINK(check_user_id)
-           APR_HOOK_LINK(fixups)
-           APR_HOOK_LINK(type_checker)
-           APR_HOOK_LINK(access_checker)
-           APR_HOOK_LINK(auth_checker)
-           APR_HOOK_LINK(insert_filter)
-)
-
-AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
-                            (request_rec *r),(r),DECLINED)
-AP_IMPLEMENT_HOOK_RUN_FIRST(int,check_user_id,
-                            (request_rec *r),(r),DECLINED)
-AP_IMPLEMENT_HOOK_RUN_ALL(int,fixups,
-                          (request_rec *r),(r),OK,DECLINED)
-AP_IMPLEMENT_HOOK_RUN_FIRST(int,type_checker,
-                            (request_rec *r),(r),DECLINED)
-AP_IMPLEMENT_HOOK_RUN_ALL(int,access_checker,
-                          (request_rec *r),(r),OK,DECLINED)
-AP_IMPLEMENT_HOOK_RUN_FIRST(int,auth_checker,
-                            (request_rec *r),(r),DECLINED)
-AP_IMPLEMENT_HOOK_VOID(insert_filter, (request_rec *r), (r))
-
-/*****************************************************************
- *
- * Getting and checking directory configuration.  Also checks the
- * FollowSymlinks and FollowSymOwner stuff, since this is really the
- * only place that can happen (barring a new mid_dir_walk callout).
- *
- * We can't do it as an access_checker module function which gets
- * called with the final per_dir_config, since we could have a directory
- * with FollowSymLinks disabled, which contains a symlink to another
- * with a .htaccess file which turns FollowSymLinks back on --- and
- * access in such a case must be denied.  So, whatever it is that
- * checks FollowSymLinks needs to know the state of the options as
- * they change, all the way down.
- */
-
-/*
- * We don't want people able to serve up pipes, or unix sockets, or other
- * scary things.  Note that symlink tests are performed later.
- */
-static int check_safe_file(request_rec *r)
-{
-
-    if (r->finfo.filetype == 0      /* doesn't exist */
-        || r->finfo.filetype == APR_DIR
-        || r->finfo.filetype == APR_REG
-        || r->finfo.filetype == APR_LNK) {
-        return OK;
-    }
-
-    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                "object is not a file, directory or symlink: %s",
-                r->filename);
-    return HTTP_FORBIDDEN;
-}
-
-
-static int check_symlinks(char *d, int opts, apr_pool_t *p)
-{
-#if defined(OS2)
-    /* OS/2 doesn't have symlinks */
-    return OK;
-#else
-    apr_finfo_t lfi, fi;
-    char *lastp;
-    int res;
-
-    if (opts & OPT_SYM_LINKS)
-        return OK;
-
-    /*
-     * Strip trailing '/', if any, off what we're checking; trailing slashes
-     * make some systems follow symlinks to directories even in lstat().
-     * After we've done the lstat, put it back.  Also, don't bother checking
-     * '/' at all...
-     * 
-     * Note that we don't have to worry about multiple slashes here because of
-     * no2slash() below...
-     */
-
-    lastp = d + strlen(d) - 1;
-    if (lastp == d)
-        return OK;              /* Root directory, '/' */
-
-    if (*lastp == '/')
-        *lastp = '\0';
-    else
-        lastp = NULL;
-
-    res = apr_lstat(&lfi, d, APR_FINFO_TYPE | APR_FINFO_OWNER, p);
-
-    if (lastp)
-        *lastp = '/';
-
-    /*
-     * Note that we don't reject accesses to nonexistent files (multiviews or
-     * the like may cons up a way to run the transaction anyway)...
-     */
-
-    if ((res != APR_SUCCESS && res != APR_INCOMPLETE)
-           || (lfi.filetype != APR_LNK))
-        return OK;
-
-    /* OK, it's a symlink.  May still be OK with OPT_SYM_OWNER */
-
-    if (!(opts & OPT_SYM_OWNER))
-        return HTTP_FORBIDDEN;
-
-    /* OPT_SYM_OWNER only works if we can get the owner from the file */
-
-    if (res != APR_SUCCESS)
-        return HTTP_FORBIDDEN;
-
-    if (apr_stat(&fi, d, APR_FINFO_OWNER, p) != APR_SUCCESS)
-        return HTTP_FORBIDDEN;
-
-    /* TODO: replace with an apr_compare_users() fn */
-    return (fi.user == lfi.user) ? OK : HTTP_FORBIDDEN;
-
-#endif
-}
-
-/* Dealing with the file system to get PATH_INFO
- */
-static int get_path_info(request_rec *r)
-{
-    char *cp;
-    char *path = r->filename;
-    char *end = &path[strlen(path)];
-    char *last_cp = NULL;
-    int rv;
-#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
-    char bStripSlash=1;
-#endif
-
-    if (r->finfo.filetype) {
-       /* assume path_info already set */
-       return OK;
-    }
-
-#ifdef HAVE_DRIVE_LETTERS
-    /* If the directory is x:\, then we don't want to strip
-     * the trailing slash since x: is not a valid directory.
-     */
-    if (strlen(path) == 3 && path[1] == ':' && path[2] == '/')
-        bStripSlash = 0;
-#endif
-
-#ifdef HAVE_UNC_PATHS
-    /* If UNC name == //machine/share/, do not 
-     * advance over the trailing slash.  Any other
-     * UNC name is OK to strip the slash.
-     */
-    cp = end;
-    if (path[0] == '/' && path[1] == '/' && 
-        path[2] != '/' && cp[-1] == '/') {
-        char *p;
-        int iCount=0;
-        p = path;
-        while ((p = strchr(p,'/')) != NULL) {
-            p++;
-            iCount++;
-        }
-    
-        if (iCount == 4)
-            bStripSlash = 0;
-    }
-#endif
-   
-#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
-    if (bStripSlash)
-#endif
-        /* Advance over trailing slashes ... NOT part of filename 
-         * if file is not a UNC name (Win32 only).
-         */
-        for (cp = end; cp > path && cp[-1] == '/'; --cp)
-            continue;
-
-    while (cp > path) {
-
-        /* See if the pathname ending here exists... */
-        *cp = '\0';
-
-        /* ### We no longer need the test ap_os_is_filename_valid() here 
-         * since apr_stat isn't a posix thing - it's apr_stat's responsibility
-         * to handle whatever path string arrives at it's door - by platform
-         * and volume restrictions as applicable... 
-         * TODO: This code becomes even simpler if apr_stat grows 
-         * an APR_PATHINCOMPLETE result to indicate that we are staring at
-         * an partial virtual root.  Only OS2/Win32/Netware need apply it :-)
-         */
-        rv = apr_stat(&r->finfo, path, APR_FINFO_MIN, r->pool);
-
-        if (cp != end)
-            *cp = '/';
-
-        if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) {
-            /*
-             * Aha!  Found something.  If it was a directory, we will search
-             * contents of that directory for a multi_match, so the PATH_INFO
-             * argument starts with the component after that.
-             */
-            if (r->finfo.filetype == APR_DIR && last_cp) {
-                r->finfo.filetype = APR_NOFILE;  /* No such file... */
-                cp = last_cp;
-            }
-
-            r->path_info = apr_pstrdup(r->pool, cp);
-            *cp = '\0';
-            return OK;
-        }
-        
-        if (APR_STATUS_IS_ENOENT(rv) || APR_STATUS_IS_ENOTDIR(rv)) {
-            last_cp = cp;
-
-            while (--cp > path && *cp != '/')
-                continue;
-
-            while (cp > path && cp[-1] == '/')
-                --cp;
-        }
-        else {
-            if (APR_STATUS_IS_EACCES(rv))
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
-                              "access to %s denied", r->uri);
-            else
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
-                              "access to %s failed", r->uri);
-            return HTTP_FORBIDDEN;
-        }
-    }
-    return OK;
-}
-
-static int directory_walk(request_rec *r)
-{
-    core_server_config *sconf = ap_get_module_config(r->server->module_config,
-                                                     &core_module);
-    ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
-    ap_conf_vector_t **sec = (ap_conf_vector_t **) sconf->sec->elts;
-    int num_sec = sconf->sec->nelts;
-    char *test_filename;
-    char *test_dirname;
-    int res;
-    unsigned i, num_dirs;
-    int j, test_filename_len;
-#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
-    unsigned iStart = 1;
-#endif
-    ap_conf_vector_t *entry_config;
-    ap_conf_vector_t *this_conf;
-    core_dir_config *entry_core;
-
-    /*
-     * Are we dealing with a file? If not, we can (hopefuly) safely assume we
-     * have a handler that doesn't require one, but for safety's sake, and so
-     * we have something find_types() can get something out of, fake one. But
-     * don't run through the directory entries.
-     */
-
-    if (r->filename == NULL) {
-        r->filename = apr_pstrdup(r->pool, r->uri);
-        r->finfo.filetype = APR_NOFILE;
-        r->per_dir_config = per_dir_defaults;
-
-        return OK;
-    }
-
-    /*
-     * Go down the directory hierarchy.  Where we have to check for symlinks,
-     * do so.  Where a .htaccess file has permission to override anything,
-     * try to find one.  If either of these things fails, we could poke
-     * around, see why, and adjust the lookup_rec accordingly --- this might
-     * save us a call to get_path_info (with the attendant stat()s); however,
-     * for the moment, that's not worth the trouble.
-     *
-     * Fake filenames (i.e. proxy:) only match Directory sections.
-     */
-
-    if (!ap_os_is_path_absolute(r->filename))
-    {
-        const char *entry_dir;
-
-        for (j = 0; j < num_sec; ++j) {
-
-            entry_config = sec[j];
-            entry_core = ap_get_module_config(entry_config, &core_module);
-            entry_dir = entry_core->d;
-
-            this_conf = NULL;
-            if (entry_core->r) {
-                if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0))
-                    this_conf = entry_config;
-            }
-            else if (entry_core->d_is_fnmatch) {
-                if (!apr_fnmatch(entry_dir, r->filename, 0))
-                    this_conf = entry_config;
-            }
-            else if (!strncmp(r->filename, entry_dir, strlen(entry_dir)))
-                this_conf = entry_config;
-
-            if (this_conf)
-                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
-                                                            per_dir_defaults,
-                                                            this_conf);
-        }
-
-        r->per_dir_config = per_dir_defaults;
-
-        return OK;
-    }
-
-    /* XXX This needs to be rolled into APR, the APR function will not
-     * be allowed to fold the case of any non-existant segment of the path:
-     */
-    r->filename = ap_os_case_canonical_filename(r->pool, r->filename);
-
-    /* TODO This is rather silly right here, we should simply be setting
-     * filename and path_info at the end of our directory_walk
-     */
-    res = get_path_info(r);
-    if (res != OK) {
-        return res;
-    }
-
-    /* XXX This becomes moot, and will already happen above for elements
-     * that actually exist:
-     */
-    r->filename = ap_os_canonical_filename(r->pool, r->filename);
-
-    test_filename = apr_pstrdup(r->pool, r->filename);
-
-    /* XXX This becomes mute, since the APR canonical parsing will handle
-     * 2slash and dot directory issues:
-     */
-    ap_no2slash(test_filename);
-    num_dirs = ap_count_dirs(test_filename);
-
-    /* XXX This needs to be rolled into APR: */
-    if ((res = check_safe_file(r))) {
-        return res;
-    }
-
-    test_filename_len = strlen(test_filename);
-    if (test_filename[test_filename_len - 1] == '/')
-        --num_dirs;
-
-    if (r->finfo.filetype == APR_DIR)
-        ++num_dirs;
-
-    /*
-     * We will use test_dirname as scratch space while we build directory
-     * names during the walk.  Profiling shows directory_walk to be a busy
-     * function so we try to avoid allocating lots of extra memory here.
-     * We need 2 extra bytes, one for trailing \0 and one because
-     * make_dirstr_prefix will add potentially one extra /.
-     */
-    test_dirname = apr_palloc(r->pool, test_filename_len + 2);
-
-    /* XXX These exception cases go away if apr_stat() returns the
-     * APR_PATHINCOMPLETE status, so we skip hard filesystem testing
-     * of the initial 'pseudo' elements:
-     */
-
-#if defined(HAVE_UNC_PATHS)
-    /* If the name is a UNC name, then do not perform any true file test
-     * against the machine name (start at //machine/share/)
-     * This is optimized to use the normal walk (skips the redundant '/' root)
-     */
-    if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/')
-        iStart = 4;
-#endif
-
-#if defined(NETWARE)
-    /* If the name is a fully qualified volume name, then do not perform any
-     * true file test on the machine name (start at machine/share:/)
-     * XXX: The implementation eludes me at this moment... 
-     *      Does this make sense?  Please test!
-     */
-    if (num_dirs > 1 && strchr(test_filename, '/') < strchr(test_filename, ':'))
-        iStart = 2;
-#endif
-
-#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
-    /* Should match <Directory> sections starting from '/', not 'e:/' 
-     * (for example).  WIN32/OS2/NETWARE do not have a single root directory,
-     * they have one for each filesystem.  Traditionally, Apache has treated 
-     * <Directory /> permissions as the base for the whole server, and this 
-     * tradition should probably be preserved. 
-     *
-     * NOTE: MUST SYNC WITH ap_make_dirstr_prefix() CHANGE IN src/main/util.c
-     */
-    if (test_filename[0] == '/')
-        i = 1;
-    else
-        i = 0;
-#else
-    /* Normal File Systems are rooted at / */
-    i = 1;
-#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
-
-    /* j keeps track of which section we're on, see core_reorder_directories */
-    j = 0;
-    for (; i <= num_dirs; ++i) {
-        int overrides_here;
-        core_dir_config *core_dir = ap_get_module_config(per_dir_defaults,
-                                                         &core_module);
-
-        /*
-         * XXX: this could be made faster by only copying the next component
-         * rather than copying the entire thing all over.
-         */
-        ap_make_dirstr_prefix(test_dirname, test_filename, i);
-
-        /*
-         * Do symlink checks first, because they are done with the
-         * permissions appropriate to the *parent* directory...
-         */
-
-#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
-        /* Test only legal names against the real filesystem */
-        if (i >= iStart)
-#endif
-        if ((res = check_symlinks(test_dirname, core_dir->opts, r->pool))) {
-            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                        "Symbolic link not allowed: %s", test_dirname);
-            return res;
-        }
-
-        /*
-         * Begin *this* level by looking for matching <Directory> sections
-         * from access.conf.
-         */
-
-        for (; j < num_sec; ++j) {
-            char *entry_dir;
-
-            entry_config = sec[j];
-            entry_core = ap_get_module_config(entry_config, &core_module);
-            entry_dir = entry_core->d;
-
-            if (entry_core->r
-               || !ap_os_is_path_absolute(entry_dir)
-#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
-    /* To account for the top-level "/" directory when i == 0 
-     * XXX: The net test may be wrong... may fail ap_os_is_path_absolute
-     */
-                || (entry_core->d_components > 1
-                    && entry_core->d_components > i)
-#else
-                || entry_core->d_components > i
-#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
-                )
-                break;
-
-            this_conf = NULL;
-            if (entry_core->d_is_fnmatch) {
-                if (!apr_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) {
-                    this_conf = entry_config;
-                }
-            }
-            else if (!strcmp(test_dirname, entry_dir))
-                this_conf = entry_config;
-
-            if (this_conf) {
-                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
-                                                            per_dir_defaults,
-                                                            this_conf);
-                core_dir = ap_get_module_config(per_dir_defaults,
-                                                &core_module);
-            }
-#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
-            /* So that other top-level directory sections (e.g. "e:/") aren't
-             * skipped when i == 0
-             * XXX: I don't get you here, Tim... That's a level 1 section, but
-             *      we are at level 0. Did you mean fast-forward to the next?
-             */
-            else if (!i)
-                break;
-#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
-        }
-        overrides_here = core_dir->override;
-
-        /* If .htaccess files are enabled, check for one. */
-
-#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
-        /* Test only legal names against the real filesystem */
-        if (i >= iStart)
-#endif
-        if (overrides_here) {
-            ap_conf_vector_t *htaccess_conf = NULL;
-
-            res = ap_parse_htaccess(&htaccess_conf, r, overrides_here,
-                                    apr_pstrdup(r->pool, test_dirname),
-                                    sconf->access_name);
-            if (res)
-                return res;
-
-            if (htaccess_conf) {
-                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
-                                                           per_dir_defaults,
-                                                           htaccess_conf);
-               r->per_dir_config = per_dir_defaults;
-           }
-        }
-    }
-
-    /*
-     * There's two types of IS_SPECIAL sections (see http_core.c), and we've
-     * already handled the proxy:-style stuff.  Now we'll deal with the
-     * regexes.
-     */
-    for (; j < num_sec; ++j) {
-
-        entry_config = sec[j];
-        entry_core = ap_get_module_config(entry_config, &core_module);
-
-        if (entry_core->r) {
-            if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) {
-                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
-                                                            per_dir_defaults,
-                                                            entry_config);
-            }
-        }
-    }
-    r->per_dir_config = per_dir_defaults;
-
-    /*
-     * Symlink permissions are determined by the parent.  If the request is
-     * for a directory then applying the symlink test here would use the
-     * permissions of the directory as opposed to its parent.  Consider a
-     * symlink pointing to a dir with a .htaccess disallowing symlinks.  If
-     * you access /symlink (or /symlink/) you would get a 403 without this
-     * S_ISDIR test.  But if you accessed /symlink/index.html, for example,
-     * you would *not* get the 403.
-     */
-    if (r->finfo.filetype != APR_DIR
-        && (res = check_symlinks(r->filename, ap_allow_options(r), r->pool))) {
-        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                    "Symbolic link not allowed: %s", r->filename);
-        return res;
-    }
-    return OK;                  /* Can only "fail" if access denied by the
-                                 * symlink goop. */
-}
-
-static int location_walk(request_rec *r)
-{
-    core_server_config *sconf = ap_get_module_config(r->server->module_config,
-                                                     &core_module);
-    ap_conf_vector_t *per_dir_defaults = r->per_dir_config;
-    ap_conf_vector_t **url = (ap_conf_vector_t **) sconf->sec_url->elts;
-    int len, num_url = sconf->sec_url->nelts;
-    char *test_location;
-    ap_conf_vector_t *this_conf;
-    ap_conf_vector_t *entry_config;
-    core_dir_config *entry_core;
-    char *entry_url;
-    int j;
-
-    if (!num_url) {
-       return OK;
-    }
-
-    /* Location and LocationMatch differ on their behaviour w.r.t. multiple
-     * slashes.  Location matches multiple slashes with a single slash,
-     * LocationMatch doesn't.  An exception, for backwards brokenness is
-     * absoluteURIs... in which case neither match multiple slashes.
-     */
-    if (r->uri[0] != '/') {
-       test_location = r->uri;
-    }
-    else {
-       test_location = apr_pstrdup(r->pool, r->uri);
-       ap_no2slash(test_location);
-    }
-
-    /* Go through the location entries, and check for matches. */
-
-    /* we apply the directive sections in some order;
-     * should really try them with the most general first.
-     */
-    for (j = 0; j < num_url; ++j) {
-
-       entry_config = url[j];
-
-       entry_core = ap_get_module_config(entry_config, &core_module);
-       entry_url = entry_core->d;
-
-       len = strlen(entry_url);
-
-       this_conf = NULL;
-
-       if (entry_core->r) {
-           if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0))
-               this_conf = entry_config;
-       }
-       else if (entry_core->d_is_fnmatch) {
-           if (!apr_fnmatch(entry_url, test_location, FNM_PATHNAME)) {
-               this_conf = entry_config;
-           }
-       }
-       else if (!strncmp(test_location, entry_url, len) &&
-                 (entry_url[len - 1] == '/' ||
-                  test_location[len] == '/' || test_location[len] == '\0'))
-           this_conf = entry_config;
-
-       if (this_conf)
-           per_dir_defaults = ap_merge_per_dir_configs(r->pool,
-                                                        per_dir_defaults,
-                                                        this_conf);
-    }
-    r->per_dir_config = per_dir_defaults;
-
-    return OK;
-}
-
-static int file_walk(request_rec *r)
-{
-    core_dir_config *conf = ap_get_module_config(r->per_dir_config,
-                                                 &core_module);
-    ap_conf_vector_t *per_dir_defaults = r->per_dir_config;
-    ap_conf_vector_t **file = (ap_conf_vector_t **) conf->sec->elts;
-    int num_files = conf->sec->nelts;
-    char *test_file;
-
-    /* get the basename */
-    test_file = strrchr(r->filename, '/');
-    if (test_file == NULL) {
-       test_file = r->filename;
-    }
-    else {
-       ++test_file;
-    }
-
-    /* Go through the file entries, and check for matches. */
-
-    if (num_files) {
-        ap_conf_vector_t *this_conf;
-        ap_conf_vector_t *entry_config;
-        core_dir_config *entry_core;
-        char *entry_file;
-        int j;
-
-        /* we apply the directive sections in some order;
-         * should really try them with the most general first.
-         */
-        for (j = 0; j < num_files; ++j) {
-
-            entry_config = file[j];
-
-            entry_core = ap_get_module_config(entry_config, &core_module);
-            entry_file = entry_core->d;
-
-            this_conf = NULL;
-
-            if (entry_core->r) {
-                if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0))
-                    this_conf = entry_config;
-            }
-            else if (entry_core->d_is_fnmatch) {
-                if (!apr_fnmatch(entry_file, test_file, FNM_PATHNAME)) {
-                    this_conf = entry_config;
-                }
-            }
-            else if (!strcmp(test_file, entry_file)) {
-                this_conf = entry_config;
-           }
-
-            if (this_conf)
-                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
-                                                            per_dir_defaults,
-                                                            this_conf);
-        }
-        r->per_dir_config = per_dir_defaults;
-    }
-    return OK;
-}
-
-/*****************************************************************
- *
- * The sub_request mechanism.
- *
- * Fns to look up a relative URI from, e.g., a map file or SSI document.
- * These do all access checks, etc., but don't actually run the transaction
- * ... use run_sub_req below for that.  Also, be sure to use destroy_sub_req
- * as appropriate if you're likely to be creating more than a few of these.
- * (An early Apache version didn't destroy the sub_reqs used in directory
- * indexing.  The result, when indexing a directory with 800-odd files in
- * it, was massively excessive storage allocation).
- *
- * Note more manipulation of protocol-specific vars in the request
- * structure...
- */
-
-static request_rec *make_sub_request(const request_rec *r)
-{
-    apr_pool_t *rrp;
-    request_rec *rr;
-    
-    apr_pool_create(&rrp, r->pool);
-    rr = apr_pcalloc(rrp, sizeof(request_rec));
-    rr->pool = rrp;
-    return rr;
-}
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_sub_req_output_filter(ap_filter_t *f,
-                                                        apr_bucket_brigade *bb)
-{
-    apr_bucket *e = APR_BRIGADE_LAST(bb);
-
-    if (APR_BUCKET_IS_EOS(e)) {
-        apr_bucket_delete(e);
-    }
-    ap_pass_brigade(f->next, bb);
-    return APR_SUCCESS;
-}
-
-AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
-                                                const char *new_file,
-                                                const request_rec *r,
-                                                ap_filter_t *next_filter)
-{
-    request_rec *rnew;
-    int res;
-    char *udir;
-
-    rnew = make_sub_request(r);
-    rnew->hostname       = r->hostname;
-    rnew->request_time   = r->request_time;
-    rnew->connection     = r->connection;
-    rnew->server         = r->server;
-
-    rnew->request_config = ap_create_request_config(rnew->pool);
-    ap_set_module_config(rnew->request_config, &core_module,
-              ap_get_module_config(r->request_config, &core_module));
-
-    rnew->htaccess       = r->htaccess;
-    rnew->per_dir_config = r->server->lookup_defaults;
-    rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
-
-    /* make a copy of the allowed-methods list */
-    ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
-
-    /* start with the same set of output filters */
-    if (next_filter) {
-        rnew->output_filters = next_filter;
-    }
-    else {
-        rnew->output_filters = r->output_filters;
-    }
-    ap_add_output_filter("SUBREQ_CORE", NULL, rnew, rnew->connection); 
-
-    /* no input filters for a subrequest */
-
-    ap_set_sub_req_protocol(rnew, r);
-
-    /* would be nicer to pass "method" to ap_set_sub_req_protocol */
-    rnew->method = method;
-    rnew->method_number = ap_method_number_of(method);
-
-    if (new_file[0] == '/')
-        ap_parse_uri(rnew, new_file);
-    else {
-        udir = ap_make_dirstr_parent(rnew->pool, r->uri);
-        udir = ap_escape_uri(rnew->pool, udir);    /* re-escape it */
-        ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
-    }
-
-    res = ap_unescape_url(rnew->uri);
-    if (res) {
-        rnew->status = res;
-        return rnew;
-    }
-
-    ap_getparents(rnew->uri);
-
-    if ((res = location_walk(rnew))) {
-        rnew->status = res;
-        return rnew;
-    }
-
-    res = ap_run_translate_name(rnew);
-    if (res) {
-        rnew->status = res;
-        return rnew;
-    }
-
-    /*
-     * We could be clever at this point, and avoid calling directory_walk,
-     * etc. However, we'd need to test that the old and new filenames contain
-     * the same directory components, so it would require duplicating the
-     * start of translate_name. Instead we rely on the cache of .htaccess
-     * results.
-     *
-     * NB: directory_walk() clears the per_dir_config, so we don't inherit
-     * from location_walk() above
-     */
-
-    if ((res = directory_walk(rnew))
-        || (res = file_walk(rnew))
-        || (res = location_walk(rnew))
-        || ((ap_satisfies(rnew) == SATISFY_ALL
-             || ap_satisfies(rnew) == SATISFY_NOSPEC)
-            ? ((res = ap_run_access_checker(rnew))
-               || (ap_some_auth_required(rnew)
-                   && ((res = ap_run_check_user_id(rnew))
-                       || (res = ap_run_auth_checker(rnew)))))
-            : ((res = ap_run_access_checker(rnew))
-               && (!ap_some_auth_required(rnew)
-                   || ((res = ap_run_check_user_id(rnew))
-                       || (res = ap_run_auth_checker(rnew)))))
-           )
-        || (res = ap_run_type_checker(rnew))
-        || (res = ap_run_fixups(rnew))
-       ) {
-        rnew->status = res;
-    }
-    return rnew;
-}
-
-AP_DECLARE(request_rec *) ap_sub_req_lookup_uri(const char *new_file,
-                                                const request_rec *r,
-                                                ap_filter_t *next_filter)
-{
-    return ap_sub_req_method_uri("GET", new_file, r, next_filter);
-}
-
-AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
-                                              const request_rec *r,
-                                              ap_filter_t *next_filter)
-{
-    request_rec *rnew;
-    int res;
-    char *fdir;
-
-    rnew = make_sub_request(r);
-    rnew->hostname       = r->hostname;
-    rnew->request_time   = r->request_time;
-    rnew->connection     = r->connection;
-    rnew->server         = r->server;
-
-    rnew->request_config = ap_create_request_config(rnew->pool);
-    ap_set_module_config(rnew->request_config, &core_module,
-              ap_get_module_config(r->request_config, &core_module));
-
-    rnew->htaccess       = r->htaccess;
-    rnew->chunked        = r->chunked;
-    rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
-
-    /* make a copy of the allowed-methods list */
-    ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
-
-    /* start with the same set of output filters */
-    if (next_filter) {
-        rnew->output_filters = next_filter;
-    }
-    else {
-        rnew->output_filters = r->output_filters;
-    }
-    ap_add_output_filter("SUBREQ_CORE", NULL, rnew, rnew->connection); 
-
-    /* no input filters for a subrequest */
-
-    ap_set_sub_req_protocol(rnew, r);
-    fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
-
-    /*
-     * Check for a special case... if there are no '/' characters in new_file
-     * at all, then we are looking at a relative lookup in the same
-     * directory. That means we won't have to redo directory_walk, and we may
-     * not even have to redo access checks.
-     */
-
-    if (ap_strchr_c(new_file, '/') == NULL) {
-        char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
-        apr_status_t rv;
-
-        rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
-        rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
-        ap_parse_uri(rnew, rnew->uri);    /* fill in parsed_uri values */
-
-        if (((rv = apr_stat(&rnew->finfo, rnew->filename,
-                            APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
-                                                 && (rv != APR_INCOMPLETE)) {
-            rnew->finfo.filetype = 0;
-        }
-
-        if ((res = check_safe_file(rnew))) {
-            rnew->status = res;
-            return rnew;
-        }
-
-        rnew->per_dir_config = r->per_dir_config;
-
-        /*
-         * no matter what, if it's a subdirectory, we need to re-run
-         * directory_walk
-         */
-        if (rnew->finfo.filetype == APR_DIR) {
-            res = directory_walk(rnew);
-            if (!res) {
-                res = file_walk(rnew);
-            }
-        }
-        else {
-            if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew),
-                                      rnew->pool))) {
-                ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, rnew,
-                            "Symbolic link not allowed: %s", rnew->filename);
-                rnew->status = res;
-                return rnew;
-            }
-            /*
-             * do a file_walk, if it doesn't change the per_dir_config then
-             * we know that we don't have to redo all the access checks
-             */
-            if ((res = file_walk(rnew))) {
-                rnew->status = res;
-                return rnew;
-            }
-            if (rnew->per_dir_config == r->per_dir_config) {
-                if ((res = ap_run_type_checker(rnew)) || (res = ap_run_fixups(rnew))) {
-                    rnew->status = res;
-                }
-                return rnew;
-            }
-        }
-    }
-    else {
-       /* XXX: @@@: What should be done with the parsed_uri values? */
-       ap_parse_uri(rnew, new_file);   /* fill in parsed_uri values */
-        /*
-         * XXX: this should be set properly like it is in the same-dir case
-         * but it's actually sometimes to impossible to do it... because the
-         * file may not have a uri associated with it -djg
-         */
-        rnew->uri = "INTERNALLY GENERATED file-relative req";
-        rnew->filename = ((ap_os_is_path_absolute(new_file)) ?
-                          apr_pstrdup(rnew->pool, new_file) :
-                          ap_make_full_path(rnew->pool, fdir, new_file));
-        rnew->per_dir_config = r->server->lookup_defaults;
-        res = directory_walk(rnew);
-        if (!res) {
-            res = file_walk(rnew);
-        }
-    }
-
-    if (res
-        || ((ap_satisfies(rnew) == SATISFY_ALL
-             || ap_satisfies(rnew) == SATISFY_NOSPEC)
-            ? ((res = ap_run_access_checker(rnew))
-               || (ap_some_auth_required(rnew)
-                   && ((res = ap_run_check_user_id(rnew))
-                       || (res = ap_run_auth_checker(rnew)))))
-            : ((res = ap_run_access_checker(rnew))
-               && (!ap_some_auth_required(rnew)
-                   || ((res = ap_run_check_user_id(rnew))
-                       || (res = ap_run_auth_checker(rnew)))))
-           )
-        || (res = ap_run_type_checker(rnew))
-        || (res = ap_run_fixups(rnew))
-       ) {
-        rnew->status = res;
-    }
-    return rnew;
-}
-
-AP_DECLARE(int) ap_run_sub_req(request_rec *r)
-{
-    int retval;
-
-    /* see comments in process_request_internal() */
-    ap_run_insert_filter(r);
-    retval = ap_invoke_handler(r);
-    ap_finalize_sub_req_protocol(r);
-    return retval;
-}
-
-AP_DECLARE(void) ap_destroy_sub_req(request_rec *r)
-{
-    /* Reclaim the space */
-    apr_pool_destroy(r->pool);
-}
-
 /*****************************************************************
  *
  * Mainline request processing...
@@ -1203,26 +215,6 @@ static void decl_die(int status, char *phase, request_rec *r)
         ap_die(status, r);
 }
 
-AP_DECLARE(int) ap_some_auth_required(request_rec *r)
-{
-    /* Is there a require line configured for the type of *this* req? */
-
-    const apr_array_header_t *reqs_arr = ap_requires(r);
-    require_line *reqs;
-    int i;
-
-    if (!reqs_arr)
-        return 0;
-
-    reqs = (require_line *) reqs_arr->elts;
-
-    for (i = 0; i < reqs_arr->nelts; ++i)
-        if (reqs[i].method_mask & (1 << r->method_number))
-            return 1;
-
-    return 0;
-}
-
 static void process_request_internal(request_rec *r)
 {
     int access_status;
@@ -1531,28 +523,6 @@ AP_DECLARE(void) ap_internal_redirect_handler(const char *new_uri, request_rec *
     process_request_internal(new);
 }
 
-/*
- * Is it the initial main request, which we only get *once* per HTTP request?
- */
-AP_DECLARE(int) ap_is_initial_req(request_rec *r)
-{
-    return
-        (r->main == NULL)       /* otherwise, this is a sub-request */
-        &&
-        (r->prev == NULL);      /* otherwise, this is an internal redirect */
-}
-
-/*
- * Function to set the r->mtime field to the specified value if it's later
- * than what's already there.
- */
-AP_DECLARE(void) ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
-{
-    if (r->mtime < dependency_mtime) {
-       r->mtime = dependency_mtime;
-    }
-}
-
 AP_DECLARE(void) ap_allow_methods(request_rec *r, int reset, ...) 
 {
     const char *method;
index b3b0fa83fca77b57104558f247e129c34684db30..945fdbbfab1b765432d5b06bdbfe3fd5a0bea0b2 100644 (file)
@@ -13,7 +13,7 @@ LTLIBRARY_SOURCES = \
        rfc1413.c connection.c listen.c \
         mpm_common.c util_charset.c util_debug.c util_xml.c \
        util_filter.c exports.c buildmark.c scoreboard.c \
-       error_bucket.c protocol.c core.c
+       error_bucket.c protocol.c core.c request.c
 
 targets = delete-exports $(LTLIBRARY_NAME)
 
diff --git a/server/request.c b/server/request.c
new file mode 100644 (file)
index 0000000..bacf8fc
--- /dev/null
@@ -0,0 +1,1124 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * http_request.c: functions to get and process requests
+ *
+ * Rob McCool 3/21/93
+ *
+ * Thoroughly revamped by rst for Apache.  NB this file reads
+ * best from the bottom up.
+ *
+ */
+
+#include "apr_strings.h"
+#include "apr_file_io.h"
+#include "apr_fnmatch.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_filter.h"
+#include "util_charset.h"
+
+#include "mod_core.h"
+
+#if APR_HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+APR_HOOK_STRUCT(
+           APR_HOOK_LINK(translate_name)
+           APR_HOOK_LINK(check_user_id)
+           APR_HOOK_LINK(fixups)
+           APR_HOOK_LINK(type_checker)
+           APR_HOOK_LINK(access_checker)
+           APR_HOOK_LINK(auth_checker)
+           APR_HOOK_LINK(insert_filter)
+)
+
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
+                            (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,check_user_id,
+                            (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int,fixups,
+                          (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,type_checker,
+                            (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int,access_checker,
+                          (request_rec *r),(r),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,auth_checker,
+                            (request_rec *r),(r),DECLINED)
+AP_IMPLEMENT_HOOK_VOID(insert_filter, (request_rec *r), (r))
+
+/*****************************************************************
+ *
+ * Getting and checking directory configuration.  Also checks the
+ * FollowSymlinks and FollowSymOwner stuff, since this is really the
+ * only place that can happen (barring a new mid_dir_walk callout).
+ *
+ * We can't do it as an access_checker module function which gets
+ * called with the final per_dir_config, since we could have a directory
+ * with FollowSymLinks disabled, which contains a symlink to another
+ * with a .htaccess file which turns FollowSymLinks back on --- and
+ * access in such a case must be denied.  So, whatever it is that
+ * checks FollowSymLinks needs to know the state of the options as
+ * they change, all the way down.
+ */
+
+/*
+ * We don't want people able to serve up pipes, or unix sockets, or other
+ * scary things.  Note that symlink tests are performed later.
+ */
+static int check_safe_file(request_rec *r)
+{
+
+    if (r->finfo.filetype == 0      /* doesn't exist */
+        || r->finfo.filetype == APR_DIR
+        || r->finfo.filetype == APR_REG
+        || r->finfo.filetype == APR_LNK) {
+        return OK;
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                "object is not a file, directory or symlink: %s",
+                r->filename);
+    return HTTP_FORBIDDEN;
+}
+
+
+static int check_symlinks(char *d, int opts, apr_pool_t *p)
+{
+#if defined(OS2)
+    /* OS/2 doesn't have symlinks */
+    return OK;
+#else
+    apr_finfo_t lfi, fi;
+    char *lastp;
+    int res;
+
+    if (opts & OPT_SYM_LINKS)
+        return OK;
+
+    /*
+     * Strip trailing '/', if any, off what we're checking; trailing slashes
+     * make some systems follow symlinks to directories even in lstat().
+     * After we've done the lstat, put it back.  Also, don't bother checking
+     * '/' at all...
+     * 
+     * Note that we don't have to worry about multiple slashes here because of
+     * no2slash() below...
+     */
+
+    lastp = d + strlen(d) - 1;
+    if (lastp == d)
+        return OK;              /* Root directory, '/' */
+
+    if (*lastp == '/')
+        *lastp = '\0';
+    else
+        lastp = NULL;
+
+    res = apr_lstat(&lfi, d, APR_FINFO_TYPE | APR_FINFO_OWNER, p);
+
+    if (lastp)
+        *lastp = '/';
+
+    /*
+     * Note that we don't reject accesses to nonexistent files (multiviews or
+     * the like may cons up a way to run the transaction anyway)...
+     */
+
+    if ((res != APR_SUCCESS && res != APR_INCOMPLETE)
+           || (lfi.filetype != APR_LNK))
+        return OK;
+
+    /* OK, it's a symlink.  May still be OK with OPT_SYM_OWNER */
+
+    if (!(opts & OPT_SYM_OWNER))
+        return HTTP_FORBIDDEN;
+
+    /* OPT_SYM_OWNER only works if we can get the owner from the file */
+
+    if (res != APR_SUCCESS)
+        return HTTP_FORBIDDEN;
+
+    if (apr_stat(&fi, d, APR_FINFO_OWNER, p) != APR_SUCCESS)
+        return HTTP_FORBIDDEN;
+
+    /* TODO: replace with an apr_compare_users() fn */
+    return (fi.user == lfi.user) ? OK : HTTP_FORBIDDEN;
+
+#endif
+}
+
+/* Dealing with the file system to get PATH_INFO
+ */
+static int get_path_info(request_rec *r)
+{
+    char *cp;
+    char *path = r->filename;
+    char *end = &path[strlen(path)];
+    char *last_cp = NULL;
+    int rv;
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+    char bStripSlash=1;
+#endif
+
+    if (r->finfo.filetype) {
+       /* assume path_info already set */
+       return OK;
+    }
+
+#ifdef HAVE_DRIVE_LETTERS
+    /* If the directory is x:\, then we don't want to strip
+     * the trailing slash since x: is not a valid directory.
+     */
+    if (strlen(path) == 3 && path[1] == ':' && path[2] == '/')
+        bStripSlash = 0;
+#endif
+
+#ifdef HAVE_UNC_PATHS
+    /* If UNC name == //machine/share/, do not 
+     * advance over the trailing slash.  Any other
+     * UNC name is OK to strip the slash.
+     */
+    cp = end;
+    if (path[0] == '/' && path[1] == '/' && 
+        path[2] != '/' && cp[-1] == '/') {
+        char *p;
+        int iCount=0;
+        p = path;
+        while ((p = strchr(p,'/')) != NULL) {
+            p++;
+            iCount++;
+        }
+    
+        if (iCount == 4)
+            bStripSlash = 0;
+    }
+#endif
+   
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+    if (bStripSlash)
+#endif
+        /* Advance over trailing slashes ... NOT part of filename 
+         * if file is not a UNC name (Win32 only).
+         */
+        for (cp = end; cp > path && cp[-1] == '/'; --cp)
+            continue;
+
+    while (cp > path) {
+
+        /* See if the pathname ending here exists... */
+        *cp = '\0';
+
+        /* ### We no longer need the test ap_os_is_filename_valid() here 
+         * since apr_stat isn't a posix thing - it's apr_stat's responsibility
+         * to handle whatever path string arrives at it's door - by platform
+         * and volume restrictions as applicable... 
+         * TODO: This code becomes even simpler if apr_stat grows 
+         * an APR_PATHINCOMPLETE result to indicate that we are staring at
+         * an partial virtual root.  Only OS2/Win32/Netware need apply it :-)
+         */
+        rv = apr_stat(&r->finfo, path, APR_FINFO_MIN, r->pool);
+
+        if (cp != end)
+            *cp = '/';
+
+        if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) {
+            /*
+             * Aha!  Found something.  If it was a directory, we will search
+             * contents of that directory for a multi_match, so the PATH_INFO
+             * argument starts with the component after that.
+             */
+            if (r->finfo.filetype == APR_DIR && last_cp) {
+                r->finfo.filetype = APR_NOFILE;  /* No such file... */
+                cp = last_cp;
+            }
+
+            r->path_info = apr_pstrdup(r->pool, cp);
+            *cp = '\0';
+            return OK;
+        }
+        
+        if (APR_STATUS_IS_ENOENT(rv) || APR_STATUS_IS_ENOTDIR(rv)) {
+            last_cp = cp;
+
+            while (--cp > path && *cp != '/')
+                continue;
+
+            while (cp > path && cp[-1] == '/')
+                --cp;
+        }
+        else {
+            if (APR_STATUS_IS_EACCES(rv))
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                              "access to %s denied", r->uri);
+            else
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                              "access to %s failed", r->uri);
+            return HTTP_FORBIDDEN;
+        }
+    }
+    return OK;
+}
+
+AP_DECLARE(int) directory_walk(request_rec *r)
+{
+    core_server_config *sconf = ap_get_module_config(r->server->module_config,
+                                                     &core_module);
+    ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
+    ap_conf_vector_t **sec = (ap_conf_vector_t **) sconf->sec->elts;
+    int num_sec = sconf->sec->nelts;
+    char *test_filename;
+    char *test_dirname;
+    int res;
+    unsigned i, num_dirs;
+    int j, test_filename_len;
+#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
+    unsigned iStart = 1;
+#endif
+    ap_conf_vector_t *entry_config;
+    ap_conf_vector_t *this_conf;
+    core_dir_config *entry_core;
+
+    /*
+     * Are we dealing with a file? If not, we can (hopefuly) safely assume we
+     * have a handler that doesn't require one, but for safety's sake, and so
+     * we have something find_types() can get something out of, fake one. But
+     * don't run through the directory entries.
+     */
+
+    if (r->filename == NULL) {
+        r->filename = apr_pstrdup(r->pool, r->uri);
+        r->finfo.filetype = APR_NOFILE;
+        r->per_dir_config = per_dir_defaults;
+
+        return OK;
+    }
+
+    /*
+     * Go down the directory hierarchy.  Where we have to check for symlinks,
+     * do so.  Where a .htaccess file has permission to override anything,
+     * try to find one.  If either of these things fails, we could poke
+     * around, see why, and adjust the lookup_rec accordingly --- this might
+     * save us a call to get_path_info (with the attendant stat()s); however,
+     * for the moment, that's not worth the trouble.
+     *
+     * Fake filenames (i.e. proxy:) only match Directory sections.
+     */
+
+    if (!ap_os_is_path_absolute(r->filename))
+    {
+        const char *entry_dir;
+
+        for (j = 0; j < num_sec; ++j) {
+
+            entry_config = sec[j];
+            entry_core = ap_get_module_config(entry_config, &core_module);
+            entry_dir = entry_core->d;
+
+            this_conf = NULL;
+            if (entry_core->r) {
+                if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0))
+                    this_conf = entry_config;
+            }
+            else if (entry_core->d_is_fnmatch) {
+                if (!apr_fnmatch(entry_dir, r->filename, 0))
+                    this_conf = entry_config;
+            }
+            else if (!strncmp(r->filename, entry_dir, strlen(entry_dir)))
+                this_conf = entry_config;
+
+            if (this_conf)
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                            per_dir_defaults,
+                                                            this_conf);
+        }
+
+        r->per_dir_config = per_dir_defaults;
+
+        return OK;
+    }
+
+    /* XXX This needs to be rolled into APR, the APR function will not
+     * be allowed to fold the case of any non-existant segment of the path:
+     */
+    r->filename = ap_os_case_canonical_filename(r->pool, r->filename);
+
+    /* TODO This is rather silly right here, we should simply be setting
+     * filename and path_info at the end of our directory_walk
+     */
+    res = get_path_info(r);
+    if (res != OK) {
+        return res;
+    }
+
+    /* XXX This becomes moot, and will already happen above for elements
+     * that actually exist:
+     */
+    r->filename = ap_os_canonical_filename(r->pool, r->filename);
+
+    test_filename = apr_pstrdup(r->pool, r->filename);
+
+    /* XXX This becomes mute, since the APR canonical parsing will handle
+     * 2slash and dot directory issues:
+     */
+    ap_no2slash(test_filename);
+    num_dirs = ap_count_dirs(test_filename);
+
+    /* XXX This needs to be rolled into APR: */
+    if ((res = check_safe_file(r))) {
+        return res;
+    }
+
+    test_filename_len = strlen(test_filename);
+    if (test_filename[test_filename_len - 1] == '/')
+        --num_dirs;
+
+    if (r->finfo.filetype == APR_DIR)
+        ++num_dirs;
+
+    /*
+     * We will use test_dirname as scratch space while we build directory
+     * names during the walk.  Profiling shows directory_walk to be a busy
+     * function so we try to avoid allocating lots of extra memory here.
+     * We need 2 extra bytes, one for trailing \0 and one because
+     * make_dirstr_prefix will add potentially one extra /.
+     */
+    test_dirname = apr_palloc(r->pool, test_filename_len + 2);
+
+    /* XXX These exception cases go away if apr_stat() returns the
+     * APR_PATHINCOMPLETE status, so we skip hard filesystem testing
+     * of the initial 'pseudo' elements:
+     */
+
+#if defined(HAVE_UNC_PATHS)
+    /* If the name is a UNC name, then do not perform any true file test
+     * against the machine name (start at //machine/share/)
+     * This is optimized to use the normal walk (skips the redundant '/' root)
+     */
+    if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/')
+        iStart = 4;
+#endif
+
+#if defined(NETWARE)
+    /* If the name is a fully qualified volume name, then do not perform any
+     * true file test on the machine name (start at machine/share:/)
+     * XXX: The implementation eludes me at this moment... 
+     *      Does this make sense?  Please test!
+     */
+    if (num_dirs > 1 && strchr(test_filename, '/') < strchr(test_filename, ':'))
+        iStart = 2;
+#endif
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+    /* Should match <Directory> sections starting from '/', not 'e:/' 
+     * (for example).  WIN32/OS2/NETWARE do not have a single root directory,
+     * they have one for each filesystem.  Traditionally, Apache has treated 
+     * <Directory /> permissions as the base for the whole server, and this 
+     * tradition should probably be preserved. 
+     *
+     * NOTE: MUST SYNC WITH ap_make_dirstr_prefix() CHANGE IN src/main/util.c
+     */
+    if (test_filename[0] == '/')
+        i = 1;
+    else
+        i = 0;
+#else
+    /* Normal File Systems are rooted at / */
+    i = 1;
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+
+    /* j keeps track of which section we're on, see core_reorder_directories */
+    j = 0;
+    for (; i <= num_dirs; ++i) {
+        int overrides_here;
+        core_dir_config *core_dir = ap_get_module_config(per_dir_defaults,
+                                                         &core_module);
+
+        /*
+         * XXX: this could be made faster by only copying the next component
+         * rather than copying the entire thing all over.
+         */
+        ap_make_dirstr_prefix(test_dirname, test_filename, i);
+
+        /*
+         * Do symlink checks first, because they are done with the
+         * permissions appropriate to the *parent* directory...
+         */
+
+#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
+        /* Test only legal names against the real filesystem */
+        if (i >= iStart)
+#endif
+        if ((res = check_symlinks(test_dirname, core_dir->opts, r->pool))) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                        "Symbolic link not allowed: %s", test_dirname);
+            return res;
+        }
+
+        /*
+         * Begin *this* level by looking for matching <Directory> sections
+         * from access.conf.
+         */
+
+        for (; j < num_sec; ++j) {
+            char *entry_dir;
+
+            entry_config = sec[j];
+            entry_core = ap_get_module_config(entry_config, &core_module);
+            entry_dir = entry_core->d;
+
+            if (entry_core->r
+               || !ap_os_is_path_absolute(entry_dir)
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+    /* To account for the top-level "/" directory when i == 0 
+     * XXX: The net test may be wrong... may fail ap_os_is_path_absolute
+     */
+                || (entry_core->d_components > 1
+                    && entry_core->d_components > i)
+#else
+                || entry_core->d_components > i
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+                )
+                break;
+
+            this_conf = NULL;
+            if (entry_core->d_is_fnmatch) {
+                if (!apr_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) {
+                    this_conf = entry_config;
+                }
+            }
+            else if (!strcmp(test_dirname, entry_dir))
+                this_conf = entry_config;
+
+            if (this_conf) {
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                            per_dir_defaults,
+                                                            this_conf);
+                core_dir = ap_get_module_config(per_dir_defaults,
+                                                &core_module);
+            }
+#if defined(HAVE_DRIVE_LETTERS) || defined(NETWARE)
+            /* So that other top-level directory sections (e.g. "e:/") aren't
+             * skipped when i == 0
+             * XXX: I don't get you here, Tim... That's a level 1 section, but
+             *      we are at level 0. Did you mean fast-forward to the next?
+             */
+            else if (!i)
+                break;
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+        }
+        overrides_here = core_dir->override;
+
+        /* If .htaccess files are enabled, check for one. */
+
+#if defined(HAVE_UNC_PATHS) || defined(NETWARE)
+        /* Test only legal names against the real filesystem */
+        if (i >= iStart)
+#endif
+        if (overrides_here) {
+            ap_conf_vector_t *htaccess_conf = NULL;
+
+            res = ap_parse_htaccess(&htaccess_conf, r, overrides_here,
+                                    apr_pstrdup(r->pool, test_dirname),
+                                    sconf->access_name);
+            if (res)
+                return res;
+
+            if (htaccess_conf) {
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                           per_dir_defaults,
+                                                           htaccess_conf);
+               r->per_dir_config = per_dir_defaults;
+           }
+        }
+    }
+
+    /*
+     * There's two types of IS_SPECIAL sections (see http_core.c), and we've
+     * already handled the proxy:-style stuff.  Now we'll deal with the
+     * regexes.
+     */
+    for (; j < num_sec; ++j) {
+
+        entry_config = sec[j];
+        entry_core = ap_get_module_config(entry_config, &core_module);
+
+        if (entry_core->r) {
+            if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) {
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                            per_dir_defaults,
+                                                            entry_config);
+            }
+        }
+    }
+    r->per_dir_config = per_dir_defaults;
+
+    /*
+     * Symlink permissions are determined by the parent.  If the request is
+     * for a directory then applying the symlink test here would use the
+     * permissions of the directory as opposed to its parent.  Consider a
+     * symlink pointing to a dir with a .htaccess disallowing symlinks.  If
+     * you access /symlink (or /symlink/) you would get a 403 without this
+     * S_ISDIR test.  But if you accessed /symlink/index.html, for example,
+     * you would *not* get the 403.
+     */
+    if (r->finfo.filetype != APR_DIR
+        && (res = check_symlinks(r->filename, ap_allow_options(r), r->pool))) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                    "Symbolic link not allowed: %s", r->filename);
+        return res;
+    }
+    return OK;                  /* Can only "fail" if access denied by the
+                                 * symlink goop. */
+}
+
+AP_DECLARE(int) location_walk(request_rec *r)
+{
+    core_server_config *sconf = ap_get_module_config(r->server->module_config,
+                                                     &core_module);
+    ap_conf_vector_t *per_dir_defaults = r->per_dir_config;
+    ap_conf_vector_t **url = (ap_conf_vector_t **) sconf->sec_url->elts;
+    int len, num_url = sconf->sec_url->nelts;
+    char *test_location;
+    ap_conf_vector_t *this_conf;
+    ap_conf_vector_t *entry_config;
+    core_dir_config *entry_core;
+    char *entry_url;
+    int j;
+
+    if (!num_url) {
+       return OK;
+    }
+
+    /* Location and LocationMatch differ on their behaviour w.r.t. multiple
+     * slashes.  Location matches multiple slashes with a single slash,
+     * LocationMatch doesn't.  An exception, for backwards brokenness is
+     * absoluteURIs... in which case neither match multiple slashes.
+     */
+    if (r->uri[0] != '/') {
+       test_location = r->uri;
+    }
+    else {
+       test_location = apr_pstrdup(r->pool, r->uri);
+       ap_no2slash(test_location);
+    }
+
+    /* Go through the location entries, and check for matches. */
+
+    /* we apply the directive sections in some order;
+     * should really try them with the most general first.
+     */
+    for (j = 0; j < num_url; ++j) {
+
+       entry_config = url[j];
+
+       entry_core = ap_get_module_config(entry_config, &core_module);
+       entry_url = entry_core->d;
+
+       len = strlen(entry_url);
+
+       this_conf = NULL;
+
+       if (entry_core->r) {
+           if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0))
+               this_conf = entry_config;
+       }
+       else if (entry_core->d_is_fnmatch) {
+           if (!apr_fnmatch(entry_url, test_location, FNM_PATHNAME)) {
+               this_conf = entry_config;
+           }
+       }
+       else if (!strncmp(test_location, entry_url, len) &&
+                 (entry_url[len - 1] == '/' ||
+                  test_location[len] == '/' || test_location[len] == '\0'))
+           this_conf = entry_config;
+
+       if (this_conf)
+           per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                        per_dir_defaults,
+                                                        this_conf);
+    }
+    r->per_dir_config = per_dir_defaults;
+
+    return OK;
+}
+
+AP_DECLARE(int) file_walk(request_rec *r)
+{
+    core_dir_config *conf = ap_get_module_config(r->per_dir_config,
+                                                 &core_module);
+    ap_conf_vector_t *per_dir_defaults = r->per_dir_config;
+    ap_conf_vector_t **file = (ap_conf_vector_t **) conf->sec->elts;
+    int num_files = conf->sec->nelts;
+    char *test_file;
+
+    /* get the basename */
+    test_file = strrchr(r->filename, '/');
+    if (test_file == NULL) {
+       test_file = r->filename;
+    }
+    else {
+       ++test_file;
+    }
+
+    /* Go through the file entries, and check for matches. */
+
+    if (num_files) {
+        ap_conf_vector_t *this_conf;
+        ap_conf_vector_t *entry_config;
+        core_dir_config *entry_core;
+        char *entry_file;
+        int j;
+
+        /* we apply the directive sections in some order;
+         * should really try them with the most general first.
+         */
+        for (j = 0; j < num_files; ++j) {
+
+            entry_config = file[j];
+
+            entry_core = ap_get_module_config(entry_config, &core_module);
+            entry_file = entry_core->d;
+
+            this_conf = NULL;
+
+            if (entry_core->r) {
+                if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0))
+                    this_conf = entry_config;
+            }
+            else if (entry_core->d_is_fnmatch) {
+                if (!apr_fnmatch(entry_file, test_file, FNM_PATHNAME)) {
+                    this_conf = entry_config;
+                }
+            }
+            else if (!strcmp(test_file, entry_file)) {
+                this_conf = entry_config;
+           }
+
+            if (this_conf)
+                per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+                                                            per_dir_defaults,
+                                                            this_conf);
+        }
+        r->per_dir_config = per_dir_defaults;
+    }
+    return OK;
+}
+
+/*****************************************************************
+ *
+ * The sub_request mechanism.
+ *
+ * Fns to look up a relative URI from, e.g., a map file or SSI document.
+ * These do all access checks, etc., but don't actually run the transaction
+ * ... use run_sub_req below for that.  Also, be sure to use destroy_sub_req
+ * as appropriate if you're likely to be creating more than a few of these.
+ * (An early Apache version didn't destroy the sub_reqs used in directory
+ * indexing.  The result, when indexing a directory with 800-odd files in
+ * it, was massively excessive storage allocation).
+ *
+ * Note more manipulation of protocol-specific vars in the request
+ * structure...
+ */
+
+static request_rec *make_sub_request(const request_rec *r)
+{
+    apr_pool_t *rrp;
+    request_rec *rr;
+    
+    apr_pool_create(&rrp, r->pool);
+    rr = apr_pcalloc(rrp, sizeof(request_rec));
+    rr->pool = rrp;
+    return rr;
+}
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_sub_req_output_filter(ap_filter_t *f,
+                                                        apr_bucket_brigade *bb)
+{
+    apr_bucket *e = APR_BRIGADE_LAST(bb);
+
+    if (APR_BUCKET_IS_EOS(e)) {
+        apr_bucket_delete(e);
+    }
+    ap_pass_brigade(f->next, bb);
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(int) ap_some_auth_required(request_rec *r)
+{
+    /* Is there a require line configured for the type of *this* req? */
+    const apr_array_header_t *reqs_arr = ap_requires(r);
+    require_line *reqs;
+    int i;
+    if (!reqs_arr)
+        return 0;
+    reqs = (require_line *) reqs_arr->elts;
+    for (i = 0; i < reqs_arr->nelts; ++i)
+        if (reqs[i].method_mask & (1 << r->method_number))
+            return 1;
+    return 0;
+} 
+
+AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
+                                                const char *new_file,
+                                                const request_rec *r,
+                                                ap_filter_t *next_filter)
+{
+    request_rec *rnew;
+    int res;
+    char *udir;
+
+    rnew = make_sub_request(r);
+    rnew->hostname       = r->hostname;
+    rnew->request_time   = r->request_time;
+    rnew->connection     = r->connection;
+    rnew->server         = r->server;
+
+    rnew->request_config = ap_create_request_config(rnew->pool);
+    ap_set_module_config(rnew->request_config, &core_module,
+              ap_get_module_config(r->request_config, &core_module));
+
+    rnew->htaccess       = r->htaccess;
+    rnew->per_dir_config = r->server->lookup_defaults;
+    rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
+
+    /* make a copy of the allowed-methods list */
+    ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
+
+    /* start with the same set of output filters */
+    if (next_filter) {
+        rnew->output_filters = next_filter;
+    }
+    else {
+        rnew->output_filters = r->output_filters;
+    }
+    ap_add_output_filter("SUBREQ_CORE", NULL, rnew, rnew->connection); 
+
+    /* no input filters for a subrequest */
+
+    ap_set_sub_req_protocol(rnew, r);
+
+    /* would be nicer to pass "method" to ap_set_sub_req_protocol */
+    rnew->method = method;
+    rnew->method_number = ap_method_number_of(method);
+
+    if (new_file[0] == '/')
+        ap_parse_uri(rnew, new_file);
+    else {
+        udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+        udir = ap_escape_uri(rnew->pool, udir);    /* re-escape it */
+        ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
+    }
+
+    res = ap_unescape_url(rnew->uri);
+    if (res) {
+        rnew->status = res;
+        return rnew;
+    }
+
+    ap_getparents(rnew->uri);
+
+    if ((res = location_walk(rnew))) {
+        rnew->status = res;
+        return rnew;
+    }
+
+    res = ap_run_translate_name(rnew);
+    if (res) {
+        rnew->status = res;
+        return rnew;
+    }
+
+    /*
+     * We could be clever at this point, and avoid calling directory_walk,
+     * etc. However, we'd need to test that the old and new filenames contain
+     * the same directory components, so it would require duplicating the
+     * start of translate_name. Instead we rely on the cache of .htaccess
+     * results.
+     *
+     * NB: directory_walk() clears the per_dir_config, so we don't inherit
+     * from location_walk() above
+     */
+
+    if ((res = directory_walk(rnew))
+        || (res = file_walk(rnew))
+        || (res = location_walk(rnew))
+        || ((ap_satisfies(rnew) == SATISFY_ALL
+             || ap_satisfies(rnew) == SATISFY_NOSPEC)
+            ? ((res = ap_run_access_checker(rnew))
+               || (ap_some_auth_required(rnew)
+                   && ((res = ap_run_check_user_id(rnew))
+                       || (res = ap_run_auth_checker(rnew)))))
+            : ((res = ap_run_access_checker(rnew))
+               && (!ap_some_auth_required(rnew)
+                   || ((res = ap_run_check_user_id(rnew))
+                       || (res = ap_run_auth_checker(rnew)))))
+           )
+        || (res = ap_run_type_checker(rnew))
+        || (res = ap_run_fixups(rnew))
+       ) {
+        rnew->status = res;
+    }
+    return rnew;
+}
+
+AP_DECLARE(request_rec *) ap_sub_req_lookup_uri(const char *new_file,
+                                                const request_rec *r,
+                                                ap_filter_t *next_filter)
+{
+    return ap_sub_req_method_uri("GET", new_file, r, next_filter);
+}
+
+AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
+                                              const request_rec *r,
+                                              ap_filter_t *next_filter)
+{
+    request_rec *rnew;
+    int res;
+    char *fdir;
+
+    rnew = make_sub_request(r);
+    rnew->hostname       = r->hostname;
+    rnew->request_time   = r->request_time;
+    rnew->connection     = r->connection;
+    rnew->server         = r->server;
+
+    rnew->request_config = ap_create_request_config(rnew->pool);
+    ap_set_module_config(rnew->request_config, &core_module,
+              ap_get_module_config(r->request_config, &core_module));
+
+    rnew->htaccess       = r->htaccess;
+    rnew->chunked        = r->chunked;
+    rnew->allowed_methods = ap_make_method_list(rnew->pool, 2);
+
+    /* make a copy of the allowed-methods list */
+    ap_copy_method_list(rnew->allowed_methods, r->allowed_methods);
+
+    /* start with the same set of output filters */
+    if (next_filter) {
+        rnew->output_filters = next_filter;
+    }
+    else {
+        rnew->output_filters = r->output_filters;
+    }
+    ap_add_output_filter("SUBREQ_CORE", NULL, rnew, rnew->connection); 
+
+    /* no input filters for a subrequest */
+
+    ap_set_sub_req_protocol(rnew, r);
+    fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
+
+    /*
+     * Check for a special case... if there are no '/' characters in new_file
+     * at all, then we are looking at a relative lookup in the same
+     * directory. That means we won't have to redo directory_walk, and we may
+     * not even have to redo access checks.
+     */
+
+    if (ap_strchr_c(new_file, '/') == NULL) {
+        char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+        apr_status_t rv;
+
+        rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
+        rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
+        ap_parse_uri(rnew, rnew->uri);    /* fill in parsed_uri values */
+
+        if (((rv = apr_stat(&rnew->finfo, rnew->filename,
+                            APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
+                                                 && (rv != APR_INCOMPLETE)) {
+            rnew->finfo.filetype = 0;
+        }
+
+        if ((res = check_safe_file(rnew))) {
+            rnew->status = res;
+            return rnew;
+        }
+
+        rnew->per_dir_config = r->per_dir_config;
+
+        /*
+         * no matter what, if it's a subdirectory, we need to re-run
+         * directory_walk
+         */
+        if (rnew->finfo.filetype == APR_DIR) {
+            res = directory_walk(rnew);
+            if (!res) {
+                res = file_walk(rnew);
+            }
+        }
+        else {
+            if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew),
+                                      rnew->pool))) {
+                ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, rnew,
+                            "Symbolic link not allowed: %s", rnew->filename);
+                rnew->status = res;
+                return rnew;
+            }
+            /*
+             * do a file_walk, if it doesn't change the per_dir_config then
+             * we know that we don't have to redo all the access checks
+             */
+            if ((res = file_walk(rnew))) {
+                rnew->status = res;
+                return rnew;
+            }
+            if (rnew->per_dir_config == r->per_dir_config) {
+                if ((res = ap_run_type_checker(rnew)) || (res = ap_run_fixups(rnew))) {
+                    rnew->status = res;
+                }
+                return rnew;
+            }
+        }
+    }
+    else {
+       /* XXX: @@@: What should be done with the parsed_uri values? */
+       ap_parse_uri(rnew, new_file);   /* fill in parsed_uri values */
+        /*
+         * XXX: this should be set properly like it is in the same-dir case
+         * but it's actually sometimes to impossible to do it... because the
+         * file may not have a uri associated with it -djg
+         */
+        rnew->uri = "INTERNALLY GENERATED file-relative req";
+        rnew->filename = ((ap_os_is_path_absolute(new_file)) ?
+                          apr_pstrdup(rnew->pool, new_file) :
+                          ap_make_full_path(rnew->pool, fdir, new_file));
+        rnew->per_dir_config = r->server->lookup_defaults;
+        res = directory_walk(rnew);
+        if (!res) {
+            res = file_walk(rnew);
+        }
+    }
+
+    if (res
+        || ((ap_satisfies(rnew) == SATISFY_ALL
+             || ap_satisfies(rnew) == SATISFY_NOSPEC)
+            ? ((res = ap_run_access_checker(rnew))
+               || (ap_some_auth_required(rnew)
+                   && ((res = ap_run_check_user_id(rnew))
+                       || (res = ap_run_auth_checker(rnew)))))
+            : ((res = ap_run_access_checker(rnew))
+               && (!ap_some_auth_required(rnew)
+                   || ((res = ap_run_check_user_id(rnew))
+                       || (res = ap_run_auth_checker(rnew)))))
+           )
+        || (res = ap_run_type_checker(rnew))
+        || (res = ap_run_fixups(rnew))
+       ) {
+        rnew->status = res;
+    }
+    return rnew;
+}
+
+AP_DECLARE(int) ap_run_sub_req(request_rec *r)
+{
+    int retval;
+
+    /* see comments in process_request_internal() */
+    ap_run_insert_filter(r);
+    retval = ap_invoke_handler(r);
+    ap_finalize_sub_req_protocol(r);
+    return retval;
+}
+
+AP_DECLARE(void) ap_destroy_sub_req(request_rec *r)
+{
+    /* Reclaim the space */
+    apr_pool_destroy(r->pool);
+}
+
+/*
+ * Function to set the r->mtime field to the specified value if it's later
+ * than what's already there.
+ */
+AP_DECLARE(void) ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
+{
+    if (r->mtime < dependency_mtime) {
+       r->mtime = dependency_mtime;
+    }
+}
+
+/*
+ * Is it the initial main request, which we only get *once* per HTTP request?
+ */
+AP_DECLARE(int) ap_is_initial_req(request_rec *r)
+{
+    return
+        (r->main == NULL)       /* otherwise, this is a sub-request */
+        &&
+        (r->prev == NULL);      /* otherwise, this is an internal redirect */
+} 
+