~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-2012 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
50
48
 
51
49
import errno
52
50
import httplib
53
 
import os
 
51
try:
 
52
    import kerberos
 
53
except ImportError:
 
54
    have_kerberos = False
 
55
else:
 
56
    have_kerberos = True
54
57
import socket
55
58
import urllib
56
59
import urllib2
64
67
    config,
65
68
    debug,
66
69
    errors,
67
 
    lazy_import,
68
70
    osutils,
69
71
    trace,
70
72
    transport,
71
73
    ui,
72
74
    urlutils,
73
75
    )
74
 
lazy_import.lazy_import(globals(), """
75
 
import ssl
76
 
""")
77
 
 
78
 
 
79
 
# Note for packagers: if there is no package providing certs for your platform,
80
 
# the curl project produces http://curl.haxx.se/ca/cacert.pem weekly.
81
 
_ssl_ca_certs_known_locations = [
82
 
    u'/etc/ssl/certs/ca-certificates.crt', # Ubuntu/debian/gentoo
83
 
    u'/etc/pki/tls/certs/ca-bundle.crt', # Fedora/CentOS/RH
84
 
    u'/etc/ssl/ca-bundle.pem', # OpenSuse
85
 
    u'/etc/ssl/cert.pem', # OpenSuse
86
 
    u"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD
87
 
    # XXX: Needs checking, can't trust the interweb ;) -- vila 2012-01-25
88
 
    u'/etc/openssl/certs/ca-certificates.crt', # Solaris
89
 
    ]
90
 
def default_ca_certs():
91
 
    if sys.platform == 'win32':
92
 
        return os.path.join(os.path.dirname(sys.executable), u"cacert.pem")
93
 
    elif sys.platform == 'darwin':
94
 
        # FIXME: Needs some default value for osx, waiting for osx installers
95
 
        # guys feedback -- vila 2012-01-25
96
 
        pass
97
 
    else:
98
 
        # Try known locations for friendly OSes providing the root certificates
99
 
        # without making them hard to use for any https client.
100
 
        for path in _ssl_ca_certs_known_locations:
101
 
            if os.path.exists(path):
102
 
                # First found wins
103
 
                return path
104
 
    # A default path that makes sense and will be mentioned in the error
105
 
    # presented to the user, even if not correct for all platforms
106
 
    return _ssl_ca_certs_known_locations[0]
107
 
 
108
 
 
109
 
def ca_certs_from_store(path):
110
 
    if not os.path.exists(path):
111
 
        raise ValueError("ca certs path %s does not exist" % path)
112
 
    return path
113
 
 
114
 
 
115
 
def cert_reqs_from_store(unicode_str):
116
 
    import ssl
117
 
    try:
118
 
        return {
119
 
            "required": ssl.CERT_REQUIRED,
120
 
            "none": ssl.CERT_NONE
121
 
            }[unicode_str]
122
 
    except KeyError:
123
 
        raise ValueError("invalid value %s" % unicode_str)
124
 
 
125
 
def default_ca_reqs():
126
 
    if sys.platform in ('win32', 'darwin'):
127
 
        # FIXME: Once we get a native access to root certificates there, this
128
 
        # won't needed anymore. See http://pad.lv/920455 -- vila 2012-02-15
129
 
        return u'none'
130
 
    else:
131
 
        return u'required'
132
 
 
133
 
opt_ssl_ca_certs = config.Option('ssl.ca_certs',
134
 
        from_unicode=ca_certs_from_store,
135
 
        default=default_ca_certs,
136
 
        invalid='warning',
137
 
        help="""\
138
 
Path to certification authority certificates to trust.
139
 
 
140
 
This should be a valid path to a bundle containing all root Certificate
141
 
Authorities used to verify an https server certificate.
142
 
 
143
 
Use ssl.cert_reqs=none to disable certificate verification.
144
 
""")
145
 
 
146
 
opt_ssl_cert_reqs = config.Option('ssl.cert_reqs',
147
 
        default=default_ca_reqs,
148
 
        from_unicode=cert_reqs_from_store,
149
 
        invalid='error',
150
 
        help="""\
151
 
Whether to require a certificate from the remote side. (default:required)
152
 
 
153
 
Possible values:
154
 
 * none: Certificates ignored
155
 
 * required: Certificates required and validated
156
 
""")
157
 
 
158
 
checked_kerberos = False
159
 
kerberos = None
160
 
 
161
 
 
162
 
