You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by fu...@apache.org on 2019/09/20 11:18:27 UTC

svn commit: r1867214 [2/2] - in /subversion/branches/swig-py3: ./ build/generator/ build/generator/templates/ contrib/client-side/svn_load_dirs/ notes/commit-access-templates/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/incl...

Modified: subversion/branches/swig-py3/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/svnserve/serve.c?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/svnserve/serve.c (original)
+++ subversion/branches/swig-py3/subversion/svnserve/serve.c Fri Sep 20 11:18:26 2019
@@ -271,17 +271,23 @@ canonicalize_access_file(const char **ac
 {
   if (svn_path_is_url(*access_file))
     {
-      *access_file = svn_uri_canonicalize(*access_file, pool);
+      const char *canonical_url;
+      SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, *access_file,
+                                        pool, pool));
+      *access_file = canonical_url;
     }
   else if (svn_path_is_repos_relative_url(*access_file))
     {
       const char *repos_root_url;
+      const char *canonical_url;
 
       SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root,
                                                pool));
       SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file,
                                                   repos_root_url, pool));
-      *access_file = svn_uri_canonicalize(*access_file, pool);
+      SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, *access_file,
+                                        pool, pool));
+      *access_file = canonical_url;
     }
   else
     {
@@ -854,7 +860,7 @@ static svn_error_t *set_path(svn_ra_svn_
                              svn_ra_svn__list_t *params, void *baton)
 {
   report_driver_baton_t *b = baton;
-  const char *path, *lock_token, *depth_word;
+  const char *path, *lock_token, *depth_word, *canonical_relpath;
   svn_revnum_t rev;
   /* Default to infinity, for old clients that don't send depth. */
   svn_depth_t depth = svn_depth_infinity;
@@ -865,7 +871,9 @@ static svn_error_t *set_path(svn_ra_svn_
                                   &depth_word));
   if (depth_word)
     depth = svn_depth_from_word(depth_word);
-  path = svn_relpath_canonicalize(path, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_relpath, NULL, path,
+                                        pool, pool));
+  path = canonical_relpath;
   if (b->from_rev && strcmp(path, "") == 0)
     *b->from_rev = rev;
   if (!b->err)
@@ -881,10 +889,12 @@ static svn_error_t *delete_path(svn_ra_s
                                 svn_ra_svn__list_t *params, void *baton)
 {
   report_driver_baton_t *b = baton;
-  const char *path;
+  const char *path, *canonical_relpath;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path));
-  path = svn_relpath_canonicalize(path, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_relpath, NULL, path,
+                                        pool, pool));
+  path = canonical_relpath;
   if (!b->err)
     b->err = svn_repos_delete_path(b->report_baton, path, pool);
   return SVN_NO_ERROR;
@@ -894,7 +904,8 @@ static svn_error_t *link_path(svn_ra_svn
                               svn_ra_svn__list_t *params, void *baton)
 {
   report_driver_baton_t *b = baton;
-  const char *path, *url, *lock_token, *fs_path, *depth_word;
+  const char *path, *url, *lock_token, *fs_path, *depth_word, *canonical_url;
+  const char *canonical_path;
   svn_revnum_t rev;
   svn_boolean_t start_empty;
   /* Default to infinity, for old clients that don't send depth. */
@@ -906,8 +917,12 @@ static svn_error_t *link_path(svn_ra_svn
 
   /* ### WHAT?!  The link path is an absolute URL?!  Didn't see that
      coming...   -- cmpilato  */
-  path = svn_relpath_canonicalize(path, pool);
-  url = svn_uri_canonicalize(url, pool);
+
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
+  path = canonical_path;
+  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, url, pool, pool));
+  url = canonical_url;
   if (depth_word)
     depth = svn_depth_from_word(depth_word);
   if (!b->err)
@@ -1123,11 +1138,12 @@ reparent(svn_ra_svn_conn_t *conn,
          void *baton)
 {
   server_baton_t *b = baton;
-  const char *url;
+  const char *url, *canonical_url;
   const char *fs_path;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &url));
-  url = svn_uri_canonicalize(url, pool);
+  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, url, pool, pool));
+  url = canonical_url;
   SVN_ERR(trivial_auth_request(conn, pool, b));
   SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool),
                           svn_path_uri_decode(url, pool),
@@ -1346,6 +1362,7 @@ add_lock_tokens(const svn_ra_svn__list_t
                 server_baton_t *sb,
                 apr_pool_t *pool)
 {
+  const char *canonical_path;
   int i;
   svn_fs_access_t *fs_access;
 
@@ -1375,9 +1392,10 @@ add_lock_tokens(const svn_ra_svn__list_t
                                 "Lock token isn't a string");
 
       path = path_item->u.string.data;
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                            pool, pool));
       full_path = svn_fspath__join(sb->repository->fs_path->data,
-                                   svn_relpath_canonicalize(path, pool),
-                                   pool);
+                                   canonical_path, pool);
 
       if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE))
         return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
@@ -1415,6 +1433,7 @@ unlock_paths(const svn_ra_svn__list_t *l
   int i;
   apr_pool_t *subpool = svn_pool_create(pool);
   apr_hash_t *targets = apr_hash_make(subpool);
+  const char *canonical_path;
   svn_error_t *err;
 
   for (i = 0; i < lock_tokens->nelts; ++i)
@@ -1427,9 +1446,10 @@ unlock_paths(const svn_ra_svn__list_t *l
       token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1);
 
       path = path_item->u.string.data;
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                            subpool, subpool));
       full_path = svn_fspath__join(sb->repository->fs_path->data,
-                                   svn_relpath_canonicalize(path, subpool),
-                                   subpool);
+                                   canonical_path, subpool);
       token = token_item->u.string.data;
       svn_hash_sets(targets, full_path, token);
     }
@@ -1569,7 +1589,7 @@ get_file(svn_ra_svn_conn_t *conn,
          void *baton)
 {
   server_baton_t *b = baton;
-  const char *path, *full_path, *hex_digest;
+  const char *path, *full_path, *hex_digest, *canonical_path;
   svn_revnum_t rev;
   svn_fs_root_t *root;
   svn_stream_t *contents;
@@ -1596,8 +1616,10 @@ get_file(svn_ra_svn_conn_t *conn,
   if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
     wants_inherited_props = FALSE;
 
-  full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, pool,
+                                        pool));
+  full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path,
+                               pool);
 
   /* Check authorizations */
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
@@ -1754,7 +1776,7 @@ get_dir(svn_ra_svn_conn_t *conn,
         void *baton)
 {
   server_baton_t *b = baton;
-  const char *path, *full_path;
+  const char *path, *full_path, *canonical_path;
   svn_revnum_t rev;
   apr_hash_t *entries, *props = NULL;
   apr_array_header_t *inherited_props;
@@ -1780,8 +1802,10 @@ get_dir(svn_ra_svn_conn_t *conn,
     wants_inherited_props = FALSE;
 
   SVN_ERR(parse_dirent_fields(&dirent_fields, dirent_fields_list));
-  full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
+  full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path,
+                               pool);
 
   /* Check authorizations */
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
@@ -1928,7 +1952,7 @@ update(svn_ra_svn_conn_t *conn,
 {
   server_baton_t *b = baton;
   svn_revnum_t rev;
-  const char *target, *full_path, *depth_word;
+  const char *target, *full_path, *depth_word, *canonical_target;
   svn_boolean_t recurse;
   svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */
   svn_tristate_t ignore_ancestry; /* Optional; default FALSE */
@@ -1941,7 +1965,9 @@ update(svn_ra_svn_conn_t *conn,
   SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cb?w3?3", &rev, &target,
                                   &recurse, &depth_word,
                                   &send_copyfrom_args, &ignore_ancestry));
-  target = svn_relpath_canonicalize(target, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
+                                        pool, pool));
+  target = canonical_target;
 
   if (depth_word)
     depth = svn_depth_from_word(depth_word);
@@ -1987,7 +2013,7 @@ switch_cmd(svn_ra_svn_conn_t *conn,
   server_baton_t *b = baton;
   svn_revnum_t rev;
   const char *target, *depth_word;
-  const char *switch_url, *switch_path;
+  const char *switch_url, *switch_path, *canonical_url, *canonical_target;
   svn_boolean_t recurse;
   /* Default to unknown.  Old clients won't send depth, but we'll
      handle that by converting recurse if necessary. */
@@ -1999,9 +2025,12 @@ switch_cmd(svn_ra_svn_conn_t *conn,
   SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbc?w?33", &rev, &target,
                                   &recurse, &switch_url, &depth_word,
                                   &send_copyfrom_args, &ignore_ancestry));
-  target = svn_relpath_canonicalize(target, pool);
-  switch_url = svn_uri_canonicalize(switch_url, pool);
-
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
+                                        pool, pool));
+  target = canonical_target;
+  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, switch_url, pool,
+                                    pool));
+  switch_url = canonical_url;
   if (depth_word)
     depth = svn_depth_from_word(depth_word);
   else
