merge with i18n stable
authorMatt Mackall <mpm@selenic.com>
Thu, 30 Sep 2010 19:09:58 -0500
branchstable
changeset 12597 10dcfba4f16d
parent 12596 e356c5c21b15 (current diff)
parent 12594 bb324910e40a (diff)
child 12598 e660e4a178c6
child 12600 3439d8d5636d
child 12602 14198926975d
merge with i18n
--- a/doc/hgrc.5.txt	Thu Sep 30 14:07:57 2010 -0300
+++ b/doc/hgrc.5.txt	Thu Sep 30 19:09:58 2010 -0500
@@ -951,8 +951,9 @@
     third-party tools like email notification hooks can construct
     URLs. Example: ``http://hgserver/repos/``.
 ``cacerts``
-    Path to file containing a list of PEM encoded certificate authorities
-    that may be used to verify an SSL server's identity. The form must be
+    Path to file containing a list of PEM encoded certificate authority
+    certificates. If specified on the client, then it will verify the identity
+    of remote HTTPS servers with these certificates. The form must be
     as follows::
 
         -----BEGIN CERTIFICATE-----
@@ -962,8 +963,8 @@
         ... (certificate in base64 PEM encoding) ...
         -----END CERTIFICATE-----
 
-    This feature is only supported when using Python 2.6. If you wish to
-    use it with earlier versions of Python, install the backported
+    This feature is only supported when using Python 2.6 or later. If you wish
+    to use it with earlier versions of Python, install the backported
     version of the ssl library that is available from
     ``http://pypi.python.org``.
 
--- a/mercurial/help/urls.txt	Thu Sep 30 14:07:57 2010 -0300
+++ b/mercurial/help/urls.txt	Thu Sep 30 19:09:58 2010 -0500
@@ -18,6 +18,9 @@
 possible if the feature is explicitly enabled on the remote Mercurial
 server.
 
+Note that the security of HTTPS URLs depends on proper configuration of
+web.cacerts.
+
 Some notes about using SSH with Mercurial:
 
 - SSH requires an accessible shell account on the destination machine
--- a/mercurial/patch.py	Thu Sep 30 14:07:57 2010 -0300
+++ b/mercurial/patch.py	Thu Sep 30 19:09:58 2010 -0500
@@ -1184,7 +1184,9 @@
                 gp.path = pathstrip(gp.path, strip - 1)[1]
                 if gp.oldpath:
                     gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
-                if gp.op in ('COPY', 'RENAME'):
+                # Binary patches really overwrite target files, copying them
+                # will just make it fails with "target file exists"
+                if gp.op in ('COPY', 'RENAME') and not gp.binary:
                     copyfn(gp.oldpath, gp.path, cwd)
                 changed[gp.path] = gp
         else:
@@ -1567,6 +1569,9 @@
                         header.append('new file mode %s\n' % mode)
                     elif ctx2.flags(f):
                         losedatafn(f)
+                # In theory, if tn was copied or renamed we should check
+                # if the source is binary too but the copy record already
+                # forces git mode.
                 if util.binary(tn):
                     if opts.git:
                         dodiff = 'binary'
@@ -1586,7 +1591,7 @@
                     else:
                         header.append('deleted file mode %s\n' %
                                       gitmode[man1.flags(f)])
-                elif not to:
+                elif not to or util.binary(to):
                     # regular diffs cannot represent empty file deletion
                     losedatafn(f)
             else:
--- a/mercurial/url.py	Thu Sep 30 14:07:57 2010 -0300
+++ b/mercurial/url.py	Thu Sep 30 19:09:58 2010 -0500
@@ -7,7 +7,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
+import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO, time
 from i18n import _
 import keepalive, util
 
@@ -469,6 +469,31 @@
         _generic_start_transaction(self, h, req)
         return keepalive.HTTPHandler._start_transaction(self, h, req)
 
