##// END OF EJS Templates
ssl: on OS X, use a dummy cert to trick Python/OpenSSL to use system CA certs...
Mads Kiilerich -
r22575:d7f7f186 default
parent child Browse files
Show More
@@ -0,0 +1,56
1 A dummy certificate that will make OS X 10.6+ Python use the system CA
2 certificate store:
3
4 -----BEGIN CERTIFICATE-----
5 MIIBIzCBzgIJANjmj39sb3FmMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNVBAMTDmhn
6 LmV4YW1wbGUuY29tMB4XDTE0MDgzMDA4NDU1OVoXDTE0MDgyOTA4NDU1OVowGTEX
7 MBUGA1UEAxMOaGcuZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEA
8 mh/ZySGlcq0ALNLmA1gZqt61HruywPrRk6WyrLJRgt+X7OP9FFlEfl2tzHfzqvmK
9 CtSQoPINWOdAJMekBYFgKQIDAQABMA0GCSqGSIb3DQEBBQUAA0EAF9h49LkSqJ6a
10 IlpogZuUHtihXeKZBsiktVIDlDccYsNy0RSh9XxUfhk+XMLw8jBlYvcltSXdJ7We
11 aKdQRekuMQ==
12 -----END CERTIFICATE-----
13
14 This certificate was generated to be syntactically valid but never be usable;
15 it expired before it became valid.
16
17 Created as:
18
19 $ cat > cn.conf << EOT
20 > [req]
21 > distinguished_name = req_distinguished_name
22 > [req_distinguished_name]
23 > commonName = Common Name
24 > commonName_default = no.example.com
25 > EOT
26 $ openssl req -nodes -new -x509 -keyout /dev/null \
27 > -out dummycert.pem -days -1 -config cn.conf -subj '/CN=hg.example.com'
28
29 To verify the content of this certificate:
30
31 $ openssl x509 -in dummycert.pem -noout -text
32 Certificate:
33 Data:
34 Version: 1 (0x0)
35 Serial Number: 15629337334278746470 (0xd8e68f7f6c6f7166)
36 Signature Algorithm: sha1WithRSAEncryption
37 Issuer: CN=hg.example.com
38 Validity
39 Not Before: Aug 30 08:45:59 2014 GMT
40 Not After : Aug 29 08:45:59 2014 GMT
41 Subject: CN=hg.example.com
42 Subject Public Key Info:
43 Public Key Algorithm: rsaEncryption
44 Public-Key: (512 bit)
45 Modulus:
46 00:9a:1f:d9:c9:21:a5:72:ad:00:2c:d2:e6:03:58:
47 19:aa:de:b5:1e:bb:b2:c0:fa:d1:93:a5:b2:ac:b2:
48 51:82:df:97:ec:e3:fd:14:59:44:7e:5d:ad:cc:77:
49 f3:aa:f9:8a:0a:d4:90:a0:f2:0d:58:e7:40:24:c7:
50 a4:05:81:60:29
51 Exponent: 65537 (0x10001)
52 Signature Algorithm: sha1WithRSAEncryption
53 17:d8:78:f4:b9:12:a8:9e:9a:22:5a:68:81:9b:94:1e:d8:a1:
54 5d:e2:99:06:c8:a4:b5:52:03:94:37:1c:62:c3:72:d1:14:a1:
55 f5:7c:54:7e:19:3e:5c:c2:f0:f2:30:65:62:f7:25:b5:25:dd:
56 27:b5:9e:68:a7:50:45:e9:2e:31
@@ -1,172 +1,179
1 1 # sslutil.py - SSL handling for mercurial
2 2 #
3 3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 import os
9 import os, sys
10 10
11 11 from mercurial import util
12 12 from mercurial.i18n import _
13 13 try:
14 14 # avoid using deprecated/broken FakeSocket in python 2.6
15 15 import ssl
16 16 CERT_REQUIRED = ssl.CERT_REQUIRED
17 17 PROTOCOL_SSLv23 = ssl.PROTOCOL_SSLv23
18 18 PROTOCOL_TLSv1 = ssl.PROTOCOL_TLSv1
19 19 def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
20 20 cert_reqs=ssl.CERT_NONE, ca_certs=None):
21 21 sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
22 22 cert_reqs=cert_reqs, ca_certs=ca_certs,
23 23 ssl_version=ssl_version)
24 24 # check if wrap_socket failed silently because socket had been closed
25 25 # - see http://bugs.python.org/issue13721
26 26 if not sslsocket.cipher():
27 27 raise util.Abort(_('ssl connection failed'))
28 28 return sslsocket
29 29 except ImportError:
30 30 CERT_REQUIRED = 2
31 31
32 32 PROTOCOL_SSLv23 = 2
33 33 PROTOCOL_TLSv1 = 3
34 34
35 35 import socket, httplib
36 36
37 37 def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
38 38 cert_reqs=CERT_REQUIRED, ca_certs=None):
39 39 if not util.safehasattr(socket, 'ssl'):
40 40 raise util.Abort(_('Python SSL support not found'))
41 41 if ca_certs:
42 42 raise util.Abort(_(
43 43 'certificate checking requires Python 2.6'))
44 44
45 45 ssl = socket.ssl(sock, keyfile, certfile)
46 46 return httplib.FakeSocket(sock, ssl)
47 47
48 48 def _verifycert(cert, hostname):
49 49 '''Verify that cert (in socket.getpeercert() format) matches hostname.
50 50 CRLs is not handled.
51 51
52 52 Returns error message if any problems are found and None on success.
53 53 '''
54 54 if not cert:
55 55 return _('no certificate received')
56 56 dnsname = hostname.lower()
57 57 def matchdnsname(certname):
58 58 return (certname == dnsname or
59 59 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
60 60
61 61 san = cert.get('subjectAltName', [])
62 62 if san:
63 63 certnames = [value.lower() for key, value in san if key == 'DNS']
64 64 for name in certnames:
65 65 if matchdnsname(name):
66 66 return None
67 67 if certnames:
68 68 return _('certificate is for %s') % ', '.join(certnames)
69 69
70 70 # subject is only checked when subjectAltName is empty
71 71 for s in cert.get('subject', []):
72 72 key, value = s[0]
73 73 if key == 'commonName':
74 74 try:
75 75 # 'subject' entries are unicode
76 76 certname = value.lower().encode('ascii')
77 77 except UnicodeEncodeError:
78 78 return _('IDN in certificate not supported')
79 79 if matchdnsname(certname):
80 80 return None
81 81 return _('certificate is for %s') % certname
82 82 return _('no commonName or subjectAltName found in certificate')
83 83
84 84
85 85 # CERT_REQUIRED means fetch the cert from the server all the time AND
86 86 # validate it against the CA store provided in web.cacerts.
87 87 #
88 88 # We COMPLETELY ignore CERT_REQUIRED on Python <= 2.5, as it's totally
89 89 # busted on those versions.
90 90
91 91 def sslkwargs(ui, host):
92 92 forcetls = ui.configbool('ui', 'tls', default=True)
93 93 if forcetls:
94 94 ssl_version = PROTOCOL_TLSv1
95 95 else:
96 96 ssl_version = PROTOCOL_SSLv23
97 97 kws = {'ssl_version': ssl_version,
98 98 }
99 99 hostfingerprint = ui.config('hostfingerprints', host)
100 100 if hostfingerprint:
101 101 return kws
102 102 cacerts = ui.config('web', 'cacerts')
103 103 if cacerts:
104 104 cacerts = util.expandpath(cacerts)
105 105 if not os.path.exists(cacerts):
106 106 raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
107 elif cacerts is None and sys.platform == 'darwin' and not util.mainfrozen():
108 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
109 if os.path.exists(dummycert):
110 ui.debug('using %s to enable OS X system CA\n' % dummycert)
111 ui.setconfig('web', 'cacerts', dummycert, 'dummy')
112 cacerts = dummycert
113 if cacerts:
107 114 kws.update({'ca_certs': cacerts,
108 115 'cert_reqs': CERT_REQUIRED,
109 116 })
110 117 return kws
111 118
112 119 class validator(object):
113 120 def __init__(self, ui, host):
114 121 self.ui = ui
115 122 self.host = host
116 123
117 124 def __call__(self, sock, strict=False):
118 125 host = self.host
119 126 cacerts = self.ui.config('web', 'cacerts')
120 127 hostfingerprint = self.ui.config('hostfingerprints', host)
121 128 if not getattr(sock, 'getpeercert', False): # python 2.5 ?
122 129 if hostfingerprint:
123 130 raise util.Abort(_("host fingerprint for %s can't be "
124 131 "verified (Python too old)") % host)
125 132 if strict:
126 133 raise util.Abort(_("certificate for %s can't be verified "
127 134 "(Python too old)") % host)
128 135 if self.ui.configbool('ui', 'reportoldssl', True):
129 136 self.ui.warn(_("warning: certificate for %s can't be verified "
130 137 "(Python too old)\n") % host)
131 138 return
132 139
133 140 if not sock.cipher(): # work around http://bugs.python.org/issue13721
134 141 raise util.Abort(_('%s ssl connection error') % host)
135 142 try:
136 143 peercert = sock.getpeercert(True)
137 144 peercert2 = sock.getpeercert()
138 145 except AttributeError:
139 146 raise util.Abort(_('%s ssl connection error') % host)
140 147
141 148 if not peercert:
142 149 raise util.Abort(_('%s certificate error: '
143 150 'no certificate received') % host)
144 151 peerfingerprint = util.sha1(peercert).hexdigest()
145 152 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
146 153 for x in xrange(0, len(peerfingerprint), 2)])
147 154 if hostfingerprint:
148 155 if peerfingerprint.lower() != \
149 156 hostfingerprint.replace(':', '').lower():
150 157 raise util.Abort(_('certificate for %s has unexpected '
151 158 'fingerprint %s') % (host, nicefingerprint),
152 159 hint=_('check hostfingerprint configuration'))
153 160 self.ui.debug('%s certificate matched fingerprint %s\n' %
154 161 (host, nicefingerprint))
155 162 elif cacerts:
156 163 msg = _verifycert(peercert2, host)
157 164 if msg:
158 165 raise util.Abort(_('%s certificate error: %s') % (host, msg),
159 166 hint=_('configure hostfingerprint %s or use '
160 167 '--insecure to connect insecurely') %
161 168 nicefingerprint)
162 169 self.ui.debug('%s certificate successfully verified\n' % host)
163 170 elif strict:
164 171 raise util.Abort(_('%s certificate with fingerprint %s not '
165 172 'verified') % (host, nicefingerprint),
166 173 hint=_('check hostfingerprints or web.cacerts '
167 174 'config setting'))
168 175 else:
169 176 self.ui.warn(_('warning: %s certificate with fingerprint %s not '
170 177 'verified (check hostfingerprints or web.cacerts '
171 178 'config setting)\n') %
172 179 (host, nicefingerprint))
@@ -1,592 +1,593
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 import sys, platform
8 8 if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
9 9 raise SystemExit("Mercurial requires Python 2.4 or later.")
10 10
11 11 if sys.version_info[0] >= 3:
12 12 def b(s):
13 13 '''A helper function to emulate 2.6+ bytes literals using string
14 14 literals.'''
15 15 return s.encode('latin1')
16 16 printf = eval('print')
17 17 libdir_escape = 'unicode_escape'
18 18 else:
19 19 libdir_escape = 'string_escape'
20 20 def b(s):
21 21 '''A helper function to emulate 2.6+ bytes literals using string
22 22 literals.'''
23 23 return s
24 24 def printf(*args, **kwargs):
25 25 f = kwargs.get('file', sys.stdout)
26 26 end = kwargs.get('end', '\n')
27 27 f.write(b(' ').join(args) + end)
28 28
29 29 # Solaris Python packaging brain damage
30 30 try:
31 31 import hashlib
32 32 sha = hashlib.sha1()
33 33 except ImportError:
34 34 try:
35 35 import sha
36 36 sha.sha # silence unused import warning
37 37 except ImportError:
38 38 raise SystemExit(
39 39 "Couldn't import standard hashlib (incomplete Python install).")
40 40
41 41 try:
42 42 import zlib
43 43 zlib.compressobj # silence unused import warning
44 44 except ImportError:
45 45 raise SystemExit(
46 46 "Couldn't import standard zlib (incomplete Python install).")
47 47
48 48 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
49 49 isironpython = False
50 50 try:
51 51 isironpython = (platform.python_implementation()
52 52 .lower().find("ironpython") != -1)
53 53 except AttributeError:
54 54 pass
55 55
56 56 if isironpython:
57 57 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
58 58 else:
59 59 try:
60 60 import bz2
61 61 bz2.BZ2Compressor # silence unused import warning
62 62 except ImportError:
63 63 raise SystemExit(
64 64 "Couldn't import standard bz2 (incomplete Python install).")
65 65
66 66 import os, subprocess, time
67 67 import re
68 68 import shutil
69 69 import tempfile
70 70 from distutils import log
71 71 from distutils.core import setup, Command, Extension
72 72 from distutils.dist import Distribution
73 73 from distutils.command.build import build
74 74 from distutils.command.build_ext import build_ext
75 75 from distutils.command.build_py import build_py
76 76 from distutils.command.install_scripts import install_scripts
77 77 from distutils.spawn import spawn, find_executable
78 78 from distutils import cygwinccompiler
79 79 from distutils.errors import CCompilerError, DistutilsExecError
80 80 from distutils.sysconfig import get_python_inc, get_config_var
81 81 from distutils.version import StrictVersion
82 82
83 83 convert2to3 = '--c2to3' in sys.argv
84 84 if convert2to3:
85 85 try:
86 86 from distutils.command.build_py import build_py_2to3 as build_py
87 87 from lib2to3.refactor import get_fixers_from_package as getfixers
88 88 except ImportError:
89 89 if sys.version_info[0] < 3:
90 90 raise SystemExit("--c2to3 is only compatible with python3.")
91 91 raise
92 92 sys.path.append('contrib')
93 93 elif sys.version_info[0] >= 3:
94 94 raise SystemExit("setup.py with python3 needs --c2to3 (experimental)")
95 95
96 96 scripts = ['hg']
97 97 if os.name == 'nt':
98 98 scripts.append('contrib/win32/hg.bat')
99 99
100 100 # simplified version of distutils.ccompiler.CCompiler.has_function
101 101 # that actually removes its temporary files.
102 102 def hasfunction(cc, funcname):
103 103 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
104 104 devnull = oldstderr = None
105 105 try:
106 106 try:
107 107 fname = os.path.join(tmpdir, 'funcname.c')
108 108 f = open(fname, 'w')
109 109 f.write('int main(void) {\n')
110 110 f.write(' %s();\n' % funcname)
111 111 f.write('}\n')
112 112 f.close()
113 113 # Redirect stderr to /dev/null to hide any error messages
114 114 # from the compiler.
115 115 # This will have to be changed if we ever have to check
116 116 # for a function on Windows.
117 117 devnull = open('/dev/null', 'w')
118 118 oldstderr = os.dup(sys.stderr.fileno())
119 119 os.dup2(devnull.fileno(), sys.stderr.fileno())
120 120 objects = cc.compile([fname], output_dir=tmpdir)
121 121 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
122 122 except Exception:
123 123 return False
124 124 return True
125 125 finally:
126 126 if oldstderr is not None:
127 127 os.dup2(oldstderr, sys.stderr.fileno())
128 128 if devnull is not None:
129 129 devnull.close()
130 130 shutil.rmtree(tmpdir)
131 131
132 132 # py2exe needs to be installed to work
133 133 try:
134 134 import py2exe
135 135 py2exe.Distribution # silence unused import warning
136 136 py2exeloaded = True
137 137 # import py2exe's patched Distribution class
138 138 from distutils.core import Distribution
139 139 except ImportError:
140 140 py2exeloaded = False
141 141
142 142 def runcmd(cmd, env):
143 143 if sys.platform == 'plan9':
144 144 # subprocess kludge to work around issues in half-baked Python
145 145 # ports, notably bichued/python:
146 146 _, out, err = os.popen3(cmd)
147 147 return str(out), str(err)
148 148 else:
149 149 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
150 150 stderr=subprocess.PIPE, env=env)
151 151 out, err = p.communicate()
152 152 return out, err
153 153
154 154 def runhg(cmd, env):
155 155 out, err = runcmd(cmd, env)
156 156 # If root is executing setup.py, but the repository is owned by
157 157 # another user (as in "sudo python setup.py install") we will get
158 158 # trust warnings since the .hg/hgrc file is untrusted. That is
159 159 # fine, we don't want to load it anyway. Python may warn about
160 160 # a missing __init__.py in mercurial/locale, we also ignore that.
161 161 err = [e for e in err.splitlines()
162 162 if not e.startswith(b('not trusting file')) \
163 163 and not e.startswith(b('warning: Not importing')) \
164 164 and not e.startswith(b('obsolete feature not enabled'))]
165 165 if err:
166 166 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
167 167 printf(b('\n').join([b(' ') + e for e in err]), file=sys.stderr)
168 168 return ''
169 169 return out
170 170
171 171 version = ''
172 172
173 173 # Execute hg out of this directory with a custom environment which
174 174 # includes the pure Python modules in mercurial/pure. We also take
175 175 # care to not use any hgrc files and do no localization.
176 176 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
177 177 env = {'PYTHONPATH': os.pathsep.join(pypath),
178 178 'HGRCPATH': '',
179 179 'LANGUAGE': 'C'}
180 180 if 'LD_LIBRARY_PATH' in os.environ:
181 181 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
182 182 if 'SystemRoot' in os.environ:
183 183 # Copy SystemRoot into the custom environment for Python 2.6
184 184 # under Windows. Otherwise, the subprocess will fail with
185 185 # error 0xc0150004. See: http://bugs.python.org/issue3440
186 186 env['SystemRoot'] = os.environ['SystemRoot']
187 187
188 188 if os.path.isdir('.hg'):
189 189 cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
190 190 numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
191 191 hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
192 192 if numerictags: # tag(s) found
193 193 version = numerictags[-1]
194 194 if hgid.endswith('+'): # propagate the dirty status to the tag
195 195 version += '+'
196 196 else: # no tag found
197 197 cmd = [sys.executable, 'hg', 'parents', '--template',
198 198 '{latesttag}+{latesttagdistance}-']
199 199 version = runhg(cmd, env) + hgid
200 200 if version.endswith('+'):
201 201 version += time.strftime('%Y%m%d')
202 202 elif os.path.exists('.hg_archival.txt'):
203 203 kw = dict([[t.strip() for t in l.split(':', 1)]
204 204 for l in open('.hg_archival.txt')])
205 205 if 'tag' in kw:
206 206 version = kw['tag']
207 207 elif 'latesttag' in kw:
208 208 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
209 209 else:
210 210 version = kw.get('node', '')[:12]
211 211
212 212 if version:
213 213 f = open("mercurial/__version__.py", "w")
214 214 f.write('# this file is autogenerated by setup.py\n')
215 215 f.write('version = "%s"\n' % version)
216 216 f.close()
217 217
218 218
219 219 try:
220 220 from mercurial import __version__
221 221 version = __version__.version
222 222 except ImportError:
223 223 version = 'unknown'
224 224
225 225 class hgbuild(build):
226 226 # Insert hgbuildmo first so that files in mercurial/locale/ are found
227 227 # when build_py is run next.
228 228 sub_commands = [('build_mo', None),
229 229
230 230 # We also need build_ext before build_py. Otherwise, when 2to3 is
231 231 # called (in build_py), it will not find osutil & friends,
232 232 # thinking that those modules are global and, consequently, making
233 233 # a mess, now that all module imports are global.
234 234
235 235 ('build_ext', build.has_ext_modules),
236 236 ] + build.sub_commands
237 237
238 238 class hgbuildmo(build):
239 239
240 240 description = "build translations (.mo files)"
241 241
242 242 def run(self):
243 243 if not find_executable('msgfmt'):
244 244 self.warn("could not find msgfmt executable, no translations "
245 245 "will be built")
246 246 return
247 247
248 248 podir = 'i18n'
249 249 if not os.path.isdir(podir):
250 250 self.warn("could not find %s/ directory" % podir)
251 251 return
252 252
253 253 join = os.path.join
254 254 for po in os.listdir(podir):
255 255 if not po.endswith('.po'):
256 256 continue
257 257 pofile = join(podir, po)
258 258 modir = join('locale', po[:-3], 'LC_MESSAGES')
259 259 mofile = join(modir, 'hg.mo')
260 260 mobuildfile = join('mercurial', mofile)
261 261 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
262 262 if sys.platform != 'sunos5':
263 263 # msgfmt on Solaris does not know about -c
264 264 cmd.append('-c')
265 265 self.mkpath(join('mercurial', modir))
266 266 self.make_file([pofile], mobuildfile, spawn, (cmd,))
267 267
268 268
269 269 class hgdist(Distribution):
270 270 pure = 0
271 271
272 272 global_options = Distribution.global_options + \
273 273 [('pure', None, "use pure (slow) Python "
274 274 "code instead of C extensions"),
275 275 ('c2to3', None, "(experimental!) convert "
276 276 "code with 2to3"),
277 277 ]
278 278
279 279 def has_ext_modules(self):
280 280 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
281 281 # too late for some cases
282 282 return not self.pure and Distribution.has_ext_modules(self)
283 283
284 284 class hgbuildext(build_ext):
285 285
286 286 def build_extension(self, ext):
287 287 try:
288 288 build_ext.build_extension(self, ext)
289 289 except CCompilerError:
290 290 if not getattr(ext, 'optional', False):
291 291 raise
292 292 log.warn("Failed to build optional extension '%s' (skipping)",
293 293 ext.name)
294 294
295 295 class hgbuildpy(build_py):
296 296 if convert2to3:
297 297 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
298 298 getfixers("hgfixes")))
299 299
300 300 def finalize_options(self):
301 301 build_py.finalize_options(self)
302 302
303 303 if self.distribution.pure:
304 304 if self.py_modules is None:
305 305 self.py_modules = []
306 306 for ext in self.distribution.ext_modules:
307 307 if ext.name.startswith("mercurial."):
308 308 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
309 309 self.distribution.ext_modules = []
310 310 else:
311 311 h = os.path.join(get_python_inc(), 'Python.h')
312 312 if not os.path.exists(h):
313 313 raise SystemExit('Python headers are required to build '
314 314 'Mercurial but weren\'t found in %s' % h)
315 315
316 316 def find_modules(self):
317 317 modules = build_py.find_modules(self)
318 318 for module in modules:
319 319 if module[0] == "mercurial.pure":
320 320 if module[1] != "__init__":
321 321 yield ("mercurial", module[1], module[2])
322 322 else:
323 323 yield module
324 324
325 325 class buildhgextindex(Command):
326 326 description = 'generate prebuilt index of hgext (for frozen package)'
327 327 user_options = []
328 328 _indexfilename = 'hgext/__index__.py'
329 329
330 330 def initialize_options(self):
331 331 pass
332 332
333 333 def finalize_options(self):
334 334 pass
335 335
336 336 def run(self):
337 337 if os.path.exists(self._indexfilename):
338 338 f = open(self._indexfilename, 'w')
339 339 f.write('# empty\n')
340 340 f.close()
341 341
342 342 # here no extension enabled, disabled() lists up everything
343 343 code = ('import pprint; from mercurial import extensions; '
344 344 'pprint.pprint(extensions.disabled())')
345 345 out, err = runcmd([sys.executable, '-c', code], env)
346 346 if err:
347 347 raise DistutilsExecError(err)
348 348
349 349 f = open(self._indexfilename, 'w')
350 350 f.write('# this file is autogenerated by setup.py\n')
351 351 f.write('docs = ')
352 352 f.write(out)
353 353 f.close()
354 354
355 355 class buildhgexe(build_ext):
356 356 description = 'compile hg.exe from mercurial/exewrapper.c'
357 357
358 358 def build_extensions(self):
359 359 if os.name != 'nt':
360 360 return
361 361 if isinstance(self.compiler, HackedMingw32CCompiler):
362 362 self.compiler.compiler_so = self.compiler.compiler # no -mdll
363 363 self.compiler.dll_libraries = [] # no -lmsrvc90
364 364 hv = sys.hexversion
365 365 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
366 366 f = open('mercurial/hgpythonlib.h', 'wb')
367 367 f.write('/* this file is autogenerated by setup.py */\n')
368 368 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
369 369 f.close()
370 370 objects = self.compiler.compile(['mercurial/exewrapper.c'],
371 371 output_dir=self.build_temp)
372 372 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
373 373 target = os.path.join(dir, 'hg')
374 374 self.compiler.link_executable(objects, target,
375 375 libraries=[],
376 376 output_dir=self.build_temp)
377 377
378 378 class hginstallscripts(install_scripts):
379 379 '''
380 380 This is a specialization of install_scripts that replaces the @LIBDIR@ with
381 381 the configured directory for modules. If possible, the path is made relative
382 382 to the directory for scripts.
383 383 '''
384 384
385 385 def initialize_options(self):
386 386 install_scripts.initialize_options(self)
387 387
388 388 self.install_lib = None
389 389
390 390 def finalize_options(self):
391 391 install_scripts.finalize_options(self)
392 392 self.set_undefined_options('install',
393 393 ('install_lib', 'install_lib'))
394 394
395 395 def run(self):
396 396 install_scripts.run(self)
397 397
398 398 if (os.path.splitdrive(self.install_dir)[0] !=
399 399 os.path.splitdrive(self.install_lib)[0]):
400 400 # can't make relative paths from one drive to another, so use an
401 401 # absolute path instead
402 402 libdir = self.install_lib
403 403 else:
404 404 common = os.path.commonprefix((self.install_dir, self.install_lib))
405 405 rest = self.install_dir[len(common):]
406 406 uplevel = len([n for n in os.path.split(rest) if n])
407 407
408 408 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
409 409
410 410 for outfile in self.outfiles:
411 411 fp = open(outfile, 'rb')
412 412 data = fp.read()
413 413 fp.close()
414 414
415 415 # skip binary files
416 416 if b('\0') in data:
417 417 continue
418 418
419 419 data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
420 420 fp = open(outfile, 'wb')
421 421 fp.write(data)
422 422 fp.close()
423 423
424 424 cmdclass = {'build': hgbuild,
425 425 'build_mo': hgbuildmo,
426 426 'build_ext': hgbuildext,
427 427 'build_py': hgbuildpy,
428 428 'build_hgextindex': buildhgextindex,
429 429 'install_scripts': hginstallscripts,
430 430 'build_hgexe': buildhgexe,
431 431 }
432 432
433 433 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
434 434 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
435 435 'hgext.largefiles']
436 436
437 437 pymodules = []
438 438
439 439 common_depends = ['mercurial/util.h']
440 440
441 441 extmodules = [
442 442 Extension('mercurial.base85', ['mercurial/base85.c'],
443 443 depends=common_depends),
444 444 Extension('mercurial.bdiff', ['mercurial/bdiff.c'],
445 445 depends=common_depends),
446 446 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
447 447 depends=common_depends),
448 448 Extension('mercurial.mpatch', ['mercurial/mpatch.c'],
449 449 depends=common_depends),
450 450 Extension('mercurial.parsers', ['mercurial/dirs.c',
451 451 'mercurial/parsers.c',
452 452 'mercurial/pathencode.c'],
453 453 depends=common_depends),
454 454 ]
455 455
456 456 osutil_ldflags = []
457 457
458 458 if sys.platform == 'darwin':
459 459 osutil_ldflags += ['-framework', 'ApplicationServices']
460 460
461 461 # disable osutil.c under windows + python 2.4 (issue1364)
462 462 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
463 463 pymodules.append('mercurial.pure.osutil')
464 464 else:
465 465 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
466 466 extra_link_args=osutil_ldflags,
467 467 depends=common_depends))
468 468
469 469 # the -mno-cygwin option has been deprecated for years
470 470 Mingw32CCompiler = cygwinccompiler.Mingw32CCompiler
471 471
472 472 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
473 473 def __init__(self, *args, **kwargs):
474 474 Mingw32CCompiler.__init__(self, *args, **kwargs)
475 475 for i in 'compiler compiler_so linker_exe linker_so'.split():
476 476 try:
477 477 getattr(self, i).remove('-mno-cygwin')
478 478 except ValueError:
479 479 pass
480 480
481 481 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
482 482
483 483 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
484 'help/*.txt']}
484 'help/*.txt',
485 'dummycert.pem']}
485 486
486 487 def ordinarypath(p):
487 488 return p and p[0] != '.' and p[-1] != '~'
488 489
489 490 for root in ('templates',):
490 491 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
491 492 curdir = curdir.split(os.sep, 1)[1]
492 493 dirs[:] = filter(ordinarypath, dirs)
493 494 for f in filter(ordinarypath, files):
494 495 f = os.path.join(curdir, f)
495 496 packagedata['mercurial'].append(f)
496 497
497 498 datafiles = []
498 499 setupversion = version
499 500 extra = {}
500 501
501 502 if py2exeloaded:
502 503 extra['console'] = [
503 504 {'script':'hg',
504 505 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
505 506 'product_version':version}]
506 507 # sub command of 'build' because 'py2exe' does not handle sub_commands
507 508 build.sub_commands.insert(0, ('build_hgextindex', None))
508 509
509 510 if os.name == 'nt':
510 511 # Windows binary file versions for exe/dll files must have the
511 512 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
512 513 setupversion = version.split('+', 1)[0]
513 514
514 515 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
515 516 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
516 517 if version:
517 518 version = version[0]
518 519 xcode4 = (version.startswith('Xcode') and
519 520 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
520 521 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
521 522 else:
522 523 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
523 524 # installed, but instead with only command-line tools. Assume
524 525 # that only happens on >= Lion, thus no PPC support.
525 526 xcode4 = True
526 527 xcode51 = False
527 528
528 529 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
529 530 # distutils.sysconfig
530 531 if xcode4:
531 532 os.environ['ARCHFLAGS'] = ''
532 533
533 534 # XCode 5.1 changes clang such that it now fails to compile if the
534 535 # -mno-fused-madd flag is passed, but the version of Python shipped with
535 536 # OS X 10.9 Mavericks includes this flag. This causes problems in all
536 537 # C extension modules, and a bug has been filed upstream at
537 538 # http://bugs.python.org/issue21244. We also need to patch this here
538 539 # so Mercurial can continue to compile in the meantime.
539 540 if xcode51:
540 541 cflags = get_config_var('CFLAGS')
541 542 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
542 543 os.environ['CFLAGS'] = (
543 544 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
544 545
545 546 setup(name='mercurial',
546 547 version=setupversion,
547 548 author='Matt Mackall and many others',
548 549 author_email='mercurial@selenic.com',
549 550 url='http://mercurial.selenic.com/',
550 551 download_url='http://mercurial.selenic.com/release/',
551 552 description=('Fast scalable distributed SCM (revision control, version '
552 553 'control) system'),
553 554 long_description=('Mercurial is a distributed SCM tool written in Python.'
554 555 ' It is used by a number of large projects that require'
555 556 ' fast, reliable distributed revision control, such as '
556 557 'Mozilla.'),
557 558 license='GNU GPLv2 or any later version',
558 559 classifiers=[
559 560 'Development Status :: 6 - Mature',
560 561 'Environment :: Console',
561 562 'Intended Audience :: Developers',
562 563 'Intended Audience :: System Administrators',
563 564 'License :: OSI Approved :: GNU General Public License (GPL)',
564 565 'Natural Language :: Danish',
565 566 'Natural Language :: English',
566 567 'Natural Language :: German',
567 568 'Natural Language :: Italian',
568 569 'Natural Language :: Japanese',
569 570 'Natural Language :: Portuguese (Brazilian)',
570 571 'Operating System :: Microsoft :: Windows',
571 572 'Operating System :: OS Independent',
572 573 'Operating System :: POSIX',
573 574 'Programming Language :: C',
574 575 'Programming Language :: Python',
575 576 'Topic :: Software Development :: Version Control',
576 577 ],
577 578 scripts=scripts,
578 579 packages=packages,
579 580 py_modules=pymodules,
580 581 ext_modules=extmodules,
581 582 data_files=datafiles,
582 583 package_data=packagedata,
583 584 cmdclass=cmdclass,
584 585 distclass=hgdist,
585 586 options={'py2exe': {'packages': ['hgext', 'email']},
586 587 'bdist_mpkg': {'zipdist': False,
587 588 'license': 'COPYING',
588 589 'readme': 'contrib/macosx/Readme.html',
589 590 'welcome': 'contrib/macosx/Welcome.html',
590 591 },
591 592 },
592 593 **extra)
@@ -1,343 +1,347
1 1 import os, stat
2 2 import re
3 3 import sys
4 4 import tempfile
5 5
6 6 tempprefix = 'hg-hghave-'
7 7
8 8 checks = {
9 9 "true": (lambda: True, "yak shaving"),
10 10 "false": (lambda: False, "nail clipper"),
11 11 }
12 12
13 13 def check(name, desc):
14 14 def decorator(func):
15 15 checks[name] = (func, desc)
16 16 return func
17 17 return decorator
18 18
19 19 def matchoutput(cmd, regexp, ignorestatus=False):
20 20 """Return True if cmd executes successfully and its output
21 21 is matched by the supplied regular expression.
22 22 """
23 23 r = re.compile(regexp)
24 24 fh = os.popen(cmd)
25 25 s = fh.read()
26 26 try:
27 27 ret = fh.close()
28 28 except IOError:
29 29 # Happen in Windows test environment
30 30 ret = 1
31 31 return (ignorestatus or ret is None) and r.search(s)
32 32
33 33 @check("baz", "GNU Arch baz client")
34 34 def has_baz():
35 35 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
36 36
37 37 @check("bzr", "Canonical's Bazaar client")
38 38 def has_bzr():
39 39 try:
40 40 import bzrlib
41 41 return bzrlib.__doc__ is not None
42 42 except ImportError:
43 43 return False
44 44
45 45 @check("bzr114", "Canonical's Bazaar client >= 1.14")
46 46 def has_bzr114():
47 47 try:
48 48 import bzrlib
49 49 return (bzrlib.__doc__ is not None
50 50 and bzrlib.version_info[:2] >= (1, 14))
51 51 except ImportError:
52 52 return False
53 53
54 54 @check("cvs", "cvs client/server")
55 55 def has_cvs():
56 56 re = r'Concurrent Versions System.*?server'
57 57 return matchoutput('cvs --version 2>&1', re) and not has_msys()
58 58
59 59 @check("cvs112", "cvs client/server >= 1.12")
60 60 def has_cvs112():
61 61 re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
62 62 return matchoutput('cvs --version 2>&1', re) and not has_msys()
63 63
64 64 @check("darcs", "darcs client")
65 65 def has_darcs():
66 66 return matchoutput('darcs --version', r'2\.[2-9]', True)
67 67
68 68 @check("mtn", "monotone client (>= 1.0)")
69 69 def has_mtn():
70 70 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
71 71 'mtn --version', r'monotone 0\.', True)
72 72
73 73 @check("eol-in-paths", "end-of-lines in paths")
74 74 def has_eol_in_paths():
75 75 try:
76 76 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
77 77 os.close(fd)
78 78 os.remove(path)
79 79 return True
80 80 except (IOError, OSError):
81 81 return False
82 82
83 83 @check("execbit", "executable bit")
84 84 def has_executablebit():
85 85 try:
86 86 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
87 87 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
88 88 try:
89 89 os.close(fh)
90 90 m = os.stat(fn).st_mode & 0777
91 91 new_file_has_exec = m & EXECFLAGS
92 92 os.chmod(fn, m ^ EXECFLAGS)
93 93 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
94 94 finally:
95 95 os.unlink(fn)
96 96 except (IOError, OSError):
97 97 # we don't care, the user probably won't be able to commit anyway
98 98 return False
99 99 return not (new_file_has_exec or exec_flags_cannot_flip)
100 100
101 101 @check("icasefs", "case insensitive file system")
102 102 def has_icasefs():
103 103 # Stolen from mercurial.util
104 104 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
105 105 os.close(fd)
106 106 try:
107 107 s1 = os.stat(path)
108 108 d, b = os.path.split(path)
109 109 p2 = os.path.join(d, b.upper())
110 110 if path == p2:
111 111 p2 = os.path.join(d, b.lower())
112 112 try:
113 113 s2 = os.stat(p2)
114 114 return s2 == s1
115 115 except OSError:
116 116 return False
117 117 finally:
118 118 os.remove(path)
119 119
120 120 @check("fifo", "named pipes")
121 121 def has_fifo():
122 122 if getattr(os, "mkfifo", None) is None:
123 123 return False
124 124 name = tempfile.mktemp(dir='.', prefix=tempprefix)
125 125 try:
126 126 os.mkfifo(name)
127 127 os.unlink(name)
128 128 return True
129 129 except OSError:
130 130 return False
131 131
132 132 @check("killdaemons", 'killdaemons.py support')
133 133 def has_killdaemons():
134 134 return True
135 135
136 136 @check("cacheable", "cacheable filesystem")
137 137 def has_cacheable_fs():
138 138 from mercurial import util
139 139
140 140 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
141 141 os.close(fd)
142 142 try:
143 143 return util.cachestat(path).cacheable()
144 144 finally:
145 145 os.remove(path)
146 146
147 147 @check("lsprof", "python lsprof module")
148 148 def has_lsprof():
149 149 try:
150 150 import _lsprof
151 151 _lsprof.Profiler # silence unused import warning
152 152 return True
153 153 except ImportError:
154 154 return False
155 155
156 156 @check("gettext", "GNU Gettext (msgfmt)")
157 157 def has_gettext():
158 158 return matchoutput('msgfmt --version', 'GNU gettext-tools')
159 159
160 160 @check("git", "git command line client")
161 161 def has_git():
162 162 return matchoutput('git --version 2>&1', r'^git version')
163 163
164 164 @check("docutils", "Docutils text processing library")
165 165 def has_docutils():
166 166 try:
167 167 from docutils.core import publish_cmdline
168 168 publish_cmdline # silence unused import
169 169 return True
170 170 except ImportError:
171 171 return False
172 172
173 173 def getsvnversion():
174 174 m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
175 175 if not m:
176 176 return (0, 0)
177 177 return (int(m.group(1)), int(m.group(2)))
178 178
179 179 @check("svn15", "subversion client and admin tools >= 1.5")
180 180 def has_svn15():
181 181 return getsvnversion() >= (1, 5)
182 182
183 183 @check("svn13", "subversion client and admin tools >= 1.3")
184 184 def has_svn13():
185 185 return getsvnversion() >= (1, 3)
186 186
187 187 @check("svn", "subversion client and admin tools")
188 188 def has_svn():
189 189 return matchoutput('svn --version 2>&1', r'^svn, version') and \
190 190 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
191 191
192 192 @check("svn-bindings", "subversion python bindings")
193 193 def has_svn_bindings():
194 194 try:
195 195 import svn.core
196 196 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
197 197 if version < (1, 4):
198 198 return False
199 199 return True
200 200 except ImportError:
201 201 return False
202 202
203 203 @check("p4", "Perforce server and client")
204 204 def has_p4():
205 205 return (matchoutput('p4 -V', r'Rev\. P4/') and
206 206 matchoutput('p4d -V', r'Rev\. P4D/'))
207 207
208 208 @check("symlink", "symbolic links")
209 209 def has_symlink():
210 210 if getattr(os, "symlink", None) is None:
211 211 return False
212 212 name = tempfile.mktemp(dir='.', prefix=tempprefix)
213 213 try:
214 214 os.symlink(".", name)
215 215 os.unlink(name)
216 216 return True
217 217 except (OSError, AttributeError):
218 218 return False
219 219
220 220 @check("hardlink", "hardlinks")
221 221 def has_hardlink():
222 222 from mercurial import util
223 223 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
224 224 os.close(fh)
225 225 name = tempfile.mktemp(dir='.', prefix=tempprefix)
226 226 try:
227 227 try:
228 228 util.oslink(fn, name)
229 229 os.unlink(name)
230 230 return True
231 231 except OSError:
232 232 return False
233 233 finally:
234 234 os.unlink(fn)
235 235
236 236 @check("tla", "GNU Arch tla client")
237 237 def has_tla():
238 238 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
239 239
240 240 @check("gpg", "gpg client")
241 241 def has_gpg():
242 242 return matchoutput('gpg --version 2>&1', r'GnuPG')
243 243
244 244 @check("unix-permissions", "unix-style permissions")
245 245 def has_unix_permissions():
246 246 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
247 247 try:
248 248 fname = os.path.join(d, 'foo')
249 249 for umask in (077, 007, 022):
250 250 os.umask(umask)
251 251 f = open(fname, 'w')
252 252 f.close()
253 253 mode = os.stat(fname).st_mode
254 254 os.unlink(fname)
255 255 if mode & 0777 != ~umask & 0666:
256 256 return False
257 257 return True
258 258 finally:
259 259 os.rmdir(d)
260 260
261 261 @check("root", "root permissions")
262 262 def has_root():
263 263 return getattr(os, 'geteuid', None) and os.geteuid() == 0
264 264
265 265 @check("pyflakes", "Pyflakes python linter")
266 266 def has_pyflakes():
267 267 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
268 268 r"<stdin>:1: 're' imported but unused",
269 269 True)
270 270
271 271 @check("pygments", "Pygments source highlighting library")
272 272 def has_pygments():
273 273 try:
274 274 import pygments
275 275 pygments.highlight # silence unused import warning
276 276 return True
277 277 except ImportError:
278 278 return False
279 279
280 280 @check("python243", "python >= 2.4.3")
281 281 def has_python243():
282 282 return sys.version_info >= (2, 4, 3)
283 283
284 284 @check("outer-repo", "outer repo")
285 285 def has_outer_repo():
286 286 # failing for other reasons than 'no repo' imply that there is a repo
287 287 return not matchoutput('hg root 2>&1',
288 288 r'abort: no repository found', True)
289 289
290 290 @check("ssl", "python >= 2.6 ssl module and python OpenSSL")
291 291 def has_ssl():
292 292 try:
293 293 import ssl
294 294 ssl.wrap_socket # silence unused import warning
295 295 import OpenSSL
296 296 OpenSSL.SSL.Context
297 297 return True
298 298 except ImportError:
299 299 return False
300 300
301 301 @check("windows", "Windows")
302 302 def has_windows():
303 303 return os.name == 'nt'
304 304
305 305 @check("system-sh", "system() uses sh")
306 306 def has_system_sh():
307 307 return os.name != 'nt'
308 308
309 309 @check("serve", "platform and python can manage 'hg serve -d'")
310 310 def has_serve():
311 311 return os.name != 'nt' # gross approximation
312 312
313 313 @check("test-repo", "running tests from repository")
314 314 def has_test_repo():
315 315 t = os.environ["TESTDIR"]
316 316 return os.path.isdir(os.path.join(t, "..", ".hg"))
317 317
318 318 @check("tic", "terminfo compiler and curses module")
319 319 def has_tic():
320 320 try:
321 321 import curses
322 322 curses.COLOR_BLUE
323 323 return matchoutput('test -x "`which tic`"', '')
324 324 except ImportError:
325 325 return False
326 326
327 327 @check("msys", "Windows with MSYS")
328 328 def has_msys():
329 329 return os.getenv('MSYSTEM')
330 330
331 331 @check("aix", "AIX")
332 332 def has_aix():
333 333 return sys.platform.startswith("aix")
334 334
335 @check("osx", "OS X")
336 def has_osx():
337 return sys.platform == 'darwin'
338
335 339 @check("absimport", "absolute_import in __future__")
336 340 def has_absimport():
337 341 import __future__
338 342 from mercurial import util
339 343 return util.safehasattr(__future__, "absolute_import")
340 344
341 345 @check("py3k", "running with Python 3.x")
342 346 def has_py3k():
343 347 return 3 == sys.version_info[0]
@@ -1,281 +1,292
1 1 #require serve ssl
2 2
3 3 Proper https client requires the built-in ssl from Python 2.6.
4 4
5 5 Certificates created with:
6 6 printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \
7 7 openssl req -newkey rsa:512 -keyout priv.pem -nodes -x509 -days 9000 -out pub.pem
8 8 Can be dumped with:
9 9 openssl x509 -in pub.pem -text
10 10
11 11 $ cat << EOT > priv.pem
12 12 > -----BEGIN PRIVATE KEY-----
13 13 > MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApjCWeYGrIa/Vo7LH
14 14 > aRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8
15 15 > j/xgSwIDAQABAkBxHC6+Qlf0VJXGlb6NL16yEVVTQxqDS6hA9zqu6TZjrr0YMfzc
16 16 > EGNIiZGt7HCBL0zO+cPDg/LeCZc6HQhf0KrhAiEAzlJq4hWWzvguWFIJWSoBeBUG
17 17 > MF1ACazQO7PYE8M0qfECIQDONHHP0SKZzz/ZwBZcAveC5K61f/v9hONFwbeYulzR
18 18 > +wIgc9SvbtgB/5Yzpp//4ZAEnR7oh5SClCvyB+KSx52K3nECICbhQphhoXmI10wy
19 19 > aMTellaq0bpNMHFDziqH9RsqAHhjAiEAgYGxfzkftt5IUUn/iFK89aaIpyrpuaAh
20 20 > HY8gUVkVRVs=
21 21 > -----END PRIVATE KEY-----
22 22 > EOT
23 23
24 24 $ cat << EOT > pub.pem
25 25 > -----BEGIN CERTIFICATE-----
26 26 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
27 27 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
28 28 > MTAxNDIwMzAxNFoXDTM1MDYwNTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0
29 29 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
30 30 > ADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX
31 31 > 6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA+amm
32 32 > r24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQw
33 33 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAFArvQFiAZJgQczRsbYlG1xl
34 34 > t+truk37w5B3m3Ick1ntRcQrqs+hf0CO1q6Squ144geYaQ8CDirSR92fICELI1c=
35 35 > -----END CERTIFICATE-----
36 36 > EOT
37 37 $ cat priv.pem pub.pem >> server.pem
38 38 $ PRIV=`pwd`/server.pem
39 39
40 40 $ cat << EOT > pub-other.pem
41 41 > -----BEGIN CERTIFICATE-----
42 42 > MIIBqzCCAVWgAwIBAgIJALwZS731c/ORMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
43 43 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
44 44 > MTAxNDIwNDUxNloXDTM1MDYwNTIwNDUxNlowMTESMBAGA1UEAwwJbG9jYWxob3N0
45 45 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
46 46 > ADBIAkEAsxsapLbHrqqUKuQBxdpK4G3m2LjtyrTSdpzzzFlecxd5yhNP6AyWrufo
47 47 > K4VMGo2xlu9xOo88nDSUNSKPuD09MwIDAQABo1AwTjAdBgNVHQ4EFgQUoIB1iMhN
48 48 > y868rpQ2qk9dHnU6ebswHwYDVR0jBBgwFoAUoIB1iMhNy868rpQ2qk9dHnU6ebsw
49 49 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJ544f125CsE7J2t55PdFaF6
50 50 > bBlNBb91FCywBgSjhBjf+GG3TNPwrPdc3yqeq+hzJiuInqbOBv9abmMyq8Wsoig=
51 51 > -----END CERTIFICATE-----
52 52 > EOT
53 53
54 54 pub.pem patched with other notBefore / notAfter:
55 55
56 56 $ cat << EOT > pub-not-yet.pem
57 57 > -----BEGIN CERTIFICATE-----
58 58 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
59 59 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTM1MDYwNTIwMzAxNFoXDTM1MDYw
60 60 > NTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
61 61 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
62 62 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
63 63 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
64 64 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJXV41gWnkgC7jcpPpFRSUSZaxyzrXmD1CIqQf0WgVDb
65 65 > /12E0vR2DuZitgzUYtBaofM81aTtc0a2/YsrmqePGm0=
66 66 > -----END CERTIFICATE-----
67 67 > EOT
68 68 $ cat priv.pem pub-not-yet.pem > server-not-yet.pem
69 69
70 70 $ cat << EOT > pub-expired.pem
71 71 > -----BEGIN CERTIFICATE-----
72 72 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
73 73 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEwMTAxNDIwMzAxNFoXDTEwMTAx
74 74 > NDIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
75 75 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
76 76 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
77 77 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
78 78 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJfk57DTRf2nUbYaMSlVAARxMNbFGOjQhAUtY400GhKt
79 79 > 2uiKCNGKXVXD3AHWe13yHc5KttzbHQStE5Nm/DlWBWQ=
80 80 > -----END CERTIFICATE-----
81 81 > EOT
82 82 $ cat priv.pem pub-expired.pem > server-expired.pem
83 83
84 84 $ hg init test
85 85 $ cd test
86 86 $ echo foo>foo
87 87 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
88 88 $ echo foo>foo.d/foo
89 89 $ echo bar>foo.d/bAr.hg.d/BaR
90 90 $ echo bar>foo.d/baR.d.hg/bAR
91 91 $ hg commit -A -m 1
92 92 adding foo
93 93 adding foo.d/bAr.hg.d/BaR
94 94 adding foo.d/baR.d.hg/bAR
95 95 adding foo.d/foo
96 96 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
97 97 $ cat ../hg0.pid >> $DAEMON_PIDS
98 98
99 99 cacert not found
100 100
101 101 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
102 102 abort: could not find web.cacerts: no-such.pem
103 103 [255]
104 104
105 105 Test server address cannot be reused
106 106
107 107 #if windows
108 108 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
109 109 abort: cannot start server at ':$HGPORT':
110 110 [255]
111 111 #else
112 112 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
113 113 abort: cannot start server at ':$HGPORT': Address already in use
114 114 [255]
115 115 #endif
116 116 $ cd ..
117 117
118 OS X has a dummy CA cert that enables use of the system CA store
119
120 $ DISABLEOSXDUMMYCERT=
121 #if osx
122 $ hg clone https://localhost:$HGPORT/ copy-pull
123 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
124 [255]
125
126 $ DISABLEOSXDUMMYCERT="--config=web.cacerts="
127 #endif
128
118 129 clone via pull
119 130
120 $ hg clone https://localhost:$HGPORT/ copy-pull
131 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLEOSXDUMMYCERT
121 132 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
122 133 requesting all changes
123 134 adding changesets
124 135 adding manifests
125 136 adding file changes
126 137 added 1 changesets with 4 changes to 4 files
127 138 updating to branch default
128 139 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 140 $ hg verify -R copy-pull
130 141 checking changesets
131 142 checking manifests
132 143 crosschecking files in changesets and manifests
133 144 checking files
134 145 4 files, 1 changesets, 4 total revisions
135 146 $ cd test
136 147 $ echo bar > bar
137 148 $ hg commit -A -d '1 0' -m 2
138 149 adding bar
139 150 $ cd ..
140 151
141 152 pull without cacert
142 153
143 154 $ cd copy-pull
144 155 $ echo '[hooks]' >> .hg/hgrc
145 156 $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc
146 $ hg pull
157 $ hg pull $DISABLEOSXDUMMYCERT
147 158 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
148 159 pulling from https://localhost:$HGPORT/
149 160 searching for changes
150 161 adding changesets
151 162 adding manifests
152 163 adding file changes
153 164 added 1 changesets with 1 changes to 1 files
154 165 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
155 166 (run 'hg update' to get a working copy)
156 167 $ cd ..
157 168
158 169 cacert configured in local repo
159 170
160 171 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
161 172 $ echo "[web]" >> copy-pull/.hg/hgrc
162 173 $ echo "cacerts=`pwd`/pub.pem" >> copy-pull/.hg/hgrc
163 174 $ hg -R copy-pull pull --traceback
164 175 pulling from https://localhost:$HGPORT/
165 176 searching for changes
166 177 no changes found
167 178 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
168 179
169 180 cacert configured globally, also testing expansion of environment
170 181 variables in the filename
171 182
172 183 $ echo "[web]" >> $HGRCPATH
173 184 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
174 185 $ P=`pwd` hg -R copy-pull pull
175 186 pulling from https://localhost:$HGPORT/
176 187 searching for changes
177 188 no changes found
178 189 $ P=`pwd` hg -R copy-pull pull --insecure
179 190 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
180 191 pulling from https://localhost:$HGPORT/
181 192 searching for changes
182 193 no changes found
183 194
184 195 cacert mismatch
185 196
186 197 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/
187 198 abort: 127.0.0.1 certificate error: certificate is for localhost
188 199 (configure hostfingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca or use --insecure to connect insecurely)
189 200 [255]
190 201 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ --insecure
191 202 warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
192 203 pulling from https://127.0.0.1:$HGPORT/
193 204 searching for changes
194 205 no changes found
195 206 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem
196 207 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
197 208 [255]
198 209 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem --insecure
199 210 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
200 211 pulling from https://localhost:$HGPORT/
201 212 searching for changes
202 213 no changes found
203 214
204 215 Test server cert which isn't valid yet
205 216
206 217 $ hg -R test serve -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
207 218 $ cat hg1.pid >> $DAEMON_PIDS
208 219 $ hg -R copy-pull pull --config web.cacerts=pub-not-yet.pem https://localhost:$HGPORT1/
209 220 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
210 221 [255]
211 222
212 223 Test server cert which no longer is valid
213 224
214 225 $ hg -R test serve -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
215 226 $ cat hg2.pid >> $DAEMON_PIDS
216 227 $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
217 228 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
218 229 [255]
219 230
220 231 Fingerprints
221 232
222 233 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
223 234 $ echo "localhost = 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca" >> copy-pull/.hg/hgrc
224 235 $ echo "127.0.0.1 = 914f1aff87249c09b6859b88b1906d30756491ca" >> copy-pull/.hg/hgrc
225 236
226 237 - works without cacerts
227 238 $ hg -R copy-pull id https://localhost:$HGPORT/ --config web.cacerts=
228 239 5fed3813f7f5
229 240
230 241 - fails when cert doesn't match hostname (port is ignored)
231 242 $ hg -R copy-pull id https://localhost:$HGPORT1/
232 243 abort: certificate for localhost has unexpected fingerprint 28:ff:71:bf:65:31:14:23:ad:62:92:b4:0e:31:99:18:fc:83:e3:9b
233 244 (check hostfingerprint configuration)
234 245 [255]
235 246
236 247
237 248 - ignores that certificate doesn't match hostname
238 249 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/
239 250 5fed3813f7f5
240 251
241 252 HGPORT1 is reused below for tinyproxy tests. Kill that server.
242 253 $ "$TESTDIR/killdaemons.py" hg1.pid
243 254
244 255 Prepare for connecting through proxy
245 256
246 257 $ "$TESTDIR/tinyproxy.py" $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
247 258 $ while [ ! -f proxy.pid ]; do sleep 0; done
248 259 $ cat proxy.pid >> $DAEMON_PIDS
249 260
250 261 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
251 262 $ echo "always=True" >> copy-pull/.hg/hgrc
252 263 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
253 264 $ echo "localhost =" >> copy-pull/.hg/hgrc
254 265
255 266 Test unvalidated https through proxy
256 267
257 268 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
258 269 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
259 270 pulling from https://localhost:$HGPORT/
260 271 searching for changes
261 272 no changes found
262 273
263 274 Test https with cacert and fingerprint through proxy
264 275
265 276 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub.pem
266 277 pulling from https://localhost:$HGPORT/
267 278 searching for changes
268 279 no changes found
269 280 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/
270 281 pulling from https://127.0.0.1:$HGPORT/
271 282 searching for changes
272 283 no changes found
273 284
274 285 Test https with cert problems through proxy
275 286
276 287 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-other.pem
277 288 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
278 289 [255]
279 290 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
280 291 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
281 292 [255]
General Comments 0
You need to be logged in to leave comments. Login now