~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

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