Index: subversion/mod_authz_svn/mod_authz_svn.c
===================================================================
--- subversion/mod_authz_svn/mod_authz_svn.c	(revision 15230)
+++ subversion/mod_authz_svn/mod_authz_svn.c	(working copy)
@@ -3,7 +3,7 @@
  *                  based authorization for a Subversion repository.
  *
  * ====================================================================
- * Copyright (c) 2003-2004 CollabNet.  All rights reserved.
+ * Copyright (c) 2003-2005 CollabNet.  All rights reserved.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution.  The terms
@@ -33,17 +33,12 @@
 #include "svn_path.h"
 #include "svn_config.h"
 #include "svn_string.h"
+#include "svn_types.h"
+#include "svn_repos.h"
 
 
 extern module AP_MODULE_DECLARE_DATA authz_svn_module;
 
-enum {
-    AUTHZ_SVN_NONE = 0,
-    AUTHZ_SVN_READ = 1,
-    AUTHZ_SVN_WRITE = 2,
-    AUTHZ_SVN_RECURSIVE = 4
-};
-
 typedef struct {
     int authoritative;
     int anonymous;
@@ -51,20 +46,6 @@
     const char *access_file;
 } authz_svn_config_rec;
 
-struct parse_authz_baton {
-    apr_pool_t *pool;
-    svn_config_t *config;
-    const char *user;
-    int allow;
-    int deny;
-
-    int required_access;
-    const char *repos_path;
-    const char *qualified_repos_path;
-
-    int access;
-};
-
 /*
  * Configuration
  */
@@ -100,223 +81,6 @@
     { NULL }
 };
 
