~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/_urllib2_wrappers.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:56:05 UTC
  • mfrom: (6615.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201195605-o7rl92wf6uyum3fk
(vila) Open trunk again as 2.8b1 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2013, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Implementaion of urllib2 tailored to bzr needs
 
17
"""Implementation of urllib2 tailored to bzr needs
18
18
 
19
19
This file complements the urllib2 class hierarchy with custom classes.
20
20
 
36
36
request (see AbstractHTTPHandler.do_open).
37
37
"""
38
38
 
 
39
from __future__ import absolute_import
 
40
 
39
41
DEBUG = 0
40
42
 
41
43
# FIXME: Oversimplifying, two kind of exceptions should be
48
50
 
49
51
import errno
50
52
import httplib
51
 
try:
52
 
    import kerberos
53
 
except ImportError:
54
 
    have_kerberos = False
55
 
else:
56
 
    have_kerberos = True
 
53
import os
57
54
import socket
58
55
import urllib
59
56
import urllib2
60
57
import urlparse
61
58
import re
 
59
import ssl
62
60
import sys
63
61
import time
64
62
 
67
65
    config,
68
66
    debug,
69
67
    errors,
 
68
    lazy_import,
70
69
    osutils,
71
70
    trace,
72
71
    transport,
73
72
    ui,
74
73
    urlutils,
75
 
    )
 
74
)
 
75
 
 
76
try:
 
77
    _ = (ssl.match_hostname, ssl.CertificateError)
 
78
except AttributeError:
 
79
    # Provide fallbacks for python < 2.7.9
 
80
    def match_hostname(cert, host):
 
81
        trace.warning(
 
82
            '%s cannot be verified, https certificates verification is only'
 
83
            ' available for python versions >= 2.7.9' % (host,))
 
84
    ssl.match_hostname = match_hostname
 
85
    ssl.CertificateError = ValueError
 
86
 
 
87
 
 
88
# Note for packagers: if there is no package providing certs for your platform,
 
89
# the curl project produces http://curl.haxx.se/ca/cacert.pem weekly.
 
90
_ssl_ca_certs_known_locations = [
 
91
    u'/etc/ssl/certs/ca-certificates.crt',  # Ubuntu/debian/gentoo
 
92
    u'/etc/pki/tls/certs/ca-bundle.crt',  # Fedora/CentOS/RH
 
93
    u'/etc/ssl/ca-bundle.pem',  # OpenSuse
 
94
    u'/etc/ssl/cert.pem',  # OpenSuse
 
95
    u"/usr/local/share/certs/ca-root-nss.crt",  # FreeBSD
 
96
    # XXX: Needs checking, can't trust the interweb ;) -- vila 2012-01-25
 
97
    u'/etc/openssl/certs/ca-certificates.crt',  # Solaris
 
98
]
 
99
 
 
100
 
 
101
def default_ca_certs():
 
102
    if sys.platform == 'win32':
 
103
        return os.path.join(os.path.dirname(sys.executable), u"cacert.pem")
 
104
    elif sys.platform == 'darwin':
 
105
        # FIXME: Needs some default value for osx, waiting for osx installers
 
106
        # guys feedback -- vila 2012-01-25
 
107
        pass
 
108
    else:
 
109
        # Try known locations for friendly OSes providing the root certificates
 
110
        # without making them hard to use for any https client.
 
111
        for path in _ssl_ca_certs_known_locations:
 
112
            if os.path.exists(path):
 
113
                # First found wins
 
114
                return path
 
115
    # A default path that makes sense and will be mentioned in the error
 
116
    # presented to the user, even if not correct for all platforms
 
117
    return _ssl_ca_certs_known_locations[0]
 
118
 
 
119
 
 
120
def ca_certs_from_store(path):
 
121
    if not os.path.exists(path):
 
122
        raise ValueError("ca certs path %s does not exist" % path)
 
123
    return path
 