@@ -2039,7 +2068,7 @@ status(svn_ra_svn_conn_t *conn,
 {
   server_baton_t *b = baton;
   svn_revnum_t rev;
-  const char *target, *depth_word;
+  const char *target, *depth_word, *canonical_target;
   svn_boolean_t recurse;
   /* Default to unknown.  Old clients won't send depth, but we'll
      handle that by converting recurse if necessary. */
@@ -2048,7 +2077,9 @@ status(svn_ra_svn_conn_t *conn,
   /* Parse the arguments. */
   SVN_ERR(svn_ra_svn__parse_tuple(params, "cb?(?r)?w",
                                   &target, &recurse, &rev, &depth_word));
-  target = svn_relpath_canonicalize(target, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
+                                        pool, pool));
+  target = canonical_target;
 
   if (depth_word)
     depth = svn_depth_from_word(depth_word);
@@ -2078,7 +2109,8 @@ diff(svn_ra_svn_conn_t *conn,
 {
   server_baton_t *b = baton;
   svn_revnum_t rev;
-  const char *target, *versus_url, *versus_path, *depth_word;
+  const char *target, *versus_url, *versus_path, *depth_word, *canonical_url;
+  const char *canonical_target;
   svn_boolean_t recurse, ignore_ancestry;
   svn_boolean_t text_deltas;
   /* Default to unknown.  Old clients won't send depth, but we'll
@@ -2101,8 +2133,12 @@ diff(svn_ra_svn_conn_t *conn,
                                       &ignore_ancestry, &versus_url,
                                       &text_deltas, &depth_word));
     }
-  target = svn_relpath_canonicalize(target, pool);
-  versus_url = svn_uri_canonicalize(versus_url, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
+                                        pool, pool));
+  target = canonical_target;
+  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, versus_url,
+                                    pool, pool));
+  versus_url = canonical_url;
 
   if (depth_word)
     depth = svn_depth_from_word(depth_word);
@@ -2227,13 +2263,15 @@ get_mergeinfo(svn_ra_svn_conn_t *conn,
   for (i = 0; i < paths->nelts; i++)
      {
         svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(paths, i);
-        const char *full_path;
+        const char *full_path, *canonical_path;
 
         if (item->kind != SVN_RA_SVN_STRING)
           return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                   _("Path is not a string"));
-        full_path = svn_relpath_canonicalize(item->u.string.data, pool);
-        full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool);
+        SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL,
+            item->u.string.data, pool, pool));
+        full_path = svn_fspath__join(b->repository->fs_path->data,
+                                     canonical_path, pool);
         APR_ARRAY_PUSH(canonical_paths, const char *) = full_path;
      }
 
@@ -2395,7 +2433,7 @@ log_cmd(svn_ra_svn_conn_t *conn,
   svn_error_t *err, *write_err;
   server_baton_t *b = baton;
   svn_revnum_t start_rev, end_rev;
-  const char *full_path;
+  const char *full_path, *canonical_path;
   svn_boolean_t send_changed_paths, strict_node, include_merged_revisions;
   apr_array_header_t *full_paths, *revprops;
   svn_ra_svn__list_t *paths, *revprop_items;
@@ -2459,9 +2497,10 @@ log_cmd(svn_ra_svn_conn_t *conn,
       if (elt->kind != SVN_RA_SVN_STRING)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Log path entry not a string"));
-      full_path = svn_relpath_canonicalize(elt->u.string.data, pool),
-      full_path = svn_fspath__join(b->repository->fs_path->data, full_path,
-                                   pool);
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL,
+                                            elt->u.string.data, pool, pool));
+      full_path = svn_fspath__join(b->repository->fs_path->data,
+                                   canonical_path, pool);
       APR_ARRAY_PUSH(full_paths, const char *) = full_path;
     }
   SVN_ERR(trivial_auth_request(conn, pool, b));
@@ -2504,13 +2543,15 @@ check_path(svn_ra_svn_conn_t *conn,
 {
   server_baton_t *b = baton;
   svn_revnum_t rev;
-  const char *path, *full_path;
+  const char *path, *full_path, *canonical_path;
   svn_fs_root_t *root;
   svn_node_kind_t kind;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev));
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));;
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
 
   /* Check authorizations */
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
@@ -2537,13 +2578,15 @@ stat_cmd(svn_ra_svn_conn_t *conn,
 {
   server_baton_t *b = baton;
   svn_revnum_t rev;
-  const char *path, *full_path, *cdate;
+  const char *path, *full_path, *cdate, *canonical_path;
   svn_fs_root_t *root;
   svn_dirent_t *dirent;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev));
-  full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, pool,
+                                        pool));
+  full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path,
+                               pool);
 
   /* Check authorizations */
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
@@ -2592,7 +2635,7 @@ get_locations(svn_ra_svn_conn_t *conn,
   svn_ra_svn__list_t *loc_revs_proto;
   svn_ra_svn__item_t *elt;
   int i;
-  const char *relative_path;
+  const char *relative_path, *canonical_path;
   svn_revnum_t peg_revision;
   apr_hash_t *fs_locations;
   const char *abs_path;
@@ -2605,7 +2648,9 @@ get_locations(svn_ra_svn_conn_t *conn,
   SVN_ERR(svn_ra_svn__parse_tuple(params, "crl", &relative_path,
                                   &peg_revision,
                                   &loc_revs_proto));
-  relative_path = svn_relpath_canonicalize(relative_path, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, relative_path,
+                                        pool, pool));
+  relative_path = canonical_path;
 
   abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
                               pool);
@@ -2691,7 +2736,7 @@ get_location_segments(svn_ra_svn_conn_t
   svn_error_t *err, *write_err;
   server_baton_t *b = baton;
   svn_revnum_t peg_revision, start_rev, end_rev;
-  const char *relative_path;
+  const char *relative_path, *canonical_path;
   const char *abs_path;
   authz_baton_t ab;
 
@@ -2702,7 +2747,9 @@ get_location_segments(svn_ra_svn_conn_t
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)(?r)",
                                   &relative_path, &peg_revision,
                                   &start_rev, &end_rev));
-  relative_path = svn_relpath_canonicalize(relative_path, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, relative_path,
+                                        pool, pool));
+  relative_path = canonical_path;
 
   abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
                               pool);
@@ -2854,6 +2901,7 @@ get_file_revs(svn_ra_svn_conn_t *conn,
   svn_revnum_t start_rev, end_rev;
   const char *path;
   const char *full_path;
+  const char *canonical_path;
   apr_uint64_t include_merged_revs_param;
   svn_boolean_t include_merged_revisions;
   authz_baton_t ab;
@@ -2865,7 +2913,9 @@ get_file_revs(svn_ra_svn_conn_t *conn,
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)?B",
                                   &path, &start_rev, &end_rev,
                                   &include_merged_revs_param));
-  path = svn_relpath_canonicalize(path, pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
+  path = canonical_path;
   SVN_ERR(trivial_auth_request(conn, pool, b));
   full_path = svn_fspath__join(b->repository->fs_path->data, path, pool);
 
@@ -2908,14 +2958,17 @@ lock(svn_ra_svn_conn_t *conn,
   const char *path;
   const char *comment;
   const char *full_path;
+  const char *canonical_path;
   svn_boolean_t steal_lock;
   svn_revnum_t current_rev;
   svn_lock_t *l;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b(?r)", &path, &comment,
                                   &steal_lock, &current_rev));
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));;
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
 
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
                            full_path, TRUE));
@@ -3007,7 +3060,7 @@ lock_many(svn_ra_svn_conn_t *conn,
   /* Parse the lock requests from PATH_REVS into TARGETS. */
   for (i = 0; i < path_revs->nelts; ++i)
     {
-      const char *path, *full_path;
+      const char *path, *full_path, *canonical_path;
       svn_revnum_t current_rev;
       svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i);
       svn_fs_lock_target_t *target;
@@ -3021,9 +3074,10 @@ lock_many(svn_ra_svn_conn_t *conn,
       SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?r)", &path,
                                       &current_rev));
 
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                            subpool, subpool));
       full_path = svn_fspath__join(b->repository->fs_path->data,
-                                   svn_relpath_canonicalize(path, subpool),
-                                   pool);
+                                   canonical_path, pool);
       target = svn_fs_lock_target_create(NULL, current_rev, pool);
 
       /* Any duplicate paths, once canonicalized, get collapsed into a
@@ -3070,7 +3124,7 @@ lock_many(svn_ra_svn_conn_t *conn,
   /* Return results in the same order as the paths were supplied. */
   for (i = 0; i < path_revs->nelts; ++i)
     {
-      const char *path, *full_path;
+      const char *path, *full_path, *canonical_path;
       svn_revnum_t current_rev;
       svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i);
       struct lock_result_t *result;
