--- a/.hgsigs Mon Aug 01 14:53:10 2011 -0500
+++ b/.hgsigs Mon Aug 01 18:10:05 2011 -0500
@@ -40,3 +40,4 @@
3cb1e95676ad089596bd81d0937cad37d6e3b7fb 0 iD8DBQBNvTy4ywK+sNU5EO8RAmp8AJ9QnxK4jTJ7G722MyeBxf0UXEdGwACgtlM7BKtNQfbEH/fOW5y+45W88VI=
733af5d9f6b22387913e1d11350fb8cb7c1487dd 0 iD8DBQBN5q/8ywK+sNU5EO8RArRGAKCNGT94GKIYtSuwZ57z1sQbcw6uLACfffpbMV4NAPMl8womAwg+7ZPKnIU=
de9eb6b1da4fc522b1cab16d86ca166204c24f25 0 iD8DBQBODhfhywK+sNU5EO8RAr2+AJ4ugbAj8ae8/K0bYZzx3sascIAg1QCeK3b+zbbVVqd3b7CDpwFnaX8kTd4=
+4a43e23b8c55b4566b8200bf69fe2158485a2634 0 iD8DBQBONzIMywK+sNU5EO8RAj5SAJ0aPS3+JHnyI6bHB2Fl0LImbDmagwCdGbDLp1S7TFobxXudOH49bX45Iik=
--- a/.hgtags Mon Aug 01 14:53:10 2011 -0500
+++ b/.hgtags Mon Aug 01 18:10:05 2011 -0500
@@ -52,3 +52,4 @@
3cb1e95676ad089596bd81d0937cad37d6e3b7fb 1.8.3
733af5d9f6b22387913e1d11350fb8cb7c1487dd 1.8.4
de9eb6b1da4fc522b1cab16d86ca166204c24f25 1.9
+4a43e23b8c55b4566b8200bf69fe2158485a2634 1.9.1
--- a/mercurial/help/config.txt Mon Aug 01 14:53:10 2011 -0500
+++ b/mercurial/help/config.txt Mon Aug 01 18:10:05 2011 -0500
@@ -266,7 +266,9 @@
Optional. Username to authenticate with. If not given, and the
remote site requires basic or digest authentication, the user will
be prompted for it. Environment variables are expanded in the
- username letting you do ``foo.username = $USER``.
+ username letting you do ``foo.username = $USER``. If the URI
+ includes a username, only ``[auth]`` entries with a matching
+ username or without a username will be considered.
``password``
Optional. Password to authenticate with. If not given, and the
@@ -1158,6 +1160,13 @@
be present in this list. The contents of the allow_push list are
examined after the deny_push list.
+``guessmime``
+ Control MIME types for raw download of file content.
+ Set to True to let hgweb guess the content type from the file
+ extension. This will serve HTML files as ``text/html`` and might
+ allow cross-site scripting attacks when serving untrusted
+ repositories. Default is False.
+
``allow_read``
If the user has not already been denied repository access due to
the contents of deny_read, this list determines whether to grant
--- a/mercurial/hgweb/webcommands.py Mon Aug 01 14:53:10 2011 -0500
+++ b/mercurial/hgweb/webcommands.py Mon Aug 01 18:10:05 2011 -0500
@@ -32,6 +32,8 @@
return changelog(web, req, tmpl)
def rawfile(web, req, tmpl):
+ guessmime = web.configbool('web', 'guessmime', False)
+
path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
if not path:
content = manifest(web, req, tmpl)
@@ -50,9 +52,11 @@
path = fctx.path()
text = fctx.data()
- mt = mimetypes.guess_type(path)[0]
- if mt is None:
- mt = binary(text) and 'application/octet-stream' or 'text/plain'
+ mt = 'application/binary'
+ if guessmime:
+ mt = mimetypes.guess_type(path)[0]
+ if mt is None:
+ mt = binary(text) and 'application/binary' or 'text/plain'
if mt.startswith('text/'):
mt += '; charset="%s"' % encoding.encoding
--- a/mercurial/httpconnection.py Mon Aug 01 14:53:10 2011 -0500
+++ b/mercurial/httpconnection.py Mon Aug 01 18:10:05 2011 -0500
@@ -72,10 +72,19 @@
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
bestauth = None
for group, auth in config.iteritems():
+ if user and user != auth.get('username', user):
+ # If a username was set in the URI, the entry username
+ # must either match it or be unset
+ continue
prefix = auth.get('prefix')
if not prefix:
continue
@@ -85,9 +94,14 @@
else:
schemes = (auth.get('schemes') or 'https').split()
if (prefix == '*' or hostpath.startswith(prefix)) and \
- len(prefix) > bestlen and scheme in schemes:
+ (len(prefix) > bestlen or (len(prefix) == bestlen and \
+ not bestuser and 'username' in auth)) \
+ and scheme in schemes:
bestlen = len(prefix)
bestauth = group, auth
+ bestuser = auth.get('username')
+ if user and not bestuser:
+ auth['username'] = user
return bestauth
# Mercurial (at least until we can remove the old codepath) requires
--- a/mercurial/url.py Mon Aug 01 14:53:10 2011 -0500
+++ b/mercurial/url.py Mon Aug 01 18:10:05 2011 -0500
@@ -25,7 +25,7 @@
self._writedebug(user, passwd)
return (user, passwd)
- if not user:
+ if not user or not passwd:
res = httpconnectionmod.readauthforuri(self.ui, authuri)
if res:
group, auth = res
--- a/tests/test-hgweb-auth.py Mon Aug 01 14:53:10 2011 -0500
+++ b/tests/test-hgweb-auth.py Mon Aug 01 18:10:05 2011 -0500
@@ -1,5 +1,5 @@
from mercurial import demandimport; demandimport.enable()
-from mercurial import ui
+from mercurial import ui, util
from mercurial import url
from mercurial.error import Abort
@@ -19,13 +19,16 @@
return '{' + ', '.join(['%s: %s' % (k, dict[k])
for k in sorted(dict.iterkeys())]) + '}'
-def test(auth):
+def test(auth, urls=None):
print 'CFG:', dumpdict(auth)
prefixes = set()
for k in auth:
prefixes.add(k.split('.', 1)[0])
for p in prefixes:
- auth.update({p + '.username': p, p + '.password': p})
+ for name in ('.username', '.password'):
+ if (p + name) not in auth:
+ auth[p + name] = p
+ auth = dict((k, v) for k, v in auth.iteritems() if v is not None)
ui = writeauth(auth)
@@ -33,16 +36,26 @@
print 'URI:', uri
try:
pm = url.passwordmgr(ui)
+ authinfo = util.url(uri).authinfo()[1]
+ if authinfo is not None:
+ pm.add_password(*authinfo)
print ' ', pm.find_user_password('test', uri)
except Abort, e:
print 'abort'
- _test('http://example.org/foo')
- _test('http://example.org/foo/bar')
- _test('http://example.org/bar')
- _test('https://example.org/foo')
- _test('https://example.org/foo/bar')
- _test('https://example.org/bar')
+ if not urls:
+ urls = [
+ 'http://example.org/foo',
+ 'http://example.org/foo/bar',
+ 'http://example.org/bar',
+ 'https://example.org/foo',
+ 'https://example.org/foo/bar',
+ 'https://example.org/bar',
+ 'https://x@example.org/bar',
+ 'https://y@example.org/bar',
+ ]
+ for u in urls:
+ _test(u)
print '\n*** Test in-uri schemes\n'
@@ -62,3 +75,23 @@
test({'x.prefix': 'http://example.org/foo',
'y.prefix': 'http://example.org/foo/bar'})
test({'x.prefix': '*', 'y.prefix': 'https://example.org/bar'})
+
+print '\n*** Test user matching\n'
+test({'x.prefix': 'http://example.org/foo',
+ 'x.username': None,
+ 'x.password': 'xpassword'},
+ urls=['http://y@example.org/foo'])
+test({'x.prefix': 'http://example.org/foo',
+ 'x.username': None,
+ 'x.password': 'xpassword',
+ 'y.prefix': 'http://example.org/foo',
+ 'y.username': 'y',
+ 'y.password': 'ypassword'},
+ urls=['http://y@example.org/foo'])
+test({'x.prefix': 'http://example.org/foo/bar',
+ 'x.username': None,
+ 'x.password': 'xpassword',
+ 'y.prefix': 'http://example.org/foo',
+ 'y.username': 'y',
+ 'y.password': 'ypassword'},
+ urls=['http://y@example.org/foo/bar'])
--- a/tests/test-hgweb-auth.py.out Mon Aug 01 14:53:10 2011 -0500
+++ b/tests/test-hgweb-auth.py.out Mon Aug 01 18:10:05 2011 -0500
@@ -14,6 +14,10 @@
abort
URI: https://example.org/bar
abort
+URI: https://x@example.org/bar
+ abort
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: https://example.org}
URI: http://example.org/foo
abort
@@ -27,6 +31,10 @@
('x', 'x')
URI: https://example.org/bar
('x', 'x')
+URI: https://x@example.org/bar
+ ('x', 'x')
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: http://example.org, x.schemes: https}
URI: http://example.org/foo
('x', 'x')
@@ -40,6 +48,10 @@
abort
URI: https://example.org/bar
abort
+URI: https://x@example.org/bar
+ abort
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: https://example.org, x.schemes: http}
URI: http://example.org/foo
abort
@@ -53,6 +65,10 @@
('x', 'x')
URI: https://example.org/bar
('x', 'x')
+URI: https://x@example.org/bar
+ ('x', 'x')
+URI: https://y@example.org/bar
+ abort
*** Test separately configured schemes
@@ -69,6 +85,10 @@
abort
URI: https://example.org/bar
abort
+URI: https://x@example.org/bar
+ abort
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: example.org, x.schemes: https}
URI: http://example.org/foo
abort
@@ -82,6 +102,10 @@
('x', 'x')
URI: https://example.org/bar
('x', 'x')
+URI: https://x@example.org/bar
+ ('x', 'x')
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: example.org, x.schemes: http https}
URI: http://example.org/foo
('x', 'x')
@@ -95,6 +119,10 @@
('x', 'x')
URI: https://example.org/bar
('x', 'x')
+URI: https://x@example.org/bar
+ ('x', 'x')
+URI: https://y@example.org/bar
+ abort
*** Test prefix matching
@@ -111,6 +139,10 @@
abort
URI: https://example.org/bar
abort
+URI: https://x@example.org/bar
+ abort
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/foo/bar}
URI: http://example.org/foo
('x', 'x')
@@ -124,6 +156,10 @@
abort
URI: https://example.org/bar
abort
+URI: https://x@example.org/bar
+ abort
+URI: https://y@example.org/bar
+ abort
CFG: {x.prefix: *, y.prefix: https://example.org/bar}
URI: http://example.org/foo
abort
@@ -137,3 +173,19 @@
('x', 'x')
URI: https://example.org/bar
('y', 'y')
+URI: https://x@example.org/bar
+ ('x', 'x')
+URI: https://y@example.org/bar
+ ('y', 'y')
+
+*** Test user matching
+
+CFG: {x.password: xpassword, x.prefix: http://example.org/foo, x.username: None}
+URI: http://y@example.org/foo
+ ('y', 'xpassword')
+CFG: {x.password: xpassword, x.prefix: http://example.org/foo, x.username: None, y.password: ypassword, y.prefix: http://example.org/foo, y.username: y}
+URI: http://y@example.org/foo
+ ('y', 'ypassword')
+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')
--- a/tests/test-hgweb-raw.t Mon Aug 01 14:53:10 2011 -0500
+++ b/tests/test-hgweb-raw.t Mon Aug 01 18:10:05 2011 -0500
@@ -22,6 +22,28 @@
$ sleep 1 # wait for server to scream and die
$ cat getoutput.txt
200 Script output follows
+ content-type: application/binary
+ content-length: 157
+ content-disposition: inline; filename="some \"text\".txt"
+
+ This is just some random text
+ that will go inside the file and take a few lines.
+ It is very boring to read, but computers don't
+ care about things like that.
+ $ cat access.log error.log
+ 127.0.0.1 - - [*] "GET /?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw HTTP/1.1" 200 - (glob)
+
+ $ rm access.log error.log
+ $ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid \
+ > --config web.guessmime=True
+
+ $ cat hg.pid >> $DAEMON_PIDS
+ $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw' content-type content-length content-disposition) >getoutput.txt &
+ $ sleep 5
+ $ kill `cat hg.pid`
+ $ sleep 1 # wait for server to scream and die
+ $ cat getoutput.txt
+ 200 Script output follows
content-type: text/plain; charset="ascii"
content-length: 157
content-disposition: inline; filename="some \"text\".txt"