124
 
 
125
 
 
126
def cert_reqs_from_store(unicode_str):
 
127
    import ssl
 
128
    try:
 
129
        return {"required": ssl.CERT_REQUIRED,
 
130
                "none": ssl.CERT_NONE}[unicode_str]
 
131
    except KeyError:
 
132
        raise ValueError("invalid value %s" % unicode_str)
 
133
 
 
134
 
 
135
def default_ca_reqs():
 
136
    if sys.platform in ('win32', 'darwin'):
 
137
        # FIXME: Once we get a native access to root certificates there, this
 
138
        # won't needed anymore. See http://pad.lv/920455 -- vila 2012-02-15
 
139
        return u'none'
 
140
    else:
 
141
        return u'required'
 
142
 
 
143
opt_ssl_ca_certs = config.Option('ssl.ca_certs',
 
144
                                 from_unicode=ca_certs_from_store,
 
145
                                 default=default_ca_certs,
 
146
                                 invalid='warning',
 
147
                                 help="""\
 
148
Path to certification authority certificates to trust.
 
149
 
 
150
This should be a valid path to a bundle containing all root Certificate
 
151
Authorities used to verify an https server certificate.
 
152
 
 
153
Use ssl.cert_reqs=none to disable certificate verification.
 
154
""")
 
155
 
 
156
opt_ssl_cert_reqs = config.Option('ssl.cert_reqs',
 
157
                                  default=default_ca_reqs,
 
158
                                  from_unicode=cert_reqs_from_store,
 
159
                                  invalid='error',
 
160
                                  help="""\
 
161
Whether to require a certificate from the remote side. (default:required)
 
162
 
 
163
Possible values:
 
164
 * none: Certificates ignored
 
165
 * required: Certificates required and validated
 
166
""")
 
167
 
 
168
checked_kerberos = False
 
169
kerberos = None
 
170
 
 
171
 
 
172
class addinfourl(urllib2.addinfourl):
 
173
    '''Replacement addinfourl class compatible with python-2.7's xmlrpclib
 
174
 
 
175
    In python-2.7, xmlrpclib expects that the response object that it receives
 
176
    has a getheader method.  httplib.HTTPResponse provides this but
 
177
    urllib2.addinfourl does not.  Add the necessary functions here, ported to
 
178
    use the internal data structures of addinfourl.
 
179
    '''
 
180
 
 
181
    def getheader(self, name, default=None):
 
182
        if self.headers is None:
 
183
            raise httplib.ResponseNotReady()
 
184
        return self.headers.getheader(name, default)
 
185
 
 
186
    def getheaders(self):
 
187
        if self.headers is None:
 
188
            raise httplib.ResponseNotReady()
 
189
        return self.headers.items()
76
190
 
77
191
 
78
192
class _ReportingFileSocket(object):
90
204
        self.report_activity(len(s), 'read')
91
205
        return s
92
206
 
93
 
    def readline(self):
94
 
        # This should be readline(self, size=-1), but httplib in python 2.4 and
95
 
        #  2.5 defines a SSLFile wrapper whose readline method lacks the size
96
 
        #  parameter.  So until we drop support for 2.4 and 2.5 and since we
97
 
        #  don't *need* the size parameter we'll stay with readline(self)
98
 
        #  --  vila 20090209
99
 
        s = self.filesock.readline()
 
207
    def readline(self, size=-1):
 
208
        s = self.filesock.readline(size)
100
209
        self.report_activity(len(s), 'read')
101
210
        return s
102
211
 