@@ -3082,9 +3136,10 @@ lock_many(svn_ra_svn_conn_t *conn,
       if (write_err)
         break;
 
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                            subpool, subpool));
       full_path = svn_fspath__join(b->repository->fs_path->data,
-                                   svn_relpath_canonicalize(path, subpool),
-                                   subpool);
+                                   canonical_path, subpool);
 
       result = svn_hash_gets(lmb.results, full_path);
       if (!result)
@@ -3139,14 +3194,16 @@ unlock(svn_ra_svn_conn_t *conn,
        void *baton)
 {
   server_baton_t *b = baton;
-  const char *path, *token, *full_path;
+  const char *path, *token, *full_path, *canonical_path;
   svn_boolean_t break_lock;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b", &path, &token,
                                  &break_lock));
 
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
 
   /* Username required unless break_lock was specified. */
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
@@ -3190,7 +3247,7 @@ unlock_many(svn_ra_svn_conn_t *conn,
   for (i = 0; i < unlock_tokens->nelts; i++)
     {
       svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i);
-      const char *path, *full_path, *token;
+      const char *path, *full_path, *token, *canonical_path;
 
       svn_pool_clear(subpool);
 
@@ -3203,9 +3260,10 @@ unlock_many(svn_ra_svn_conn_t *conn,
       if (!token)
         token = "";
 
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                            subpool, subpool));
       full_path = svn_fspath__join(b->repository->fs_path->data,
-                                   svn_relpath_canonicalize(path, subpool),
-                                   pool);
+                                   canonical_path, pool);
 
       /* Any duplicate paths, once canonicalized, get collapsed into a
          single path that is processed once.  The result is then
@@ -3250,7 +3308,7 @@ unlock_many(svn_ra_svn_conn_t *conn,
   /* Return results in the same order as the paths were supplied. */
   for (i = 0; i < unlock_tokens->nelts; ++i)
     {
-      const char *path, *token, *full_path;
+      const char *path, *token, *full_path, *canonical_path;
       svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i);
       struct lock_result_t *result;
 
@@ -3261,9 +3319,10 @@ unlock_many(svn_ra_svn_conn_t *conn,
       if (write_err)
         break;
 
+      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                            subpool, subpool));
       full_path = svn_fspath__join(b->repository->fs_path->data,
-                                   svn_relpath_canonicalize(path, subpool),
-                                   pool);
+                                   canonical_path, pool);
 
       result = svn_hash_gets(lmb.results, full_path);
       if (!result)
@@ -3313,12 +3372,15 @@ get_lock(svn_ra_svn_conn_t *conn,
   server_baton_t *b = baton;
   const char *path;
   const char *full_path;
+  const char *canonical_path;
   svn_lock_t *l;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path));
 
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
 
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
                            full_path, FALSE));
@@ -3344,6 +3406,7 @@ get_locks(svn_ra_svn_conn_t *conn,
   server_baton_t *b = baton;
   const char *path;
   const char *full_path;
+  const char *canonical_path;
   const char *depth_word;
   svn_depth_t depth;
   apr_hash_t *locks;
@@ -3367,8 +3430,10 @@ get_locks(svn_ra_svn_conn_t *conn,
       return log_fail_and_flush(err, b, conn, pool);
     }
 
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
 
@@ -3505,15 +3570,17 @@ get_deleted_rev(svn_ra_svn_conn_t *conn,
                 void *baton)
 {
   server_baton_t *b = baton;
-  const char *path, *full_path;
+  const char *path, *full_path, *canonical_path;
   svn_revnum_t peg_revision;
   svn_revnum_t end_revision;
   svn_revnum_t revision_deleted;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, "crr",
                                  &path, &peg_revision, &end_revision));
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
   SVN_ERR(log_command(b, conn, pool, "get-deleted-rev"));
   SVN_ERR(trivial_auth_request(conn, pool, b));
   SVN_CMD_ERR(svn_repos_deleted_rev(b->repository->fs, full_path, peg_revision,
@@ -3542,7 +3609,7 @@ get_inherited_props(svn_ra_svn_conn_t *c
                     void *baton)
 {
   server_baton_t *b = baton;
-  const char *path, *full_path;
+  const char *path, *full_path, *canonical_path;
   svn_revnum_t rev;
   svn_fs_root_t *root;
   apr_array_header_t *inherited_props;
@@ -3557,9 +3624,10 @@ get_inherited_props(svn_ra_svn_conn_t *c
   /* Parse arguments. */
   SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev));
 
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        iterpool, iterpool));
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, iterpool),
-                               pool);
+                               canonical_path, pool);
 
   /* Check authorizations */
   SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read,
@@ -3632,7 +3700,7 @@ list(svn_ra_svn_conn_t *conn,
      void *baton)
 {
   server_baton_t *b = baton;
-  const char *path, *full_path;
+  const char *path, *full_path, *canonical_path;
   svn_revnum_t rev;
   svn_depth_t depth;
   apr_array_header_t *patterns = NULL;
@@ -3658,8 +3726,10 @@ list(svn_ra_svn_conn_t *conn,
   SVN_ERR(parse_dirent_fields(&rb.dirent_fields, dirent_fields_list));
 
   depth = svn_depth_from_word(depth_word);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        pool, pool));
   full_path = svn_fspath__join(b->repository->fs_path->data,
-                               svn_relpath_canonicalize(path, pool), pool);
+                               canonical_path, pool);
 
   /* Read the patterns list.  */
   if (patterns_list)
@@ -3822,7 +3892,8 @@ find_repos(const char *url,
            apr_pool_t *result_pool,
            apr_pool_t *scratch_pool)
 {
-  const char *path, *full_path, *fs_path, *hooks_env;
+  const char *path, *full_path, *fs_path, *hooks_env, *canonical_path;
+  const char *canonical_root;
   svn_stringbuf_t *url_buf;
   svn_boolean_t sasl_requested;
 
@@ -3838,8 +3909,9 @@ find_repos(const char *url,
       if (path == NULL)
         path = "";
     }
-  path = svn_relpath_canonicalize(path, scratch_pool);
-  path = svn_path_uri_decode(path, scratch_pool);
+  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
+                                        scratch_pool, scratch_pool));
+  path = svn_path_uri_decode(canonical_path, scratch_pool);
 
   /* Ensure that it isn't possible to escape the root by disallowing
      '..' segments. */
@@ -3848,8 +3920,9 @@ find_repos(const char *url,
                             "Couldn't determine repository path");
 
   /* Join the server-configured root with the client path. */
-  full_path = svn_dirent_join(svn_dirent_canonicalize(root, scratch_pool),
-                              path, scratch_pool);
+  SVN_ERR(svn_dirent_canonicalize_safe(&canonical_root, NULL, root,
+                                       scratch_pool, scratch_pool));
+  full_path = svn_dirent_join(canonical_root, path, scratch_pool);
 
   /* Search for a repository in the full path. */
   repository->repos_root = svn_repos_find_root_path(full_path, result_pool);
@@ -3870,7 +3943,7 @@ find_repos(const char *url,
   svn_path_remove_components(url_buf,
                         svn_path_component_count(repository->fs_path->data));
   repository->repos_url = url_buf->data;
-  repository->authz_repos_name = svn_dirent_is_child(root,
+  repository->authz_repos_name = svn_dirent_is_child(canonical_root,
                                                      repository->repos_root,
                                                      result_pool);
   if (repository->authz_repos_name == NULL)
@@ -4140,7 +4213,7 @@ construct_server_baton(server_baton_t **
 {
   svn_error_t *err;
   apr_uint64_t ver;
-  const char *client_url, *ra_client_string, *client_string;
+  const char *client_url, *ra_client_string, *client_string, *canonical_url;
   svn_ra_svn__list_t *caplist;
   apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn);
   server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b));
@@ -4207,15 +4280,21 @@ construct_server_baton(server_baton_t **
                                  &ra_client_string,
                                  &client_string));
   if (ver != 2)
-    return SVN_NO_ERROR;
-
-  client_url = svn_uri_canonicalize(client_url, conn_pool);
+    return svn_error_createf(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
+                             "Unsupported ra_svn protocol version"
+                             " %"APR_UINT64_T_FMT
+                             " (supported versions: [2])", ver);
+
+  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, client_url,
+                                    conn_pool, scratch_pool));
+  client_url = canonical_url;
   SVN_ERR(svn_ra_svn__set_capabilities(conn, caplist));
 
   /* All released versions of Subversion support edit-pipeline,
    * so we do not accept connections from clients that do not. */
   if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE))
