merge with stable
authorMatt Mackall <mpm@selenic.com>
Fri, 05 Aug 2011 16:07:51 -0500
changeset 15027 1e45b92f4fb2
parent 15023 157a294444b2 (current diff)
parent 15026 f32a2989ff58 (diff)
child 15028 eb97a3e38656
merge with stable
mercurial/dispatch.py
mercurial/url.py
mercurial/util.py
--- a/mercurial/dispatch.py	Thu Aug 04 16:12:58 2011 -0500
+++ b/mercurial/dispatch.py	Fri Aug 05 16:07:51 2011 -0500
@@ -357,6 +357,15 @@
     # but only if they have been defined prior to the current definition.
     for alias, definition in ui.configitems('alias'):
         aliasdef = cmdalias(alias, definition, cmdtable)
+
+        try:
+            olddef = cmdtable[aliasdef.cmd][0]
+            if olddef.definition == aliasdef.definition:
+                continue
+        except (KeyError, AttributeError):
+            # definition might not exist or it might not be a cmdalias
+            pass
+
         cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
         if aliasdef.norepo:
             commands.norepo += ' %s' % alias
--- a/mercurial/httpconnection.py	Thu Aug 04 16:12:58 2011 -0500
+++ b/mercurial/httpconnection.py	Fri Aug 05 16:07:51 2011 -0500
@@ -58,7 +58,7 @@
         return self._len
 
 # moved here from url.py to avoid a cycle
-def readauthforuri(ui, uri):
+def readauthforuri(ui, uri, user):
     # Read configuration
     config = dict()
     for key, val in ui.configitems('auth'):
@@ -72,10 +72,6 @@
         gdict[setting] = val
 
     # Find the best match
-    uri = util.url(uri)
-    user = uri.user
-    uri.user = uri.password = None
-    uri = str(uri)
     scheme, hostpath = uri.split('://', 1)
     bestuser = None
     bestlen = 0
@@ -238,7 +234,11 @@
         return self.do_open(HTTPConnection, req, False)
 
     def https_open(self, req):
-        res = readauthforuri(self.ui, req.get_full_url())
+        # req.get_full_url() does not contain credentials and we may
+        # need them to match the certificates.
+        url = req.get_full_url()
+        user, password = self.pwmgr.find_stored_password(url)
+        res = readauthforuri(self.ui, url, user)
         if res:
             group, auth = res
             self.auth = auth
--- a/mercurial/url.py	Thu Aug 04 16:12:58 2011 -0500
+++ b/mercurial/url.py	Fri Aug 05 16:07:51 2011 -0500
@@ -26,7 +26,7 @@
             return (user, passwd)
 
         if not user or not passwd:
-            res = httpconnectionmod.readauthforuri(self.ui, authuri)
+            res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
             if res:
                 group, auth = res
                 user, passwd = auth.get('username'), auth.get('password')
@@ -53,6 +53,10 @@
         msg = _('http auth: user %s, password %s\n')
         self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
 
+    def find_stored_password(self, authuri):
+        return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
+            self, None, authuri)
+
 class proxyhandler(urllib2.ProxyHandler):
     def __init__(self, ui):
         proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
@@ -342,7 +346,11 @@
             return keepalive.KeepAliveHandler._start_transaction(self, h, req)
 
         def https_open(self, req):
-            res = httpconnectionmod.readauthforuri(self.ui, req.get_full_url())
+            # req.get_full_url() does not contain credentials and we may
+            # need them to match the certificates.
+            url = req.get_full_url()
+            user, password = self.pwmgr.find_stored_password(url)
+            res = httpconnectionmod.readauthforuri(self.ui, url, user)
             if res:
                 group, auth = res
                 self.auth = auth
--- a/mercurial/util.py	Thu Aug 04 16:12:58 2011 -0500
+++ b/mercurial/util.py	Fri Aug 05 16:07:51 2011 -0500
@@ -1468,6 +1468,8 @@
                     path = None
                 if not self.host:
                     self.host = None
+                    # path of file:///d is /d
+                    # path of file:///d:/ is d:/, not /d:/
                     if path and not hasdriveletter(path):
                         path = '/' + path
 
@@ -1587,7 +1589,9 @@
             self.user, self.passwd = user, passwd
         if not self.user:
             return (s, None)
-        return (s, (None, (str(self), self.host),
+        # authinfo[1] is passed to urllib2 password manager, and its URIs
+        # must not contain credentials.
+        return (s, (None, (s, self.host),
                     self.user, self.passwd or ''))
 
     def isabs(self):
@@ -1610,11 +1614,6 @@
                 path = self._hostport + '/' + self.path
             elif self.host is not None and self.path:
                 path = '/' + path
-            # We also need to handle the case of file:///C:/, which
-            # should return C:/, not /C:/.
-            elif hasdriveletter(path):
-                # Strip leading slash from paths with drive names
-                return path[1:]
             return path
         return self._origpath
 
--- a/tests/test-hgweb-auth.py	Thu Aug 04 16:12:58 2011 -0500
+++ b/tests/test-hgweb-auth.py	Fri Aug 05 16:07:51 2011 -0500
@@ -1,4 +1,5 @@
 from mercurial import demandimport; demandimport.enable()
+import urllib2
 from mercurial import ui, util
 from mercurial import url
 from mercurial.error import Abort
@@ -36,10 +37,10 @@
         print 'URI:', uri
         try:
             pm = url.passwordmgr(ui)
-            authinfo = util.url(uri).authinfo()[1]
+            u, authinfo = util.url(uri).authinfo()
             if authinfo is not None:
                 pm.add_password(*authinfo)
-            print '    ', pm.find_user_password('test', uri)
+            print '    ', pm.find_user_password('test', u)
         except Abort, e:
             print 'abort'
 
@@ -95,3 +96,12 @@
       'y.username': 'y',
       'y.password': 'ypassword'},
      urls=['http://y@example.org/foo/bar'])