145
254
    """
146
255
 
147
256
    # Some responses have bodies in which we have no interest
148
 
    _body_ignored_responses = [301,302, 303, 307, 401, 403, 404]
 
257
    _body_ignored_responses = [301,302, 303, 307, 400, 401, 403, 404, 501]
149
258
 
150
259
    # in finish() below, we may have to discard several MB in the worst
151
260
    # case. To avoid buffering that much, we read and discard by chunks
246
355
    def cleanup_pipe(self):
247
356
        """Read the remaining bytes of the last response if any."""
248
357
        if self._response is not None:
249
 
            pending = self._response.finish()
250
 
            # Warn the user (once)
251
 
            if (self._ranges_received_whole_file is None
252
 
                and self._response.status == 200
253
 
                and pending and pending > self._range_warning_thresold
254
 
                ):
255
 
                self._ranges_received_whole_file = True
256
 
                trace.warning(
257
 
                    'Got a 200 response when asking for multiple ranges,'
258
 
                    ' does your server at %s:%s support range requests?',
259
 
                    self.host, self.port)
 
358
            try:
 
359
                pending = self._response.finish()
 
360
                # Warn the user (once)
 
361
                if (self._ranges_received_whole_file is None
 
362
                    and self._response.status == 200
 
363
                    and pending and pending > self._range_warning_thresold
 
364
                    ):
 
365
                    self._ranges_received_whole_file = True
 
366
                    trace.warning(
 
367
                        'Got a 200 response when asking for multiple ranges,'
 
368
                        ' does your server at %s:%s support range requests?',
 
369
                        self.host, self.port)
 
370
            except socket.error, e:
 
371
                # It's conceivable that the socket is in a bad state here
 
372
                # (including some test cases) and in this case, it doesn't need
 
373
                # cleaning anymore, so no need to fail, we just get rid of the
 
374
                # socket and let callers reconnect
 
375
                if (len(e.args) == 0
 
376
                    or e.args[0] not in (errno.ECONNRESET, errno.ECONNABORTED)):
 
377
                    raise
 
378
                self.close()
260
379
            self._response = None
261
380
        # Preserve our preciousss
262
381
        sock = self.sock
275
394
 
276
395
    # XXX: Needs refactoring at the caller level.
277
396
    def __init__(self, host, port=None, proxied_host=None,
278
 
                 report_activity=None):
 
397
                 report_activity=None, ca_certs=None):
279
398
        AbstractHTTPConnection.__init__(self, report_activity=report_activity)
280
399
        # Use strict=True since we don't support HTTP/0.9
281
400
        httplib.HTTPConnection.__init__(self, host, port, strict=True)
282
401
        self.proxied_host = proxied_host
 
402
        # ca_certs is ignored, it's only relevant for https
283
403
 
284
404
    def connect(self):
285
405
        if 'http' in debug.debug_flags:
288
408
        self._wrap_socket_for_reporting(self.sock)
289
409
 
290
410
 
291
 
# Build the appropriate socket wrapper for ssl
292
 
try:
293
 
    # python 2.6 introduced a better ssl package
294
 
    import ssl
295
 
    _ssl_wrap_socket = ssl.wrap_socket
296
 
except ImportError:
297
 
    # python versions prior to 2.6 don't have ssl and ssl.wrap_socket instead
298
 
    # they use httplib.FakeSocket
299
 
    def _ssl_wrap_socket(sock, key_file, cert_file):
300
 
        ssl_sock = socket.ssl(sock, key_file, cert_file)
301
 
        return httplib.FakeSocket(sock, ssl_sock)
302
 
 
303
 
 
304
411
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
305
412
 
306
413
    def __init__(self, host, port=None, key_file=None, cert_file=None,
307
414
                 proxied_host=None,
308
 
                 report_activity=None):
 
415
                 report_activity=None, ca_certs=None):
309
416
        AbstractHTTPConnection.__init__(self, report_activity=report_activity)
310
417
        # Use strict=True since we don't support HTTP/0.9
311
418
        httplib.HTTPSConnection.__init__(self, host, port,
312
419
                                         key_file, cert_file, strict=True)
313
420
        self.proxied_host = proxied_host
 
421
        self.ca_certs = ca_certs
314
422
 
315
423
    def connect(self):
316
424
        if 'http' in debug.debug_flags:
321
429
            self.connect_to_origin()
322
430
 
323
431
    def connect_to_origin(self):
324
 
        ssl_sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file)
 
432
        # FIXME JRV 2011-12-18: Use location config here?
 
433
        config_stack = config.GlobalStack()
 
434
        cert_reqs = config_stack.get('ssl.cert_reqs')
 
435
        if self.proxied_host is not None:
 
436
            host = self.proxied_host.split(":", 1)[0]
 
437
        else:
 
438
            host = self.host
 
439
        if cert_reqs == ssl.CERT_NONE:
 
440
            ui.ui_factory.show_user_warning('not_checking_ssl_cert', host=host)
 
441
            ui.ui_factory.suppressed_warnings.add('not_checking_ssl_cert')
 
442
            ca_certs = None
 
443
        else:
 
444
            if self.ca_certs is None:
 
445
                ca_certs = config_stack.get('ssl.ca_certs')
 
446
            else:
 
447
                ca_certs = self.ca_certs
 
448
            if ca_certs is None:
 
449
                trace.warning(
 
450
                    "No valid trusted SSL CA certificates file set. See "
 
451
                    "'bzr help ssl.ca_certs' for more information on setting "
 
452
                    "trusted CAs.")
 
453
        try:
 
454
            ssl_sock = ssl.wrap_socket(
 
455
                self.sock, self.key_file, self.cert_file,
 
456
                cert_reqs=cert_reqs, ca_certs=ca_certs)
 
457
        except ssl.SSLError:
 
458
            trace.note(
 
459
                "\n"
 
460
                "See `bzr help ssl.ca_certs` for how to specify trusted CA"
 
461
                "certificates.\n"
 
462
                "Pass -Ossl.cert_reqs=none to disable certificate "
 
463
                "verification entirely.\n")
 
464
            raise
 
465
        if cert_reqs == ssl.CERT_REQUIRED:
 
466
            peer_cert = ssl_sock.getpeercert()
 
467
            ssl.match_hostname(peer_cert, host)
 
468
 
325
469
        # Wrap the ssl socket before anybody use it
326
470
        self._wrap_socket_for_reporting(ssl_sock)
327
471
 
429
573
 
430
574
    handler_order = 1000 # after all pre-processings
431
575
 
432
 
    def __init__(self, report_activity=None):
 
576
    def __init__(self, report_activity=None, ca_certs=None):
433
577
        self._report_activity = report_activity
 
578
        self.ca_certs = ca_certs
434
579
 
435
580
    def create_connection(self, request, http_connection_class):
436
581
        host = request.get_host()
444
589
        try:
445
590
            connection = http_connection_class(
446
591
                host, proxied_host=request.proxied_host,
447
 
                report_activity=self._report_activity)
 
592
                report_activity=self._report_activity,
 
593
                ca_certs=self.ca_certs)
448
594
        except httplib.InvalidURL, exception:
449
595
            # There is only one occurrence of InvalidURL in httplib
450
596
            raise errors.InvalidURL(request.get_full_url(),
564
710
                        'Bad status line received',
565
711
                        orig_error=exc_val)
566
712
                elif (isinstance(exc_val, socket.error) and len(exc_val.args)
567
 
                      and exc_val.args[0] in (errno.ECONNRESET, 10054)):
 
713
                      and exc_val.args[0] in (errno.ECONNRESET, 10053, 10054)):
 
714
                      # 10053 == WSAECONNABORTED
 
715
                      # 10054 == WSAECONNRESET
568
716
                    raise errors.ConnectionReset(
569
717
                        "Connection lost while sending request.")
570
718
                else:
621
769
                                     headers)
622
770
            if 'http' in debug.debug_flags:
623
771
                trace.mutter('> %s %s' % (method, url))
624
 
                hdrs = ['%s: %s' % (k, v) for k,v in headers.items()]
 
772
                hdrs = []
 
773
                for k,v in headers.iteritems():
 
774
                    # People are often told to paste -Dhttp output to help
 
775
                    # debug. Don't compromise credentials.
 
776
                    if k in ('Authorization', 'Proxy-Authorization'):
 
777
                        v = '<masked>'
 
778
                    hdrs.append('%s: %s' % (k, v))
625
779
                trace.mutter('> ' + '\n> '.join(hdrs) + '\n')
626
780
            if self._debuglevel >= 1:
627
781
                print 'Request sent: [%r] from (%s)' \
628
782
                    % (request, request.connection.sock.getsockname())
629
783
            response = connection.getresponse()
630
784
            convert_to_addinfourl = True
 
785
        except (ssl.SSLError, ssl.CertificateError):
 
786
            # Something is wrong with either the certificate or the hostname,
 
787
            # re-trying won't help
 
788
            raise
631
789
        except (socket.gaierror, httplib.BadStatusLine, httplib.UnknownProtocol,
632
790
                socket.error, httplib.HTTPException):
633
791
            response = self.retry_or_raise(http_class, request, first_try)
656
814
            r = response
657
815
            r.recv = r.read
658
816
            fp = socket._fileobject(r, bufsize=65536)
659
 
            resp = urllib2.addinfourl(fp, r.msg, req.get_full_url())
 
817
            resp = addinfourl(fp, r.msg, req.get_full_url())
660
818
            resp.code = r.status
661
819
            resp.msg = r.reason
662
820
            resp.version = r.version
906
1064
        return None
907
1065
 
908
1066
    def proxy_bypass(self, host):
909
 
        """Check if host should be proxied or not"""
 
1067
        """Check if host should be proxied or not.
 