-    return SVN_NO_ERROR;
+    return svn_error_create(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
+                            "Missing edit-pipeline capability");
 
   /* find_repos needs the capabilities as a list of words (eventually
      they get handed to the start-commit hook).  While we could add a

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/changelist_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/changelist_tests.py?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/changelist_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/changelist_tests.py Fri Sep 20 11:18:26 2019
@@ -73,6 +73,33 @@ def changelist_all_files(wc_dir, name_fu
         else:
           svntest.main.run_svn(None, "changelist", clname, full_path)
 
+def select_paths(target_path, depth, changelists, name_func):
+  """Return the subset of paths found on disk at TARGET_PATH, to a depth
+     of DEPTH, that match CHANGELISTS.
+     NAME_FUNC, rather than the working copy, determines what
+     changelist each path is associated with.
+     Returned paths are relative to the CWD.
+
+     ### Only the paths of files are returned.
+  """
+  dot_svn = svntest.main.get_admin_name()
+  for dirpath, dirs, files in os.walk(target_path):
+    # prepare to return paths relative to WC_DIR
+    if dot_svn in dirs:
+      dirs.remove(dot_svn)
+    if not changelists:  # When changelists support dirs, add: "or name_func(name) in changelists"
+      yield os.path.normpath(dirpath)
+    if depth == 'empty':
+      dirs[:] = []  # process no subdirs
+      continue      # nor files
+    for name in files:
+      if not changelists or name_func(name) in changelists:
+        yield os.path.normpath(os.path.join(dirpath, name))
+    if depth == 'files':
+      dirs[:] = []  # process no subdirs
+    if depth == 'immediates':
+      depth = 'empty'  # process subdirs, but no files nor dirs in them
+
 def clname_from_lastchar_cb(full_path):
   """Callback for changelist_all_files() that returns a changelist
   name matching the last character in the file's name.  For example,
@@ -544,6 +571,8 @@ def info_with_changelists(sbox):
 
 #----------------------------------------------------------------------
 
+@XFail()
+@Issue(4826)
 def diff_with_changelists(sbox):
   "diff --changelist (wc-wc and repos-wc)"
 
@@ -553,31 +582,20 @@ def diff_with_changelists(sbox):
   # Add a line of text to all the versioned files in the tree.
   mod_all_files(wc_dir, "New text.\n")
 
+  # Also make a property modification on each directory.
+  svntest.main.run_svn(None, 'propset', 'p', 'v', '-R', wc_dir)
+
   # Add files to changelists based on the last character in their names.
   changelist_all_files(wc_dir, clname_from_lastchar_cb)
 
   # Now, test various combinations of changelist specification and depths.
   for is_repos_wc in [0, 1]:
-    for clname in [['a'], ['i'], ['a', 'i']]:
-      for depth in ['files', 'infinity']:
+    for clname in [['a'], ['a', 'i'], []]:
+      for depth in ['empty', 'files', 'immediates', 'infinity', None]:
+       for subdir in ['.', 'A', 'A/D']:
 
         # Figure out what we expect to see in our diff output.
-        expected_paths = []
-        if 'a' in clname:
-          if depth == 'infinity':
-            expected_paths.append('A/B/lambda')
-            expected_paths.append('A/B/E/alpha')
-            expected_paths.append('A/B/E/beta')
-            expected_paths.append('A/D/gamma')
-            expected_paths.append('A/D/H/omega')
-          if depth == 'files' or depth == 'infinity':
-            expected_paths.append('iota')
-        if 'i' in clname:
-          if depth == 'infinity':
-            expected_paths.append('A/D/G/pi')
-            expected_paths.append('A/D/H/chi')
-            expected_paths.append('A/D/H/psi')
-        expected_paths = sorted([os.path.join(wc_dir, x.replace('/', os.sep)) for x in expected_paths])
+        expected_paths = sorted(select_paths(sbox.ospath(subdir), depth, clname, clname_from_lastchar_cb))
 
         # Build the command line.
         args = ['diff']
@@ -589,11 +607,11 @@ def diff_with_changelists(sbox):
           args.append(depth)
         if is_repos_wc:
           args.append('--old')
-          args.append(sbox.repo_url)
+          args.append(sbox.repo_url + '/' + subdir)
           args.append('--new')
-          args.append(sbox.wc_dir)
+          args.append(os.path.join(wc_dir, subdir))
         else:
-          args.append(wc_dir)
+          args.append(os.path.join(wc_dir, subdir))
 
         # Run 'svn diff ...'
         exit_code, output, errput = svntest.main.run_svn(None, *args)
@@ -1180,6 +1198,44 @@ def readd_after_revert(sbox):
   svntest.actions.run_and_verify_svn(None, [],
                                      'add', dummy)
 
+#----------------------------------------------------------------------
+
+# A wc-wc diff returned no results if changelists were specified and the
+# diff target dir was not the WC root.
+@Issue(4822)
+def diff_with_changelists_subdir(sbox):
+  "diff --changelist (wc-wc) in subdir of WC"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  expected_paths = sbox.ospaths(['A/D/gamma'])
+  subdir = 'A/D'
+  clname = 'a'
+
+  for path in expected_paths:
+    svntest.main.file_append(path, "New text.\n")
+  svntest.main.run_svn(None, "changelist", clname, *expected_paths)
+
+  # Run 'svn diff ...'
+  exit_code, output, errput = svntest.main.run_svn(None,
+                                'diff', '--changelist', clname,
+                                sbox.ospath(subdir))
+
+  # Filter the output for lines that begin with 'Index:', and
+  # reduce even those lines to just the actual path.
+  paths = sorted([x[7:].rstrip() for x in output if x[:7] == 'Index: '])
+
+  # Diff output on Win32 uses '/' path separators.
+  if sys.platform == 'win32':
+    paths = [x.replace('/', os.sep) for x in paths]
+
+  # And, compare!
+  if (paths != expected_paths):
+    raise svntest.Failure("Expected paths (%s) and actual paths (%s) "
+                          "don't gel"
+                          % (str(expected_paths), str(paths)))
+
 
 ########################################################################
 # Run the tests
@@ -1203,6 +1259,7 @@ test_list = [ None,
               add_remove_non_existent_target,
               add_remove_unversioned_target,
               readd_after_revert,
+              diff_with_changelists_subdir,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout Fri Sep 20 11:18:26 2019
@@ -47,15 +47,8 @@ Available subcommands:
    unlock
    update (up)
    upgrade
-   x-shelf-diff
-   x-shelf-drop
-   x-shelf-list (x-shelves)
-   x-shelf-list-by-paths
-   x-shelf-log
-   x-shelf-save
-   x-shelve
-   x-unshelve
-   x-wc-copy-mods
+
+(Use '-v' to show experimental subcommands.)
 
 Subversion is a tool for version control.
 For additional information, see http://subversion.apache.org/

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout Fri Sep 20 11:18:26 2019
@@ -135,30 +135,7 @@ Valid options:
                              to prevent shell expansion)
   --search-and ARG         : combine ARG with the previous search pattern
 
-Global options:
-  --username ARG           : specify a username ARG
-  --password ARG           : specify a password ARG (caution: on many operating
-                             systems, other users will be able to see this)
-  --password-from-stdin    : read password from stdin
-  --no-auth-cache          : do not cache authentication tokens
-  --non-interactive        : do no interactive prompting (default is to prompt
-                             only if standard input is a terminal device)
-  --force-interactive      : do interactive prompting even if standard input
-                             is not a terminal device
-  --trust-server-cert      : deprecated; same as
-                             --trust-server-cert-failures=unknown-ca
-  --trust-server-cert-failures ARG : with --non-interactive, accept SSL server
-                             certificates with failures; ARG is comma-separated
-                             list of 'unknown-ca' (Unknown Authority),
-                             'cn-mismatch' (Hostname mismatch), 'expired'
-                             (Expired certificate), 'not-yet-valid' (Not yet
-                             valid certificate) and 'other' (all other not
-                             separately classified certificate errors).
-  --config-dir ARG         : read user configuration files from directory ARG
-  --config-option ARG      : set user configuration option in the format:
-                                 FILE:SECTION:OPTION=[VALUE]
-                             For example:
-                                 servers:global:http-library=serf
+(Use '-v' to show global and experimental options.)
 
 switch (sw): Update the working copy to a different URL within the same repository.
 usage: 1. switch URL[@PEGREV] [PATH]
@@ -226,28 +203,5 @@ Valid options:
                              'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l', 'r')
   --relocate               : deprecated; use 'svn relocate'
 
-Global options:
-  --username ARG           : specify a username ARG
-  --password ARG           : specify a password ARG (caution: on many operating
-                             systems, other users will be able to see this)
-  --password-from-stdin    : read password from stdin
-  --no-auth-cache          : do not cache authentication tokens
-  --non-interactive        : do no interactive prompting (default is to prompt
-                             only if standard input is a terminal device)
-  --force-interactive      : do interactive prompting even if standard input
-                             is not a terminal device
-  --trust-server-cert      : deprecated; same as
-                             --trust-server-cert-failures=unknown-ca
-  --trust-server-cert-failures ARG : with --non-interactive, accept SSL server
-                             certificates with failures; ARG is comma-separated
-                             list of 'unknown-ca' (Unknown Authority),
-                             'cn-mismatch' (Hostname mismatch), 'expired'
-                             (Expired certificate), 'not-yet-valid' (Not yet
-                             valid certificate) and 'other' (all other not
-                             separately classified certificate errors).
-  --config-dir ARG         : read user configuration files from directory ARG
-  --config-option ARG      : set user configuration option in the format:
-                                 FILE:SECTION:OPTION=[VALUE]
-                             For example:
-                                 servers:global:http-library=serf
+(Use '-v' to show global and experimental options.)
 

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout Fri Sep 20 11:18:26 2019
@@ -47,15 +47,8 @@ Available subcommands:
    unlock
    update (up)
    upgrade
-   x-shelf-diff
-   x-shelf-drop
-   x-shelf-list (x-shelves)
-   x-shelf-list-by-paths
-   x-shelf-log
-   x-shelf-save
-   x-shelve
-   x-unshelve
-   x-wc-copy-mods
+
+(Use '-v' to show experimental subcommands.)
 
 Subversion is a tool for version control.
 For additional information, see http://subversion.apache.org/

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/mod_dav_svn_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/mod_dav_svn_tests.py?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/mod_dav_svn_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/mod_dav_svn_tests.py Fri Sep 20 11:18:26 2019
@@ -640,6 +640,53 @@ def propfind_propname(sbox):
   actual_response = r.read()
   verify_xml_response(expected_response, actual_response)
 
+@SkipUnless(svntest.main.is_ra_type_dav)
+def last_modified_header(sbox):
+  "verify 'Last-Modified' header on 'external' GETs"
+
+  sbox.build(create_wc=False, read_only=True)
+
+  headers = {
+    'Authorization': 'Basic ' + base64.b64encode(b'jconstant:rayjandom').decode(),
+  }
+
+  h = svntest.main.create_http_connection(sbox.repo_url)
+
+  # GET /repos/iota
+  # Expect to see a Last-Modified header.
+  h.request('GET', sbox.repo_url + '/iota', None, headers)
+  r = h.getresponse()
+  if r.status != httplib.OK:
+    raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason))
+  svntest.verify.compare_and_display_lines(None, 'Last-Modified',
+                                           svntest.verify.RegexOutput('.+'),
+                                           r.getheader('Last-Modified'))
+  r.read()
+
+  # HEAD /repos/iota
+  # Expect to see a Last-Modified header.
+  h.request('HEAD', sbox.repo_url + '/iota', None, headers)
+  r = h.getresponse()
+  if r.status != httplib.OK:
+    raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason))
+  svntest.verify.compare_and_display_lines(None, 'Last-Modified',
+                                           svntest.verify.RegexOutput('.+'),
+                                           r.getheader('Last-Modified'))
+  r.read()
+
+  # GET /repos/!svn/rvr/1/iota
+  # There should not be a Last-Modified header (it's costly and not useful,
+  # see r1724790)
+  h.request('GET', sbox.repo_url + '/!svn/rvr/1/iota', None, headers)
+  r = h.getresponse()
+  if r.status != httplib.OK:
+    raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason))
+  last_modified = r.getheader('Last-Modified')
+  if last_modified:
+    raise svntest.Failure('Unexpected Last-Modified header: %s' % last_modified)
+  r.read()
+
+  
 ########################################################################
 # Run the tests
 
@@ -652,6 +699,7 @@ test_list = [ None,
               propfind_404,
               propfind_allprop,
               propfind_propname,
+              last_modified_header,
              ]
 serial_only = True
 

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/shelf_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/shelf_tests.py?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/shelf_tests.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/shelf_tests.py Fri Sep 20 11:18:26 2019
@@ -900,6 +900,8 @@ def run_and_verify_shelf_diff_summarize(
     svntest.verify.display_trees(None, 'DIFF OUTPUT TREE', output_tree, actual)
     raise
 
+#----------------------------------------------------------------------
+
 # Exercise a very basic case of shelf-diff.
 def shelf_diff_simple(sbox):
   "shelf diff simple"
@@ -945,6 +947,23 @@ def shelf_diff_simple(sbox):
   })
   run_and_verify_shelf_diff_summarize(expected_diff, 'foo')
 
+#----------------------------------------------------------------------
+
+@XFail()
+@Issue(4827)
+def shelve_with_kw_translation(sbox):
+  "shelve with kw translation"
+  sbox.build(empty=True)
+  sbox.simple_add_text('$Rev$\n', 'file')
+  sbox.simple_propset('svn:keywords', 'rev', 'file')
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  def modifier(sbox):
+    sbox.simple_append('file', 'New line\n')
+
+  shelve_unshelve(sbox, modifier)
+
 
 ########################################################################
 # Run the tests
@@ -982,6 +1001,7 @@ test_list = [ None,
               unshelve_text_prop_merge,
               unshelve_text_prop_conflict,
               shelf_diff_simple,
+              shelve_with_kw_translation,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/swig-py3/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/cmdline/svntest/main.py?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/swig-py3/subversion/tests/cmdline/svntest/main.py Fri Sep 20 11:18:26 2019
@@ -57,7 +57,7 @@ from svntest import Failure
 from svntest import Skip
 from svntest.wc import StateItem as Item
 
-SVN_VER_MINOR = 13
+SVN_VER_MINOR = 14
 
 ######################################################################
 #

Propchange: subversion/branches/swig-py3/subversion/tests/libsvn_subr/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Sep 20 11:18:26 2019
@@ -59,3 +59,4 @@ test_apr_trunc_workaround
 save-cleartext
 test_stream_readline_file_crlf
 test_stream_readline_file_lf
+test_stream_readline_file_nul

Modified: subversion/branches/swig-py3/subversion/tests/libsvn_subr/stream-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/libsvn_subr/stream-test.c?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/libsvn_subr/stream-test.c (original)
+++ subversion/branches/swig-py3/subversion/tests/libsvn_subr/stream-test.c Fri Sep 20 11:18:26 2019
@@ -1000,6 +1000,71 @@ test_stream_readline_file_crlf(apr_pool_
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_stream_readline_file_nul(apr_pool_t *pool)
+{
+  /* Test is written based on the problem report in
+       https://lists.apache.org/thread.html/c96eb5618ac0bf6e083345e0fdcdcf834e30913f26eabe6ada7bab62@%3Cusers.subversion.apache.org%3E
+     where the user had an OOM when calling `svndumpfilter` with
+     a (most likely) invalid dump containing nul bytes.
+   */
+  const char data[] =
+    {
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n'
+    };
+  const char *tmp_dir;
+  const char *tmp_file;
+  svn_stream_t *stream;
+
+  SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_stream_readline_file_nul", pool));
+  SVN_ERR(svn_io_remove_dir2(tmp_dir, TRUE, NULL, NULL, pool));
+  SVN_ERR(svn_io_make_dir_recursively(tmp_dir, pool));
+  svn_test_add_dir_cleanup(tmp_dir);
+
+  tmp_file = svn_dirent_join(tmp_dir, "file", pool);
+  SVN_ERR(svn_io_file_create_bytes(tmp_file, data, sizeof(data), pool));
+  SVN_ERR(svn_stream_open_readonly(&stream, tmp_file, pool, pool));
+
+  while (1)
+    {
+      svn_boolean_t eof;
+      svn_stringbuf_t *line;
+
+      SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, pool));
+      if (eof)
+        break;
+
+      /* Don't check the contents of the `line` here, at least for now.
+
+         In other words, we just test that this case does not crash or cause
+         unexpected errors.  The reason is that currently our `readline_fn`
+         implementations have inconsistent behavior when dealing with \0 bytes,
+         handling those differently, either in strchr() or memchr() styles.
+
+         Once we make them consistent (or even decide to bail out with an error
+         for \0), this part of the test should start properly checking `data`;
+         maybe also for non-file streams.
+       */
+    }
+
+  SVN_ERR(svn_stream_close(stream));
+
+  return SVN_NO_ERROR;
+}
+
 /* The test table.  */
 
 static int max_threads = 1;
