Index: build.conf =================================================================== --- build.conf (revision 19226) +++ build.conf (working copy) @@ -87,6 +87,7 @@ subversion/tests/cmdline/import_tests.py subversion/tests/cmdline/svnsync_tests.py subversion/tests/cmdline/authz_tests.py + subversion/tests/cmdline/path_tests.py bdb-test-scripts = Index: subversion/include/svn_path.h =================================================================== --- subversion/include/svn_path.h (revision 19226) +++ subversion/include/svn_path.h (working copy) @@ -185,6 +185,8 @@ * separator characters, and possibly other semantically inoperative * transformations. * + * Convert the scheme and hostname to lowercase ( see issue 2475 ) + * * The returned path may be statically allocated, equal to @a path, or * allocated from @a pool. */ Index: subversion/libsvn_ra_dav/session.c =================================================================== --- subversion/libsvn_ra_dav/session.c (revision 19226) +++ subversion/libsvn_ra_dav/session.c (working copy) @@ -639,15 +639,6 @@ /* we want to know if the repository is actually somewhere else */ /* ### not yet: http_redirect_register(sess, ... ); */ - /* HACK! Neon uses strcmp when checking for https, but RFC 2396 says - * we should be using case-insensitive comparisons when checking for - * URI schemes. To allow our users to use WeIrd CasE HttPS we force - * the scheme to lower case before we pass it on to Neon, otherwise we - * would crash later on when we assume Neon has set up its https stuff - * but it really didn't. */ - for (itr = uri.scheme; *itr; ++itr) - *itr = tolower(*itr); - is_ssl_session = (strcasecmp(uri.scheme, "https") == 0); if (is_ssl_session) { Index: subversion/libsvn_subr/path.c =================================================================== --- subversion/libsvn_subr/path.c (revision 19226) +++ subversion/libsvn_subr/path.c (working copy) @@ -23,6 +23,7 @@ #include #include +#include #include "svn_string.h" #include "svn_path.h" @@ -1106,22 +1107,14 @@ apr_size_t seglen; apr_size_t canon_segments = 0; svn_boolean_t uri; + apr_uri_t host_uri; dst = canon = apr_pcalloc(pool, strlen(path) + 1); + src = path; - /* Copy over the URI scheme if present. */ - src = skip_uri_scheme(path); - if (src) - { - uri = TRUE; - memcpy(dst, path, src - path); - dst += (src - path); - } - else - { - uri = FALSE; - src = path; - } + /* parse the uri, check if we found a scheme */ + apr_uri_parse(pool, path, &host_uri); + uri = host_uri.scheme ? TRUE : FALSE; /* If this is an absolute path, then just copy over the initial separator character. */ @@ -1139,6 +1132,33 @@ } + if (uri) + { + /* convert scheme and hostname to lowercase */ + apr_size_t offset; + int i; + + for(i = 0; host_uri.scheme[i]; i++) + host_uri.scheme[i] = tolower(host_uri.scheme[i]); + for(i = 0; host_uri.hostname[i]; i++) + host_uri.hostname[i] = tolower(host_uri.hostname[i]); + + /* path will be pointing to a new memory location, so update src to + * point to the new location too. */ + offset = strlen(host_uri.scheme) + 3; /* "(scheme)://" */ + path = apr_uri_unparse(pool, &host_uri, APR_URI_UNP_REVEALPASSWORD); + + /* skip 3rd '/' in file:/// uri */ + if (path[offset] == '/') + offset++; + + /* copy src to dst */ + memcpy(dst, path, offset); + dst += offset; + + src = path + offset; + } + while (*src) { /* Parse each segment, find the closing '/' */ Index: subversion/libsvn_wc/entries.c =================================================================== --- subversion/libsvn_wc/entries.c (revision 19226) +++ subversion/libsvn_wc/entries.c (working copy) @@ -247,7 +247,8 @@ if (entry->url) { *modify_flags |= SVN_WC__ENTRY_MODIFY_URL; - entry->url = apr_pstrdup(pool, entry->url); + entry->url = apr_pstrdup(pool, + svn_path_canonicalize(entry->url, pool)); } } @@ -257,6 +258,7 @@ APR_HASH_KEY_STRING); if (entry->repos) { + entry->repos = svn_path_canonicalize(entry->repos, pool); if (entry->url && ! svn_path_is_ancestor(entry->repos, entry->url)) return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, _("Entry for '%s' has invalid repository " Index: subversion/tests/cmdline/path_tests.py =================================================================== --- subversion/tests/cmdline/path_tests.py (revision 0) +++ subversion/tests/cmdline/path_tests.py (revision 0) @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# +# path_tests.py: testing case insensitive hostname handling. +# +# Subversion is a tool for revision control. +# See http://subversion.tigris.org for more information. +# +# ==================================================================== +# Copyright (c) 2000-2006 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. +# +###################################################################### + +# General modules +import os +from string import lower, upper + +# Our testing module +import svntest + +import re + +# (abbreviation) +Item = svntest.wc.StateItem + +###################################################################### +# Utilities +# + +def change_case_of_hostname(input): + "Change the case of the hostname, try uppercase first" + + m = re.match(r"^(.*://)([^/]*)(.*)", input) + if m: + scheme = m.group(1) + host = upper(m.group(2)) + if host == m.group(2): + host = lower(m.group(2)) + + path = m.group(3) + + return scheme + host + path + +###################################################################### +# Tests +# +# Each test must return on success or raise on failure. + + +#---------------------------------------------------------------------- + +# regression test for issue #2475 - move file and folder +def path_move_and_copy_between_wcs(sbox): + "issue #2475 - move and copy between working copies" + sbox.build() + + # checkout a second working copy, use repository url with different case + wc2_dir = sbox.wc_dir + '2' + repo_url2 = change_case_of_hostname(svntest.main.current_repo_url) + + expected_output = svntest.main.greek_state.copy() + expected_output.wc_dir = wc2_dir + expected_output.tweak(status='A ', contents=None) + + expected_wc = svntest.main.greek_state + + # Do a checkout, and verify the resulting output and disk contents. + svntest.actions.run_and_verify_checkout(repo_url2, + wc2_dir, + expected_output, + expected_wc) + + # Copy a file from wc to wc2 + mu_path = os.path.join(sbox.wc_dir, 'A', 'mu') + E_path = os.path.join(wc2_dir, 'A', 'B', 'E') + + svntest.main.run_svn(None, 'cp', mu_path, E_path) + + # Copy a folder from wc to wc2 + C_path = os.path.join(sbox.wc_dir, 'A', 'C') + B_path = os.path.join(wc2_dir, 'A', 'B') + + svntest.main.run_svn(None, 'cp', C_path, B_path) + + # Move a file from wc to wc2 + mu_path = os.path.join(sbox.wc_dir, 'A', 'mu') + B_path = os.path.join(wc2_dir, 'A', 'B') + + svntest.main.run_svn(None, 'mv', mu_path, B_path) + + # Move a folder from wc to wc2 + C_path = os.path.join(sbox.wc_dir, 'A', 'C') + D_path = os.path.join(wc2_dir, 'A', 'D') + + svntest.main.run_svn(None, 'mv', C_path, D_path) + + # Verify modified status + expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1) + expected_status.tweak('A/mu', 'A/C', status='D ') + svntest.actions.run_and_verify_status(sbox.wc_dir, expected_status) + expected_status2 = svntest.actions.get_virginal_state(wc2_dir, 1) + expected_status2.add({ 'A/B/mu' : + Item(status='A ', copied='+', wc_rev='-') }) + expected_status2.add({ 'A/B/C' : + Item(status='A ', copied='+', wc_rev='-') }) + expected_status2.add({ 'A/B/E/mu' : + Item(status='A ', copied='+', wc_rev='-') }) + expected_status2.add({ 'A/D/C' : + Item(status='A ', copied='+', wc_rev='-') }) + svntest.actions.run_and_verify_status(wc2_dir, expected_status2) + + +# regression test for issue #2475 - direct copy in the repository +# this test handles the 'direct move' case too, that uses the same code. +def path_copy_in_repo(sbox): + "issue #2475 - direct copy in the repository" + sbox.build() + + repo_url2 = change_case_of_hostname(svntest.main.current_repo_url) + + # Copy a file from repo to repo2 + mu_url = svntest.main.current_repo_url + '/A/mu' + E_url = repo_url2 + '/A/B/E' + + svntest.main.run_svn(None, 'cp', mu_url, E_url, '-m', 'copy mu to /A/B/E') + + # For completeness' sake, update to HEAD, and verify we have a full + # greek tree again, all at revision 2. + expected_output = svntest.wc.State(sbox.wc_dir, { + 'A/B/E/mu' : Item(status='A '), + }) + + expected_disk = svntest.main.greek_state.copy() + expected_disk.add({'A/B/E/mu' : Item("This is the file 'mu'.\n") }) + + expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 2) + expected_status.add({'A/B/E/mu' : Item(status=' ', wc_rev=2) }) + svntest.actions.run_and_verify_update(sbox.wc_dir, + expected_output, + expected_disk, + expected_status) + +######################################################################## +# Run the tests + + +# list all tests here, starting with None: +test_list = [ None, + path_move_and_copy_between_wcs, + path_copy_in_repo, + ] + +if __name__ == '__main__': + svntest.main.run_tests(test_list) + # NOTREACHED + + +### End of file. Index: subversion/tests/libsvn_subr/path-test.c =================================================================== --- subversion/tests/libsvn_subr/path-test.c (revision 19226) +++ subversion/tests/libsvn_subr/path-test.c (working copy) @@ -670,6 +670,12 @@ { "http://hst", "http://hst" }, { "http://hst/foo/../bar","http://hst/foo/../bar" }, { "http://hst/", "http://hst" }, + { "http://HST/", "http://hst" }, + { "http://HST/FOO/BaR", "http://hst/FOO/BaR" }, + { "svn+ssh://j.raNDom@HST/HST/FOO/BaR", "svn+ssh://j.raNDom@hst/HST/FOO/BaR" }, + { "svn+SSH://j.random:jRaY@HST/HST/FOO/BaR", "svn+ssh://j.random:jRaY@hst/HST/FOO/BaR" }, + { "SVN+ssh://j.raNDom:jray@HST/HST/FOO/BaR", "svn+ssh://j.raNDom:jray@hst/HST/FOO/BaR" }, + { "fILe:///Users/jrandom/wc", "file:///Users/jrandom/wc" }, #if defined(WIN32) || defined(__CYGWIN__) /* We permit UNC paths on Windows. By definition UNC * paths must have two components so we should remove the