-
-/*
- * Access checking
- */
-
-static int group_contains_user_internal(svn_config_t *cfg,
-    const char *group, const char *user, apr_hash_t *checked_groups,
-    apr_pool_t *pool)
-{
-    const char *value;
-    apr_array_header_t *list;
-    int i;
-
-    svn_config_get(cfg, &value, "groups", group, "");
-    list = svn_cstring_split(value, ",", TRUE, pool);
-
-    for (i = 0; i < list->nelts; i++) {
-       const char *group_user = APR_ARRAY_IDX(list, i, char *);
-
-       if (*group_user == '@') {
-           /* Guard against circular dependencies by checking group
-            * name against hash.
-            */
-           if (apr_hash_get(checked_groups, &group_user[1],
-                            APR_HASH_KEY_STRING))
-               continue;
-	   
-           /* Add group to hash of checked groups. */
-           apr_hash_set(checked_groups, &group_user[1],
-                        APR_HASH_KEY_STRING, "");
-
-           if (group_contains_user_internal(cfg, &group_user[1], user,
-                                            checked_groups, pool))
-               return 1;
-
-       } else if (!strcmp(user, group_user)) {
-           return 1;
-       }
-    }
-
-    return 0;
-}
-
-static int group_contains_user(svn_config_t *cfg,
-    const char *group, const char *user, apr_pool_t *pool)
-{
-    return group_contains_user_internal(cfg, group, user,
-                                        apr_hash_make(pool), pool);
-}
-
-static svn_boolean_t parse_authz_line(const char *name, const char *value,
-                                      void *baton)
-{
-    struct parse_authz_baton *b = baton;
-
-    if (strcmp(name, "*")) {
-        if (!b->user) {
-            return TRUE;
-        }
-
-        if (*name == '@') {
-            if (!group_contains_user(b->config, &name[1], b->user, b->pool))
-                return TRUE;
-        }
-        else if (strcmp(name, b->user)) {
-            return TRUE;
-        }
-    }
-
-    if (ap_strchr_c(value, 'r')) {
-        b->allow |= AUTHZ_SVN_READ;
-    }
-    else {
-        b->deny |= AUTHZ_SVN_READ;
-    }
-
-    if (ap_strchr_c(value, 'w')) {
-        b->allow |= AUTHZ_SVN_WRITE;
-    }
-    else {
-        b->deny |= AUTHZ_SVN_WRITE;
-    }
-
-    ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, b->pool,
-                  "%s = %s => allow = %i, deny = %i",
-                  name, value, b->allow, b->deny);
-
-    return TRUE;
-}
-
-/*
- * Return TRUE when ACCESS has been determined.
- */
-static int parse_authz_lines(svn_config_t *cfg,
-                             const char *repos_name, const char *repos_path,
-                             const char *user,
-                             int required_access, int *granted_access,
-                             apr_pool_t *pool)
-{
-    const char *qualified_repos_path;
-    struct parse_authz_baton baton = { 0 };
-
-    baton.pool = pool;
-    baton.config = cfg;
-    baton.user = user;
-
-    /* First try repos specific */
-    qualified_repos_path = apr_pstrcat(pool, repos_name, ":", repos_path,
-                                       NULL);
-    svn_config_enumerate(cfg, qualified_repos_path,
-                         parse_authz_line, &baton);
-    *granted_access = !(baton.deny & required_access)
-                      || (baton.allow & required_access);
-
-    if ((baton.deny & required_access)
-        || (baton.allow & required_access))
-        return TRUE;
-
-    svn_config_enumerate(cfg, repos_path,
-                         parse_authz_line, &baton);
-    *granted_access = !(baton.deny & required_access)
-                      || (baton.allow & required_access);
-
-    return (baton.deny & required_access)
-           || (baton.allow & required_access);
-}
-
-static svn_boolean_t parse_authz_section(const char *section_name,
-                                         void *baton)
-{
-  struct parse_authz_baton *b = baton;
-  int conclusive;
-
-  if (strncmp(section_name, b->qualified_repos_path,
-              strlen(b->qualified_repos_path))
-      && strncmp(section_name, b->repos_path,
-                 strlen(b->repos_path))) {
-      /* No match, move on to the next section. */
-      return TRUE;
-  }
-
-  b->allow = b->deny = 0;
-  svn_config_enumerate(b->config, section_name,
-                       parse_authz_line, b);
-
-  conclusive = (b->deny & b->required_access)
-               || (b->allow & b->required_access);
-
-  b->access = !(b->deny & b->required_access)
-              || (b->allow & b->required_access)
-              || !conclusive;
-  
-  /* If access isn't denied, move on to check the next section. */
-  return b->access;
-}
-
-static int parse_authz_sections(svn_config_t *cfg,
-                                const char *repos_name, const char *repos_path,
-                                const char *user,
-                                int required_access,
-                                apr_pool_t *pool)
-{
-    struct parse_authz_baton baton = { 0 };
-
-    baton.pool = pool;
-    baton.config = cfg;
-    baton.user = user;
-    baton.required_access = required_access;
-    baton.repos_path = repos_path;
-    baton.qualified_repos_path = apr_pstrcat(pool, repos_name, ":",
-                                             repos_path, NULL);
-    
-    baton.access = 1; /* Allow by default */
-    svn_config_enumerate_sections(cfg, parse_authz_section, &baton);
-
-    return baton.access;
-}
-
-static int check_access(svn_config_t *cfg, const char *repos_name,
-                        const char *repos_path, const char *user,
-                        int required_access, apr_pool_t *pool)
-{
-    const char *base_name;
-    const char *original_repos_path = repos_path;
-    int granted_access;
-
-    if (!repos_path) {
-        /* XXX: Check if the user has 'required_access' _anywhere_ in the
-         * XXX: repository.  For now, make this always succeed, until
-         * XXX: we come up with a good way of figuring this out.
-         */
-        return 1;
-    }
-
-    base_name = repos_path;
-    while (!parse_authz_lines(cfg, repos_name, repos_path,
-                              user, required_access, &granted_access,
-                              pool)) {
-        if (base_name[0] == '/' && base_name[1] == '\0') {
-            /* By default, deny access */
-            return 0;
-        }
-
-        svn_path_split(repos_path, &repos_path, &base_name, pool);
-    }
-
-    if (granted_access && (required_access & AUTHZ_SVN_RECURSIVE) != 0) {
-        /* Check access on entries below the current repos path */
-        granted_access = parse_authz_sections(cfg,
-                                              repos_name, original_repos_path,
-                                              user, required_access,
-                                              pool);
-    }
-
-    return granted_access;
-}
-
 /* Check if the current request R is allowed.  Upon exit *REPOS_PATH_REF
  * will contain the path and repository name that an operation was requested
  * on in the form 'name:path'.  *DEST_REPOS_PATH_REF will contain the
@@ -339,7 +103,8 @@
     const char *repos_path;
     const char *dest_repos_path = NULL;
     dav_error *dav_err;
-    int authz_svn_type = 0;
+    svn_authz_access_kind_t authz_svn_type = svn_authz_none;
+    svn_boolean_t authz_access_granted = FALSE;
     svn_config_t *access_conf = NULL;
     svn_error_t *svn_err;
     const char *cache_key;
@@ -348,20 +113,20 @@
     switch (r->method_number) {
     /* All methods requiring read access to all subtrees of r->uri */
     case M_COPY:
-        authz_svn_type |= AUTHZ_SVN_RECURSIVE;
+      authz_svn_type |= svn_authz_recursive;
 
     /* All methods requiring read access to r->uri */
     case M_OPTIONS:
     case M_GET:
     case M_PROPFIND:
     case M_REPORT:
-        authz_svn_type |= AUTHZ_SVN_READ;
-        break;
+      authz_svn_type |= svn_authz_read;
+      break;
 
     /* All methods requiring write access to all subtrees of r->uri */
     case M_MOVE:
     case M_DELETE:
-        authz_svn_type |= AUTHZ_SVN_RECURSIVE;
+      authz_svn_type |= svn_authz_recursive;
 
     /* All methods requiring write access to r->uri */
     case M_MKCOL:
@@ -372,13 +137,13 @@
     case M_MKACTIVITY:
     case M_LOCK:
     case M_UNLOCK:
-        authz_svn_type |= AUTHZ_SVN_WRITE;
-        break;
+      authz_svn_type |= svn_authz_write;
+      break;
 
     default:
-        /* Require most strict access for unknown methods */
-        authz_svn_type |= AUTHZ_SVN_WRITE|AUTHZ_SVN_RECURSIVE;
-        break;
+      /* Require most strict access for unknown methods */
+      authz_svn_type |= svn_authz_write | svn_authz_recursive;
+      break;
     }
 
     dav_err = dav_svn_split_uri(r,
@@ -486,12 +251,29 @@
                               NULL, r->connection->pool);
     }
 