@@ -1037,6 +1102,8 @@ static struct svn_test_descriptor_t test
                    "test reading LF-terminated lines from file"),
     SVN_TEST_PASS2(test_stream_readline_file_crlf,
                    "test reading CRLF-terminated lines from file"),
+    SVN_TEST_PASS2(test_stream_readline_file_nul,
+                   "test reading line from file with nul bytes"),
     SVN_TEST_NULL
   };
 

Modified: subversion/branches/swig-py3/subversion/tests/libsvn_wc/wc-queries-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/tests/libsvn_wc/wc-queries-test.c?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/tests/libsvn_wc/wc-queries-test.c (original)
+++ subversion/branches/swig-py3/subversion/tests/libsvn_wc/wc-queries-test.c Fri Sep 20 11:18:26 2019
@@ -747,7 +747,7 @@ test_query_expectations(apr_pool_t *scra
                      as 'DROP TABLE foo', but the performance of such
                      statements is not our concern here. */
 
-                  /* "Slow" statements do expect too see a warning, however. */
+                  /* "Slow" statements do expect to see a warning, however. */
                   if (is_slow_statement(i))
                     warned = TRUE;
                 }

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnbuild.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnbuild.sh?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnbuild.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnbuild.sh Fri Sep 20 11:18:26 2019
@@ -22,8 +22,10 @@
 set -e
 set -x
 