1068
 
 
1069
        :returns: True to skip the proxy, False otherwise.
 
1070
        """
910
1071
        no_proxy = self.get_proxy_env_var('no', default_to=None)
 
1072
        bypass = self.evaluate_proxy_bypass(host, no_proxy)
 
1073
        if bypass is None:
 
1074
            # Nevertheless, there are platform-specific ways to
 
1075
            # ignore proxies...
 
1076
            return urllib.proxy_bypass(host)
 
1077
        else:
 
1078
            return bypass
 
1079
 
 
1080
    def evaluate_proxy_bypass(self, host, no_proxy):
 
1081
        """Check the host against a comma-separated no_proxy list as a string.
 
1082
 
 
1083
        :param host: ``host:port`` being requested
 
1084
 
 
1085
        :param no_proxy: comma-separated list of hosts to access directly.
 
1086
 
 
1087
        :returns: True to skip the proxy, False not to, or None to
 
1088
            leave it to urllib.
 
1089
        """
911
1090
        if no_proxy is None:
 
1091
            # All hosts are proxied
912
1092
            return False
913
1093
        hhost, hport = urllib.splitport(host)
914
1094
        # Does host match any of the domains mentioned in
916
1096
        # are fuzzy (to say the least). We try to allow most
917
1097
        # commonly seen values.
918
1098
        for domain in no_proxy.split(','):
 
1099
            domain = domain.strip()
 
1100
            if domain == '':
 
1101
                continue
919
1102
            dhost, dport = urllib.splitport(domain)
920
1103
            if hport == dport or dport is None:
921
1104
                # Protect glob chars
924
1107
                dhost = dhost.replace("?", r".")
925
1108
                if re.match(dhost, hhost, re.IGNORECASE):
926
1109
                    return True
927
 
        # Nevertheless, there are platform-specific ways to
928
 
        # ignore proxies...
929
 
        return urllib.proxy_bypass(host)
 
1110
        # Nothing explicitly avoid the host
 
1111
        return None
930
1112
 
931
1113
    def set_proxy(self, request, type):
932
1114
        if self.proxy_bypass(request.get_host()):
939
1121
        # grok user:password@host:port as well as
940
1122
        # http://user:password@host:port
941
1123
 
942
 
        (scheme, user, password,
943
 
         host, port, path) = transport.ConnectedTransport._split_url(proxy)
944
 
        if not host:
 
1124
        parsed_url = transport.ConnectedTransport._split_url(proxy)
 
1125
        if not parsed_url.host:
945
1126
            raise errors.InvalidURL(proxy, 'No host component')
946
1127
 
947
1128
        if request.proxy_auth == {}:
949
1130
            # proxied request, intialize.  scheme (the authentication scheme)
950
1131
            # and realm will be set by the AuthHandler
951
1132
            request.proxy_auth = {
952
 
                                  'host': host, 'port': port,
953
 
                                  'user': user, 'password': password,
954
 
                                  'protocol': scheme,
 
1133
                                  'host': parsed_url.host,
 
1134
                                  'port': parsed_url.port,
 
1135
                                  'user': parsed_url.user,
 
1136
                                  'password': parsed_url.password,
 
1137
                                  'protocol': parsed_url.scheme,
955
1138
                                   # We ignore path since we connect to a proxy
956
1139
                                  'path': None}
957
 
        if port is None:
958
 
            phost = host
 
1140
        if parsed_url.port is None:
 
1141
            phost = parsed_url.host
959
1142
        else:
960
 
            phost = host + ':%d' % port
 
1143
            phost = parsed_url.host + ':%d' % parsed_url.port
961
1144
        request.set_proxy(phost, type)
962
1145
        if self._debuglevel >= 3:
963
1146
            print 'set_proxy: proxy set to %s://%s' % (type, phost)
1072
1255
        auth['modified'] = False
1073
1256
        # Put some common info in auth if the caller didn't
1074
1257
        if auth.get('path', None) is None:
1075
 
            (protocol, _, _,
1076
 
             host, port, path) = urlutils.parse_url(request.get_full_url())
1077
 
            self.update_auth(auth, 'protocol', protocol)
1078
 
            self.update_auth(auth, 'host', host)
1079
 
            self.update_auth(auth, 'port', port)
1080
 
            self.update_auth(auth, 'path', path)
 
1258
            parsed_url = urlutils.URL.from_string(request.get_full_url())
 
1259
            self.update_auth(auth, 'protocol', parsed_url.scheme)
 
1260
            self.update_auth(auth, 'host', parsed_url.host)
 
1261
            self.update_auth(auth, 'port', parsed_url.port)
 
1262
            self.update_auth(auth, 'path', parsed_url.path)
1081
1263
        # FIXME: the auth handler should be selected at a single place instead
1082
1264
        # of letting all handlers try to match all headers, but the current
1083
1265
        # design doesn't allow a simple implementation.
1182
1364
        user = auth.get('user', None)
1183
1365
        password = auth.get('password', None)
1184
1366
        realm = auth['realm']
 
1367
        port = auth.get('port', None)
1185
1368
 
1186
1369
        if user is None:
1187
1370
            user = auth_conf.get_user(auth['protocol'], auth['host'],
1188
 
                                      port=auth['port'], path=auth['path'],
 
1371
                                      port=port, path=auth['path'],
1189
1372
                                      realm=realm, ask=True,
1190
1373
                                      prompt=self.build_username_prompt(auth))
1191
1374
        if user is not None and password is None:
1192
1375
            password = auth_conf.get_password(
1193
 
                auth['protocol'], auth['host'], user, port=auth['port'],
 
1376
                auth['protocol'], auth['host'], user,
 
1377
                port=port,
1194
1378
                path=auth['path'], realm=realm,
1195
1379
                prompt=self.build_password_prompt(auth))
1196
1380
 
1207
1391
        user. The daughter classes should implements a public
1208
1392
        build_password_prompt using this method.
1209
1393
        """