-    if (!check_access(access_conf,
-                      repos_name, repos_path,
-                      r->user, authz_svn_type,
-                      r->pool)) {
-        return DECLINED;
+    /* Perform authz access control */
+    svn_err = svn_repos_authz_check_access(access_conf, repos_name,
+					   repos_path, r->user,
+					   authz_svn_type,
+					   &authz_access_granted,
+					   r->pool);
+    if (svn_err) {
+      ap_log_rerror(APLOG_MARK, APLOG_ERR,
+		    /* If it is an error code that APR can make sense
+		       of, then show it, otherwise, pass zero to avoid
+		       putting "APR does not understand this error code"
+		       in the error log. */
+		    ((svn_err->apr_err >= APR_OS_START_USERERR &&
+		      svn_err->apr_err < APR_OS_START_CANONERR) ?
+		     0 : svn_err->apr_err),
+		    r, "Failed to perform access control: %s",
+		    svn_err->message);
+      svn_error_clear(svn_err);
+
+      return DECLINED;
     }
+    if (!authz_access_granted)
+      return DECLINED;
 
     /* XXX: MKCOL, MOVE, DELETE
      * XXX: Require write access to the parent dir of repos_path.
@@ -508,13 +290,32 @@
         return OK;
     }
 
-    /* Check access on the first repos_path */
-    if (!check_access(access_conf,
-                      dest_repos_name, dest_repos_path,
-                      r->user, AUTHZ_SVN_WRITE|AUTHZ_SVN_RECURSIVE,
-                      r->pool)) {
-        return DECLINED;
+    /* Check access on the destination repos_path */
+    svn_err = svn_repos_authz_check_access(access_conf,
+					   dest_repos_name,
+					   dest_repos_path,
+					   r->user,
+					   svn_authz_write
+					   |svn_authz_recursive,
+					   &authz_access_granted,
+					   r->pool);
+    if (svn_err) {
+      ap_log_rerror(APLOG_MARK, APLOG_ERR,
+		    /* If it is an error code that APR can make sense
+		       of, then show it, otherwise, pass zero to avoid
+		       putting "APR does not understand this error code"
+		       in the error log. */
+		    ((svn_err->apr_err >= APR_OS_START_USERERR &&
+		      svn_err->apr_err < APR_OS_START_CANONERR) ?
+		     0 : svn_err->apr_err),
+		    r, "Failed to perform access control: %s",
+		    svn_err->message);
+      svn_error_clear(svn_err);
+
+      return DECLINED;
     }
+    if (!authz_access_granted)
+      return DECLINED;
 
     /* XXX: MOVE and COPY, if the path doesn't exist yet, also
      * XXX: require write access to the parent dir of dest_repos_path.
@@ -577,7 +378,6 @@
                     "Access denied: - %s %s",
                     r->method, repos_path);
             }
-
         }
 
         return HTTP_FORBIDDEN;
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(revision 15230)
+++ subversion/include/svn_repos.h	(working copy)
@@ -1,7 +1,7 @@
 /**
  * @copyright
  * ====================================================================
- * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2005 CollabNet.  All rights reserved.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution.  The terms
@@ -30,6 +30,7 @@
 #include "svn_types.h"
 #include "svn_error.h"
 #include "svn_version.h"
+#include "svn_config.h"
 
 
 #ifdef __cplusplus
@@ -1672,6 +1673,19 @@
 
 /** @} */
 
+/**
+ * Check wether @a user can access @a path in the repository @a
+ * repos_name with the @a required_access. @a cfg lists the ACLs to
+ * check against. Set @a *access_granted to indicate if the requested
+ * access is granted.
+ */
+svn_error_t *
+svn_repos_authz_check_access (svn_config_t *cfg, const char *repos_name,
+			      const char *path, const char *user,
+			      svn_authz_access_kind_t required_access,
+			      svn_boolean_t *granted_access,
+			      apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/include/svn_types.h
===================================================================
--- subversion/include/svn_types.h	(revision 15230)
+++ subversion/include/svn_types.h	(working copy)
@@ -1,7 +1,7 @@
 /**
  * @copyright
  * ====================================================================
- * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2005 CollabNet.  All rights reserved.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution.  The terms
@@ -458,6 +458,22 @@
 svn_lock_t *
 svn_lock_dup (const svn_lock_t *lock, apr_pool_t *pool);
 
+/** An enum defining the kinds of access authz looks up */
+typedef enum
+  {
+    /** No access */
+    svn_authz_none = 0,
+
+    /** Path can be read */
+    svn_authz_read = 1,
+
+    /** Path can be altered */
+    svn_authz_write = 2,
+
+    /** The other access credentials are recursive */
+    svn_authz_recursive = 4
+  } svn_authz_access_kind_t;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/tests/libsvn_repos/repos-test.c
===================================================================
--- subversion/tests/libsvn_repos/repos-test.c	(revision 15230)
+++ subversion/tests/libsvn_repos/repos-test.c	(working copy)
@@ -1,7 +1,7 @@
 /* repos-test.c --- tests for the filesystem
  *
  * ====================================================================
- * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2005 CollabNet.  All rights reserved.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution.  The terms
@@ -25,6 +25,8 @@
 #include "svn_repos.h"
 #include "svn_path.h"
 #include "svn_delta.h"
+#include "svn_config.h"
+#include "svn_io.h"
 
 #include "../svn_test.h"
 #include "../svn_test_fs.h"
@@ -1028,7 +1030,137 @@
 
   return SVN_NO_ERROR;
 }
+
 
+/* Test that authz is giving out the right authorizations */
+static svn_error_t *
+authz(const char **msg,
+      svn_boolean_t msg_only,
+      svn_test_opts_t *opts,
+      apr_pool_t *pool)
+{
+  const char *authz_path = "/tmp/authz.tmp";
+  const char *contents;
+  svn_config_t *cfg;
+  svn_boolean_t access_granted;
+  apr_pool_t *subpool = svn_pool_create(pool);
+  int i;
+  /* Definition of the paths to test and expected replies for each */
+  struct
+  {
+    const char *path;
+    const char *user;
+    const svn_authz_access_kind_t required;
+    const svn_boolean_t expected;
+  } test_set[] = {
+    { "/A", NULL, svn_authz_read, TRUE },
+    { "/iota", NULL, svn_authz_read, FALSE },
+    { "/A", "plato", svn_authz_write, TRUE },
+    { "/A", NULL, svn_authz_write, FALSE },
+    { "/A/B/lambda", "plato", svn_authz_read, TRUE },
+    { "/A/B/lambda", NULL, svn_authz_read, FALSE },
+    { "/A/C", NULL, svn_authz_read, TRUE },
+    { "/A/D", "plato", svn_authz_read | svn_authz_recursive, TRUE },
+    { "/A/D", NULL, svn_authz_read | svn_authz_recursive, FALSE },
+    { NULL, NULL, svn_authz_none, FALSE } /* sentinel */
+  };
+
+  *msg = "test authz access control";
+
+  if (msg_only)
+    return SVN_NO_ERROR;
+
+  /* The test logic: We dump a test authz file to disk, then load it
+   * and perform various access tests on it. Each test has a known
+   * outcome and tests different aspects of authz, such as inheriting
+   * parent-path authz, pan-repository rules or recursive access.
+   * 'plato' is our friendly neighborhood user with more access rights
+   * than other anonymous philosophers.
+   */
+
+  /* Output the authz file to disk */
+  contents =
+    "[greek:/A]"
+    APR_EOL_STR
+    "* = r"
+    APR_EOL_STR
+    "plato = w"
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/iota]"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR
+    "[/A/B/lambda]"
+    APR_EOL_STR
+    "plato = r"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/A/D]"
+    APR_EOL_STR
+    "plato = r"
+    APR_EOL_STR
+    "* = r"
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/A/D/G]"
+    APR_EOL_STR
+    "plato = r"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/A/B/E/beta]"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR;
+
+  SVN_ERR_W (svn_io_file_create (authz_path, contents, subpool),
+	     "Writing test authz file");
+
+  /* Read the authz configuration back and start testing */
+  SVN_ERR_W (svn_config_read(&cfg, authz_path, TRUE, subpool),
+	     "Opening test authz file");
+  SVN_ERR_W (svn_io_remove_file (authz_path, subpool),
+	     "Removing test authz file");
+
+  for(i = 0; test_set[i].path != NULL; i++)
+    {
+      SVN_ERR (svn_repos_authz_check_access(cfg, "greek",
+					    test_set[i].path,
+					    test_set[i].user,
+					    test_set[i].required,
+					    &access_granted, subpool));
+
+      if (access_granted != test_set[i].expected)
+	{
+	  return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+				   "Authz incorrectly %s %s%s access "
+				   "to greek:%s for user %s",
+				   access_granted ?
+				     "grants" : "denies",
+				   test_set[i].required
+				   & svn_authz_recursive ?
+				     "recursive " : "",
+				   test_set[i].required
+				   & svn_authz_read ?
+				     "read" : "write",
+				   test_set[i].path,
+				   test_set[i].user ?
+ 				     test_set[i].user : "-");
+	}
+    }
+
+  /* That's a wrap! */
+  svn_pool_destroy(subpool);
+  return SVN_NO_ERROR;
+}
+
+
 /* The test table.  */
 
 struct svn_test_descriptor_t test_funcs[] =