+
+def testauthinfo(fullurl, authurl):
+    print 'URIs:', fullurl, authurl
+    pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
+    pm.add_password(*util.url(fullurl).authinfo()[1])
+    print pm.find_user_password('test', authurl)
+
+print '\n*** Test urllib2 and util.url\n'
+testauthinfo('http://user@example.com:8080/foo', 'http://example.com:8080/foo')
--- a/tests/test-hgweb-auth.py.out	Thu Aug 04 16:12:58 2011 -0500
+++ b/tests/test-hgweb-auth.py.out	Fri Aug 05 16:07:51 2011 -0500
@@ -189,3 +189,8 @@
 CFG: {x.password: xpassword, x.prefix: http://example.org/foo/bar, x.username: None, y.password: ypassword, y.prefix: http://example.org/foo, y.username: y}
 URI: http://y@example.org/foo/bar
      ('y', 'xpassword')
+
+*** Test urllib2 and util.url
+
+URIs: http://user@example.com:8080/foo http://example.com:8080/foo
+('user', '')
--- a/tests/test-http.t	Thu Aug 04 16:12:58 2011 -0500
+++ b/tests/test-http.t	Fri Aug 05 16:07:51 2011 -0500
@@ -110,6 +110,55 @@
   abort: HTTP Error 404: Not Found
   [255]
 
+test http authentication
+
+  $ cd test
+  $ cat << EOT > userpass.py
+  > import base64
+  > from mercurial.hgweb import common
+  > def perform_authentication(hgweb, req, op):
+  >     auth = req.env.get('HTTP_AUTHORIZATION')
+  >     if not auth:
+  >         raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
+  >                 [('WWW-Authenticate', 'Basic Realm="mercurial"')])
+  >     if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']: 
+  >         raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
+  > def extsetup():
+  >     common.permhooks.insert(0, perform_authentication)
+  > EOT
+  $ hg --config extensions.x=userpass.py serve -p $HGPORT2 -d --pid-file=pid
+  $ cat pid >> $DAEMON_PIDS
+
+  $ hg id http://localhost:$HGPORT2/  
+  abort: http authorization required
+  [255]
+  $ hg id http://user@localhost:$HGPORT2/ 
+  abort: http authorization required
+  [255]
+  $ hg id http://user:pass@localhost:$HGPORT2/
+  5fed3813f7f5
+  $ echo '[auth]' >> .hg/hgrc 
+  $ echo 'l.schemes=http' >> .hg/hgrc
+  $ echo 'l.prefix=lo' >> .hg/hgrc
+  $ echo 'l.username=user' >> .hg/hgrc
+  $ echo 'l.password=pass' >> .hg/hgrc
+  $ hg id http://localhost:$HGPORT2/ 
+  5fed3813f7f5
+  $ hg id http://localhost:$HGPORT2/  
+  5fed3813f7f5
+  $ hg id http://user@localhost:$HGPORT2/ 
+  5fed3813f7f5
+  $ hg id http://user:pass@localhost:$HGPORT2/
+  5fed3813f7f5
+  $ hg id http://user2@localhost:$HGPORT2/ 
+  abort: http authorization required
+  [255]
+  $ hg id http://user:pass2@localhost:$HGPORT2/
+  abort: HTTP Error 403: no
+  [255]
+
+  $ cd ..
+
 check error log
 
   $ cat error.log
--- a/tests/test-url.py	Thu Aug 04 16:12:58 2011 -0500
+++ b/tests/test-url.py	Fri Aug 05 16:07:51 2011 -0500
@@ -204,18 +204,32 @@
     <url scheme: 'file', path: '/foo/bar/baz'>
     >>> str(u)
     'file:///foo/bar/baz'
+    >>> u.localpath()
+    '/foo/bar/baz'
 
     >>> u = url('file:///foo/bar/baz')
     >>> u
     <url scheme: 'file', path: '/foo/bar/baz'>
     >>> str(u)
     'file:///foo/bar/baz'
+    >>> u.localpath()
+    '/foo/bar/baz'
+
+    >>> u = url('file:///f:oo/bar/baz')
+    >>> u
+    <url scheme: 'file', path: 'f:oo/bar/baz'>
+    >>> str(u)
+    'file:f%3Aoo/bar/baz'
+    >>> u.localpath()
+    'f:oo/bar/baz'
 
     >>> u = url('file:foo/bar/baz')
     >>> u
     <url scheme: 'file', path: 'foo/bar/baz'>
     >>> str(u)
     'file:foo/bar/baz'
+    >>> u.localpath()
+    'foo/bar/baz'
     """
 
 doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)