Fix for issue #2475: Make sure hostnames in URI's are handled in a case 
insenitive manner. 
  
Patch by: Lieven Govaerts <lgo@mobsol.be>

* subversion/include/svn_path.h: 
  (svn_path_canonicalize): add comment: hostname is converted to lowercase
  
* subversion/tests/cmdline/path_tests.py: 
  new file, contains tests for issue #2475.
  
* subversion/libsvn_subr/path.c:
  (canonicalize_host_name): new function, convert hostname to lowercase
  (svn_path_canonicalize): invoke canonicalize_host_name for uri's.
  
Index: subversion/include/svn_path.h
===================================================================
--- subversion/include/svn_path.h   (revision 18843)
+++ subversion/include/svn_path.h   (working copy)
@@ -185,6 +185,9 @@
  * separator characters, and possibly other semantically inoperative
  * transformations.
  *
+ * If the URL contains a hostname, convert that to lowercase to support
+ * case-insensitive handling of hostnames ( see issue 2475 )
+ *
  * The returned path may be statically allocated, equal to @a path, or
  * allocated from @a pool.
  */
Index: subversion/libsvn_subr/path.c
===================================================================
--- subversion/libsvn_subr/path.c   (revision 18843)
+++ subversion/libsvn_subr/path.c   (working copy)
@@ -742,6 +742,46 @@
   return NULL;
 }
 
+/* Convert the hostname of the URI to lowercase ( to solve issue #2475 )
+   The optional 'username:password@' section is not converted! */
+static const char *
+canonicalize_host_name(const char *path, char **dst)
+{
+  apr_size_t seglen;
+  const char *host = path;
+
+  while (*host && (*host != '/'))
+    {
+      // skip username and password ( both optional )
+      switch (*host)
+        {
+        case ':':
+        case '@':
+          seglen = host - path + 1;
+          memcpy(*dst, path, seglen);
+          *dst += seglen;
+          path = host+1;
+          break;
+        }
+      ++host;
+    }
+
+  // convert hostname to lowercase
+  while (path < host) 
+    {
+        **dst = apr_tolower(*path);
+        ++path;
+        ++(*dst);
+    }
+
+  if (*path == '/')
+    {
+      **dst = *(path++);
+      ++(*dst);
+    }
+  return path;
+}
+
 svn_boolean_t 
 svn_path_is_url(const char *path)
 {
@@ -1139,6 +1179,10 @@
       
     }
 
+  if (uri)
+    {
+      src = canonicalize_host_name(src, &dst);
+    }
   while (*src)
     {
       /* Parse each segment, find the closing '/' */
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,187 @@
+#!/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 urlparse import urlparse, urlunparse
+from string import lower, upper
+
+# Our testing module
+import svntest
+
+import re
+
+######################################################################
+# 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_between_wcs(sbox):
+  "case issue #2475 - move file and folder"
+  sbox.build()
+
+  # checkout a second working copy, use repository url with different case
+  wc_dir2 = sbox.wc_dir + '2'
+  repo_url2 = change_case_of_hostname(svntest.main.current_repo_url)
+  
+  # Generate the expected output tree.
+  expected_output = svntest.main.greek_state.copy()
+  expected_output.wc_dir = wc_dir2
+  expected_output.tweak(status='A ', contents=None)
+
+  # Generate an expected wc tree.
+  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,
+                          wc_dir2,
+                          expected_output,
+                          expected_wc)
+  
+  # Move a file from wc to wc2
+  mu_path = os.path.join(sbox.wc_dir, 'A', 'mu')
+  E_path = os.path.join(wc_dir2, 'A', 'B', 'E')  
+
+  svntest.main.run_svn(None, 'mv', mu_path, E_path)
+  
+  # Move a folder from wc to wc2
+  E_path = os.path.join(sbox.wc_dir, 'A', 'B', 'E')
+  A_path = os.path.join(wc_dir2, 'A')
+  
+  svntest.main.run_svn(None, 'mv', E_path, A_path)  
+
+# regression test for issue #2475 - move file and folder
+def path_copy_between_wcs(sbox):
+  "case issue #2475 - copy file and folder"
+  sbox.build()
+
+  # checkout a second working copy, use repository url with different case
+  wc_dir2 = sbox.wc_dir + '2'
+  repo_url2 = change_case_of_hostname(svntest.main.current_repo_url)
+  
+  # Generate the expected output tree.
+  expected_output = svntest.main.greek_state.copy()
+  expected_output.wc_dir = wc_dir2
+  expected_output.tweak(status='A ', contents=None)
+
+  # Generate an expected wc tree.
+  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,
+                          wc_dir2,
+                          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(wc_dir2, 'A', 'B', 'E')  
+
+  svntest.main.run_svn(None, 'cp', mu_path, E_path)
+  
+  # Copy a folder from wc to wc2
+  E_path = os.path.join(sbox.wc_dir, 'A', 'B', 'E')
+  A_path = os.path.join(wc_dir2, 'A')
+  
+  svntest.main.run_svn(None, 'cp', E_path, A_path)
+  
+# 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):
+  "case 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')
+  
+# regression test for issue #2475 - checkout in a working copy
+def path_checkout(sbox):
+  "case issue #2475 - checkout in wc"
+  sbox.build()
+  
+  # checkout a working copy non-recursively
+  wc_dir2 = sbox.wc_dir + '2'
+
+  output, errput = svntest.main.run_svn (None, 'co', 
+                                 '--username', svntest.main.wc_author,
+                                 '--password', svntest.main.wc_passwd,
+                                 '-N',
+                                 svntest.main.current_repo_url, wc_dir2)
+  
+  # checkout in the same working copy, use repository url with different case
+  repo_url2 = change_case_of_hostname(svntest.main.current_repo_url)
+  
+  # Generate the expected output tree.
+  expected_output = svntest.main.greek_state.copy()
+  expected_output.wc_dir = wc_dir2
+  expected_output.tweak(status='A ', contents=None)
+
+  # Generate an expected wc tree.
+  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,
+                          wc_dir2,
+                          expected_output,
+                          expected_wc)
+
+########################################################################
+# Run the tests
+
+
+# list all tests here, starting with None:
+test_list = [ None,
+              path_move_between_wcs,
+              path_copy_between_wcs,
+              path_copy_in_repo,
+              path_checkout
+             ]
+
+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 18843)
+++ subversion/tests/libsvn_subr/path-test.c    (working copy)
@@ -670,6 +670,11 @@
     { "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" },
+
 #if defined(WIN32) || defined(__CYGWIN__)
     /* We permit UNC paths on Windows.  By definition UNC
      * paths must have two components so we should remove the