+(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile)
+(cd .. && gmake dirs-create fetch)
 url="$(svn info --show-item url)"
 branch="${url##*/}"
-(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile)
+mkdir -p ../objdir/svn-${branch}
 touch ../objdir/svn-${branch}/.retrieved
 (cd .. && gmake BRANCH="$branch" THREADING="no" JAVA="no" MAKE_JOBS=8)

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svncheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svncheck.sh?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svncheck.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svncheck.sh Fri Sep 20 11:18:26 2019
@@ -26,7 +26,7 @@ url="$(svn info --show-item url)"
 branch="${url##*/}"
 export MALLOC_OPTIONS=S
 (cd .. && gmake BRANCH="$branch" PARALLEL="4" THREADING="no" JAVA="no" \
-                EXCLUSIVE_WC_LOCKS=1 \
+                EXCLUSIVE_WC_LOCKS=1 SVN_CHECK_FS_BACKENDS=fsfs \
                                   svn-check-local \
                                   svn-check-svn \
                                   svn-check-neon \

Modified: subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnclean.sh
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnclean.sh?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnclean.sh (original)
+++ subversion/branches/swig-py3/tools/buildbot/slaves/bb-openbsd/svnclean.sh Fri Sep 20 11:18:26 2019
@@ -22,8 +22,6 @@
 set -e
 set -x
 
-url="$(svn info --show-item url)"
-branch="${url##*/}"
 (test -h ../svn-trunk || ln -s build ../svn-trunk)
 for i in $(jot - 6 12); do
   (test -h ../svn-1.${i}.x || ln -s build ../svn-1.${i}.x)
@@ -33,6 +31,8 @@ svn update ../../unix-build
 newlastchangedrev="$(svn info --show-item=last-changed-revision ../../unix-build/Makefile.svn)"
 (test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile)
 # always rebuild svn, but only rebuild dependencies if Makefile.svn has changed
+url="$(svn info --show-item url)"
+branch="${url##*/}"
 if [ "$lastchangedrev" != "$newlastchangedrev" ]; then
   (cd .. && gmake BRANCH="$branch" reset clean)
 else

Modified: subversion/branches/swig-py3/tools/dev/unix-build/Makefile.svn
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dev/unix-build/Makefile.svn?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dev/unix-build/Makefile.svn (original)
+++ subversion/branches/swig-py3/tools/dev/unix-build/Makefile.svn Fri Sep 20 11:18:26 2019
@@ -46,6 +46,7 @@ USE_HTTPV1 ?= no
 USE_AUTHZ_SHORT_CIRCUIT ?= no
 RAMDISK ?= /ramdisk
 MAKE_JOBS ?= 4
+SVN_CHECK_FS_BACKENDS ?= fsfs bdb
 
 PWD		= $(shell pwd)
 UNAME		= $(shell uname)
@@ -191,12 +192,12 @@ NEON_URL	= http://ftp.openbsd.org/pub/Op
 SERF_URL	= https://svn.apache.org/repos/asf/serf/tags/$(SERF_VER)
 SERF_OLD_URL	= https://svn.apache.org/repos/asf/serf/tags/$(SERF_OLD_VER)
 SQLITE_URL	= https://www.sqlite.org/2017/$(SQLITE_DIST)
-CYRUS_SASL_URL	= ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/$(CYRUS_SASL_DIST)
+CYRUS_SASL_URL	= https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-${CYRUS_SASL_VER}/$(CYRUS_SASL_DIST)
 LIBMAGIC_URL	= ftp://ftp.astron.com/pub/file/$(LIBMAGIC_DIST)
 RUBY_URL	= https://cache.ruby-lang.org/pub/ruby/2.4/$(RUBY_DIST)
-BZ2_URL		= https://ftp.openbsd.org/pub/OpenBSD/distfiles/$(BZ2_DIST)
+BZ2_URL		= https://stsp.name/distfiles/$(BZ2_DIST)
 PYTHON_URL	= https://python.org/ftp/python/$(PYTHON_VER)/$(PYTHON_DIST)
-JUNIT_URL	= https://downloads.sourceforge.net/project/junit/junit/$(JUNIT_VER)/$(JUNIT_DIST)
+JUNIT_URL	= https://stsp.name/distfiles/$(JUNIT_DIST)
 GETTEXT_URL	= https://ftp.gnu.org/pub/gnu/gettext/$(GETTEXT_DIST)
 LZ4_URL		= https://github.com/lz4/lz4/archive/v$(LZ4_VER).tar.gz
 
@@ -2135,7 +2136,7 @@ stop-svnserve:
 	$(SVNSERVE_STOP_CMD)
 
 define do_check
--cd $(svn_builddir) && for fs in fsfs bdb; do \
+-cd $(svn_builddir) && for fs in $(SVN_CHECK_FS_BACKENDS); do \
     echo "Begin test: $(subst svn-check-,,$@) x $$fs"; \
     test -d "$(RAMDISK)/tmp" && export TMPDIR="$(RAMDISK)/tmp"; \
     env LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(LIB_PTHREAD_HACK) \

Modified: subversion/branches/swig-py3/tools/dist/release.py
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dist/release.py?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dist/release.py (original)
+++ subversion/branches/swig-py3/tools/dist/release.py Fri Sep 20 11:18:26 2019
@@ -77,6 +77,14 @@ tool_versions = {
             'swig'     : ['3.0.12',
             '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
   },
+  '1.13' : {
+            'autoconf' : ['2.69',
+            '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
+            'libtool'  : ['2.4.6',
+            'e3bd4d5d3d025a36c21dd6af7ea818a2afcd4dfc1ea5a17b39d7854bcd0c06e3'],
+            'swig'     : ['3.0.12',
+            '7cf9f447ae7ed1c51722efc45e7f14418d15d7a1e143ac9f09a668999f4fc94d'],
+  },
   '1.12' : {
             'autoconf' : ['2.69',
             '954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969'],
@@ -126,11 +134,11 @@ recommended_release = '1.12'
 supported_release_lines = frozenset({"1.9", "1.10", "1.12", "1.13"})
 
 # Some constants
-repos = 'https://svn.apache.org/repos/asf/subversion'
-secure_repos = 'https://svn.apache.org/repos/asf/subversion'
+svn_repos = 'https://svn.apache.org/repos/asf/subversion'
 dist_repos = 'https://dist.apache.org/repos/dist'
 dist_dev_url = dist_repos + '/dev/subversion'
 dist_release_url = dist_repos + '/release/subversion'
+dist_archive_url = 'https://archive.apache.org/dist/subversion'
 KEYS = 'https://people.apache.org/keys/group/subversion.asc'
 extns = ['zip', 'tar.gz', 'tar.bz2']
 
@@ -284,6 +292,15 @@ def get_target(args):
     else:
         return get_deploydir(args.base_dir)
 
+def get_branch_path(args):
+    if not args.branch:
+        try:
+            args.branch = 'branches/%d.%d.x' % (args.version.major, args.version.minor)
+        except AttributeError:
+            raise RuntimeError("Please specify the release version label or --branch-path")
+
+    return args.branch.rstrip('/')  # canonicalize for later comparisons
+
 def get_tmpldir():
     return os.path.join(os.path.abspath(sys.path[0]), 'templates')
 
@@ -292,12 +309,14 @@ def get_tmplfile(filename):
         return open(os.path.join(get_tmpldir(), filename))
     except IOError:
         # Hmm, we had a problem with the local version, let's try the repo
-        return urllib2.urlopen(repos + '/trunk/tools/dist/templates/' + filename)
+        return urllib2.urlopen(svn_repos + '/trunk/tools/dist/templates/' + filename)
 
 def get_nullfile():
     return open(os.path.devnull, 'w')
 
-def run_script(verbose, script, hide_stderr=False):
+def run_command(cmd, verbose=True, hide_stderr=False):
+    if verbose:
+        print("+ " + ' '.join(cmd))
     stderr = None
     if verbose:
         stdout = None
@@ -306,8 +325,11 @@ def run_script(verbose, script, hide_std
         if hide_stderr:
             stderr = get_nullfile()
 
+    subprocess.check_call(cmd, stdout=stdout, stderr=stderr)
+
+def run_script(verbose, script, hide_stderr=False):
     for l in script.split('\n'):
-        subprocess.check_call(l.split(), stdout=stdout, stderr=stderr)
+        run_command(l.split(), verbose, hide_stderr)
 
 def download_file(url, target, checksum):
     response = urllib2.urlopen(url)
@@ -323,6 +345,16 @@ def download_file(url, target, checksum)
                            "downloaded: '%s'; expected: '%s'" % \
                            (target, checksum, checksum2))
 
+def run_svn(cmd, verbose=True, username=None):
+    if (username):
+        cmd[:0] = ['--username', username]
+    run_command(['svn'] + cmd, verbose)
+
+def run_svnmucc(cmd, verbose=True, username=None):
+    if (username):
+        cmd[:0] = ['--username', username]
+    run_command(['svnmucc'] + cmd, verbose)
+
 #----------------------------------------------------------------------
 # ezt helpers
 
@@ -552,16 +584,12 @@ def replace_lines(path, actions):
 def roll_tarballs(args):
     'Create the release artifacts.'
 
-    if not args.branch:
-        args.branch = 'branches/%d.%d.x' % (args.version.major, args.version.minor)
-
-    branch = args.branch # shorthand
-    branch = branch.rstrip('/') # canonicalize for later comparisons
+    branch = get_branch_path(args)
 
     logging.info('Rolling release %s from branch %s@%d' % (args.version,
                                                            branch, args.revnum))
 
-    check_copyright_year(repos, args.branch, args.revnum)
+    check_copyright_year(svn_repos, branch, args.revnum)
 
     # Ensure we've got the appropriate rolling dependencies available
     autoconf = AutoconfDep(args.base_dir, False, args.verbose,
@@ -580,7 +608,7 @@ def roll_tarballs(args):
 
     if branch != 'trunk':
         # Make sure CHANGES is sync'd.
-        compare_changes(repos, branch, args.revnum)
+        compare_changes(svn_repos, branch, args.revnum)
 
     # Ensure the output directory doesn't already exist
     if os.path.exists(get_deploydir(args.base_dir)):
@@ -591,9 +619,10 @@ def roll_tarballs(args):
 
     logging.info('Preparing working copy source')
     shutil.rmtree(get_workdir(args.base_dir), True)
-    run_script(args.verbose, 'svn checkout %s %s'
-               % (repos + '/' + branch + '@' + str(args.revnum),
-                  get_workdir(args.base_dir)))
+    run_svn(['checkout',
+             svn_repos + '/' + branch + '@' + str(args.revnum),
+             get_workdir(args.base_dir)],
+            verbose=args.verbose)
 
     # Exclude stuff we don't want in the tarball, it will not be present
     # in the exported tree.
@@ -604,8 +633,8 @@ def roll_tarballs(args):
             exclude += ['packages', 'www']
     cwd = os.getcwd()
     os.chdir(get_workdir(args.base_dir))
-    run_script(args.verbose,
-               'svn update --set-depth exclude %s' % " ".join(exclude))
+    run_svn(['update', '--set-depth=exclude'] + exclude,
+            verbose=args.verbose)
     os.chdir(cwd)
 
     if args.patches:
@@ -615,10 +644,10 @@ def roll_tarballs(args):
         for name in os.listdir(args.patches):
             if name.find(majmin) != -1 and name.endswith('patch'):
                 logging.info('Applying patch %s' % name)
-                run_script(args.verbose,
-                           '''svn patch %s %s'''
-                           % (os.path.join(args.patches, name),
-                              get_workdir(args.base_dir)))
+                run_svn(['patch',
+                         os.path.join(args.patches, name),
+                         get_workdir(args.base_dir)],
+                        verbose=args.verbose)
 
     # Massage the new version number into svn_version.h.
     ver_tag, ver_numtag = args.version.get_ver_tags(args.revnum)
@@ -656,8 +685,9 @@ def roll_tarballs(args):
             eol_style = "--native-eol CRLF"
         else:
             eol_style = "--native-eol LF"
-        run_script(args.verbose, "svn export %s %s %s"
-                   % (eol_style, get_workdir(args.base_dir), exportdir))
+        run_svn(['export',
+                 eol_style, get_workdir(args.base_dir), exportdir],
+                verbose=args.verbose)
 
     def transform_sql():
         for root, dirs, files in os.walk(exportdir):
@@ -775,7 +805,7 @@ def sign_candidates(args):
     for e in extns:
         filename = os.path.join(target, 'subversion-%s.%s' % (args.version, e))
         sign_file(filename)
-        if args.version.major >= 1 and args.version.minor <= 6:
+        if args.version.major == 1 and args.version.minor <= 6:
             filename = os.path.join(target,
                                    'subversion-deps-%s.%s' % (args.version, e))
             sign_file(filename)
@@ -791,14 +821,12 @@ def post_candidates(args):
 
     logging.info('Importing tarballs to %s' % dist_dev_url)
     ver = str(args.version)
-    svn_cmd = ['svn', 'import', '-m',
+    svn_cmd = ['import', '-m',
                'Add Subversion %s candidate release artifacts' % ver,
                '--auto-props', '--config-option',
                'config:auto-props:*.asc=svn:eol-style=native;svn:mime-type=text/plain',
                target, dist_dev_url]
-    if (args.username):
-        svn_cmd += ['--username', args.username]
-    subprocess.check_call(svn_cmd)
+    run_svn(svn_cmd, verbose=args.verbose, username=args.username)
 
 #----------------------------------------------------------------------
 # Create tag
@@ -811,25 +839,19 @@ def create_tag_only(args):
 
     logging.info('Creating tag for %s' % str(args.version))
 
-    if not args.branch:
-        args.branch = 'branches/%d.%d.x' % (args.version.major, args.version.minor)
-
-    branch = secure_repos + '/' + args.branch.rstrip('/')
+    branch_url = svn_repos + '/' + get_branch_path(args)
 
-    tag = secure_repos + '/tags/' + str(args.version)
+    tag = svn_repos + '/tags/' + str(args.version)
 
-    svnmucc_cmd = ['svnmucc', '-m',
-                   'Tagging release ' + str(args.version)]
-    if (args.username):
-        svnmucc_cmd += ['--username', args.username]
-    svnmucc_cmd += ['cp', str(args.revnum), branch, tag]
+    svnmucc_cmd = ['-m', 'Tagging release ' + str(args.version)]
+    svnmucc_cmd += ['cp', str(args.revnum), branch_url, tag]
     svnmucc_cmd += ['put', os.path.join(target, 'svn_version.h.dist' + '-' +
                                         str(args.version)),
                     tag + '/subversion/include/svn_version.h']
 
     # don't redirect stdout/stderr since svnmucc might ask for a password
     try:
-        subprocess.check_call(svnmucc_cmd)
+        run_svnmucc(svnmucc_cmd, verbose=args.verbose, username=args.username)
     except subprocess.CalledProcessError:
         if args.version.is_prerelease():
             logging.error("Do you need to pass --branch=trunk?")
@@ -840,10 +862,7 @@ def bump_versions_on_branch(args):
 
     logging.info('Bumping version numbers on the branch')
 
-    if not args.branch:
-        args.branch = 'branches/%d.%d.x' % (args.version.major, args.version.minor)
-
-    branch = secure_repos + '/' + args.branch.rstrip('/')
+    branch_url = svn_repos + '/' + get_branch_path(args)
 
     def replace_in_place(fd, startofline, flat, spare):
         """In file object FD, replace FLAT with SPARE in the first line
@@ -871,11 +890,11 @@ def bump_versions_on_branch(args):
                            args.version.patch + 1))
 
     HEAD = subprocess.check_output(['svn', 'info', '--show-item=revision',
-                                    '--', branch]).strip()
+                                    '--', branch_url]).strip()
     HEAD = int(HEAD)
     def file_object_for(relpath):
         fd = tempfile.NamedTemporaryFile()
-        url = branch + '/' + relpath
+        url = branch_url + '/' + relpath
         fd.url = url
         subprocess.check_call(['svn', 'cat', '%s@%d' % (url, HEAD)],
                               stdout=fd)
@@ -891,13 +910,14 @@ def bump_versions_on_branch(args):
 
     svn_version_h.seek(0, os.SEEK_SET)
     STATUS.seek(0, os.SEEK_SET)
-    subprocess.check_call(['svnmucc', '-r', str(HEAD),
-                           '-m', 'Post-release housekeeping: '
-                                 'bump the %s branch to %s.'
-                           % (branch.split('/')[-1], str(new_version)),
-                           'put', svn_version_h.name, svn_version_h.url,
-                           'put', STATUS.name, STATUS.url,
-                          ])
+    run_svnmucc(['-r', str(HEAD),
+                 '-m', 'Post-release housekeeping: '
+                       'bump the %s branch to %s.'
+                 % (branch_url.split('/')[-1], str(new_version)),
+                 'put', svn_version_h.name, svn_version_h.url,
+                 'put', STATUS.name, STATUS.url,
+                ],
+                verbose=args.verbose, username=args.username)
     del svn_version_h
     del STATUS
 
@@ -939,11 +959,8 @@ def clean_dist(args):
     for i in sorted(to_keep):
         logging.info("Saving release '%s'", i)
 
-    svnmucc_cmd = ['svnmucc', '-m', 'Remove old Subversion releases.\n' +
-                   'They are still available at ' +
-                   'https://archive.apache.org/dist/subversion/']
-    if (args.username):
-        svnmucc_cmd += ['--username', args.username]
+    svnmucc_cmd = ['-m', 'Remove old Subversion releases.\n' +
+                   'They are still available at ' + dist_archive_url]
     for filename in filenames:
         if Version(filename) not in to_keep:
             logging.info("Removing %r", filename)
@@ -951,7 +968,7 @@ def clean_dist(args):
 
     # don't redirect stdout/stderr since svnmucc might ask for a password
     if 'rm' in svnmucc_cmd:
-        subprocess.check_call(svnmucc_cmd)
+        run_svnmucc(svnmucc_cmd, verbose=args.verbose, username=args.username)
     else:
         logging.info("Nothing to remove")
 
@@ -967,10 +984,8 @@ def move_to_dist(args):
     for entry in stdout.split('\n'):
       if fnmatch.fnmatch(entry, 'subversion-%s.*' % str(args.version)):
         filenames.append(entry)
-    svnmucc_cmd = ['svnmucc', '-m',
+    svnmucc_cmd = ['-m',
                    'Publish Subversion-%s.' % str(args.version)]
-    if (args.username):
-        svnmucc_cmd += ['--username', args.username]
     svnmucc_cmd += ['rm', dist_dev_url + '/' + 'svn_version.h.dist'
                           + '-' + str(args.version)]
     for filename in filenames:
@@ -979,7 +994,7 @@ def move_to_dist(args):
 
     # don't redirect stdout/stderr since svnmucc might ask for a password
     logging.info('Moving release artifacts to %s' % dist_release_url)
-    subprocess.check_call(svnmucc_cmd)
+    run_svnmucc(svnmucc_cmd, verbose=args.verbose, username=args.username)
 
 #----------------------------------------------------------------------
 # Write announcements
@@ -1026,7 +1041,7 @@ def get_fileinfo(args):
 
     target = get_target(args)
 
-    files = glob.glob(os.path.join(target, 'subversion*-%s*.asc' % args.version))
+    files = glob.glob(os.path.join(target, 'subversion*-%s.*.asc' % args.version))
     files.sort()
 
     class info(object):
@@ -1117,14 +1132,12 @@ def get_siginfo(args, quiet=False):
         import security._gnupg as gnupg
     gpg = gnupg.GPG()
 
-    target = get_target(args)
-
     good_sigs = {}
     fingerprints = {}
     output = []
 
-    glob_pattern = os.path.join(target, 'subversion*-%s*.asc' % args.version)
-    for filename in glob.glob(glob_pattern):
+    for fileinfo in get_fileinfo(args):
+        filename = os.path.join(get_target(args), fileinfo.filename + '.asc')
         text = open(filename).read()
         keys = text.split(key_start)
 
@@ -1315,13 +1328,13 @@ def write_changelog(args):
     #   New svn_ra_list() API function [D:api]
     #   [D:bindings] JavaHL: Allow access to constructors of a couple JavaHL classes
 
-    branch = secure_repos + '/' + args.branch
-    previous = secure_repos + '/' + args.previous
+    branch_url = svn_repos + '/' + get_branch_path(args)
+    previous = svn_repos + '/' + args.previous
     include_unlabeled = args.include_unlabeled
     separator_line = ('-' * 72) + '\n'
     
     mergeinfo = subprocess.check_output(['svn', 'mergeinfo', '--show-revs',
-                    'eligible', '--log', branch, previous])
+                    'eligible', '--log', branch_url, previous])
     log_messages_dict = {
         # This is a dictionary mapping revision numbers to their respective
         # log messages.  The expression in the "key:" part of the dict
@@ -1462,6 +1475,13 @@ def main():
                    help='''The directory in which to create needed files and
                            folders.  The default is the current working
                            directory.''')
+    parser.add_argument('--branch',
+                   help='''The branch to base the release on,
+                           as a path relative to ^/subversion/.
+                           Default: 'branches/MAJOR.MINOR.x'.''')
+    parser.add_argument('--username',
+                   help='Username for committing to ' + svn_repos +
+                        ' or ' + dist_repos + '.')
     subparsers = parser.add_subparsers(title='subcommands')
 
     # Setup the parser for the build-env subcommand
@@ -1487,9 +1507,6 @@ def main():
                     help='''The release label, such as '1.7.0-alpha1'.''')
     subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
                     help='''The revision number to base the release on.''')
-    subparser.add_argument('--branch',
-                    help='''The branch to base the release on,
-                            relative to ^/subversion/.''')
     subparser.add_argument('--patches',
                     help='''The path to the directory containing patches.''')
 
@@ -1514,8 +1531,6 @@ def main():
     subparser.set_defaults(func=post_candidates)
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
-    subparser.add_argument('--username',
-                    help='''Username for ''' + dist_repos + '''.''')
     subparser.add_argument('--target',
                     help='''The full path to the directory containing
                             release artifacts.''')
@@ -1529,11 +1544,6 @@ def main():
                     help='''The release label, such as '1.7.0-alpha1'.''')
     subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
                     help='''The revision number to base the release on.''')
-    subparser.add_argument('--branch',
-                    help='''The branch to base the release on,
-                            relative to ^/subversion/.''')
-    subparser.add_argument('--username',
-                    help='''Username for ''' + secure_repos + '''.''')
     subparser.add_argument('--target',
                     help='''The full path to the directory containing
                             release artifacts.''')
@@ -1546,11 +1556,6 @@ def main():
                     help='''The release label, such as '1.7.0-alpha1'.''')
     subparser.add_argument('revnum', type=lambda arg: int(arg.lstrip('r')),
                     help='''The revision number to base the release on.''')
-    subparser.add_argument('--branch',
-                    help='''The branch to base the release on,
-                            relative to ^/subversion/.''')
-    subparser.add_argument('--username',
-                    help='''Username for ''' + secure_repos + '''.''')
     subparser.add_argument('--target',
                     help='''The full path to the directory containing
                             release artifacts.''')
@@ -1561,8 +1566,6 @@ def main():
     subparser.set_defaults(func=clean_dist)
     subparser.add_argument('--dist-dir',
                     help='''The directory to clean.''')
-    subparser.add_argument('--username',
-                    help='''Username for ''' + dist_repos + '''.''')
 
     # The move-to-dist subcommand
     subparser = subparsers.add_parser('move-to-dist',
@@ -1572,8 +1575,6 @@ def main():
     subparser.set_defaults(func=move_to_dist)
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
-    subparser.add_argument('--username',
-                    help='''Username for ''' + dist_repos + '''.''')
 
     # The write-news subcommand
     subparser = subparsers.add_parser('write-news',
@@ -1641,10 +1642,6 @@ def main():
                             commit messages, optionally labeled with a category
                             like [U:client], [D:api], [U], ...''')
     subparser.set_defaults(func=write_changelog)
-    subparser.add_argument('branch',
-                    help='''The branch (or tag or trunk), relative to
-                            ^/subversion/, of which to generate the
-                            changelog, when compared to "previous".''')
     subparser.add_argument('previous',
                     help='''The "previous" branch or tag, relative to 
                             ^/subversion/, to compare "branch" against.''')

Modified: subversion/branches/swig-py3/tools/dist/templates/rc-release-ann.ezt
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dist/templates/rc-release-ann.ezt?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dist/templates/rc-release-ann.ezt (original)
+++ subversion/branches/swig-py3/tools/dist/templates/rc-release-ann.ezt Fri Sep 20 11:18:26 2019
@@ -1,5 +1,6 @@
 From: ...@apache.org
 To: announce@subversion.apache.org, users@subversion.apache.org, dev@subversion.apache.org, announce@apache.org
+Reply-To: users@subversion.apache.org
 Subject: [[]ANNOUNCE] Apache Subversion [version] released
 
 I'm happy to announce the release of Apache Subversion [version].

Modified: subversion/branches/swig-py3/tools/dist/templates/stable-release-ann.ezt
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/tools/dist/templates/stable-release-ann.ezt?rev=1867214&r1=1867213&r2=1867214&view=diff
==============================================================================
--- subversion/branches/swig-py3/tools/dist/templates/stable-release-ann.ezt (original)
+++ subversion/branches/swig-py3/tools/dist/templates/stable-release-ann.ezt Fri Sep 20 11:18:26 2019
@@ -1,5 +1,6 @@
 From: ...@apache.org
 To: announce@subversion.apache.org, users@subversion.apache.org, dev@subversion.apache.org, announce@apache.org
+Reply-To: users@subversion.apache.org
 [if-any security]Cc: security@apache.org, oss-security@lists.openwall.com, bugtraq@securityfocus.com
 [end][if-any security]Subject: [[]SECURITY][[]ANNOUNCE] Apache Subversion [version] released
 [else]Subject: [[]ANNOUNCE] Apache Subversion [version] released