@@ -1039,5 +1171,6 @@
     SVN_TEST_PASS (revisions_changed),
     SVN_TEST_PASS (node_locations),
     SVN_TEST_PASS (rmlocks),
+    SVN_TEST_PASS (authz),
     SVN_TEST_NULL
   };
Index: subversion/libsvn_repos/authz.c
===================================================================
--- subversion/libsvn_repos/authz.c	(revision 0)
+++ subversion/libsvn_repos/authz.c	(revision 0)
@@ -0,0 +1,339 @@
+/* authz.c : path-based access control
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2005 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include <assert.h>
+
+#include <apr_pools.h>
+#include <apr_file_io.h>
+
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_path.h"
+#include "svn_repos.h"
+
+
+/* A voodoo macro that evaluates to TRUE if access is granted. This
+ * means it is true if (1) no requested access was denied (implicit
+ * grant) OR if (2) the requested access was explicitely granted,
+ * overriding any denial. */
+#define AUTHZ_ACCESS_GRANTED(allow, deny, required)      \
+  (((deny & required) == svn_authz_none                  \
+   || (allow & required) ==                              \
+        ((svn_authz_read | svn_authz_write) & required)) \
+   ? TRUE : FALSE)
+
+/* Another voodoo macro that evaluates to TRUE if the access
+ * credentials have been determined. Access is determined when some
+ * (or all) of the required access was either granted or denied. */
+#define AUTHZ_ACCESS_DETERMINED(allow, deny, requested) \
+  (((deny & requested) || (allow & requested)) ? TRUE : FALSE)
+
+
+struct authz_baton {
+  apr_pool_t *pool;
+  svn_config_t *config;
+
+  const char *user;
+  svn_authz_access_kind_t allow;
+  svn_authz_access_kind_t deny;
+
+  svn_authz_access_kind_t required_access;
+  const char *repos_path;
+  const char *qualified_repos_path;
+
+  svn_boolean_t access;
+};
+
+
+static svn_boolean_t
+authz_group_contains_user_internal(svn_config_t *cfg,
+				   const char *group,
+				   const char *user,
+				   apr_hash_t *checked_groups,
+				   apr_pool_t *pool)
+{
+  const char *value;
+  apr_array_header_t *list;
+  int i;
+
+  svn_config_get(cfg, &value, "groups", group, "");
+  list = svn_cstring_split(value, ",", TRUE, pool);
+
+  for (i = 0; i < list->nelts; i++)
+    {
+      const char *group_user = APR_ARRAY_IDX(list, i, char *);
+
+      /* If the 'user' is a subgroup, recurse into it */
+      if (*group_user == '@')
+	{
+           /* Guard against circular dependencies by checking group
+              name against hash. */
+	  /* XXX: Should this return an error? */
+	  if (apr_hash_get(checked_groups, &group_user[1],
+			   APR_HASH_KEY_STRING))
+	    continue;
+
+	  /* Add group to hash of checked groups */
+	  apr_hash_set(checked_groups, &group_user[1],
+		       APR_HASH_KEY_STRING, "");
+
+	  /* Recurse on that group */
+	  if (authz_group_contains_user_internal(cfg, &group_user[1],
+						 user, checked_groups,
+						 pool))
+	    return TRUE;
+	}
+      /* If the user matches, stop */
+      else if (strcmp(user, group_user) == 0)
+	return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+static svn_boolean_t
+authz_group_contains_user(svn_config_t *cfg, const char *group,
+			  const char *user, apr_pool_t *pool)
+{
+  return authz_group_contains_user_internal(cfg, group, user,
+					    apr_hash_make(pool),
+					    pool);
+}
+
+
+
+/*
+ * Callback to parse one line of an authz file and update the
+ * authz_baton accordingly.
+ */
+static svn_boolean_t
+authz_parse_line (const char *name, const char *value, void *baton)
+{
+  struct authz_baton *b = baton;
+
+  /* Work out wether this ACL line applies to the user */
+  if (strcmp(name, "*") != 0)
+    {
+      /* non-anon rule, anon user. Stop. */
+      if (!b->user)
+	return TRUE;
+
+      /* group rule and user not in group. Stop. */
+      if (*name == '@')
+	{
+	  if (!authz_group_contains_user(b->config, &name[1],
+					 b->user, b->pool))
+	    return TRUE;
+	}
+      /* user rule for wrong user. Stop. */
+      else if (strcmp(name, b->user) != 0)
+	return TRUE;
+    }
+
+  /* Set the access grants for the rule */
+  if (strchr(value, 'r'))
+    b->allow |= svn_authz_read;
+  else
+    b->deny |= svn_authz_read;
+
+  if (strchr(value, 'w'))
+    b->allow |= svn_authz_write;
+  else
+    b->deny |= svn_authz_write;
+
+  return TRUE;
+}
+
+
+
+/*
+ * Callback to parse a section and update the authz_baton if the
+ * section denies access to the subtree the baton describes.
+ */
+static svn_boolean_t
+authz_parse_section(const char *section_name, void *baton)
+{
+  struct authz_baton *b = baton;
+  svn_boolean_t conclusive;
+
+  /* Does the section apply to us? */
+  if (strncmp(section_name, b->qualified_repos_path,
+	      strlen(b->qualified_repos_path)) != 0
+      && strncmp(section_name, b->repos_path,
+		 strlen(b->repos_path)) != 0)
+    return TRUE;
+
+  /* Work out what this section grants */
+  b->allow = b->deny = 0;
+  svn_config_enumerate(b->config, section_name,
+		       authz_parse_line, b);
+
+  /* Has the section explicitely determined an access? */
+  conclusive = AUTHZ_ACCESS_DETERMINED(b->allow, b->deny,
+				       b->required_access);
+
+  /* Is access granted OR inconclusive? */
+  b->access = AUTHZ_ACCESS_GRANTED(b->allow, b->deny,
+				   b->required_access)
+              || !conclusive;
+
+  /* As long as access isn't conclusively denied, carry on */
+  return b->access;
+}
+
+
+
+/*
+ * Validate access to the given user for the given path. This function
+ * checks rules for exactly the given path, and first tries to access
+ * a section specific to the given repository before falling back to
+ * pan-repository rules.
+ *
+ * Return a boolean which tells the caller wether we were able to
+ * determine the requested access rights. Access status is in
+ * *access_granted.
+ */
+static svn_boolean_t
+authz_get_path_access (svn_config_t *cfg, const char *repos_name,
+		       const char *path, const char *user,
+		       svn_authz_access_kind_t required_access,
+		       svn_boolean_t *access_granted,
+		       apr_pool_t *pool)
+{
+  const char *qualified_path;
+  struct authz_baton baton = { 0 };
+
+  baton.pool = pool;
+  baton.config = cfg;
+  baton.user = user;
+
+  /* Try to locate a repository-specific block first */
+  qualified_path = apr_pstrcat(pool, repos_name, ":", path, NULL);
+  svn_config_enumerate(cfg, qualified_path,
+		       authz_parse_line, &baton);
+
+  *access_granted = AUTHZ_ACCESS_GRANTED(baton.allow, baton.deny,
+					 required_access);
+
+  /* If the first test has determined access, stop now */
+  if (AUTHZ_ACCESS_DETERMINED(baton.allow, baton.deny,
+			      required_access))
+    return TRUE;
+
+  /* No repository specific rule, try pan-repository rules */
+  svn_config_enumerate(cfg, path, authz_parse_line, &baton);
+
+  *access_granted = AUTHZ_ACCESS_GRANTED(baton.allow, baton.deny,
+					 required_access);
+  return AUTHZ_ACCESS_DETERMINED(baton.allow, baton.deny,
+				 required_access);
+}
+
+
+
+/*
+ * Validate access to the given user for the subtree starting at the
+ * given path. This function walks the whole authz file in search of
+ * rules applying to paths in the requested subtree which deny the
+ * requested access.
+ *
+ * As soon as one is found, or else when the whole ACL file has been
+ * searched, return the updated authorization in access_granted.
+ */
+static void
+authz_get_tree_access (svn_config_t *cfg, const char *repos_name,
+		       const char *path, const char *user,
+		       svn_authz_access_kind_t required_access,
+		       svn_boolean_t *access_granted,
+		       apr_pool_t *pool)
+{
+  struct authz_baton baton = { 0 };
+
+  baton.pool = pool;
+  baton.config = cfg;
+  baton.user = user;
+  baton.required_access = required_access;
+  baton.repos_path = path;
+  baton.qualified_repos_path = apr_pstrcat(pool, repos_name,
+					   ":", path, NULL);
+  /* Default to access granted if no rules say otherwise */
+  baton.access = TRUE;
+
+  svn_config_enumerate_sections(cfg, authz_parse_section, &baton);
+
+  *access_granted = baton.access;
+}
+
+
+
+svn_error_t *
+svn_repos_authz_check_access (svn_config_t *cfg, const char *repos_name,
+			      const char *path, const char *user,
+			      svn_authz_access_kind_t required_access,
+			      svn_boolean_t *access_granted,
+			      apr_pool_t *pool)
+{
+  const char *base_name = path;
+  const char *current_path = path;
+  svn_boolean_t access_determined = FALSE;
+
+  if (!path) {
+    /* XXX: Check if the user has 'required_access' _anywhere_ in the
+     * XXX: repository.  For now, make this always succeed, until
+     * XXX: we come up with a good way of figuring this out.
+     */
+    *access_granted = TRUE;
+    return SVN_NO_ERROR;
+  }
+
+  /* Determine the granted access for the requested path */
+  do
+  {
+    access_determined = authz_get_path_access(cfg, repos_name,
+					      current_path, user,
+					      required_access,
+					      access_granted, pool);
+
+    if (!access_determined)
+      {
+	/* Stop if the loop hits the repository root with no
+	   results */
+	if (base_name[0] == '/' && base_name[1] == '\0')
+	  {
+	    /* Deny access by default */
+	    *access_granted = FALSE;
+	    return SVN_NO_ERROR;
+	  }
+
+	/* Work back to the parent path */
+	svn_path_split(current_path, &current_path, &base_name, pool);
+      }
+  } while (!access_determined);
+
+  /* If the caller requested recursive access, we need to walk through
+     the entire authz config to see wether any child paths are denied
+     to the requested user. */
+  if (*access_granted
+      && (required_access & svn_authz_recursive))
+     authz_get_tree_access(cfg, repos_name, path, user,
+			   required_access, access_granted,
+			   pool);
+
+  return SVN_NO_ERROR;
+}