+def _verifycert(cert, hostname):
+    '''Verify that cert (in socket.getpeercert() format) matches hostname and is 
+    valid at this time. CRLs and subjectAltName are not handled.
+    
+    Returns error message if any problems are found and None on success.
+    '''
+    if not cert:
+        return _('no certificate received')
+    notafter = cert.get('notAfter')
+    if notafter and time.time() > ssl.cert_time_to_seconds(notafter):
+        return _('certificate expired %s') % notafter
+    notbefore = cert.get('notBefore')
+    if notbefore and time.time() < ssl.cert_time_to_seconds(notbefore):
+        return _('certificate not valid before %s') % notbefore
+    dnsname = hostname.lower()
+    for s in cert.get('subject', []):
+        key, value = s[0]
+        if key == 'commonName':
+            certname = value.lower()
+            if (certname == dnsname or
+                '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
+                return None
+            return _('certificate is for %s') % certname
+    return _('no commonName found in certificate')
+
 if has_https:
     class BetterHTTPS(httplib.HTTPSConnection):
         send = keepalive.safesend
@@ -484,7 +509,11 @@
                 self.sock = _ssl_wrap_socket(sock, self.key_file,
                         self.cert_file, cert_reqs=CERT_REQUIRED,
                         ca_certs=cacerts)
-                self.ui.debug(_('server identity verification succeeded\n'))
+                msg = _verifycert(self.sock.getpeercert(), self.host)
+                if msg:
+                    raise util.Abort('%s certificate error: %s' % (self.host, msg))
+                self.ui.debug(_('%s certificate successfully verified\n') % 
+                    self.host)
             else:
                 httplib.HTTPSConnection.connect(self)
 
--- a/tests/test-diff-upgrade	Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-diff-upgrade	Thu Sep 30 19:09:58 2010 -0500
@@ -20,6 +20,7 @@
 chmod +x unsetexec
 echo binary > binary
 python -c "file('rmbinary', 'wb').write('\0')"
+python -c "file('bintoregular', 'wb').write('\0')"
 hg ci -Am addfiles
 echo regular >> regular
 echo newregular >> newregular
@@ -28,6 +29,7 @@
 rm rmregular
 echo exec >> exec
 echo newexec > newexec
+echo bintoregular > bintoregular
 chmod +x newexec
 rm rmexec
 chmod +x setexec
@@ -43,10 +45,11 @@
 echo '% git=no: git diff for single regular file'
 hg autodiff --git=yes regular
 
-echo '% git=auto: regular diff for regular files and removals'
-hg autodiff --git=auto regular newregular rmregular rmbinary rmexec
+echo '% git=auto: regular diff for regular files and non-binary removals'
+hg autodiff --git=auto regular newregular rmregular rmexec
 
-for f in exec newexec setexec unsetexec binary newbinary newempty rmempty; do
+for f in exec newexec setexec unsetexec binary newbinary newempty rmempty \
+    rmbinary bintoregular; do
     echo '% git=auto: git diff for' $f
     hg autodiff --git=auto $f
 done
--- a/tests/test-diff-upgrade.out	Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-diff-upgrade.out	Thu Sep 30 19:09:58 2010 -0500
@@ -1,5 +1,6 @@
 % make a combination of new, changed and deleted file
 adding binary
+adding bintoregular
 adding exec
 adding regular
 adding rmbinary
@@ -17,40 +18,42 @@
 removing rmexec
 removing rmregular
 % git=no: regular diff for all files
-diff -r b3f053cd7c7f binary
+diff -r a66d19b9302d binary
 Binary file binary has changed
-diff -r b3f053cd7c7f exec
+diff -r a66d19b9302d bintoregular
+Binary file bintoregular has changed
+diff -r a66d19b9302d exec
 --- a/exec
 +++ b/exec
 @@ -1,1 +1,2 @@
  exec
 +exec
-diff -r b3f053cd7c7f newbinary
+diff -r a66d19b9302d newbinary
 Binary file newbinary has changed
-diff -r b3f053cd7c7f newexec
+diff -r a66d19b9302d newexec
 --- /dev/null
 +++ b/newexec
 @@ -0,0 +1,1 @@
 +newexec
-diff -r b3f053cd7c7f newregular
+diff -r a66d19b9302d newregular
 --- /dev/null
 +++ b/newregular
 @@ -0,0 +1,1 @@
 +newregular
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
 --- a/regular
 +++ b/regular
 @@ -1,1 +1,2 @@
  regular
 +regular
-diff -r b3f053cd7c7f rmbinary
+diff -r a66d19b9302d rmbinary
 Binary file rmbinary has changed
-diff -r b3f053cd7c7f rmexec
+diff -r a66d19b9302d rmexec
 --- a/rmexec
 +++ /dev/null
 @@ -1,1 +0,0 @@
 -rmexec
-diff -r b3f053cd7c7f rmregular
+diff -r a66d19b9302d rmregular
 --- a/rmregular
 +++ /dev/null
 @@ -1,1 +0,0 @@
@@ -62,32 +65,30 @@
 @@ -1,1 +1,2 @@
  regular
 +regular
-% git=auto: regular diff for regular files and removals
-diff -r b3f053cd7c7f newregular
+% git=auto: regular diff for regular files and non-binary removals
+diff -r a66d19b9302d newregular
 --- /dev/null
 +++ b/newregular
 @@ -0,0 +1,1 @@
 +newregular
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
 --- a/regular
 +++ b/regular
 @@ -1,1 +1,2 @@
  regular
 +regular
-diff -r b3f053cd7c7f rmbinary
-Binary file rmbinary has changed
-diff -r b3f053cd7c7f rmexec
+diff -r a66d19b9302d rmexec
 --- a/rmexec
 +++ /dev/null
 @@ -1,1 +0,0 @@
 -rmexec
-diff -r b3f053cd7c7f rmregular
+diff -r a66d19b9302d rmregular
 --- a/rmregular
 +++ /dev/null
 @@ -1,1 +0,0 @@
 -rmregular
 % git=auto: git diff for exec
-diff -r b3f053cd7c7f exec
+diff -r a66d19b9302d exec
 --- a/exec
 +++ b/exec
 @@ -1,1 +1,2 @@
@@ -129,56 +130,71 @@
 % git=auto: git diff for rmempty
 diff --git a/rmempty b/rmempty
 deleted file mode 100644
+% git=auto: git diff for rmbinary
+diff --git a/rmbinary b/rmbinary
+deleted file mode 100644
+Binary file rmbinary has changed
+% git=auto: git diff for bintoregular
+diff --git a/bintoregular b/bintoregular
+index f76dd238ade08917e6712764a16a22005a50573d..9c42f2b6427d8bf034b7bc23986152dc01bfd3ab
+GIT binary patch
+literal 13
+Uc$`bh%qz(+N=+}#Ni5<5043uE82|tP
+
 % git=warn: regular diff with data loss warnings
-diff -r b3f053cd7c7f binary
+diff -r a66d19b9302d binary
 Binary file binary has changed
-diff -r b3f053cd7c7f exec
+diff -r a66d19b9302d bintoregular
+Binary file bintoregular has changed
+diff -r a66d19b9302d exec
 --- a/exec
 +++ b/exec
 @@ -1,1 +1,2 @@
  exec
 +exec
-diff -r b3f053cd7c7f newbinary
+diff -r a66d19b9302d newbinary
 Binary file newbinary has changed
-diff -r b3f053cd7c7f newexec
+diff -r a66d19b9302d newexec
 --- /dev/null
 +++ b/newexec
 @@ -0,0 +1,1 @@
 +newexec
-diff -r b3f053cd7c7f newregular
+diff -r a66d19b9302d newregular
 --- /dev/null
 +++ b/newregular
 @@ -0,0 +1,1 @@
 +newregular
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
 --- a/regular
 +++ b/regular
 @@ -1,1 +1,2 @@
  regular
 +regular
-diff -r b3f053cd7c7f rmbinary
+diff -r a66d19b9302d rmbinary
 Binary file rmbinary has changed
-diff -r b3f053cd7c7f rmexec
+diff -r a66d19b9302d rmexec
 --- a/rmexec
 +++ /dev/null
 @@ -1,1 +0,0 @@
 -rmexec
-diff -r b3f053cd7c7f rmregular
+diff -r a66d19b9302d rmregular
 --- a/rmregular
 +++ /dev/null
 @@ -1,1 +0,0 @@
 -rmregular
 data lost for: binary
+data lost for: bintoregular
 data lost for: newbinary
 data lost for: newempty
 data lost for: newexec
+data lost for: rmbinary
 data lost for: rmempty
 data lost for: setexec
 data lost for: unsetexec
 % git=abort: fail on execute bit change
 abort: losing data for setexec
 % git=abort: succeed on regular file
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
 --- a/regular
 +++ b/regular
 @@ -1,1 +1,2 @@
--- a/tests/test-doctest.py	Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-doctest.py	Thu Sep 30 19:09:58 2010 -0500
@@ -5,18 +5,13 @@
 import doctest
 
 import mercurial.changelog
-# test doctest from changelog
-
 doctest.testmod(mercurial.changelog)
 
-import mercurial.httprepo
-doctest.testmod(mercurial.httprepo)
-
-import mercurial.util
-doctest.testmod(mercurial.util)
-
 import mercurial.dagparser
 doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE)
 
+import mercurial.url
+doctest.testmod(mercurial.url)
+
 import hgext.convert.cvsps
 doctest.testmod(hgext.convert.cvsps)
--- a/tests/test-git-import	Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-git-import	Thu Sep 30 19:09:58 2010 -0500
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+. $TESTDIR/helpers.sh
+
 hg init a
 cd a
 
@@ -226,5 +228,20 @@
 copy to foo3
 EOF
 hg tip -q
+cat foo3
 
-cat foo3
+echo % move text file and patch as binary
+echo a > text2
+hg ci -Am0
+hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
+diff --git a/text2 b/binary2
+rename from text2
+rename to binary2
+index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
+GIT binary patch
+literal 5
+Mc$`b*O5$Pw00T?_*Z=?k
+
+EOF
+cat binary2 | repr
+hg st --copies --change .
\ No newline at end of file
--- a/tests/test-git-import.out	Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-git-import.out	Thu Sep 30 19:09:58 2010 -0500
@@ -80,3 +80,10 @@
 applying patch from stdin
 14:c4cd9cdeaa74
 foo
+% move text file and patch as binary
+adding text2
+applying patch from stdin
+'a\nb\n\x00'
+A binary2
+  text2
+R text2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-url.py	Thu Sep 30 19:09:58 2010 -0500
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+def check(a, b):
+    if a != b:
+        print (a, b)
+
+from mercurial.url import _verifycert
+
+# Test non-wildcard certificates        
+check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'example.com'),
+    None)
+check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'www.example.com'),
+    'certificate is for example.com')
+check(_verifycert({'subject': ((('commonName', 'www.example.com'),),)}, 'example.com'),
+    'certificate is for www.example.com')
+
+# Test wildcard certificates
+check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'www.example.com'),
+    None)
+check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'example.com'),
+    'certificate is for *.example.com')
+check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'w.w.example.com'),
+    'certificate is for *.example.com')
+
+# Avoid some pitfalls
+check(_verifycert({'subject': ((('commonName', '*.foo'),),)}, 'foo'),
+    'certificate is for *.foo')
+check(_verifycert({'subject': ((('commonName', '*o'),),)}, 'foo'),
+    'certificate is for *o')
+
+import time
+lastyear = time.gmtime().tm_year - 1
+nextyear = time.gmtime().tm_year + 1
+check(_verifycert({'notAfter': 'May  9 00:00:00 %s GMT' % lastyear}, 'example.com'),
+    'certificate expired May  9 00:00:00 %s GMT' % lastyear)
+check(_verifycert({'notBefore': 'May  9 00:00:00 %s GMT' % nextyear}, 'example.com'),
+    'certificate not valid before May  9 00:00:00 %s GMT' % nextyear)
+check(_verifycert({'notAfter': 'Sep 29 15:29:48 %s GMT' % nextyear, 'subject': ()}, 'example.com'),
+    'no commonName found in certificate')
+check(_verifycert(None, 'example.com'),
+    'no certificate received')