class addinfourl(urllib2.addinfourl):
163
 
    '''Replacement addinfourl class compatible with python-2.7's xmlrpclib
164
 
 
165
 
    In python-2.7, xmlrpclib expects that the response object that it receives
166
 
    has a getheader method.  httplib.HTTPResponse provides this but
167
 
    urllib2.addinfourl does not.  Add the necessary functions here, ported to
168
 
    use the internal data structures of addinfourl.
169
 
    '''
170
 
 
171
 
    def getheader(self, name, default=None):
172
 
        if self.headers is None:
173
 
            raise httplib.ResponseNotReady()
174
 
        return self.headers.getheader(name, default)
175
 
 
176
 
    def getheaders(self):
177
 
        if self.headers is None:
178
 
            raise httplib.ResponseNotReady()
179
 
        return self.headers.items()
180
76
 
181
77
 
182
78
class _ReportingFileSocket(object):
194
90
        self.report_activity(len(s), 'read')
195
91
        return s
196
92
 
197
 
    def readline(self, size=-1):
198
 
        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()
199
100
        self.report_activity(len(s), 'read')
200
101
        return s
201
102
 
244
145
    """
245
146
 
246
147
    # Some responses have bodies in which we have no interest
247
 
    _body_ignored_responses = [301,302, 303, 307, 400, 401, 403, 404, 501]
 
148
    _body_ignored_responses = [301,302, 303, 307, 401, 403, 404]
248
149
 
249
150
    # in finish() below, we may have to discard several MB in the worst
250
151
    # case. To avoid buffering that much, we read and discard by chunks
345
246
    def cleanup_pipe(self):
346
247
        """Read the remaining bytes of the last response if any."""
347
248
        if self._response is not None:
348
 
            try:
349
 
                pending = self._response.finish()
350
 
                # Warn the user (once)
351
 
                if (self._ranges_received_whole_file is None
352
 
                    and self._response.status == 200
353
 
                    and pending and pending > self._range_warning_thresold
354
 
                    ):
355
 
                    self._ranges_received_whole_file = True
356
 
                    trace.warning(
357
 
                        'Got a 200 response when asking for multiple ranges,'
358
 
                        ' does your server at %s:%s support range requests?',
359
 
                        self.host, self.port)
360
 
            except socket.error, e:
361
 
                # It's conceivable that the socket is in a bad state here
362
 
                # (including some test cases) and in this case, it doesn't need
363
 
                # cleaning anymore, so no need to fail, we just get rid of the
364
 
                # socket and let callers reconnect
365
 
                if (len(e.args) == 0
366
 
                    or e.args[0] not in (errno.ECONNRESET, errno.ECONNABORTED)):
367
 
                    raise
368
 
                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)
369
260
            self._response = None
370
261
        # Preserve our preciousss
371
262
        sock = self.sock
384
275
 
385
276
    # XXX: Needs refactoring at the caller level.
386
277
    def __init__(self, host, port=None, proxied_host=None,
387
 
                 report_activity=None, ca_certs=None):
 
278
                 report_activity=None):
388
279
        AbstractHTTPConnection.__init__(self, report_activity=report_activity)
389
280
        # Use strict=True since we don't support HTTP/0.9
390
281
        httplib.HTTPConnection.__init__(self, host, port, strict=True)
391
282
        self.proxied_host = proxied_host
392
 
        # ca_certs is ignored, it's only relevant for https
393
283
 
394
284
    def connect(self):
395
285
        if 'http' in debug.debug_flags:
398
288
        self._wrap_socket_for_reporting(self.sock)
399
289
 
400
290
 
401
 
# These two methods were imported from Python 3.2's ssl module
402
 
 
403
 
def _dnsname_to_pat(dn, max_wildcards=1):
404
 
    pats = []
405
 
    for frag in dn.split(r'.'):
406
 
        if frag.count('*') > max_wildcards:
407
 
            # Python Issue #17980: avoid denials of service by refusing more
408
 
            # than one wildcard per fragment.  A survery of established
409
 
            # policy among SSL implementations showed it to be a
410
 
            # reasonable choice.
411
 
            raise ValueError(
412
 
                "too many wildcards in certificate DNS name: " + repr(dn))
413
 
        if frag == '*':
414
 
            # When '*' is a fragment by itself, it matches a non-empty dotless
415
 
            # fragment.
416
 
            pats.append('[^.]+')
417
 
        else:
418
 
            # Otherwise, '*' matches any dotless fragment.
419
 
            frag = re.escape(frag)
420
 
            pats.append(frag.replace(r'\*', '[^.]*'))
421
 
    return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
422
 
 
423
 
 
424
 
def match_hostname(cert, hostname):
425
 
    """Verify that *cert* (in decoded format as returned by
426
 
    SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 rules
427
 
    are mostly followed, but IP addresses are not accepted for *hostname*.
428
 
 
429
 
    CertificateError is raised on failure. On success, the function
430
 
    returns nothing.
431
 
    """
432
 
    if not cert:
433
 
        raise ValueError("empty or no certificate")
434
 
    dnsnames = []
435
 
    san = cert.get('subjectAltName', ())
436
 
    for key, value in san:
437
 
        if key == 'DNS':
438
 
            if _dnsname_to_pat(value).match(hostname):
439
 
                return
440
 
            dnsnames.append(value)
441
 
    if not san:
442
 
        # The subject is only checked when subjectAltName is empty
443
 
        for sub in cert.get('subject', ()):
444
 
            for key, value in sub:
445
 
                # XXX according to RFC 2818, the most specific Common Name
446
 
                # must be used.
447
 
                if key == 'commonName':
448
 
                    if _dnsname_to_pat(value).match(hostname):
449
 
                        return
450
 
                    dnsnames.append(value)
451
 
    if len(dnsnames) > 1:
452
 
        raise errors.CertificateError(
453
 
            "hostname %r doesn't match either of %s"
454
 
            % (hostname, ', '.join(map(repr, dnsnames))))
455
 
    elif len(dnsnames) == 1:
456
 
        raise errors.CertificateError("hostname %r doesn't match %r" %
457
 
                                      (hostname, dnsnames[0]))
458
 
    else:
459
 
        raise errors.CertificateError("no appropriate commonName or "
460
 
            "subjectAltName fields were found")
 
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)
461
302
 
462
303
 
463
304
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
464
305
 
465
306
    def __init__(self, host, port=None, key_file=None, cert_file=None,
466
307
                 proxied_host=None,
467
 
                 report_activity=None, ca_certs=None):
 
308
                 report_activity=None):
468
309
        AbstractHTTPConnection.__init__(self, report_activity=report_activity)
469
310
        # Use strict=True since we don't support HTTP/0.9
470
311
        httplib.HTTPSConnection.__init__(self, host, port,
471
312
                                         key_file, cert_file, strict=True)
472
313
        self.proxied_host = proxied_host
473
 
        self.ca_certs = ca_certs
474
314
 
475
315
    def connect(self):
476
316
        if 'http' in debug.debug_flags:
481
321
            self.connect_to_origin()
482
322
 
483
323
    def connect_to_origin(self):
484
 
        # FIXME JRV 2011-12-18: Use location config here?
485
 
        config_stack = config.GlobalStack()
486
 
        cert_reqs = config_stack.get('ssl.cert_reqs')
487
 
        if self.proxied_host is not None:
488
 
            host = self.proxied_host.split(":", 1)[0]
489
 
        else:
490
 
            host = self.host
491
 
        if cert_reqs == ssl.CERT_NONE:
492
 
            ui.ui_factory.show_user_warning('not_checking_ssl_cert', host=host)
493
 
            ui.ui_factory.suppressed_warnings.add('not_checking_ssl_cert')
494
 
            ca_certs = None
495
 
        else:
496
 
            if self.ca_certs is None:
497
 
                ca_certs = config_stack.get('ssl.ca_certs')
498
 
            else:
499
 
                ca_certs = self.ca_certs
500
 
            if ca_certs is None:
501
 
                trace.warning(
502
 
                    "No valid trusted SSL CA certificates file set. See "
503
 
                    "'bzr help ssl.ca_certs' for more information on setting "
504
 
                    "trusted CAs.")
505
 
        try:
506
 
            ssl_sock = ssl.wrap_socket(self.sock, self.key_file, self.cert_file,
507
 
                cert_reqs=cert_reqs, ca_certs=ca_certs)
508
 
        except ssl.SSLError, e:
509
 
            trace.note(
510
 
                "\n"
511
 
                "See `bzr help ssl.ca_certs` for how to specify trusted CA"
512
 
                "certificates.\n"
513
 
                "Pass -Ossl.cert_reqs=none to disable certificate "
514
 
                "verification entirely.\n")
515
 
            raise
516
 
        if cert_reqs == ssl.CERT_REQUIRED:
517
 
            peer_cert = ssl_sock.getpeercert()
518
 
            match_hostname(peer_cert, host)
519
 
 
 
324
        ssl_sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file)
520
325
        # Wrap the ssl socket before anybody use it
521
326
        self._wrap_socket_for_reporting(ssl_sock)
522
327
 
624
429
 
625
430
    handler_order = 1000 # after all pre-processings
626
431
 
627
 
    def __init__(self, report_activity=None, ca_certs=None):
 
432
    def __init__(self, report_activity=None):
628
433
        self._report_activity = report_activity
629
 
        self.ca_certs = ca_certs
630
434
 
631
435
    def create_connection(self, request, http_connection_class):
632
436
        host = request.get_host()
640
444
        try:
641
445
            connection = http_connection_class(
642
446
                host, proxied_host=request.proxied_host,
643
 
                report_activity=self._report_activity,
644
 
                ca_certs=self.ca_certs)
 
447
                report_activity=self._report_activity)
645
448
        except httplib.InvalidURL, exception:
646
449
            # There is only one occurrence of InvalidURL in httplib
647
450
            raise errors.InvalidURL(request.get_full_url(),
761
564
                        'Bad status line received',
762
565
                        orig_error=exc_val)
763
566
                elif (isinstance(exc_val, socket.error) and len(exc_val.args)
764
 
                      and exc_val.args[0] in (errno.ECONNRESET, 10053, 10054)):
765
 
                      # 10053 == WSAECONNABORTED
766
 
                      # 10054 == WSAECONNRESET
 
567
                      and exc_val.args[0] in (errno.ECONNRESET, 10054)):
767
568
                    raise errors.ConnectionReset(
768
569
                        "Connection lost while sending request.")
769
570
                else:
820
621
                                     headers)
821
622
            if 'http' in debug.debug_flags:
822
623
                trace.mutter('> %s %s' % (method, url))
823
 
                hdrs = []
824
 
                for k,v in headers.iteritems():
825
 
                    # People are often told to paste -Dhttp output to help
826
 
                    # debug. Don't compromise credentials.
827
 
                    if k in ('Authorization', 'Proxy-Authorization'):
828
 
                        v = '<masked>'
829
 
                    hdrs.append('%s: %s' % (k, v))
 
624
                hdrs = ['%s: %s' % (k, v) for k,v in headers.items()]
830
625
                trace.mutter('> ' + '\n> '.join(hdrs) + '\n')
831
626
            if self._debuglevel >= 1:
832
627
                print 'Request sent: [%r] from (%s)' \
833
628
                    % (request, request.connection.sock.getsockname())
834
629
            response = connection.getresponse()
835
630
            convert_to_addinfourl = True
836
 
        except (ssl.SSLError, errors.CertificateError):
837
 
            # Something is wrong with either the certificate or the hostname,
838
 
            # re-trying won't help
839
 
            raise
840
631
        except (socket.gaierror, httplib.BadStatusLine, httplib.UnknownProtocol,
841
632
                socket.error, httplib.HTTPException):
842
633
            response = self.retry_or_raise(http_class, request, first_try)
865
656
            r = response
866
657
            r.recv = r.read
867
658
            fp = socket._fileobject(r, bufsize=65536)
868
 
            resp = addinfourl(fp, r.msg, req.get_full_url())
 
659
            resp = urllib2.addinfourl(fp, r.msg, req.get_full_url())
869
660
            resp.code = r.status
870
661
            resp.msg = r.reason
871
662
            resp.version = r.version
1115
906
        return None
1116
907
 
1117
908
    def proxy_bypass(self, host):
1118
 
        """Check if host should be proxied or not.
1119
 
 
1120
 
        :returns: True to skip the proxy, False otherwise.
1121
 
        """
 
909
        """Check if host should be proxied or not"""
1122
910
        no_proxy = self.get_proxy_env_var('no', default_to=None)
1123
 
        bypass = self.evaluate_proxy_bypass(host, no_proxy)
1124
 
        if bypass is None:
1125
 
            # Nevertheless, there are platform-specific ways to
1126
 
            # ignore proxies...
1127
 
            return urllib.proxy_bypass(host)
1128
 
        else:
1129
 
            return bypass
1130
 
 
1131
 
    def evaluate_proxy_bypass(self, host, no_proxy):
1132
 
        """Check the host against a comma-separated no_proxy list as a string.
1133
 
 
1134
 
        :param host: ``host:port`` being requested
1135
 
 
1136
 
        :param no_proxy: comma-separated list of hosts to access directly.
1137
 
 
1138
 
        :returns: True to skip the proxy, False not to, or None to
1139
 
            leave it to urllib.
1140
 
        """
1141
911
        if no_proxy is None:
1142
 
            # All hosts are proxied
1143
912
            return False
1144
913
        hhost, hport = urllib.splitport(host)
1145
914
        # Does host match any of the domains mentioned in
1147
916
        # are fuzzy (to say the least). We try to allow most
1148
917
        # commonly seen values.
1149
918
        for domain in no_proxy.split(','):
1150
 
            domain = domain.strip()
1151
 
            if domain == '':
1152
 
                continue
1153
919
            dhost, dport = urllib.splitport(domain)
1154
920
            if hport == dport or dport is None:
1155
921
                # Protect glob chars
1158
924
                dhost = dhost.replace("?", r".")
1159
925
                if re.match(dhost, hhost, re.IGNORECASE):
1160
926
                    return True
1161
 
        # Nothing explicitly avoid the host
1162
 
        return None
 
927
        # Nevertheless, there are platform-specific ways to
 
928
        # ignore proxies...
 
929
        return urllib.proxy_bypass(host)
1163
930
 
1164
931
    def set_proxy(self, request, type):
1165
932
        if self.proxy_bypass(request.get_host()):
1172
939
        # grok user:password@host:port as well as
1173
940
        # http://user:password@host:port
1174
941
 
1175
 
        parsed_url = transport.ConnectedTransport._split_url(proxy)
1176
 
        if not parsed_url.host:
 
942
        (scheme, user, password,
 
943
         host, port, path) = transport.ConnectedTransport._split_url(proxy)
 
944
        if not host:
1177
945
            raise errors.InvalidURL(proxy, 'No host component')
1178
946
 
1179
947
        if request.proxy_auth == {}:
1181
949
            # proxied request, intialize.  scheme (the authentication scheme)
1182
950
            # and realm will be set by the AuthHandler
1183
951
            request.proxy_auth = {
1184
 
                                  'host': parsed_url.host,
1185
 
                                  'port': parsed_url.port,
1186
 
                                  'user': parsed_url.user,
1187
 
                                  'password': parsed_url.password,
1188
 
                                  'protocol': parsed_url.scheme,
 
952
                                  'host': host, 'port': port,
 
953
                                  'user': user, 'password': password,
 
954
                                  'protocol': scheme,
1189
955
                                   # We ignore path since we connect to a proxy
1190
956
                                  'path': None}
1191
 
        if parsed_url.port is None:
1192
 
            phost = parsed_url.host
 
957
        if port is None:
 
958
            phost = host
1193
959
        else:
1194
 
            phost = parsed_url.host + ':%d' % parsed_url.port
 
960
            phost = host + ':%d' % port
1195
961
        request.set_proxy(phost, type)
1196
962
        if self._debuglevel >= 3:
1197
963
            print 'set_proxy: proxy set to %s://%s' % (type, phost)
1306
1072
        auth['modified'] = False
1307
1073
        # Put some common info in auth if the caller didn't
1308
1074
        if auth.get('path', None) is None:
1309
 
            parsed_url = urlutils.URL.from_string(request.get_full_url())
1310
 
            self.update_auth(auth, 'protocol', parsed_url.scheme)
1311
 
            self.update_auth(auth, 'host', parsed_url.host)
1312
 
            self.update_auth(auth, 'port', parsed_url.port)
1313
 
            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)
1314
1081
        # FIXME: the auth handler should be selected at a single place instead
1315
1082
        # of letting all handlers try to match all headers, but the current
1316
1083
        # design doesn't allow a simple implementation.
1415
1182
        user = auth.get('user', None)
1416
1183
        password = auth.get('password', None)
1417
1184
        realm = auth['realm']
1418
 
        port = auth.get('port', None)
1419
1185
 
1420
1186
        if user is None:
1421
1187
            user = auth_conf.get_user(auth['protocol'], auth['host'],
1422
 
                                      port=port, path=auth['path'],
 
1188
                                      port=auth['port'], path=auth['path'],
1423
1189
                                      realm=realm, ask=True,
1424
1190
                                      prompt=self.build_username_prompt(auth))
1425
1191
        if user is not None and password is None:
1426
1192
            password = auth_conf.get_password(
1427
 
                auth['protocol'], auth['host'], user,
1428
 
                port=port,
 
1193
                auth['protocol'], auth['host'], user, port=auth['port'],
1429
1194
                path=auth['path'], realm=realm,
1430
1195
                prompt=self.build_password_prompt(auth))
1431
1196
 
1442
1207
        user. The daughter classes should implements a public
1443
1208
        build_password_prompt using this method.
1444
1209
        """
1445
 
        prompt = u'%s' % auth['protocol'].upper() + u' %(user)s@%(host)s'
 
1210
        prompt = '%s' % auth['protocol'].upper() + ' %(user)s@%(host)s'
1446
1211
        realm = auth['realm']
1447
1212
        if realm is not None:
1448
 
            prompt += u", Realm: '%s'" % realm.decode('utf8')
1449
 
        prompt += u' password'
 
1213
            prompt += ", Realm: '%s'" % realm
 
1214
        prompt += ' password'
1450
1215
        return prompt
1451
1216
 
1452
1217
    def _build_username_prompt(self, auth):
1460
1225
        user. The daughter classes should implements a public
1461
1226
        build_username_prompt using this method.
1462
1227
        """
1463
 
        prompt = u'%s' % auth['protocol'].upper() + u' %(host)s'
 
1228
        prompt = '%s' % auth['protocol'].upper() + ' %(host)s'
1464
1229
        realm = auth['realm']
1465
1230
        if realm is not None:
1466
 
            prompt += u", Realm: '%s'" % realm.decode('utf8')
1467
 
        prompt += u' username'
 
1231
            prompt += ", Realm: '%s'" % realm
 
1232
        prompt += ' username'
1468
1233
        return prompt
1469
1234
 
1470
1235
    def http_request(self, request):
1502
1267
 
1503
1268
    def _auth_match_kerberos(self, auth):
1504
1269
        """Try to create a GSSAPI response for authenticating against a host."""
1505
 
        global kerberos, checked_kerberos
1506
 
        if kerberos is None and not checked_kerberos:
1507
 
            try:
1508
 
                import kerberos
1509
 
            except ImportError:
1510
 
                kerberos = None
1511
 
            checked_kerberos = True
1512
 
        if kerberos is None:
 
1270
        if not have_kerberos:
1513
1271
            return None
1514
1272
        ret, vc = kerberos.authGSSClientInit("HTTP@%(host)s" % auth)
1515
1273
        if ret < 1:
1582
1340
    if algorithm == 'MD5':
1583
1341
        H = lambda x: osutils.md5(x).hexdigest()
1584
1342
    elif algorithm == 'SHA':
1585
 
        H = osutils.sha_string
 
1343
        H = lambda x: osutils.sha(x).hexdigest()
1586
1344
    if H is not None:
1587
1345
        KD = lambda secret, data: H("%s:%s" % (secret, data))
1588
1346
    return H, KD
1591
1349
def get_new_cnonce(nonce, nonce_count):
1592
1350
    raw = '%s:%d:%s:%s' % (nonce, nonce_count, time.ctime(),
1593
1351
                           urllib2.randombytes(8))
1594
 
    return osutils.sha_string(raw)[:16]
 
1352
    return osutils.sha(raw).hexdigest()[:16]
1595
1353
 
1596
1354
 
1597
1355
class DigestAuthHandler(AbstractAuthHandler):
1741
1499
 
1742
1500
    def build_password_prompt(self, auth):
1743
1501
        prompt = self._build_password_prompt(auth)
1744
 
        prompt = u'Proxy ' + prompt
 
1502
        prompt = 'Proxy ' + prompt
1745
1503
        return prompt
1746
1504
 
1747
1505
    def build_username_prompt(self, auth):
1748
1506
        prompt = self._build_username_prompt(auth)
1749
 
        prompt = u'Proxy ' + prompt
 
1507
        prompt = 'Proxy ' + prompt
1750
1508
        return prompt
1751
1509
 
1752
1510
    def http_error_407(self, req, fp, code, msg, headers):
1834
1592
                 connection=ConnectionHandler,
1835
1593
                 redirect=HTTPRedirectHandler,
1836
1594
                 error=HTTPErrorProcessor,
1837
 
                 report_activity=None,
1838
 
                 ca_certs=None):
 
1595
                 report_activity=None):
1839
1596
        self._opener = urllib2.build_opener(
1840
 
            connection(report_activity=report_activity, ca_certs=ca_certs),
 
1597
            connection(report_activity=report_activity),
1841
1598
            redirect, error,
1842
1599
            ProxyHandler(),
1843
1600
            HTTPBasicAuthHandler(),