1210
 
        prompt = '%s' % auth['protocol'].upper() + ' %(user)s@%(host)s'
 
1394
        prompt = u'%s' % auth['protocol'].upper() + u' %(user)s@%(host)s'
1211
1395
        realm = auth['realm']
1212
1396
        if realm is not None:
1213
 
            prompt += ", Realm: '%s'" % realm
1214
 
        prompt += ' password'
 
1397
            prompt += u", Realm: '%s'" % realm.decode('utf8')
 
1398
        prompt += u' password'
1215
1399
        return prompt
1216
1400
 
1217
1401
    def _build_username_prompt(self, auth):
1225
1409
        user. The daughter classes should implements a public
1226
1410
        build_username_prompt using this method.
1227
1411
        """
1228
 
        prompt = '%s' % auth['protocol'].upper() + ' %(host)s'
 
1412
        prompt = u'%s' % auth['protocol'].upper() + u' %(host)s'
1229
1413
        realm = auth['realm']
1230
1414
        if realm is not None:
1231
 
            prompt += ", Realm: '%s'" % realm
1232
 
        prompt += ' username'
 
1415
            prompt += u", Realm: '%s'" % realm.decode('utf8')
 
1416
        prompt += u' username'
1233
1417
        return prompt
1234
1418
 
1235
1419
    def http_request(self, request):
1267
1451
 
1268
1452
    def _auth_match_kerberos(self, auth):
1269
1453
        """Try to create a GSSAPI response for authenticating against a host."""
1270
 
        if not have_kerberos:
 
1454
        global kerberos, checked_kerberos
 
1455
        if kerberos is None and not checked_kerberos:
 
1456
            try:
 
1457
                import kerberos
 
1458
            except ImportError:
 
1459
                kerberos = None
 
1460
            checked_kerberos = True
 
1461
        if kerberos is None:
1271
1462
            return None
1272
1463
        ret, vc = kerberos.authGSSClientInit("HTTP@%(host)s" % auth)
1273
1464
        if ret < 1:
1340
1531
    if algorithm == 'MD5':
1341
1532
        H = lambda x: osutils.md5(x).hexdigest()
1342
1533
    elif algorithm == 'SHA':
1343
 
        H = lambda x: osutils.sha(x).hexdigest()
 
1534
        H = osutils.sha_string
1344
1535
    if H is not None:
1345
1536
        KD = lambda secret, data: H("%s:%s" % (secret, data))
1346
1537
    return H, KD
1349
1540
def get_new_cnonce(nonce, nonce_count):
1350
1541
    raw = '%s:%d:%s:%s' % (nonce, nonce_count, time.ctime(),
1351
1542
                           urllib2.randombytes(8))
1352
 
    return osutils.sha(raw).hexdigest()[:16]
 
1543
    return osutils.sha_string(raw)[:16]
1353
1544
 
1354
1545
 
1355
1546
class DigestAuthHandler(AbstractAuthHandler):
1499
1690
 
1500
1691
    def build_password_prompt(self, auth):
1501
1692
        prompt = self._build_password_prompt(auth)
1502
 
        prompt = 'Proxy ' + prompt
 
1693
        prompt = u'Proxy ' + prompt
1503
1694
        return prompt
1504
1695
 
1505
1696
    def build_username_prompt(self, auth):
1506
1697
        prompt = self._build_username_prompt(auth)
1507
 
        prompt = 'Proxy ' + prompt
 
1698
        prompt = u'Proxy ' + prompt
1508
1699
        return prompt
1509
1700
 
1510
1701
    def http_error_407(self, req, fp, code, msg, headers):
1592
1783
                 connection=ConnectionHandler,
1593
1784
                 redirect=HTTPRedirectHandler,
1594
1785
                 error=HTTPErrorProcessor,
1595
 
                 report_activity=None):
 
1786
                 report_activity=None,
 
1787
                 ca_certs=None):
1596
1788
        self._opener = urllib2.build_opener(
1597
 
            connection(report_activity=report_activity),
 
1789
            connection(report_activity=report_activity, ca_certs=ca_certs),
1598
1790
            redirect, error,
1599
1791
            ProxyHandler(),
1600
1792
            HTTPBasicAuthHandler(),