xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/urllib/request.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""An extensible library for opening URLs using a variety of protocols
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerThe simplest way to use this module is to call the urlopen function,
4*cda5da8dSAndroid Build Coastguard Workerwhich accepts a string containing a URL or a Request object (described
5*cda5da8dSAndroid Build Coastguard Workerbelow).  It opens the URL and returns the results as file-like
6*cda5da8dSAndroid Build Coastguard Workerobject; the returned object has some extra methods described below.
7*cda5da8dSAndroid Build Coastguard Worker
8*cda5da8dSAndroid Build Coastguard WorkerThe OpenerDirector manages a collection of Handler objects that do
9*cda5da8dSAndroid Build Coastguard Workerall the actual work.  Each Handler implements a particular protocol or
10*cda5da8dSAndroid Build Coastguard Workeroption.  The OpenerDirector is a composite object that invokes the
11*cda5da8dSAndroid Build Coastguard WorkerHandlers needed to open the requested URL.  For example, the
12*cda5da8dSAndroid Build Coastguard WorkerHTTPHandler performs HTTP GET and POST requests and deals with
13*cda5da8dSAndroid Build Coastguard Workernon-error returns.  The HTTPRedirectHandler automatically deals with
14*cda5da8dSAndroid Build Coastguard WorkerHTTP 301, 302, 303, 307, and 308 redirect errors, and the
15*cda5da8dSAndroid Build Coastguard WorkerHTTPDigestAuthHandler deals with digest authentication.
16*cda5da8dSAndroid Build Coastguard Worker
17*cda5da8dSAndroid Build Coastguard Workerurlopen(url, data=None) -- Basic usage is the same as original
18*cda5da8dSAndroid Build Coastguard Workerurllib.  pass the url and optionally data to post to an HTTP URL, and
19*cda5da8dSAndroid Build Coastguard Workerget a file-like object back.  One difference is that you can also pass
20*cda5da8dSAndroid Build Coastguard Workera Request instance instead of URL.  Raises a URLError (subclass of
21*cda5da8dSAndroid Build Coastguard WorkerOSError); for HTTP errors, raises an HTTPError, which can also be
22*cda5da8dSAndroid Build Coastguard Workertreated as a valid response.
23*cda5da8dSAndroid Build Coastguard Worker
24*cda5da8dSAndroid Build Coastguard Workerbuild_opener -- Function that creates a new OpenerDirector instance.
25*cda5da8dSAndroid Build Coastguard WorkerWill install the default handlers.  Accepts one or more Handlers as
26*cda5da8dSAndroid Build Coastguard Workerarguments, either instances or Handler classes that it will
27*cda5da8dSAndroid Build Coastguard Workerinstantiate.  If one of the argument is a subclass of the default
28*cda5da8dSAndroid Build Coastguard Workerhandler, the argument will be installed instead of the default.
29*cda5da8dSAndroid Build Coastguard Worker
30*cda5da8dSAndroid Build Coastguard Workerinstall_opener -- Installs a new opener as the default opener.
31*cda5da8dSAndroid Build Coastguard Worker
32*cda5da8dSAndroid Build Coastguard Workerobjects of interest:
33*cda5da8dSAndroid Build Coastguard Worker
34*cda5da8dSAndroid Build Coastguard WorkerOpenerDirector -- Sets up the User Agent as the Python-urllib client and manages
35*cda5da8dSAndroid Build Coastguard Workerthe Handler classes, while dealing with requests and responses.
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard WorkerRequest -- An object that encapsulates the state of a request.  The
38*cda5da8dSAndroid Build Coastguard Workerstate can be as simple as the URL.  It can also include extra HTTP
39*cda5da8dSAndroid Build Coastguard Workerheaders, e.g. a User-Agent.
40*cda5da8dSAndroid Build Coastguard Worker
41*cda5da8dSAndroid Build Coastguard WorkerBaseHandler --
42*cda5da8dSAndroid Build Coastguard Worker
43*cda5da8dSAndroid Build Coastguard Workerinternals:
44*cda5da8dSAndroid Build Coastguard WorkerBaseHandler and parent
45*cda5da8dSAndroid Build Coastguard Worker_call_chain conventions
46*cda5da8dSAndroid Build Coastguard Worker
47*cda5da8dSAndroid Build Coastguard WorkerExample usage:
48*cda5da8dSAndroid Build Coastguard Worker
49*cda5da8dSAndroid Build Coastguard Workerimport urllib.request
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Worker# set up authentication info
52*cda5da8dSAndroid Build Coastguard Workerauthinfo = urllib.request.HTTPBasicAuthHandler()
53*cda5da8dSAndroid Build Coastguard Workerauthinfo.add_password(realm='PDQ Application',
54*cda5da8dSAndroid Build Coastguard Worker                      uri='https://mahler:8092/site-updates.py',
55*cda5da8dSAndroid Build Coastguard Worker                      user='klem',
56*cda5da8dSAndroid Build Coastguard Worker                      passwd='geheim$parole')
57*cda5da8dSAndroid Build Coastguard Worker
58*cda5da8dSAndroid Build Coastguard Workerproxy_support = urllib.request.ProxyHandler({"http" : "http://ahad-haam:3128"})
59*cda5da8dSAndroid Build Coastguard Worker
60*cda5da8dSAndroid Build Coastguard Worker# build a new opener that adds authentication and caching FTP handlers
61*cda5da8dSAndroid Build Coastguard Workeropener = urllib.request.build_opener(proxy_support, authinfo,
62*cda5da8dSAndroid Build Coastguard Worker                                     urllib.request.CacheFTPHandler)
63*cda5da8dSAndroid Build Coastguard Worker
64*cda5da8dSAndroid Build Coastguard Worker# install it
65*cda5da8dSAndroid Build Coastguard Workerurllib.request.install_opener(opener)
66*cda5da8dSAndroid Build Coastguard Worker
67*cda5da8dSAndroid Build Coastguard Workerf = urllib.request.urlopen('https://www.python.org/')
68*cda5da8dSAndroid Build Coastguard Worker"""
69*cda5da8dSAndroid Build Coastguard Worker
70*cda5da8dSAndroid Build Coastguard Worker# XXX issues:
71*cda5da8dSAndroid Build Coastguard Worker# If an authentication error handler that tries to perform
72*cda5da8dSAndroid Build Coastguard Worker# authentication for some reason but fails, how should the error be
73*cda5da8dSAndroid Build Coastguard Worker# signalled?  The client needs to know the HTTP error code.  But if
74*cda5da8dSAndroid Build Coastguard Worker# the handler knows that the problem was, e.g., that it didn't know
75*cda5da8dSAndroid Build Coastguard Worker# that hash algo that requested in the challenge, it would be good to
76*cda5da8dSAndroid Build Coastguard Worker# pass that information along to the client, too.
77*cda5da8dSAndroid Build Coastguard Worker# ftp errors aren't handled cleanly
78*cda5da8dSAndroid Build Coastguard Worker# check digest against correct (i.e. non-apache) implementation
79*cda5da8dSAndroid Build Coastguard Worker
80*cda5da8dSAndroid Build Coastguard Worker# Possible extensions:
81*cda5da8dSAndroid Build Coastguard Worker# complex proxies  XXX not sure what exactly was meant by this
82*cda5da8dSAndroid Build Coastguard Worker# abstract factory for opener
83*cda5da8dSAndroid Build Coastguard Worker
84*cda5da8dSAndroid Build Coastguard Workerimport base64
85*cda5da8dSAndroid Build Coastguard Workerimport bisect
86*cda5da8dSAndroid Build Coastguard Workerimport email
87*cda5da8dSAndroid Build Coastguard Workerimport hashlib
88*cda5da8dSAndroid Build Coastguard Workerimport http.client
89*cda5da8dSAndroid Build Coastguard Workerimport io
90*cda5da8dSAndroid Build Coastguard Workerimport os
91*cda5da8dSAndroid Build Coastguard Workerimport posixpath
92*cda5da8dSAndroid Build Coastguard Workerimport re
93*cda5da8dSAndroid Build Coastguard Workerimport socket
94*cda5da8dSAndroid Build Coastguard Workerimport string
95*cda5da8dSAndroid Build Coastguard Workerimport sys
96*cda5da8dSAndroid Build Coastguard Workerimport time
97*cda5da8dSAndroid Build Coastguard Workerimport tempfile
98*cda5da8dSAndroid Build Coastguard Workerimport contextlib
99*cda5da8dSAndroid Build Coastguard Workerimport warnings
100*cda5da8dSAndroid Build Coastguard Worker
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard Workerfrom urllib.error import URLError, HTTPError, ContentTooShortError
103*cda5da8dSAndroid Build Coastguard Workerfrom urllib.parse import (
104*cda5da8dSAndroid Build Coastguard Worker    urlparse, urlsplit, urljoin, unwrap, quote, unquote,
105*cda5da8dSAndroid Build Coastguard Worker    _splittype, _splithost, _splitport, _splituser, _splitpasswd,
106*cda5da8dSAndroid Build Coastguard Worker    _splitattr, _splitquery, _splitvalue, _splittag, _to_bytes,
107*cda5da8dSAndroid Build Coastguard Worker    unquote_to_bytes, urlunparse)
108*cda5da8dSAndroid Build Coastguard Workerfrom urllib.response import addinfourl, addclosehook
109*cda5da8dSAndroid Build Coastguard Worker
110*cda5da8dSAndroid Build Coastguard Worker# check for SSL
111*cda5da8dSAndroid Build Coastguard Workertry:
112*cda5da8dSAndroid Build Coastguard Worker    import ssl
113*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
114*cda5da8dSAndroid Build Coastguard Worker    _have_ssl = False
115*cda5da8dSAndroid Build Coastguard Workerelse:
116*cda5da8dSAndroid Build Coastguard Worker    _have_ssl = True
117*cda5da8dSAndroid Build Coastguard Worker
118*cda5da8dSAndroid Build Coastguard Worker__all__ = [
119*cda5da8dSAndroid Build Coastguard Worker    # Classes
120*cda5da8dSAndroid Build Coastguard Worker    'Request', 'OpenerDirector', 'BaseHandler', 'HTTPDefaultErrorHandler',
121*cda5da8dSAndroid Build Coastguard Worker    'HTTPRedirectHandler', 'HTTPCookieProcessor', 'ProxyHandler',
122*cda5da8dSAndroid Build Coastguard Worker    'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm',
123*cda5da8dSAndroid Build Coastguard Worker    'HTTPPasswordMgrWithPriorAuth', 'AbstractBasicAuthHandler',
124*cda5da8dSAndroid Build Coastguard Worker    'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', 'AbstractDigestAuthHandler',
125*cda5da8dSAndroid Build Coastguard Worker    'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', 'HTTPHandler',
126*cda5da8dSAndroid Build Coastguard Worker    'FileHandler', 'FTPHandler', 'CacheFTPHandler', 'DataHandler',
127*cda5da8dSAndroid Build Coastguard Worker    'UnknownHandler', 'HTTPErrorProcessor',
128*cda5da8dSAndroid Build Coastguard Worker    # Functions
129*cda5da8dSAndroid Build Coastguard Worker    'urlopen', 'install_opener', 'build_opener',
130*cda5da8dSAndroid Build Coastguard Worker    'pathname2url', 'url2pathname', 'getproxies',
131*cda5da8dSAndroid Build Coastguard Worker    # Legacy interface
132*cda5da8dSAndroid Build Coastguard Worker    'urlretrieve', 'urlcleanup', 'URLopener', 'FancyURLopener',
133*cda5da8dSAndroid Build Coastguard Worker]
134*cda5da8dSAndroid Build Coastguard Worker
135*cda5da8dSAndroid Build Coastguard Worker# used in User-Agent header sent
136*cda5da8dSAndroid Build Coastguard Worker__version__ = '%d.%d' % sys.version_info[:2]
137*cda5da8dSAndroid Build Coastguard Worker
138*cda5da8dSAndroid Build Coastguard Worker_opener = None
139*cda5da8dSAndroid Build Coastguard Workerdef urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
140*cda5da8dSAndroid Build Coastguard Worker            *, cafile=None, capath=None, cadefault=False, context=None):
141*cda5da8dSAndroid Build Coastguard Worker    '''Open the URL url, which can be either a string or a Request object.
142*cda5da8dSAndroid Build Coastguard Worker
143*cda5da8dSAndroid Build Coastguard Worker    *data* must be an object specifying additional data to be sent to
144*cda5da8dSAndroid Build Coastguard Worker    the server, or None if no such data is needed.  See Request for
145*cda5da8dSAndroid Build Coastguard Worker    details.
146*cda5da8dSAndroid Build Coastguard Worker
147*cda5da8dSAndroid Build Coastguard Worker    urllib.request module uses HTTP/1.1 and includes a "Connection:close"
148*cda5da8dSAndroid Build Coastguard Worker    header in its HTTP requests.
149*cda5da8dSAndroid Build Coastguard Worker
150*cda5da8dSAndroid Build Coastguard Worker    The optional *timeout* parameter specifies a timeout in seconds for
151*cda5da8dSAndroid Build Coastguard Worker    blocking operations like the connection attempt (if not specified, the
152*cda5da8dSAndroid Build Coastguard Worker    global default timeout setting will be used). This only works for HTTP,
153*cda5da8dSAndroid Build Coastguard Worker    HTTPS and FTP connections.
154*cda5da8dSAndroid Build Coastguard Worker
155*cda5da8dSAndroid Build Coastguard Worker    If *context* is specified, it must be a ssl.SSLContext instance describing
156*cda5da8dSAndroid Build Coastguard Worker    the various SSL options. See HTTPSConnection for more details.
157*cda5da8dSAndroid Build Coastguard Worker
158*cda5da8dSAndroid Build Coastguard Worker    The optional *cafile* and *capath* parameters specify a set of trusted CA
159*cda5da8dSAndroid Build Coastguard Worker    certificates for HTTPS requests. cafile should point to a single file
160*cda5da8dSAndroid Build Coastguard Worker    containing a bundle of CA certificates, whereas capath should point to a
161*cda5da8dSAndroid Build Coastguard Worker    directory of hashed certificate files. More information can be found in
162*cda5da8dSAndroid Build Coastguard Worker    ssl.SSLContext.load_verify_locations().
163*cda5da8dSAndroid Build Coastguard Worker
164*cda5da8dSAndroid Build Coastguard Worker    The *cadefault* parameter is ignored.
165*cda5da8dSAndroid Build Coastguard Worker
166*cda5da8dSAndroid Build Coastguard Worker
167*cda5da8dSAndroid Build Coastguard Worker    This function always returns an object which can work as a
168*cda5da8dSAndroid Build Coastguard Worker    context manager and has the properties url, headers, and status.
169*cda5da8dSAndroid Build Coastguard Worker    See urllib.response.addinfourl for more detail on these properties.
170*cda5da8dSAndroid Build Coastguard Worker
171*cda5da8dSAndroid Build Coastguard Worker    For HTTP and HTTPS URLs, this function returns a http.client.HTTPResponse
172*cda5da8dSAndroid Build Coastguard Worker    object slightly modified. In addition to the three new methods above, the
173*cda5da8dSAndroid Build Coastguard Worker    msg attribute contains the same information as the reason attribute ---
174*cda5da8dSAndroid Build Coastguard Worker    the reason phrase returned by the server --- instead of the response
175*cda5da8dSAndroid Build Coastguard Worker    headers as it is specified in the documentation for HTTPResponse.
176*cda5da8dSAndroid Build Coastguard Worker
177*cda5da8dSAndroid Build Coastguard Worker    For FTP, file, and data URLs and requests explicitly handled by legacy
178*cda5da8dSAndroid Build Coastguard Worker    URLopener and FancyURLopener classes, this function returns a
179*cda5da8dSAndroid Build Coastguard Worker    urllib.response.addinfourl object.
180*cda5da8dSAndroid Build Coastguard Worker
181*cda5da8dSAndroid Build Coastguard Worker    Note that None may be returned if no handler handles the request (though
182*cda5da8dSAndroid Build Coastguard Worker    the default installed global OpenerDirector uses UnknownHandler to ensure
183*cda5da8dSAndroid Build Coastguard Worker    this never happens).
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Worker    In addition, if proxy settings are detected (for example, when a *_proxy
186*cda5da8dSAndroid Build Coastguard Worker    environment variable like http_proxy is set), ProxyHandler is default
187*cda5da8dSAndroid Build Coastguard Worker    installed and makes sure the requests are handled through the proxy.
188*cda5da8dSAndroid Build Coastguard Worker
189*cda5da8dSAndroid Build Coastguard Worker    '''
190*cda5da8dSAndroid Build Coastguard Worker    global _opener
191*cda5da8dSAndroid Build Coastguard Worker    if cafile or capath or cadefault:
192*cda5da8dSAndroid Build Coastguard Worker        import warnings
193*cda5da8dSAndroid Build Coastguard Worker        warnings.warn("cafile, capath and cadefault are deprecated, use a "
194*cda5da8dSAndroid Build Coastguard Worker                      "custom context instead.", DeprecationWarning, 2)
195*cda5da8dSAndroid Build Coastguard Worker        if context is not None:
196*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
197*cda5da8dSAndroid Build Coastguard Worker                "You can't pass both context and any of cafile, capath, and "
198*cda5da8dSAndroid Build Coastguard Worker                "cadefault"
199*cda5da8dSAndroid Build Coastguard Worker            )
200*cda5da8dSAndroid Build Coastguard Worker        if not _have_ssl:
201*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('SSL support not available')
202*cda5da8dSAndroid Build Coastguard Worker        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH,
203*cda5da8dSAndroid Build Coastguard Worker                                             cafile=cafile,
204*cda5da8dSAndroid Build Coastguard Worker                                             capath=capath)
205*cda5da8dSAndroid Build Coastguard Worker        # send ALPN extension to indicate HTTP/1.1 protocol
206*cda5da8dSAndroid Build Coastguard Worker        context.set_alpn_protocols(['http/1.1'])
207*cda5da8dSAndroid Build Coastguard Worker        https_handler = HTTPSHandler(context=context)
208*cda5da8dSAndroid Build Coastguard Worker        opener = build_opener(https_handler)
209*cda5da8dSAndroid Build Coastguard Worker    elif context:
210*cda5da8dSAndroid Build Coastguard Worker        https_handler = HTTPSHandler(context=context)
211*cda5da8dSAndroid Build Coastguard Worker        opener = build_opener(https_handler)
212*cda5da8dSAndroid Build Coastguard Worker    elif _opener is None:
213*cda5da8dSAndroid Build Coastguard Worker        _opener = opener = build_opener()
214*cda5da8dSAndroid Build Coastguard Worker    else:
215*cda5da8dSAndroid Build Coastguard Worker        opener = _opener
216*cda5da8dSAndroid Build Coastguard Worker    return opener.open(url, data, timeout)
217*cda5da8dSAndroid Build Coastguard Worker
218*cda5da8dSAndroid Build Coastguard Workerdef install_opener(opener):
219*cda5da8dSAndroid Build Coastguard Worker    global _opener
220*cda5da8dSAndroid Build Coastguard Worker    _opener = opener
221*cda5da8dSAndroid Build Coastguard Worker
222*cda5da8dSAndroid Build Coastguard Worker_url_tempfiles = []
223*cda5da8dSAndroid Build Coastguard Workerdef urlretrieve(url, filename=None, reporthook=None, data=None):
224*cda5da8dSAndroid Build Coastguard Worker    """
225*cda5da8dSAndroid Build Coastguard Worker    Retrieve a URL into a temporary location on disk.
226*cda5da8dSAndroid Build Coastguard Worker
227*cda5da8dSAndroid Build Coastguard Worker    Requires a URL argument. If a filename is passed, it is used as
228*cda5da8dSAndroid Build Coastguard Worker    the temporary file location. The reporthook argument should be
229*cda5da8dSAndroid Build Coastguard Worker    a callable that accepts a block number, a read size, and the
230*cda5da8dSAndroid Build Coastguard Worker    total file size of the URL target. The data argument should be
231*cda5da8dSAndroid Build Coastguard Worker    valid URL encoded data.
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard Worker    If a filename is passed and the URL points to a local resource,
234*cda5da8dSAndroid Build Coastguard Worker    the result is a copy from local file to new file.
235*cda5da8dSAndroid Build Coastguard Worker
236*cda5da8dSAndroid Build Coastguard Worker    Returns a tuple containing the path to the newly created
237*cda5da8dSAndroid Build Coastguard Worker    data file as well as the resulting HTTPMessage object.
238*cda5da8dSAndroid Build Coastguard Worker    """
239*cda5da8dSAndroid Build Coastguard Worker    url_type, path = _splittype(url)
240*cda5da8dSAndroid Build Coastguard Worker
241*cda5da8dSAndroid Build Coastguard Worker    with contextlib.closing(urlopen(url, data)) as fp:
242*cda5da8dSAndroid Build Coastguard Worker        headers = fp.info()
243*cda5da8dSAndroid Build Coastguard Worker
244*cda5da8dSAndroid Build Coastguard Worker        # Just return the local path and the "headers" for file://
245*cda5da8dSAndroid Build Coastguard Worker        # URLs. No sense in performing a copy unless requested.
246*cda5da8dSAndroid Build Coastguard Worker        if url_type == "file" and not filename:
247*cda5da8dSAndroid Build Coastguard Worker            return os.path.normpath(path), headers
248*cda5da8dSAndroid Build Coastguard Worker
249*cda5da8dSAndroid Build Coastguard Worker        # Handle temporary file setup.
250*cda5da8dSAndroid Build Coastguard Worker        if filename:
251*cda5da8dSAndroid Build Coastguard Worker            tfp = open(filename, 'wb')
252*cda5da8dSAndroid Build Coastguard Worker        else:
253*cda5da8dSAndroid Build Coastguard Worker            tfp = tempfile.NamedTemporaryFile(delete=False)
254*cda5da8dSAndroid Build Coastguard Worker            filename = tfp.name
255*cda5da8dSAndroid Build Coastguard Worker            _url_tempfiles.append(filename)
256*cda5da8dSAndroid Build Coastguard Worker
257*cda5da8dSAndroid Build Coastguard Worker        with tfp:
258*cda5da8dSAndroid Build Coastguard Worker            result = filename, headers
259*cda5da8dSAndroid Build Coastguard Worker            bs = 1024*8
260*cda5da8dSAndroid Build Coastguard Worker            size = -1
261*cda5da8dSAndroid Build Coastguard Worker            read = 0
262*cda5da8dSAndroid Build Coastguard Worker            blocknum = 0
263*cda5da8dSAndroid Build Coastguard Worker            if "content-length" in headers:
264*cda5da8dSAndroid Build Coastguard Worker                size = int(headers["Content-Length"])
265*cda5da8dSAndroid Build Coastguard Worker
266*cda5da8dSAndroid Build Coastguard Worker            if reporthook:
267*cda5da8dSAndroid Build Coastguard Worker                reporthook(blocknum, bs, size)
268*cda5da8dSAndroid Build Coastguard Worker
269*cda5da8dSAndroid Build Coastguard Worker            while True:
270*cda5da8dSAndroid Build Coastguard Worker                block = fp.read(bs)
271*cda5da8dSAndroid Build Coastguard Worker                if not block:
272*cda5da8dSAndroid Build Coastguard Worker                    break
273*cda5da8dSAndroid Build Coastguard Worker                read += len(block)
274*cda5da8dSAndroid Build Coastguard Worker                tfp.write(block)
275*cda5da8dSAndroid Build Coastguard Worker                blocknum += 1
276*cda5da8dSAndroid Build Coastguard Worker                if reporthook:
277*cda5da8dSAndroid Build Coastguard Worker                    reporthook(blocknum, bs, size)
278*cda5da8dSAndroid Build Coastguard Worker
279*cda5da8dSAndroid Build Coastguard Worker    if size >= 0 and read < size:
280*cda5da8dSAndroid Build Coastguard Worker        raise ContentTooShortError(
281*cda5da8dSAndroid Build Coastguard Worker            "retrieval incomplete: got only %i out of %i bytes"
282*cda5da8dSAndroid Build Coastguard Worker            % (read, size), result)
283*cda5da8dSAndroid Build Coastguard Worker
284*cda5da8dSAndroid Build Coastguard Worker    return result
285*cda5da8dSAndroid Build Coastguard Worker
286*cda5da8dSAndroid Build Coastguard Workerdef urlcleanup():
287*cda5da8dSAndroid Build Coastguard Worker    """Clean up temporary files from urlretrieve calls."""
288*cda5da8dSAndroid Build Coastguard Worker    for temp_file in _url_tempfiles:
289*cda5da8dSAndroid Build Coastguard Worker        try:
290*cda5da8dSAndroid Build Coastguard Worker            os.unlink(temp_file)
291*cda5da8dSAndroid Build Coastguard Worker        except OSError:
292*cda5da8dSAndroid Build Coastguard Worker            pass
293*cda5da8dSAndroid Build Coastguard Worker
294*cda5da8dSAndroid Build Coastguard Worker    del _url_tempfiles[:]
295*cda5da8dSAndroid Build Coastguard Worker    global _opener
296*cda5da8dSAndroid Build Coastguard Worker    if _opener:
297*cda5da8dSAndroid Build Coastguard Worker        _opener = None
298*cda5da8dSAndroid Build Coastguard Worker
299*cda5da8dSAndroid Build Coastguard Worker# copied from cookielib.py
300*cda5da8dSAndroid Build Coastguard Worker_cut_port_re = re.compile(r":\d+$", re.ASCII)
301*cda5da8dSAndroid Build Coastguard Workerdef request_host(request):
302*cda5da8dSAndroid Build Coastguard Worker    """Return request-host, as defined by RFC 2965.
303*cda5da8dSAndroid Build Coastguard Worker
304*cda5da8dSAndroid Build Coastguard Worker    Variation from RFC: returned value is lowercased, for convenient
305*cda5da8dSAndroid Build Coastguard Worker    comparison.
306*cda5da8dSAndroid Build Coastguard Worker
307*cda5da8dSAndroid Build Coastguard Worker    """
308*cda5da8dSAndroid Build Coastguard Worker    url = request.full_url
309*cda5da8dSAndroid Build Coastguard Worker    host = urlparse(url)[1]
310*cda5da8dSAndroid Build Coastguard Worker    if host == "":
311*cda5da8dSAndroid Build Coastguard Worker        host = request.get_header("Host", "")
312*cda5da8dSAndroid Build Coastguard Worker
313*cda5da8dSAndroid Build Coastguard Worker    # remove port, if present
314*cda5da8dSAndroid Build Coastguard Worker    host = _cut_port_re.sub("", host, 1)
315*cda5da8dSAndroid Build Coastguard Worker    return host.lower()
316*cda5da8dSAndroid Build Coastguard Worker
317*cda5da8dSAndroid Build Coastguard Workerclass Request:
318*cda5da8dSAndroid Build Coastguard Worker
319*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, url, data=None, headers={},
320*cda5da8dSAndroid Build Coastguard Worker                 origin_req_host=None, unverifiable=False,
321*cda5da8dSAndroid Build Coastguard Worker                 method=None):
322*cda5da8dSAndroid Build Coastguard Worker        self.full_url = url
323*cda5da8dSAndroid Build Coastguard Worker        self.headers = {}
324*cda5da8dSAndroid Build Coastguard Worker        self.unredirected_hdrs = {}
325*cda5da8dSAndroid Build Coastguard Worker        self._data = None
326*cda5da8dSAndroid Build Coastguard Worker        self.data = data
327*cda5da8dSAndroid Build Coastguard Worker        self._tunnel_host = None
328*cda5da8dSAndroid Build Coastguard Worker        for key, value in headers.items():
329*cda5da8dSAndroid Build Coastguard Worker            self.add_header(key, value)
330*cda5da8dSAndroid Build Coastguard Worker        if origin_req_host is None:
331*cda5da8dSAndroid Build Coastguard Worker            origin_req_host = request_host(self)
332*cda5da8dSAndroid Build Coastguard Worker        self.origin_req_host = origin_req_host
333*cda5da8dSAndroid Build Coastguard Worker        self.unverifiable = unverifiable
334*cda5da8dSAndroid Build Coastguard Worker        if method:
335*cda5da8dSAndroid Build Coastguard Worker            self.method = method
336*cda5da8dSAndroid Build Coastguard Worker
337*cda5da8dSAndroid Build Coastguard Worker    @property
338*cda5da8dSAndroid Build Coastguard Worker    def full_url(self):
339*cda5da8dSAndroid Build Coastguard Worker        if self.fragment:
340*cda5da8dSAndroid Build Coastguard Worker            return '{}#{}'.format(self._full_url, self.fragment)
341*cda5da8dSAndroid Build Coastguard Worker        return self._full_url
342*cda5da8dSAndroid Build Coastguard Worker
343*cda5da8dSAndroid Build Coastguard Worker    @full_url.setter
344*cda5da8dSAndroid Build Coastguard Worker    def full_url(self, url):
345*cda5da8dSAndroid Build Coastguard Worker        # unwrap('<URL:type://host/path>') --> 'type://host/path'
346*cda5da8dSAndroid Build Coastguard Worker        self._full_url = unwrap(url)
347*cda5da8dSAndroid Build Coastguard Worker        self._full_url, self.fragment = _splittag(self._full_url)
348*cda5da8dSAndroid Build Coastguard Worker        self._parse()
349*cda5da8dSAndroid Build Coastguard Worker
350*cda5da8dSAndroid Build Coastguard Worker    @full_url.deleter
351*cda5da8dSAndroid Build Coastguard Worker    def full_url(self):
352*cda5da8dSAndroid Build Coastguard Worker        self._full_url = None
353*cda5da8dSAndroid Build Coastguard Worker        self.fragment = None
354*cda5da8dSAndroid Build Coastguard Worker        self.selector = ''
355*cda5da8dSAndroid Build Coastguard Worker
356*cda5da8dSAndroid Build Coastguard Worker    @property
357*cda5da8dSAndroid Build Coastguard Worker    def data(self):
358*cda5da8dSAndroid Build Coastguard Worker        return self._data
359*cda5da8dSAndroid Build Coastguard Worker
360*cda5da8dSAndroid Build Coastguard Worker    @data.setter
361*cda5da8dSAndroid Build Coastguard Worker    def data(self, data):
362*cda5da8dSAndroid Build Coastguard Worker        if data != self._data:
363*cda5da8dSAndroid Build Coastguard Worker            self._data = data
364*cda5da8dSAndroid Build Coastguard Worker            # issue 16464
365*cda5da8dSAndroid Build Coastguard Worker            # if we change data we need to remove content-length header
366*cda5da8dSAndroid Build Coastguard Worker            # (cause it's most probably calculated for previous value)
367*cda5da8dSAndroid Build Coastguard Worker            if self.has_header("Content-length"):
368*cda5da8dSAndroid Build Coastguard Worker                self.remove_header("Content-length")
369*cda5da8dSAndroid Build Coastguard Worker
370*cda5da8dSAndroid Build Coastguard Worker    @data.deleter
371*cda5da8dSAndroid Build Coastguard Worker    def data(self):
372*cda5da8dSAndroid Build Coastguard Worker        self.data = None
373*cda5da8dSAndroid Build Coastguard Worker
374*cda5da8dSAndroid Build Coastguard Worker    def _parse(self):
375*cda5da8dSAndroid Build Coastguard Worker        self.type, rest = _splittype(self._full_url)
376*cda5da8dSAndroid Build Coastguard Worker        if self.type is None:
377*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("unknown url type: %r" % self.full_url)
378*cda5da8dSAndroid Build Coastguard Worker        self.host, self.selector = _splithost(rest)
379*cda5da8dSAndroid Build Coastguard Worker        if self.host:
380*cda5da8dSAndroid Build Coastguard Worker            self.host = unquote(self.host)
381*cda5da8dSAndroid Build Coastguard Worker
382*cda5da8dSAndroid Build Coastguard Worker    def get_method(self):
383*cda5da8dSAndroid Build Coastguard Worker        """Return a string indicating the HTTP request method."""
384*cda5da8dSAndroid Build Coastguard Worker        default_method = "POST" if self.data is not None else "GET"
385*cda5da8dSAndroid Build Coastguard Worker        return getattr(self, 'method', default_method)
386*cda5da8dSAndroid Build Coastguard Worker
387*cda5da8dSAndroid Build Coastguard Worker    def get_full_url(self):
388*cda5da8dSAndroid Build Coastguard Worker        return self.full_url
389*cda5da8dSAndroid Build Coastguard Worker
390*cda5da8dSAndroid Build Coastguard Worker    def set_proxy(self, host, type):
391*cda5da8dSAndroid Build Coastguard Worker        if self.type == 'https' and not self._tunnel_host:
392*cda5da8dSAndroid Build Coastguard Worker            self._tunnel_host = self.host
393*cda5da8dSAndroid Build Coastguard Worker        else:
394*cda5da8dSAndroid Build Coastguard Worker            self.type= type
395*cda5da8dSAndroid Build Coastguard Worker            self.selector = self.full_url
396*cda5da8dSAndroid Build Coastguard Worker        self.host = host
397*cda5da8dSAndroid Build Coastguard Worker
398*cda5da8dSAndroid Build Coastguard Worker    def has_proxy(self):
399*cda5da8dSAndroid Build Coastguard Worker        return self.selector == self.full_url
400*cda5da8dSAndroid Build Coastguard Worker
401*cda5da8dSAndroid Build Coastguard Worker    def add_header(self, key, val):
402*cda5da8dSAndroid Build Coastguard Worker        # useful for something like authentication
403*cda5da8dSAndroid Build Coastguard Worker        self.headers[key.capitalize()] = val
404*cda5da8dSAndroid Build Coastguard Worker
405*cda5da8dSAndroid Build Coastguard Worker    def add_unredirected_header(self, key, val):
406*cda5da8dSAndroid Build Coastguard Worker        # will not be added to a redirected request
407*cda5da8dSAndroid Build Coastguard Worker        self.unredirected_hdrs[key.capitalize()] = val
408*cda5da8dSAndroid Build Coastguard Worker
409*cda5da8dSAndroid Build Coastguard Worker    def has_header(self, header_name):
410*cda5da8dSAndroid Build Coastguard Worker        return (header_name in self.headers or
411*cda5da8dSAndroid Build Coastguard Worker                header_name in self.unredirected_hdrs)
412*cda5da8dSAndroid Build Coastguard Worker
413*cda5da8dSAndroid Build Coastguard Worker    def get_header(self, header_name, default=None):
414*cda5da8dSAndroid Build Coastguard Worker        return self.headers.get(
415*cda5da8dSAndroid Build Coastguard Worker            header_name,
416*cda5da8dSAndroid Build Coastguard Worker            self.unredirected_hdrs.get(header_name, default))
417*cda5da8dSAndroid Build Coastguard Worker
418*cda5da8dSAndroid Build Coastguard Worker    def remove_header(self, header_name):
419*cda5da8dSAndroid Build Coastguard Worker        self.headers.pop(header_name, None)
420*cda5da8dSAndroid Build Coastguard Worker        self.unredirected_hdrs.pop(header_name, None)
421*cda5da8dSAndroid Build Coastguard Worker
422*cda5da8dSAndroid Build Coastguard Worker    def header_items(self):
423*cda5da8dSAndroid Build Coastguard Worker        hdrs = {**self.unredirected_hdrs, **self.headers}
424*cda5da8dSAndroid Build Coastguard Worker        return list(hdrs.items())
425*cda5da8dSAndroid Build Coastguard Worker
426*cda5da8dSAndroid Build Coastguard Workerclass OpenerDirector:
427*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
428*cda5da8dSAndroid Build Coastguard Worker        client_version = "Python-urllib/%s" % __version__
429*cda5da8dSAndroid Build Coastguard Worker        self.addheaders = [('User-agent', client_version)]
430*cda5da8dSAndroid Build Coastguard Worker        # self.handlers is retained only for backward compatibility
431*cda5da8dSAndroid Build Coastguard Worker        self.handlers = []
432*cda5da8dSAndroid Build Coastguard Worker        # manage the individual handlers
433*cda5da8dSAndroid Build Coastguard Worker        self.handle_open = {}
434*cda5da8dSAndroid Build Coastguard Worker        self.handle_error = {}
435*cda5da8dSAndroid Build Coastguard Worker        self.process_response = {}
436*cda5da8dSAndroid Build Coastguard Worker        self.process_request = {}
437*cda5da8dSAndroid Build Coastguard Worker
438*cda5da8dSAndroid Build Coastguard Worker    def add_handler(self, handler):
439*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(handler, "add_parent"):
440*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("expected BaseHandler instance, got %r" %
441*cda5da8dSAndroid Build Coastguard Worker                            type(handler))
442*cda5da8dSAndroid Build Coastguard Worker
443*cda5da8dSAndroid Build Coastguard Worker        added = False
444*cda5da8dSAndroid Build Coastguard Worker        for meth in dir(handler):
445*cda5da8dSAndroid Build Coastguard Worker            if meth in ["redirect_request", "do_open", "proxy_open"]:
446*cda5da8dSAndroid Build Coastguard Worker                # oops, coincidental match
447*cda5da8dSAndroid Build Coastguard Worker                continue
448*cda5da8dSAndroid Build Coastguard Worker
449*cda5da8dSAndroid Build Coastguard Worker            i = meth.find("_")
450*cda5da8dSAndroid Build Coastguard Worker            protocol = meth[:i]
451*cda5da8dSAndroid Build Coastguard Worker            condition = meth[i+1:]
452*cda5da8dSAndroid Build Coastguard Worker
453*cda5da8dSAndroid Build Coastguard Worker            if condition.startswith("error"):
454*cda5da8dSAndroid Build Coastguard Worker                j = condition.find("_") + i + 1
455*cda5da8dSAndroid Build Coastguard Worker                kind = meth[j+1:]
456*cda5da8dSAndroid Build Coastguard Worker                try:
457*cda5da8dSAndroid Build Coastguard Worker                    kind = int(kind)
458*cda5da8dSAndroid Build Coastguard Worker                except ValueError:
459*cda5da8dSAndroid Build Coastguard Worker                    pass
460*cda5da8dSAndroid Build Coastguard Worker                lookup = self.handle_error.get(protocol, {})
461*cda5da8dSAndroid Build Coastguard Worker                self.handle_error[protocol] = lookup
462*cda5da8dSAndroid Build Coastguard Worker            elif condition == "open":
463*cda5da8dSAndroid Build Coastguard Worker                kind = protocol
464*cda5da8dSAndroid Build Coastguard Worker                lookup = self.handle_open
465*cda5da8dSAndroid Build Coastguard Worker            elif condition == "response":
466*cda5da8dSAndroid Build Coastguard Worker                kind = protocol
467*cda5da8dSAndroid Build Coastguard Worker                lookup = self.process_response
468*cda5da8dSAndroid Build Coastguard Worker            elif condition == "request":
469*cda5da8dSAndroid Build Coastguard Worker                kind = protocol
470*cda5da8dSAndroid Build Coastguard Worker                lookup = self.process_request
471*cda5da8dSAndroid Build Coastguard Worker            else:
472*cda5da8dSAndroid Build Coastguard Worker                continue
473*cda5da8dSAndroid Build Coastguard Worker
474*cda5da8dSAndroid Build Coastguard Worker            handlers = lookup.setdefault(kind, [])
475*cda5da8dSAndroid Build Coastguard Worker            if handlers:
476*cda5da8dSAndroid Build Coastguard Worker                bisect.insort(handlers, handler)
477*cda5da8dSAndroid Build Coastguard Worker            else:
478*cda5da8dSAndroid Build Coastguard Worker                handlers.append(handler)
479*cda5da8dSAndroid Build Coastguard Worker            added = True
480*cda5da8dSAndroid Build Coastguard Worker
481*cda5da8dSAndroid Build Coastguard Worker        if added:
482*cda5da8dSAndroid Build Coastguard Worker            bisect.insort(self.handlers, handler)
483*cda5da8dSAndroid Build Coastguard Worker            handler.add_parent(self)
484*cda5da8dSAndroid Build Coastguard Worker
485*cda5da8dSAndroid Build Coastguard Worker    def close(self):
486*cda5da8dSAndroid Build Coastguard Worker        # Only exists for backwards compatibility.
487*cda5da8dSAndroid Build Coastguard Worker        pass
488*cda5da8dSAndroid Build Coastguard Worker
489*cda5da8dSAndroid Build Coastguard Worker    def _call_chain(self, chain, kind, meth_name, *args):
490*cda5da8dSAndroid Build Coastguard Worker        # Handlers raise an exception if no one else should try to handle
491*cda5da8dSAndroid Build Coastguard Worker        # the request, or return None if they can't but another handler
492*cda5da8dSAndroid Build Coastguard Worker        # could.  Otherwise, they return the response.
493*cda5da8dSAndroid Build Coastguard Worker        handlers = chain.get(kind, ())
494*cda5da8dSAndroid Build Coastguard Worker        for handler in handlers:
495*cda5da8dSAndroid Build Coastguard Worker            func = getattr(handler, meth_name)
496*cda5da8dSAndroid Build Coastguard Worker            result = func(*args)
497*cda5da8dSAndroid Build Coastguard Worker            if result is not None:
498*cda5da8dSAndroid Build Coastguard Worker                return result
499*cda5da8dSAndroid Build Coastguard Worker
500*cda5da8dSAndroid Build Coastguard Worker    def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
501*cda5da8dSAndroid Build Coastguard Worker        # accept a URL or a Request object
502*cda5da8dSAndroid Build Coastguard Worker        if isinstance(fullurl, str):
503*cda5da8dSAndroid Build Coastguard Worker            req = Request(fullurl, data)
504*cda5da8dSAndroid Build Coastguard Worker        else:
505*cda5da8dSAndroid Build Coastguard Worker            req = fullurl
506*cda5da8dSAndroid Build Coastguard Worker            if data is not None:
507*cda5da8dSAndroid Build Coastguard Worker                req.data = data
508*cda5da8dSAndroid Build Coastguard Worker
509*cda5da8dSAndroid Build Coastguard Worker        req.timeout = timeout
510*cda5da8dSAndroid Build Coastguard Worker        protocol = req.type
511*cda5da8dSAndroid Build Coastguard Worker
512*cda5da8dSAndroid Build Coastguard Worker        # pre-process request
513*cda5da8dSAndroid Build Coastguard Worker        meth_name = protocol+"_request"
514*cda5da8dSAndroid Build Coastguard Worker        for processor in self.process_request.get(protocol, []):
515*cda5da8dSAndroid Build Coastguard Worker            meth = getattr(processor, meth_name)
516*cda5da8dSAndroid Build Coastguard Worker            req = meth(req)
517*cda5da8dSAndroid Build Coastguard Worker
518*cda5da8dSAndroid Build Coastguard Worker        sys.audit('urllib.Request', req.full_url, req.data, req.headers, req.get_method())
519*cda5da8dSAndroid Build Coastguard Worker        response = self._open(req, data)
520*cda5da8dSAndroid Build Coastguard Worker
521*cda5da8dSAndroid Build Coastguard Worker        # post-process response
522*cda5da8dSAndroid Build Coastguard Worker        meth_name = protocol+"_response"
523*cda5da8dSAndroid Build Coastguard Worker        for processor in self.process_response.get(protocol, []):
524*cda5da8dSAndroid Build Coastguard Worker            meth = getattr(processor, meth_name)
525*cda5da8dSAndroid Build Coastguard Worker            response = meth(req, response)
526*cda5da8dSAndroid Build Coastguard Worker
527*cda5da8dSAndroid Build Coastguard Worker        return response
528*cda5da8dSAndroid Build Coastguard Worker
529*cda5da8dSAndroid Build Coastguard Worker    def _open(self, req, data=None):
530*cda5da8dSAndroid Build Coastguard Worker        result = self._call_chain(self.handle_open, 'default',
531*cda5da8dSAndroid Build Coastguard Worker                                  'default_open', req)
532*cda5da8dSAndroid Build Coastguard Worker        if result:
533*cda5da8dSAndroid Build Coastguard Worker            return result
534*cda5da8dSAndroid Build Coastguard Worker
535*cda5da8dSAndroid Build Coastguard Worker        protocol = req.type
536*cda5da8dSAndroid Build Coastguard Worker        result = self._call_chain(self.handle_open, protocol, protocol +
537*cda5da8dSAndroid Build Coastguard Worker                                  '_open', req)
538*cda5da8dSAndroid Build Coastguard Worker        if result:
539*cda5da8dSAndroid Build Coastguard Worker            return result
540*cda5da8dSAndroid Build Coastguard Worker
541*cda5da8dSAndroid Build Coastguard Worker        return self._call_chain(self.handle_open, 'unknown',
542*cda5da8dSAndroid Build Coastguard Worker                                'unknown_open', req)
543*cda5da8dSAndroid Build Coastguard Worker
544*cda5da8dSAndroid Build Coastguard Worker    def error(self, proto, *args):
545*cda5da8dSAndroid Build Coastguard Worker        if proto in ('http', 'https'):
546*cda5da8dSAndroid Build Coastguard Worker            # XXX http[s] protocols are special-cased
547*cda5da8dSAndroid Build Coastguard Worker            dict = self.handle_error['http'] # https is not different than http
548*cda5da8dSAndroid Build Coastguard Worker            proto = args[2]  # YUCK!
549*cda5da8dSAndroid Build Coastguard Worker            meth_name = 'http_error_%s' % proto
550*cda5da8dSAndroid Build Coastguard Worker            http_err = 1
551*cda5da8dSAndroid Build Coastguard Worker            orig_args = args
552*cda5da8dSAndroid Build Coastguard Worker        else:
553*cda5da8dSAndroid Build Coastguard Worker            dict = self.handle_error
554*cda5da8dSAndroid Build Coastguard Worker            meth_name = proto + '_error'
555*cda5da8dSAndroid Build Coastguard Worker            http_err = 0
556*cda5da8dSAndroid Build Coastguard Worker        args = (dict, proto, meth_name) + args
557*cda5da8dSAndroid Build Coastguard Worker        result = self._call_chain(*args)
558*cda5da8dSAndroid Build Coastguard Worker        if result:
559*cda5da8dSAndroid Build Coastguard Worker            return result
560*cda5da8dSAndroid Build Coastguard Worker
561*cda5da8dSAndroid Build Coastguard Worker        if http_err:
562*cda5da8dSAndroid Build Coastguard Worker            args = (dict, 'default', 'http_error_default') + orig_args
563*cda5da8dSAndroid Build Coastguard Worker            return self._call_chain(*args)
564*cda5da8dSAndroid Build Coastguard Worker
565*cda5da8dSAndroid Build Coastguard Worker# XXX probably also want an abstract factory that knows when it makes
566*cda5da8dSAndroid Build Coastguard Worker# sense to skip a superclass in favor of a subclass and when it might
567*cda5da8dSAndroid Build Coastguard Worker# make sense to include both
568*cda5da8dSAndroid Build Coastguard Worker
569*cda5da8dSAndroid Build Coastguard Workerdef build_opener(*handlers):
570*cda5da8dSAndroid Build Coastguard Worker    """Create an opener object from a list of handlers.
571*cda5da8dSAndroid Build Coastguard Worker
572*cda5da8dSAndroid Build Coastguard Worker    The opener will use several default handlers, including support
573*cda5da8dSAndroid Build Coastguard Worker    for HTTP, FTP and when applicable HTTPS.
574*cda5da8dSAndroid Build Coastguard Worker
575*cda5da8dSAndroid Build Coastguard Worker    If any of the handlers passed as arguments are subclasses of the
576*cda5da8dSAndroid Build Coastguard Worker    default handlers, the default handlers will not be used.
577*cda5da8dSAndroid Build Coastguard Worker    """
578*cda5da8dSAndroid Build Coastguard Worker    opener = OpenerDirector()
579*cda5da8dSAndroid Build Coastguard Worker    default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
580*cda5da8dSAndroid Build Coastguard Worker                       HTTPDefaultErrorHandler, HTTPRedirectHandler,
581*cda5da8dSAndroid Build Coastguard Worker                       FTPHandler, FileHandler, HTTPErrorProcessor,
582*cda5da8dSAndroid Build Coastguard Worker                       DataHandler]
583*cda5da8dSAndroid Build Coastguard Worker    if hasattr(http.client, "HTTPSConnection"):
584*cda5da8dSAndroid Build Coastguard Worker        default_classes.append(HTTPSHandler)
585*cda5da8dSAndroid Build Coastguard Worker    skip = set()
586*cda5da8dSAndroid Build Coastguard Worker    for klass in default_classes:
587*cda5da8dSAndroid Build Coastguard Worker        for check in handlers:
588*cda5da8dSAndroid Build Coastguard Worker            if isinstance(check, type):
589*cda5da8dSAndroid Build Coastguard Worker                if issubclass(check, klass):
590*cda5da8dSAndroid Build Coastguard Worker                    skip.add(klass)
591*cda5da8dSAndroid Build Coastguard Worker            elif isinstance(check, klass):
592*cda5da8dSAndroid Build Coastguard Worker                skip.add(klass)
593*cda5da8dSAndroid Build Coastguard Worker    for klass in skip:
594*cda5da8dSAndroid Build Coastguard Worker        default_classes.remove(klass)
595*cda5da8dSAndroid Build Coastguard Worker
596*cda5da8dSAndroid Build Coastguard Worker    for klass in default_classes:
597*cda5da8dSAndroid Build Coastguard Worker        opener.add_handler(klass())
598*cda5da8dSAndroid Build Coastguard Worker
599*cda5da8dSAndroid Build Coastguard Worker    for h in handlers:
600*cda5da8dSAndroid Build Coastguard Worker        if isinstance(h, type):
601*cda5da8dSAndroid Build Coastguard Worker            h = h()
602*cda5da8dSAndroid Build Coastguard Worker        opener.add_handler(h)
603*cda5da8dSAndroid Build Coastguard Worker    return opener
604*cda5da8dSAndroid Build Coastguard Worker
605*cda5da8dSAndroid Build Coastguard Workerclass BaseHandler:
606*cda5da8dSAndroid Build Coastguard Worker    handler_order = 500
607*cda5da8dSAndroid Build Coastguard Worker
608*cda5da8dSAndroid Build Coastguard Worker    def add_parent(self, parent):
609*cda5da8dSAndroid Build Coastguard Worker        self.parent = parent
610*cda5da8dSAndroid Build Coastguard Worker
611*cda5da8dSAndroid Build Coastguard Worker    def close(self):
612*cda5da8dSAndroid Build Coastguard Worker        # Only exists for backwards compatibility
613*cda5da8dSAndroid Build Coastguard Worker        pass
614*cda5da8dSAndroid Build Coastguard Worker
615*cda5da8dSAndroid Build Coastguard Worker    def __lt__(self, other):
616*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(other, "handler_order"):
617*cda5da8dSAndroid Build Coastguard Worker            # Try to preserve the old behavior of having custom classes
618*cda5da8dSAndroid Build Coastguard Worker            # inserted after default ones (works only for custom user
619*cda5da8dSAndroid Build Coastguard Worker            # classes which are not aware of handler_order).
620*cda5da8dSAndroid Build Coastguard Worker            return True
621*cda5da8dSAndroid Build Coastguard Worker        return self.handler_order < other.handler_order
622*cda5da8dSAndroid Build Coastguard Worker
623*cda5da8dSAndroid Build Coastguard Worker
624*cda5da8dSAndroid Build Coastguard Workerclass HTTPErrorProcessor(BaseHandler):
625*cda5da8dSAndroid Build Coastguard Worker    """Process HTTP error responses."""
626*cda5da8dSAndroid Build Coastguard Worker    handler_order = 1000  # after all other processing
627*cda5da8dSAndroid Build Coastguard Worker
628*cda5da8dSAndroid Build Coastguard Worker    def http_response(self, request, response):
629*cda5da8dSAndroid Build Coastguard Worker        code, msg, hdrs = response.code, response.msg, response.info()
630*cda5da8dSAndroid Build Coastguard Worker
631*cda5da8dSAndroid Build Coastguard Worker        # According to RFC 2616, "2xx" code indicates that the client's
632*cda5da8dSAndroid Build Coastguard Worker        # request was successfully received, understood, and accepted.
633*cda5da8dSAndroid Build Coastguard Worker        if not (200 <= code < 300):
634*cda5da8dSAndroid Build Coastguard Worker            response = self.parent.error(
635*cda5da8dSAndroid Build Coastguard Worker                'http', request, response, code, msg, hdrs)
636*cda5da8dSAndroid Build Coastguard Worker
637*cda5da8dSAndroid Build Coastguard Worker        return response
638*cda5da8dSAndroid Build Coastguard Worker
639*cda5da8dSAndroid Build Coastguard Worker    https_response = http_response
640*cda5da8dSAndroid Build Coastguard Worker
641*cda5da8dSAndroid Build Coastguard Workerclass HTTPDefaultErrorHandler(BaseHandler):
642*cda5da8dSAndroid Build Coastguard Worker    def http_error_default(self, req, fp, code, msg, hdrs):
643*cda5da8dSAndroid Build Coastguard Worker        raise HTTPError(req.full_url, code, msg, hdrs, fp)
644*cda5da8dSAndroid Build Coastguard Worker
645*cda5da8dSAndroid Build Coastguard Workerclass HTTPRedirectHandler(BaseHandler):
646*cda5da8dSAndroid Build Coastguard Worker    # maximum number of redirections to any single URL
647*cda5da8dSAndroid Build Coastguard Worker    # this is needed because of the state that cookies introduce
648*cda5da8dSAndroid Build Coastguard Worker    max_repeats = 4
649*cda5da8dSAndroid Build Coastguard Worker    # maximum total number of redirections (regardless of URL) before
650*cda5da8dSAndroid Build Coastguard Worker    # assuming we're in a loop
651*cda5da8dSAndroid Build Coastguard Worker    max_redirections = 10
652*cda5da8dSAndroid Build Coastguard Worker
653*cda5da8dSAndroid Build Coastguard Worker    def redirect_request(self, req, fp, code, msg, headers, newurl):
654*cda5da8dSAndroid Build Coastguard Worker        """Return a Request or None in response to a redirect.
655*cda5da8dSAndroid Build Coastguard Worker
656*cda5da8dSAndroid Build Coastguard Worker        This is called by the http_error_30x methods when a
657*cda5da8dSAndroid Build Coastguard Worker        redirection response is received.  If a redirection should
658*cda5da8dSAndroid Build Coastguard Worker        take place, return a new Request to allow http_error_30x to
659*cda5da8dSAndroid Build Coastguard Worker        perform the redirect.  Otherwise, raise HTTPError if no-one
660*cda5da8dSAndroid Build Coastguard Worker        else should try to handle this url.  Return None if you can't
661*cda5da8dSAndroid Build Coastguard Worker        but another Handler might.
662*cda5da8dSAndroid Build Coastguard Worker        """
663*cda5da8dSAndroid Build Coastguard Worker        m = req.get_method()
664*cda5da8dSAndroid Build Coastguard Worker        if (not (code in (301, 302, 303, 307, 308) and m in ("GET", "HEAD")
665*cda5da8dSAndroid Build Coastguard Worker            or code in (301, 302, 303) and m == "POST")):
666*cda5da8dSAndroid Build Coastguard Worker            raise HTTPError(req.full_url, code, msg, headers, fp)
667*cda5da8dSAndroid Build Coastguard Worker
668*cda5da8dSAndroid Build Coastguard Worker        # Strictly (according to RFC 2616), 301 or 302 in response to
669*cda5da8dSAndroid Build Coastguard Worker        # a POST MUST NOT cause a redirection without confirmation
670*cda5da8dSAndroid Build Coastguard Worker        # from the user (of urllib.request, in this case).  In practice,
671*cda5da8dSAndroid Build Coastguard Worker        # essentially all clients do redirect in this case, so we do
672*cda5da8dSAndroid Build Coastguard Worker        # the same.
673*cda5da8dSAndroid Build Coastguard Worker
674*cda5da8dSAndroid Build Coastguard Worker        # Be conciliant with URIs containing a space.  This is mainly
675*cda5da8dSAndroid Build Coastguard Worker        # redundant with the more complete encoding done in http_error_302(),
676*cda5da8dSAndroid Build Coastguard Worker        # but it is kept for compatibility with other callers.
677*cda5da8dSAndroid Build Coastguard Worker        newurl = newurl.replace(' ', '%20')
678*cda5da8dSAndroid Build Coastguard Worker
679*cda5da8dSAndroid Build Coastguard Worker        CONTENT_HEADERS = ("content-length", "content-type")
680*cda5da8dSAndroid Build Coastguard Worker        newheaders = {k: v for k, v in req.headers.items()
681*cda5da8dSAndroid Build Coastguard Worker                      if k.lower() not in CONTENT_HEADERS}
682*cda5da8dSAndroid Build Coastguard Worker        return Request(newurl,
683*cda5da8dSAndroid Build Coastguard Worker                       headers=newheaders,
684*cda5da8dSAndroid Build Coastguard Worker                       origin_req_host=req.origin_req_host,
685*cda5da8dSAndroid Build Coastguard Worker                       unverifiable=True)
686*cda5da8dSAndroid Build Coastguard Worker
687*cda5da8dSAndroid Build Coastguard Worker    # Implementation note: To avoid the server sending us into an
688*cda5da8dSAndroid Build Coastguard Worker    # infinite loop, the request object needs to track what URLs we
689*cda5da8dSAndroid Build Coastguard Worker    # have already seen.  Do this by adding a handler-specific
690*cda5da8dSAndroid Build Coastguard Worker    # attribute to the Request object.
691*cda5da8dSAndroid Build Coastguard Worker    def http_error_302(self, req, fp, code, msg, headers):
692*cda5da8dSAndroid Build Coastguard Worker        # Some servers (incorrectly) return multiple Location headers
693*cda5da8dSAndroid Build Coastguard Worker        # (so probably same goes for URI).  Use first header.
694*cda5da8dSAndroid Build Coastguard Worker        if "location" in headers:
695*cda5da8dSAndroid Build Coastguard Worker            newurl = headers["location"]
696*cda5da8dSAndroid Build Coastguard Worker        elif "uri" in headers:
697*cda5da8dSAndroid Build Coastguard Worker            newurl = headers["uri"]
698*cda5da8dSAndroid Build Coastguard Worker        else:
699*cda5da8dSAndroid Build Coastguard Worker            return
700*cda5da8dSAndroid Build Coastguard Worker
701*cda5da8dSAndroid Build Coastguard Worker        # fix a possible malformed URL
702*cda5da8dSAndroid Build Coastguard Worker        urlparts = urlparse(newurl)
703*cda5da8dSAndroid Build Coastguard Worker
704*cda5da8dSAndroid Build Coastguard Worker        # For security reasons we don't allow redirection to anything other
705*cda5da8dSAndroid Build Coastguard Worker        # than http, https or ftp.
706*cda5da8dSAndroid Build Coastguard Worker
707*cda5da8dSAndroid Build Coastguard Worker        if urlparts.scheme not in ('http', 'https', 'ftp', ''):
708*cda5da8dSAndroid Build Coastguard Worker            raise HTTPError(
709*cda5da8dSAndroid Build Coastguard Worker                newurl, code,
710*cda5da8dSAndroid Build Coastguard Worker                "%s - Redirection to url '%s' is not allowed" % (msg, newurl),
711*cda5da8dSAndroid Build Coastguard Worker                headers, fp)
712*cda5da8dSAndroid Build Coastguard Worker
713*cda5da8dSAndroid Build Coastguard Worker        if not urlparts.path and urlparts.netloc:
714*cda5da8dSAndroid Build Coastguard Worker            urlparts = list(urlparts)
715*cda5da8dSAndroid Build Coastguard Worker            urlparts[2] = "/"
716*cda5da8dSAndroid Build Coastguard Worker        newurl = urlunparse(urlparts)
717*cda5da8dSAndroid Build Coastguard Worker
718*cda5da8dSAndroid Build Coastguard Worker        # http.client.parse_headers() decodes as ISO-8859-1.  Recover the
719*cda5da8dSAndroid Build Coastguard Worker        # original bytes and percent-encode non-ASCII bytes, and any special
720*cda5da8dSAndroid Build Coastguard Worker        # characters such as the space.
721*cda5da8dSAndroid Build Coastguard Worker        newurl = quote(
722*cda5da8dSAndroid Build Coastguard Worker            newurl, encoding="iso-8859-1", safe=string.punctuation)
723*cda5da8dSAndroid Build Coastguard Worker        newurl = urljoin(req.full_url, newurl)
724*cda5da8dSAndroid Build Coastguard Worker
725*cda5da8dSAndroid Build Coastguard Worker        # XXX Probably want to forget about the state of the current
726*cda5da8dSAndroid Build Coastguard Worker        # request, although that might interact poorly with other
727*cda5da8dSAndroid Build Coastguard Worker        # handlers that also use handler-specific request attributes
728*cda5da8dSAndroid Build Coastguard Worker        new = self.redirect_request(req, fp, code, msg, headers, newurl)
729*cda5da8dSAndroid Build Coastguard Worker        if new is None:
730*cda5da8dSAndroid Build Coastguard Worker            return
731*cda5da8dSAndroid Build Coastguard Worker
732*cda5da8dSAndroid Build Coastguard Worker        # loop detection
733*cda5da8dSAndroid Build Coastguard Worker        # .redirect_dict has a key url if url was previously visited.
734*cda5da8dSAndroid Build Coastguard Worker        if hasattr(req, 'redirect_dict'):
735*cda5da8dSAndroid Build Coastguard Worker            visited = new.redirect_dict = req.redirect_dict
736*cda5da8dSAndroid Build Coastguard Worker            if (visited.get(newurl, 0) >= self.max_repeats or
737*cda5da8dSAndroid Build Coastguard Worker                len(visited) >= self.max_redirections):
738*cda5da8dSAndroid Build Coastguard Worker                raise HTTPError(req.full_url, code,
739*cda5da8dSAndroid Build Coastguard Worker                                self.inf_msg + msg, headers, fp)
740*cda5da8dSAndroid Build Coastguard Worker        else:
741*cda5da8dSAndroid Build Coastguard Worker            visited = new.redirect_dict = req.redirect_dict = {}
742*cda5da8dSAndroid Build Coastguard Worker        visited[newurl] = visited.get(newurl, 0) + 1
743*cda5da8dSAndroid Build Coastguard Worker
744*cda5da8dSAndroid Build Coastguard Worker        # Don't close the fp until we are sure that we won't use it
745*cda5da8dSAndroid Build Coastguard Worker        # with HTTPError.
746*cda5da8dSAndroid Build Coastguard Worker        fp.read()
747*cda5da8dSAndroid Build Coastguard Worker        fp.close()
748*cda5da8dSAndroid Build Coastguard Worker
749*cda5da8dSAndroid Build Coastguard Worker        return self.parent.open(new, timeout=req.timeout)
750*cda5da8dSAndroid Build Coastguard Worker
751*cda5da8dSAndroid Build Coastguard Worker    http_error_301 = http_error_303 = http_error_307 = http_error_308 = http_error_302
752*cda5da8dSAndroid Build Coastguard Worker
753*cda5da8dSAndroid Build Coastguard Worker    inf_msg = "The HTTP server returned a redirect error that would " \
754*cda5da8dSAndroid Build Coastguard Worker              "lead to an infinite loop.\n" \
755*cda5da8dSAndroid Build Coastguard Worker              "The last 30x error message was:\n"
756*cda5da8dSAndroid Build Coastguard Worker
757*cda5da8dSAndroid Build Coastguard Worker
758*cda5da8dSAndroid Build Coastguard Workerdef _parse_proxy(proxy):
759*cda5da8dSAndroid Build Coastguard Worker    """Return (scheme, user, password, host/port) given a URL or an authority.
760*cda5da8dSAndroid Build Coastguard Worker
761*cda5da8dSAndroid Build Coastguard Worker    If a URL is supplied, it must have an authority (host:port) component.
762*cda5da8dSAndroid Build Coastguard Worker    According to RFC 3986, having an authority component means the URL must
763*cda5da8dSAndroid Build Coastguard Worker    have two slashes after the scheme.
764*cda5da8dSAndroid Build Coastguard Worker    """
765*cda5da8dSAndroid Build Coastguard Worker    scheme, r_scheme = _splittype(proxy)
766*cda5da8dSAndroid Build Coastguard Worker    if not r_scheme.startswith("/"):
767*cda5da8dSAndroid Build Coastguard Worker        # authority
768*cda5da8dSAndroid Build Coastguard Worker        scheme = None
769*cda5da8dSAndroid Build Coastguard Worker        authority = proxy
770*cda5da8dSAndroid Build Coastguard Worker    else:
771*cda5da8dSAndroid Build Coastguard Worker        # URL
772*cda5da8dSAndroid Build Coastguard Worker        if not r_scheme.startswith("//"):
773*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("proxy URL with no authority: %r" % proxy)
774*cda5da8dSAndroid Build Coastguard Worker        # We have an authority, so for RFC 3986-compliant URLs (by ss 3.
775*cda5da8dSAndroid Build Coastguard Worker        # and 3.3.), path is empty or starts with '/'
776*cda5da8dSAndroid Build Coastguard Worker        if '@' in r_scheme:
777*cda5da8dSAndroid Build Coastguard Worker            host_separator = r_scheme.find('@')
778*cda5da8dSAndroid Build Coastguard Worker            end = r_scheme.find("/", host_separator)
779*cda5da8dSAndroid Build Coastguard Worker        else:
780*cda5da8dSAndroid Build Coastguard Worker            end = r_scheme.find("/", 2)
781*cda5da8dSAndroid Build Coastguard Worker        if end == -1:
782*cda5da8dSAndroid Build Coastguard Worker            end = None
783*cda5da8dSAndroid Build Coastguard Worker        authority = r_scheme[2:end]
784*cda5da8dSAndroid Build Coastguard Worker    userinfo, hostport = _splituser(authority)
785*cda5da8dSAndroid Build Coastguard Worker    if userinfo is not None:
786*cda5da8dSAndroid Build Coastguard Worker        user, password = _splitpasswd(userinfo)
787*cda5da8dSAndroid Build Coastguard Worker    else:
788*cda5da8dSAndroid Build Coastguard Worker        user = password = None
789*cda5da8dSAndroid Build Coastguard Worker    return scheme, user, password, hostport
790*cda5da8dSAndroid Build Coastguard Worker
791*cda5da8dSAndroid Build Coastguard Workerclass ProxyHandler(BaseHandler):
792*cda5da8dSAndroid Build Coastguard Worker    # Proxies must be in front
793*cda5da8dSAndroid Build Coastguard Worker    handler_order = 100
794*cda5da8dSAndroid Build Coastguard Worker
795*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, proxies=None):
796*cda5da8dSAndroid Build Coastguard Worker        if proxies is None:
797*cda5da8dSAndroid Build Coastguard Worker            proxies = getproxies()
798*cda5da8dSAndroid Build Coastguard Worker        assert hasattr(proxies, 'keys'), "proxies must be a mapping"
799*cda5da8dSAndroid Build Coastguard Worker        self.proxies = proxies
800*cda5da8dSAndroid Build Coastguard Worker        for type, url in proxies.items():
801*cda5da8dSAndroid Build Coastguard Worker            type = type.lower()
802*cda5da8dSAndroid Build Coastguard Worker            setattr(self, '%s_open' % type,
803*cda5da8dSAndroid Build Coastguard Worker                    lambda r, proxy=url, type=type, meth=self.proxy_open:
804*cda5da8dSAndroid Build Coastguard Worker                        meth(r, proxy, type))
805*cda5da8dSAndroid Build Coastguard Worker
806*cda5da8dSAndroid Build Coastguard Worker    def proxy_open(self, req, proxy, type):
807*cda5da8dSAndroid Build Coastguard Worker        orig_type = req.type
808*cda5da8dSAndroid Build Coastguard Worker        proxy_type, user, password, hostport = _parse_proxy(proxy)
809*cda5da8dSAndroid Build Coastguard Worker        if proxy_type is None:
810*cda5da8dSAndroid Build Coastguard Worker            proxy_type = orig_type
811*cda5da8dSAndroid Build Coastguard Worker
812*cda5da8dSAndroid Build Coastguard Worker        if req.host and proxy_bypass(req.host):
813*cda5da8dSAndroid Build Coastguard Worker            return None
814*cda5da8dSAndroid Build Coastguard Worker
815*cda5da8dSAndroid Build Coastguard Worker        if user and password:
816*cda5da8dSAndroid Build Coastguard Worker            user_pass = '%s:%s' % (unquote(user),
817*cda5da8dSAndroid Build Coastguard Worker                                   unquote(password))
818*cda5da8dSAndroid Build Coastguard Worker            creds = base64.b64encode(user_pass.encode()).decode("ascii")
819*cda5da8dSAndroid Build Coastguard Worker            req.add_header('Proxy-authorization', 'Basic ' + creds)
820*cda5da8dSAndroid Build Coastguard Worker        hostport = unquote(hostport)
821*cda5da8dSAndroid Build Coastguard Worker        req.set_proxy(hostport, proxy_type)
822*cda5da8dSAndroid Build Coastguard Worker        if orig_type == proxy_type or orig_type == 'https':
823*cda5da8dSAndroid Build Coastguard Worker            # let other handlers take care of it
824*cda5da8dSAndroid Build Coastguard Worker            return None
825*cda5da8dSAndroid Build Coastguard Worker        else:
826*cda5da8dSAndroid Build Coastguard Worker            # need to start over, because the other handlers don't
827*cda5da8dSAndroid Build Coastguard Worker            # grok the proxy's URL type
828*cda5da8dSAndroid Build Coastguard Worker            # e.g. if we have a constructor arg proxies like so:
829*cda5da8dSAndroid Build Coastguard Worker            # {'http': 'ftp://proxy.example.com'}, we may end up turning
830*cda5da8dSAndroid Build Coastguard Worker            # a request for http://acme.example.com/a into one for
831*cda5da8dSAndroid Build Coastguard Worker            # ftp://proxy.example.com/a
832*cda5da8dSAndroid Build Coastguard Worker            return self.parent.open(req, timeout=req.timeout)
833*cda5da8dSAndroid Build Coastguard Worker
834*cda5da8dSAndroid Build Coastguard Workerclass HTTPPasswordMgr:
835*cda5da8dSAndroid Build Coastguard Worker
836*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
837*cda5da8dSAndroid Build Coastguard Worker        self.passwd = {}
838*cda5da8dSAndroid Build Coastguard Worker
839*cda5da8dSAndroid Build Coastguard Worker    def add_password(self, realm, uri, user, passwd):
840*cda5da8dSAndroid Build Coastguard Worker        # uri could be a single URI or a sequence
841*cda5da8dSAndroid Build Coastguard Worker        if isinstance(uri, str):
842*cda5da8dSAndroid Build Coastguard Worker            uri = [uri]
843*cda5da8dSAndroid Build Coastguard Worker        if realm not in self.passwd:
844*cda5da8dSAndroid Build Coastguard Worker            self.passwd[realm] = {}
845*cda5da8dSAndroid Build Coastguard Worker        for default_port in True, False:
846*cda5da8dSAndroid Build Coastguard Worker            reduced_uri = tuple(
847*cda5da8dSAndroid Build Coastguard Worker                self.reduce_uri(u, default_port) for u in uri)
848*cda5da8dSAndroid Build Coastguard Worker            self.passwd[realm][reduced_uri] = (user, passwd)
849*cda5da8dSAndroid Build Coastguard Worker
850*cda5da8dSAndroid Build Coastguard Worker    def find_user_password(self, realm, authuri):
851*cda5da8dSAndroid Build Coastguard Worker        domains = self.passwd.get(realm, {})
852*cda5da8dSAndroid Build Coastguard Worker        for default_port in True, False:
853*cda5da8dSAndroid Build Coastguard Worker            reduced_authuri = self.reduce_uri(authuri, default_port)
854*cda5da8dSAndroid Build Coastguard Worker            for uris, authinfo in domains.items():
855*cda5da8dSAndroid Build Coastguard Worker                for uri in uris:
856*cda5da8dSAndroid Build Coastguard Worker                    if self.is_suburi(uri, reduced_authuri):
857*cda5da8dSAndroid Build Coastguard Worker                        return authinfo
858*cda5da8dSAndroid Build Coastguard Worker        return None, None
859*cda5da8dSAndroid Build Coastguard Worker
860*cda5da8dSAndroid Build Coastguard Worker    def reduce_uri(self, uri, default_port=True):
861*cda5da8dSAndroid Build Coastguard Worker        """Accept authority or URI and extract only the authority and path."""
862*cda5da8dSAndroid Build Coastguard Worker        # note HTTP URLs do not have a userinfo component
863*cda5da8dSAndroid Build Coastguard Worker        parts = urlsplit(uri)
864*cda5da8dSAndroid Build Coastguard Worker        if parts[1]:
865*cda5da8dSAndroid Build Coastguard Worker            # URI
866*cda5da8dSAndroid Build Coastguard Worker            scheme = parts[0]
867*cda5da8dSAndroid Build Coastguard Worker            authority = parts[1]
868*cda5da8dSAndroid Build Coastguard Worker            path = parts[2] or '/'
869*cda5da8dSAndroid Build Coastguard Worker        else:
870*cda5da8dSAndroid Build Coastguard Worker            # host or host:port
871*cda5da8dSAndroid Build Coastguard Worker            scheme = None
872*cda5da8dSAndroid Build Coastguard Worker            authority = uri
873*cda5da8dSAndroid Build Coastguard Worker            path = '/'
874*cda5da8dSAndroid Build Coastguard Worker        host, port = _splitport(authority)
875*cda5da8dSAndroid Build Coastguard Worker        if default_port and port is None and scheme is not None:
876*cda5da8dSAndroid Build Coastguard Worker            dport = {"http": 80,
877*cda5da8dSAndroid Build Coastguard Worker                     "https": 443,
878*cda5da8dSAndroid Build Coastguard Worker                     }.get(scheme)
879*cda5da8dSAndroid Build Coastguard Worker            if dport is not None:
880*cda5da8dSAndroid Build Coastguard Worker                authority = "%s:%d" % (host, dport)
881*cda5da8dSAndroid Build Coastguard Worker        return authority, path
882*cda5da8dSAndroid Build Coastguard Worker
883*cda5da8dSAndroid Build Coastguard Worker    def is_suburi(self, base, test):
884*cda5da8dSAndroid Build Coastguard Worker        """Check if test is below base in a URI tree
885*cda5da8dSAndroid Build Coastguard Worker
886*cda5da8dSAndroid Build Coastguard Worker        Both args must be URIs in reduced form.
887*cda5da8dSAndroid Build Coastguard Worker        """
888*cda5da8dSAndroid Build Coastguard Worker        if base == test:
889*cda5da8dSAndroid Build Coastguard Worker            return True
890*cda5da8dSAndroid Build Coastguard Worker        if base[0] != test[0]:
891*cda5da8dSAndroid Build Coastguard Worker            return False
892*cda5da8dSAndroid Build Coastguard Worker        prefix = base[1]
893*cda5da8dSAndroid Build Coastguard Worker        if prefix[-1:] != '/':
894*cda5da8dSAndroid Build Coastguard Worker            prefix += '/'
895*cda5da8dSAndroid Build Coastguard Worker        return test[1].startswith(prefix)
896*cda5da8dSAndroid Build Coastguard Worker
897*cda5da8dSAndroid Build Coastguard Worker
898*cda5da8dSAndroid Build Coastguard Workerclass HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
899*cda5da8dSAndroid Build Coastguard Worker
900*cda5da8dSAndroid Build Coastguard Worker    def find_user_password(self, realm, authuri):
901*cda5da8dSAndroid Build Coastguard Worker        user, password = HTTPPasswordMgr.find_user_password(self, realm,
902*cda5da8dSAndroid Build Coastguard Worker                                                            authuri)
903*cda5da8dSAndroid Build Coastguard Worker        if user is not None:
904*cda5da8dSAndroid Build Coastguard Worker            return user, password
905*cda5da8dSAndroid Build Coastguard Worker        return HTTPPasswordMgr.find_user_password(self, None, authuri)
906*cda5da8dSAndroid Build Coastguard Worker
907*cda5da8dSAndroid Build Coastguard Worker
908*cda5da8dSAndroid Build Coastguard Workerclass HTTPPasswordMgrWithPriorAuth(HTTPPasswordMgrWithDefaultRealm):
909*cda5da8dSAndroid Build Coastguard Worker
910*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, *args, **kwargs):
911*cda5da8dSAndroid Build Coastguard Worker        self.authenticated = {}
912*cda5da8dSAndroid Build Coastguard Worker        super().__init__(*args, **kwargs)
913*cda5da8dSAndroid Build Coastguard Worker
914*cda5da8dSAndroid Build Coastguard Worker    def add_password(self, realm, uri, user, passwd, is_authenticated=False):
915*cda5da8dSAndroid Build Coastguard Worker        self.update_authenticated(uri, is_authenticated)
916*cda5da8dSAndroid Build Coastguard Worker        # Add a default for prior auth requests
917*cda5da8dSAndroid Build Coastguard Worker        if realm is not None:
918*cda5da8dSAndroid Build Coastguard Worker            super().add_password(None, uri, user, passwd)
919*cda5da8dSAndroid Build Coastguard Worker        super().add_password(realm, uri, user, passwd)
920*cda5da8dSAndroid Build Coastguard Worker
921*cda5da8dSAndroid Build Coastguard Worker    def update_authenticated(self, uri, is_authenticated=False):
922*cda5da8dSAndroid Build Coastguard Worker        # uri could be a single URI or a sequence
923*cda5da8dSAndroid Build Coastguard Worker        if isinstance(uri, str):
924*cda5da8dSAndroid Build Coastguard Worker            uri = [uri]
925*cda5da8dSAndroid Build Coastguard Worker
926*cda5da8dSAndroid Build Coastguard Worker        for default_port in True, False:
927*cda5da8dSAndroid Build Coastguard Worker            for u in uri:
928*cda5da8dSAndroid Build Coastguard Worker                reduced_uri = self.reduce_uri(u, default_port)
929*cda5da8dSAndroid Build Coastguard Worker                self.authenticated[reduced_uri] = is_authenticated
930*cda5da8dSAndroid Build Coastguard Worker
931*cda5da8dSAndroid Build Coastguard Worker    def is_authenticated(self, authuri):
932*cda5da8dSAndroid Build Coastguard Worker        for default_port in True, False:
933*cda5da8dSAndroid Build Coastguard Worker            reduced_authuri = self.reduce_uri(authuri, default_port)
934*cda5da8dSAndroid Build Coastguard Worker            for uri in self.authenticated:
935*cda5da8dSAndroid Build Coastguard Worker                if self.is_suburi(uri, reduced_authuri):
936*cda5da8dSAndroid Build Coastguard Worker                    return self.authenticated[uri]
937*cda5da8dSAndroid Build Coastguard Worker
938*cda5da8dSAndroid Build Coastguard Worker
939*cda5da8dSAndroid Build Coastguard Workerclass AbstractBasicAuthHandler:
940*cda5da8dSAndroid Build Coastguard Worker
941*cda5da8dSAndroid Build Coastguard Worker    # XXX this allows for multiple auth-schemes, but will stupidly pick
942*cda5da8dSAndroid Build Coastguard Worker    # the last one with a realm specified.
943*cda5da8dSAndroid Build Coastguard Worker
944*cda5da8dSAndroid Build Coastguard Worker    # allow for double- and single-quoted realm values
945*cda5da8dSAndroid Build Coastguard Worker    # (single quotes are a violation of the RFC, but appear in the wild)
946*cda5da8dSAndroid Build Coastguard Worker    rx = re.compile('(?:^|,)'   # start of the string or ','
947*cda5da8dSAndroid Build Coastguard Worker                    '[ \t]*'    # optional whitespaces
948*cda5da8dSAndroid Build Coastguard Worker                    '([^ \t,]+)' # scheme like "Basic"
949*cda5da8dSAndroid Build Coastguard Worker                    '[ \t]+'    # mandatory whitespaces
950*cda5da8dSAndroid Build Coastguard Worker                    # realm=xxx
951*cda5da8dSAndroid Build Coastguard Worker                    # realm='xxx'
952*cda5da8dSAndroid Build Coastguard Worker                    # realm="xxx"
953*cda5da8dSAndroid Build Coastguard Worker                    'realm=(["\']?)([^"\']*)\\2',
954*cda5da8dSAndroid Build Coastguard Worker                    re.I)
955*cda5da8dSAndroid Build Coastguard Worker
956*cda5da8dSAndroid Build Coastguard Worker    # XXX could pre-emptively send auth info already accepted (RFC 2617,
957*cda5da8dSAndroid Build Coastguard Worker    # end of section 2, and section 1.2 immediately after "credentials"
958*cda5da8dSAndroid Build Coastguard Worker    # production).
959*cda5da8dSAndroid Build Coastguard Worker
960*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, password_mgr=None):
961*cda5da8dSAndroid Build Coastguard Worker        if password_mgr is None:
962*cda5da8dSAndroid Build Coastguard Worker            password_mgr = HTTPPasswordMgr()
963*cda5da8dSAndroid Build Coastguard Worker        self.passwd = password_mgr
964*cda5da8dSAndroid Build Coastguard Worker        self.add_password = self.passwd.add_password
965*cda5da8dSAndroid Build Coastguard Worker
966*cda5da8dSAndroid Build Coastguard Worker    def _parse_realm(self, header):
967*cda5da8dSAndroid Build Coastguard Worker        # parse WWW-Authenticate header: accept multiple challenges per header
968*cda5da8dSAndroid Build Coastguard Worker        found_challenge = False
969*cda5da8dSAndroid Build Coastguard Worker        for mo in AbstractBasicAuthHandler.rx.finditer(header):
970*cda5da8dSAndroid Build Coastguard Worker            scheme, quote, realm = mo.groups()
971*cda5da8dSAndroid Build Coastguard Worker            if quote not in ['"', "'"]:
972*cda5da8dSAndroid Build Coastguard Worker                warnings.warn("Basic Auth Realm was unquoted",
973*cda5da8dSAndroid Build Coastguard Worker                              UserWarning, 3)
974*cda5da8dSAndroid Build Coastguard Worker
975*cda5da8dSAndroid Build Coastguard Worker            yield (scheme, realm)
976*cda5da8dSAndroid Build Coastguard Worker
977*cda5da8dSAndroid Build Coastguard Worker            found_challenge = True
978*cda5da8dSAndroid Build Coastguard Worker
979*cda5da8dSAndroid Build Coastguard Worker        if not found_challenge:
980*cda5da8dSAndroid Build Coastguard Worker            if header:
981*cda5da8dSAndroid Build Coastguard Worker                scheme = header.split()[0]
982*cda5da8dSAndroid Build Coastguard Worker            else:
983*cda5da8dSAndroid Build Coastguard Worker                scheme = ''
984*cda5da8dSAndroid Build Coastguard Worker            yield (scheme, None)
985*cda5da8dSAndroid Build Coastguard Worker
986*cda5da8dSAndroid Build Coastguard Worker    def http_error_auth_reqed(self, authreq, host, req, headers):
987*cda5da8dSAndroid Build Coastguard Worker        # host may be an authority (without userinfo) or a URL with an
988*cda5da8dSAndroid Build Coastguard Worker        # authority
989*cda5da8dSAndroid Build Coastguard Worker        headers = headers.get_all(authreq)
990*cda5da8dSAndroid Build Coastguard Worker        if not headers:
991*cda5da8dSAndroid Build Coastguard Worker            # no header found
992*cda5da8dSAndroid Build Coastguard Worker            return
993*cda5da8dSAndroid Build Coastguard Worker
994*cda5da8dSAndroid Build Coastguard Worker        unsupported = None
995*cda5da8dSAndroid Build Coastguard Worker        for header in headers:
996*cda5da8dSAndroid Build Coastguard Worker            for scheme, realm in self._parse_realm(header):
997*cda5da8dSAndroid Build Coastguard Worker                if scheme.lower() != 'basic':
998*cda5da8dSAndroid Build Coastguard Worker                    unsupported = scheme
999*cda5da8dSAndroid Build Coastguard Worker                    continue
1000*cda5da8dSAndroid Build Coastguard Worker
1001*cda5da8dSAndroid Build Coastguard Worker                if realm is not None:
1002*cda5da8dSAndroid Build Coastguard Worker                    # Use the first matching Basic challenge.
1003*cda5da8dSAndroid Build Coastguard Worker                    # Ignore following challenges even if they use the Basic
1004*cda5da8dSAndroid Build Coastguard Worker                    # scheme.
1005*cda5da8dSAndroid Build Coastguard Worker                    return self.retry_http_basic_auth(host, req, realm)
1006*cda5da8dSAndroid Build Coastguard Worker
1007*cda5da8dSAndroid Build Coastguard Worker        if unsupported is not None:
1008*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("AbstractBasicAuthHandler does not "
1009*cda5da8dSAndroid Build Coastguard Worker                             "support the following scheme: %r"
1010*cda5da8dSAndroid Build Coastguard Worker                             % (scheme,))
1011*cda5da8dSAndroid Build Coastguard Worker
1012*cda5da8dSAndroid Build Coastguard Worker    def retry_http_basic_auth(self, host, req, realm):
1013*cda5da8dSAndroid Build Coastguard Worker        user, pw = self.passwd.find_user_password(realm, host)
1014*cda5da8dSAndroid Build Coastguard Worker        if pw is not None:
1015*cda5da8dSAndroid Build Coastguard Worker            raw = "%s:%s" % (user, pw)
1016*cda5da8dSAndroid Build Coastguard Worker            auth = "Basic " + base64.b64encode(raw.encode()).decode("ascii")
1017*cda5da8dSAndroid Build Coastguard Worker            if req.get_header(self.auth_header, None) == auth:
1018*cda5da8dSAndroid Build Coastguard Worker                return None
1019*cda5da8dSAndroid Build Coastguard Worker            req.add_unredirected_header(self.auth_header, auth)
1020*cda5da8dSAndroid Build Coastguard Worker            return self.parent.open(req, timeout=req.timeout)
1021*cda5da8dSAndroid Build Coastguard Worker        else:
1022*cda5da8dSAndroid Build Coastguard Worker            return None
1023*cda5da8dSAndroid Build Coastguard Worker
1024*cda5da8dSAndroid Build Coastguard Worker    def http_request(self, req):
1025*cda5da8dSAndroid Build Coastguard Worker        if (not hasattr(self.passwd, 'is_authenticated') or
1026*cda5da8dSAndroid Build Coastguard Worker           not self.passwd.is_authenticated(req.full_url)):
1027*cda5da8dSAndroid Build Coastguard Worker            return req
1028*cda5da8dSAndroid Build Coastguard Worker
1029*cda5da8dSAndroid Build Coastguard Worker        if not req.has_header('Authorization'):
1030*cda5da8dSAndroid Build Coastguard Worker            user, passwd = self.passwd.find_user_password(None, req.full_url)
1031*cda5da8dSAndroid Build Coastguard Worker            credentials = '{0}:{1}'.format(user, passwd).encode()
1032*cda5da8dSAndroid Build Coastguard Worker            auth_str = base64.standard_b64encode(credentials).decode()
1033*cda5da8dSAndroid Build Coastguard Worker            req.add_unredirected_header('Authorization',
1034*cda5da8dSAndroid Build Coastguard Worker                                        'Basic {}'.format(auth_str.strip()))
1035*cda5da8dSAndroid Build Coastguard Worker        return req
1036*cda5da8dSAndroid Build Coastguard Worker
1037*cda5da8dSAndroid Build Coastguard Worker    def http_response(self, req, response):
1038*cda5da8dSAndroid Build Coastguard Worker        if hasattr(self.passwd, 'is_authenticated'):
1039*cda5da8dSAndroid Build Coastguard Worker            if 200 <= response.code < 300:
1040*cda5da8dSAndroid Build Coastguard Worker                self.passwd.update_authenticated(req.full_url, True)
1041*cda5da8dSAndroid Build Coastguard Worker            else:
1042*cda5da8dSAndroid Build Coastguard Worker                self.passwd.update_authenticated(req.full_url, False)
1043*cda5da8dSAndroid Build Coastguard Worker        return response
1044*cda5da8dSAndroid Build Coastguard Worker
1045*cda5da8dSAndroid Build Coastguard Worker    https_request = http_request
1046*cda5da8dSAndroid Build Coastguard Worker    https_response = http_response
1047*cda5da8dSAndroid Build Coastguard Worker
1048*cda5da8dSAndroid Build Coastguard Worker
1049*cda5da8dSAndroid Build Coastguard Worker
1050*cda5da8dSAndroid Build Coastguard Workerclass HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
1051*cda5da8dSAndroid Build Coastguard Worker
1052*cda5da8dSAndroid Build Coastguard Worker    auth_header = 'Authorization'
1053*cda5da8dSAndroid Build Coastguard Worker
1054*cda5da8dSAndroid Build Coastguard Worker    def http_error_401(self, req, fp, code, msg, headers):
1055*cda5da8dSAndroid Build Coastguard Worker        url = req.full_url
1056*cda5da8dSAndroid Build Coastguard Worker        response = self.http_error_auth_reqed('www-authenticate',
1057*cda5da8dSAndroid Build Coastguard Worker                                          url, req, headers)
1058*cda5da8dSAndroid Build Coastguard Worker        return response
1059*cda5da8dSAndroid Build Coastguard Worker
1060*cda5da8dSAndroid Build Coastguard Worker
1061*cda5da8dSAndroid Build Coastguard Workerclass ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
1062*cda5da8dSAndroid Build Coastguard Worker
1063*cda5da8dSAndroid Build Coastguard Worker    auth_header = 'Proxy-authorization'
1064*cda5da8dSAndroid Build Coastguard Worker
1065*cda5da8dSAndroid Build Coastguard Worker    def http_error_407(self, req, fp, code, msg, headers):
1066*cda5da8dSAndroid Build Coastguard Worker        # http_error_auth_reqed requires that there is no userinfo component in
1067*cda5da8dSAndroid Build Coastguard Worker        # authority.  Assume there isn't one, since urllib.request does not (and
1068*cda5da8dSAndroid Build Coastguard Worker        # should not, RFC 3986 s. 3.2.1) support requests for URLs containing
1069*cda5da8dSAndroid Build Coastguard Worker        # userinfo.
1070*cda5da8dSAndroid Build Coastguard Worker        authority = req.host
1071*cda5da8dSAndroid Build Coastguard Worker        response = self.http_error_auth_reqed('proxy-authenticate',
1072*cda5da8dSAndroid Build Coastguard Worker                                          authority, req, headers)
1073*cda5da8dSAndroid Build Coastguard Worker        return response
1074*cda5da8dSAndroid Build Coastguard Worker
1075*cda5da8dSAndroid Build Coastguard Worker
1076*cda5da8dSAndroid Build Coastguard Worker# Return n random bytes.
1077*cda5da8dSAndroid Build Coastguard Worker_randombytes = os.urandom
1078*cda5da8dSAndroid Build Coastguard Worker
1079*cda5da8dSAndroid Build Coastguard Worker
1080*cda5da8dSAndroid Build Coastguard Workerclass AbstractDigestAuthHandler:
1081*cda5da8dSAndroid Build Coastguard Worker    # Digest authentication is specified in RFC 2617.
1082*cda5da8dSAndroid Build Coastguard Worker
1083*cda5da8dSAndroid Build Coastguard Worker    # XXX The client does not inspect the Authentication-Info header
1084*cda5da8dSAndroid Build Coastguard Worker    # in a successful response.
1085*cda5da8dSAndroid Build Coastguard Worker
1086*cda5da8dSAndroid Build Coastguard Worker    # XXX It should be possible to test this implementation against
1087*cda5da8dSAndroid Build Coastguard Worker    # a mock server that just generates a static set of challenges.
1088*cda5da8dSAndroid Build Coastguard Worker
1089*cda5da8dSAndroid Build Coastguard Worker    # XXX qop="auth-int" supports is shaky
1090*cda5da8dSAndroid Build Coastguard Worker
1091*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, passwd=None):
1092*cda5da8dSAndroid Build Coastguard Worker        if passwd is None:
1093*cda5da8dSAndroid Build Coastguard Worker            passwd = HTTPPasswordMgr()
1094*cda5da8dSAndroid Build Coastguard Worker        self.passwd = passwd
1095*cda5da8dSAndroid Build Coastguard Worker        self.add_password = self.passwd.add_password
1096*cda5da8dSAndroid Build Coastguard Worker        self.retried = 0
1097*cda5da8dSAndroid Build Coastguard Worker        self.nonce_count = 0
1098*cda5da8dSAndroid Build Coastguard Worker        self.last_nonce = None
1099*cda5da8dSAndroid Build Coastguard Worker
1100*cda5da8dSAndroid Build Coastguard Worker    def reset_retry_count(self):
1101*cda5da8dSAndroid Build Coastguard Worker        self.retried = 0
1102*cda5da8dSAndroid Build Coastguard Worker
1103*cda5da8dSAndroid Build Coastguard Worker    def http_error_auth_reqed(self, auth_header, host, req, headers):
1104*cda5da8dSAndroid Build Coastguard Worker        authreq = headers.get(auth_header, None)
1105*cda5da8dSAndroid Build Coastguard Worker        if self.retried > 5:
1106*cda5da8dSAndroid Build Coastguard Worker            # Don't fail endlessly - if we failed once, we'll probably
1107*cda5da8dSAndroid Build Coastguard Worker            # fail a second time. Hm. Unless the Password Manager is
1108*cda5da8dSAndroid Build Coastguard Worker            # prompting for the information. Crap. This isn't great
1109*cda5da8dSAndroid Build Coastguard Worker            # but it's better than the current 'repeat until recursion
1110*cda5da8dSAndroid Build Coastguard Worker            # depth exceeded' approach <wink>
1111*cda5da8dSAndroid Build Coastguard Worker            raise HTTPError(req.full_url, 401, "digest auth failed",
1112*cda5da8dSAndroid Build Coastguard Worker                            headers, None)
1113*cda5da8dSAndroid Build Coastguard Worker        else:
1114*cda5da8dSAndroid Build Coastguard Worker            self.retried += 1
1115*cda5da8dSAndroid Build Coastguard Worker        if authreq:
1116*cda5da8dSAndroid Build Coastguard Worker            scheme = authreq.split()[0]
1117*cda5da8dSAndroid Build Coastguard Worker            if scheme.lower() == 'digest':
1118*cda5da8dSAndroid Build Coastguard Worker                return self.retry_http_digest_auth(req, authreq)
1119*cda5da8dSAndroid Build Coastguard Worker            elif scheme.lower() != 'basic':
1120*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("AbstractDigestAuthHandler does not support"
1121*cda5da8dSAndroid Build Coastguard Worker                                 " the following scheme: '%s'" % scheme)
1122*cda5da8dSAndroid Build Coastguard Worker
1123*cda5da8dSAndroid Build Coastguard Worker    def retry_http_digest_auth(self, req, auth):
1124*cda5da8dSAndroid Build Coastguard Worker        token, challenge = auth.split(' ', 1)
1125*cda5da8dSAndroid Build Coastguard Worker        chal = parse_keqv_list(filter(None, parse_http_list(challenge)))
1126*cda5da8dSAndroid Build Coastguard Worker        auth = self.get_authorization(req, chal)
1127*cda5da8dSAndroid Build Coastguard Worker        if auth:
1128*cda5da8dSAndroid Build Coastguard Worker            auth_val = 'Digest %s' % auth
1129*cda5da8dSAndroid Build Coastguard Worker            if req.headers.get(self.auth_header, None) == auth_val:
1130*cda5da8dSAndroid Build Coastguard Worker                return None
1131*cda5da8dSAndroid Build Coastguard Worker            req.add_unredirected_header(self.auth_header, auth_val)
1132*cda5da8dSAndroid Build Coastguard Worker            resp = self.parent.open(req, timeout=req.timeout)
1133*cda5da8dSAndroid Build Coastguard Worker            return resp
1134*cda5da8dSAndroid Build Coastguard Worker
1135*cda5da8dSAndroid Build Coastguard Worker    def get_cnonce(self, nonce):
1136*cda5da8dSAndroid Build Coastguard Worker        # The cnonce-value is an opaque
1137*cda5da8dSAndroid Build Coastguard Worker        # quoted string value provided by the client and used by both client
1138*cda5da8dSAndroid Build Coastguard Worker        # and server to avoid chosen plaintext attacks, to provide mutual
1139*cda5da8dSAndroid Build Coastguard Worker        # authentication, and to provide some message integrity protection.
1140*cda5da8dSAndroid Build Coastguard Worker        # This isn't a fabulous effort, but it's probably Good Enough.
1141*cda5da8dSAndroid Build Coastguard Worker        s = "%s:%s:%s:" % (self.nonce_count, nonce, time.ctime())
1142*cda5da8dSAndroid Build Coastguard Worker        b = s.encode("ascii") + _randombytes(8)
1143*cda5da8dSAndroid Build Coastguard Worker        dig = hashlib.sha1(b).hexdigest()
1144*cda5da8dSAndroid Build Coastguard Worker        return dig[:16]
1145*cda5da8dSAndroid Build Coastguard Worker
1146*cda5da8dSAndroid Build Coastguard Worker    def get_authorization(self, req, chal):
1147*cda5da8dSAndroid Build Coastguard Worker        try:
1148*cda5da8dSAndroid Build Coastguard Worker            realm = chal['realm']
1149*cda5da8dSAndroid Build Coastguard Worker            nonce = chal['nonce']
1150*cda5da8dSAndroid Build Coastguard Worker            qop = chal.get('qop')
1151*cda5da8dSAndroid Build Coastguard Worker            algorithm = chal.get('algorithm', 'MD5')
1152*cda5da8dSAndroid Build Coastguard Worker            # mod_digest doesn't send an opaque, even though it isn't
1153*cda5da8dSAndroid Build Coastguard Worker            # supposed to be optional
1154*cda5da8dSAndroid Build Coastguard Worker            opaque = chal.get('opaque', None)
1155*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
1156*cda5da8dSAndroid Build Coastguard Worker            return None
1157*cda5da8dSAndroid Build Coastguard Worker
1158*cda5da8dSAndroid Build Coastguard Worker        H, KD = self.get_algorithm_impls(algorithm)
1159*cda5da8dSAndroid Build Coastguard Worker        if H is None:
1160*cda5da8dSAndroid Build Coastguard Worker            return None
1161*cda5da8dSAndroid Build Coastguard Worker
1162*cda5da8dSAndroid Build Coastguard Worker        user, pw = self.passwd.find_user_password(realm, req.full_url)
1163*cda5da8dSAndroid Build Coastguard Worker        if user is None:
1164*cda5da8dSAndroid Build Coastguard Worker            return None
1165*cda5da8dSAndroid Build Coastguard Worker
1166*cda5da8dSAndroid Build Coastguard Worker        # XXX not implemented yet
1167*cda5da8dSAndroid Build Coastguard Worker        if req.data is not None:
1168*cda5da8dSAndroid Build Coastguard Worker            entdig = self.get_entity_digest(req.data, chal)
1169*cda5da8dSAndroid Build Coastguard Worker        else:
1170*cda5da8dSAndroid Build Coastguard Worker            entdig = None
1171*cda5da8dSAndroid Build Coastguard Worker
1172*cda5da8dSAndroid Build Coastguard Worker        A1 = "%s:%s:%s" % (user, realm, pw)
1173*cda5da8dSAndroid Build Coastguard Worker        A2 = "%s:%s" % (req.get_method(),
1174*cda5da8dSAndroid Build Coastguard Worker                        # XXX selector: what about proxies and full urls
1175*cda5da8dSAndroid Build Coastguard Worker                        req.selector)
1176*cda5da8dSAndroid Build Coastguard Worker        # NOTE: As per  RFC 2617, when server sends "auth,auth-int", the client could use either `auth`
1177*cda5da8dSAndroid Build Coastguard Worker        #     or `auth-int` to the response back. we use `auth` to send the response back.
1178*cda5da8dSAndroid Build Coastguard Worker        if qop is None:
1179*cda5da8dSAndroid Build Coastguard Worker            respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
1180*cda5da8dSAndroid Build Coastguard Worker        elif 'auth' in qop.split(','):
1181*cda5da8dSAndroid Build Coastguard Worker            if nonce == self.last_nonce:
1182*cda5da8dSAndroid Build Coastguard Worker                self.nonce_count += 1
1183*cda5da8dSAndroid Build Coastguard Worker            else:
1184*cda5da8dSAndroid Build Coastguard Worker                self.nonce_count = 1
1185*cda5da8dSAndroid Build Coastguard Worker                self.last_nonce = nonce
1186*cda5da8dSAndroid Build Coastguard Worker            ncvalue = '%08x' % self.nonce_count
1187*cda5da8dSAndroid Build Coastguard Worker            cnonce = self.get_cnonce(nonce)
1188*cda5da8dSAndroid Build Coastguard Worker            noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, 'auth', H(A2))
1189*cda5da8dSAndroid Build Coastguard Worker            respdig = KD(H(A1), noncebit)
1190*cda5da8dSAndroid Build Coastguard Worker        else:
1191*cda5da8dSAndroid Build Coastguard Worker            # XXX handle auth-int.
1192*cda5da8dSAndroid Build Coastguard Worker            raise URLError("qop '%s' is not supported." % qop)
1193*cda5da8dSAndroid Build Coastguard Worker
1194*cda5da8dSAndroid Build Coastguard Worker        # XXX should the partial digests be encoded too?
1195*cda5da8dSAndroid Build Coastguard Worker
1196*cda5da8dSAndroid Build Coastguard Worker        base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
1197*cda5da8dSAndroid Build Coastguard Worker               'response="%s"' % (user, realm, nonce, req.selector,
1198*cda5da8dSAndroid Build Coastguard Worker                                  respdig)
1199*cda5da8dSAndroid Build Coastguard Worker        if opaque:
1200*cda5da8dSAndroid Build Coastguard Worker            base += ', opaque="%s"' % opaque
1201*cda5da8dSAndroid Build Coastguard Worker        if entdig:
1202*cda5da8dSAndroid Build Coastguard Worker            base += ', digest="%s"' % entdig
1203*cda5da8dSAndroid Build Coastguard Worker        base += ', algorithm="%s"' % algorithm
1204*cda5da8dSAndroid Build Coastguard Worker        if qop:
1205*cda5da8dSAndroid Build Coastguard Worker            base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
1206*cda5da8dSAndroid Build Coastguard Worker        return base
1207*cda5da8dSAndroid Build Coastguard Worker
1208*cda5da8dSAndroid Build Coastguard Worker    def get_algorithm_impls(self, algorithm):
1209*cda5da8dSAndroid Build Coastguard Worker        # lambdas assume digest modules are imported at the top level
1210*cda5da8dSAndroid Build Coastguard Worker        if algorithm == 'MD5':
1211*cda5da8dSAndroid Build Coastguard Worker            H = lambda x: hashlib.md5(x.encode("ascii")).hexdigest()
1212*cda5da8dSAndroid Build Coastguard Worker        elif algorithm == 'SHA':
1213*cda5da8dSAndroid Build Coastguard Worker            H = lambda x: hashlib.sha1(x.encode("ascii")).hexdigest()
1214*cda5da8dSAndroid Build Coastguard Worker        # XXX MD5-sess
1215*cda5da8dSAndroid Build Coastguard Worker        else:
1216*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Unsupported digest authentication "
1217*cda5da8dSAndroid Build Coastguard Worker                             "algorithm %r" % algorithm)
1218*cda5da8dSAndroid Build Coastguard Worker        KD = lambda s, d: H("%s:%s" % (s, d))
1219*cda5da8dSAndroid Build Coastguard Worker        return H, KD
1220*cda5da8dSAndroid Build Coastguard Worker
1221*cda5da8dSAndroid Build Coastguard Worker    def get_entity_digest(self, data, chal):
1222*cda5da8dSAndroid Build Coastguard Worker        # XXX not implemented yet
1223*cda5da8dSAndroid Build Coastguard Worker        return None
1224*cda5da8dSAndroid Build Coastguard Worker
1225*cda5da8dSAndroid Build Coastguard Worker
1226*cda5da8dSAndroid Build Coastguard Workerclass HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
1227*cda5da8dSAndroid Build Coastguard Worker    """An authentication protocol defined by RFC 2069
1228*cda5da8dSAndroid Build Coastguard Worker
1229*cda5da8dSAndroid Build Coastguard Worker    Digest authentication improves on basic authentication because it
1230*cda5da8dSAndroid Build Coastguard Worker    does not transmit passwords in the clear.
1231*cda5da8dSAndroid Build Coastguard Worker    """
1232*cda5da8dSAndroid Build Coastguard Worker
1233*cda5da8dSAndroid Build Coastguard Worker    auth_header = 'Authorization'
1234*cda5da8dSAndroid Build Coastguard Worker    handler_order = 490  # before Basic auth
1235*cda5da8dSAndroid Build Coastguard Worker
1236*cda5da8dSAndroid Build Coastguard Worker    def http_error_401(self, req, fp, code, msg, headers):
1237*cda5da8dSAndroid Build Coastguard Worker        host = urlparse(req.full_url)[1]
1238*cda5da8dSAndroid Build Coastguard Worker        retry = self.http_error_auth_reqed('www-authenticate',
1239*cda5da8dSAndroid Build Coastguard Worker                                           host, req, headers)
1240*cda5da8dSAndroid Build Coastguard Worker        self.reset_retry_count()
1241*cda5da8dSAndroid Build Coastguard Worker        return retry
1242*cda5da8dSAndroid Build Coastguard Worker
1243*cda5da8dSAndroid Build Coastguard Worker
1244*cda5da8dSAndroid Build Coastguard Workerclass ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
1245*cda5da8dSAndroid Build Coastguard Worker
1246*cda5da8dSAndroid Build Coastguard Worker    auth_header = 'Proxy-Authorization'
1247*cda5da8dSAndroid Build Coastguard Worker    handler_order = 490  # before Basic auth
1248*cda5da8dSAndroid Build Coastguard Worker
1249*cda5da8dSAndroid Build Coastguard Worker    def http_error_407(self, req, fp, code, msg, headers):
1250*cda5da8dSAndroid Build Coastguard Worker        host = req.host
1251*cda5da8dSAndroid Build Coastguard Worker        retry = self.http_error_auth_reqed('proxy-authenticate',
1252*cda5da8dSAndroid Build Coastguard Worker                                           host, req, headers)
1253*cda5da8dSAndroid Build Coastguard Worker        self.reset_retry_count()
1254*cda5da8dSAndroid Build Coastguard Worker        return retry
1255*cda5da8dSAndroid Build Coastguard Worker
1256*cda5da8dSAndroid Build Coastguard Workerclass AbstractHTTPHandler(BaseHandler):
1257*cda5da8dSAndroid Build Coastguard Worker
1258*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, debuglevel=0):
1259*cda5da8dSAndroid Build Coastguard Worker        self._debuglevel = debuglevel
1260*cda5da8dSAndroid Build Coastguard Worker
1261*cda5da8dSAndroid Build Coastguard Worker    def set_http_debuglevel(self, level):
1262*cda5da8dSAndroid Build Coastguard Worker        self._debuglevel = level
1263*cda5da8dSAndroid Build Coastguard Worker
1264*cda5da8dSAndroid Build Coastguard Worker    def _get_content_length(self, request):
1265*cda5da8dSAndroid Build Coastguard Worker        return http.client.HTTPConnection._get_content_length(
1266*cda5da8dSAndroid Build Coastguard Worker            request.data,
1267*cda5da8dSAndroid Build Coastguard Worker            request.get_method())
1268*cda5da8dSAndroid Build Coastguard Worker
1269*cda5da8dSAndroid Build Coastguard Worker    def do_request_(self, request):
1270*cda5da8dSAndroid Build Coastguard Worker        host = request.host
1271*cda5da8dSAndroid Build Coastguard Worker        if not host:
1272*cda5da8dSAndroid Build Coastguard Worker            raise URLError('no host given')
1273*cda5da8dSAndroid Build Coastguard Worker
1274*cda5da8dSAndroid Build Coastguard Worker        if request.data is not None:  # POST
1275*cda5da8dSAndroid Build Coastguard Worker            data = request.data
1276*cda5da8dSAndroid Build Coastguard Worker            if isinstance(data, str):
1277*cda5da8dSAndroid Build Coastguard Worker                msg = "POST data should be bytes, an iterable of bytes, " \
1278*cda5da8dSAndroid Build Coastguard Worker                      "or a file object. It cannot be of type str."
1279*cda5da8dSAndroid Build Coastguard Worker                raise TypeError(msg)
1280*cda5da8dSAndroid Build Coastguard Worker            if not request.has_header('Content-type'):
1281*cda5da8dSAndroid Build Coastguard Worker                request.add_unredirected_header(
1282*cda5da8dSAndroid Build Coastguard Worker                    'Content-type',
1283*cda5da8dSAndroid Build Coastguard Worker                    'application/x-www-form-urlencoded')
1284*cda5da8dSAndroid Build Coastguard Worker            if (not request.has_header('Content-length')
1285*cda5da8dSAndroid Build Coastguard Worker                    and not request.has_header('Transfer-encoding')):
1286*cda5da8dSAndroid Build Coastguard Worker                content_length = self._get_content_length(request)
1287*cda5da8dSAndroid Build Coastguard Worker                if content_length is not None:
1288*cda5da8dSAndroid Build Coastguard Worker                    request.add_unredirected_header(
1289*cda5da8dSAndroid Build Coastguard Worker                            'Content-length', str(content_length))
1290*cda5da8dSAndroid Build Coastguard Worker                else:
1291*cda5da8dSAndroid Build Coastguard Worker                    request.add_unredirected_header(
1292*cda5da8dSAndroid Build Coastguard Worker                            'Transfer-encoding', 'chunked')
1293*cda5da8dSAndroid Build Coastguard Worker
1294*cda5da8dSAndroid Build Coastguard Worker        sel_host = host
1295*cda5da8dSAndroid Build Coastguard Worker        if request.has_proxy():
1296*cda5da8dSAndroid Build Coastguard Worker            scheme, sel = _splittype(request.selector)
1297*cda5da8dSAndroid Build Coastguard Worker            sel_host, sel_path = _splithost(sel)
1298*cda5da8dSAndroid Build Coastguard Worker        if not request.has_header('Host'):
1299*cda5da8dSAndroid Build Coastguard Worker            request.add_unredirected_header('Host', sel_host)
1300*cda5da8dSAndroid Build Coastguard Worker        for name, value in self.parent.addheaders:
1301*cda5da8dSAndroid Build Coastguard Worker            name = name.capitalize()
1302*cda5da8dSAndroid Build Coastguard Worker            if not request.has_header(name):
1303*cda5da8dSAndroid Build Coastguard Worker                request.add_unredirected_header(name, value)
1304*cda5da8dSAndroid Build Coastguard Worker
1305*cda5da8dSAndroid Build Coastguard Worker        return request
1306*cda5da8dSAndroid Build Coastguard Worker
1307*cda5da8dSAndroid Build Coastguard Worker    def do_open(self, http_class, req, **http_conn_args):
1308*cda5da8dSAndroid Build Coastguard Worker        """Return an HTTPResponse object for the request, using http_class.
1309*cda5da8dSAndroid Build Coastguard Worker
1310*cda5da8dSAndroid Build Coastguard Worker        http_class must implement the HTTPConnection API from http.client.
1311*cda5da8dSAndroid Build Coastguard Worker        """
1312*cda5da8dSAndroid Build Coastguard Worker        host = req.host
1313*cda5da8dSAndroid Build Coastguard Worker        if not host:
1314*cda5da8dSAndroid Build Coastguard Worker            raise URLError('no host given')
1315*cda5da8dSAndroid Build Coastguard Worker
1316*cda5da8dSAndroid Build Coastguard Worker        # will parse host:port
1317*cda5da8dSAndroid Build Coastguard Worker        h = http_class(host, timeout=req.timeout, **http_conn_args)
1318*cda5da8dSAndroid Build Coastguard Worker        h.set_debuglevel(self._debuglevel)
1319*cda5da8dSAndroid Build Coastguard Worker
1320*cda5da8dSAndroid Build Coastguard Worker        headers = dict(req.unredirected_hdrs)
1321*cda5da8dSAndroid Build Coastguard Worker        headers.update({k: v for k, v in req.headers.items()
1322*cda5da8dSAndroid Build Coastguard Worker                        if k not in headers})
1323*cda5da8dSAndroid Build Coastguard Worker
1324*cda5da8dSAndroid Build Coastguard Worker        # TODO(jhylton): Should this be redesigned to handle
1325*cda5da8dSAndroid Build Coastguard Worker        # persistent connections?
1326*cda5da8dSAndroid Build Coastguard Worker
1327*cda5da8dSAndroid Build Coastguard Worker        # We want to make an HTTP/1.1 request, but the addinfourl
1328*cda5da8dSAndroid Build Coastguard Worker        # class isn't prepared to deal with a persistent connection.
1329*cda5da8dSAndroid Build Coastguard Worker        # It will try to read all remaining data from the socket,
1330*cda5da8dSAndroid Build Coastguard Worker        # which will block while the server waits for the next request.
1331*cda5da8dSAndroid Build Coastguard Worker        # So make sure the connection gets closed after the (only)
1332*cda5da8dSAndroid Build Coastguard Worker        # request.
1333*cda5da8dSAndroid Build Coastguard Worker        headers["Connection"] = "close"
1334*cda5da8dSAndroid Build Coastguard Worker        headers = {name.title(): val for name, val in headers.items()}
1335*cda5da8dSAndroid Build Coastguard Worker
1336*cda5da8dSAndroid Build Coastguard Worker        if req._tunnel_host:
1337*cda5da8dSAndroid Build Coastguard Worker            tunnel_headers = {}
1338*cda5da8dSAndroid Build Coastguard Worker            proxy_auth_hdr = "Proxy-Authorization"
1339*cda5da8dSAndroid Build Coastguard Worker            if proxy_auth_hdr in headers:
1340*cda5da8dSAndroid Build Coastguard Worker                tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr]
1341*cda5da8dSAndroid Build Coastguard Worker                # Proxy-Authorization should not be sent to origin
1342*cda5da8dSAndroid Build Coastguard Worker                # server.
1343*cda5da8dSAndroid Build Coastguard Worker                del headers[proxy_auth_hdr]
1344*cda5da8dSAndroid Build Coastguard Worker            h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
1345*cda5da8dSAndroid Build Coastguard Worker
1346*cda5da8dSAndroid Build Coastguard Worker        try:
1347*cda5da8dSAndroid Build Coastguard Worker            try:
1348*cda5da8dSAndroid Build Coastguard Worker                h.request(req.get_method(), req.selector, req.data, headers,
1349*cda5da8dSAndroid Build Coastguard Worker                          encode_chunked=req.has_header('Transfer-encoding'))
1350*cda5da8dSAndroid Build Coastguard Worker            except OSError as err: # timeout error
1351*cda5da8dSAndroid Build Coastguard Worker                raise URLError(err)
1352*cda5da8dSAndroid Build Coastguard Worker            r = h.getresponse()
1353*cda5da8dSAndroid Build Coastguard Worker        except:
1354*cda5da8dSAndroid Build Coastguard Worker            h.close()
1355*cda5da8dSAndroid Build Coastguard Worker            raise
1356*cda5da8dSAndroid Build Coastguard Worker
1357*cda5da8dSAndroid Build Coastguard Worker        # If the server does not send us a 'Connection: close' header,
1358*cda5da8dSAndroid Build Coastguard Worker        # HTTPConnection assumes the socket should be left open. Manually
1359*cda5da8dSAndroid Build Coastguard Worker        # mark the socket to be closed when this response object goes away.
1360*cda5da8dSAndroid Build Coastguard Worker        if h.sock:
1361*cda5da8dSAndroid Build Coastguard Worker            h.sock.close()
1362*cda5da8dSAndroid Build Coastguard Worker            h.sock = None
1363*cda5da8dSAndroid Build Coastguard Worker
1364*cda5da8dSAndroid Build Coastguard Worker        r.url = req.get_full_url()
1365*cda5da8dSAndroid Build Coastguard Worker        # This line replaces the .msg attribute of the HTTPResponse
1366*cda5da8dSAndroid Build Coastguard Worker        # with .headers, because urllib clients expect the response to
1367*cda5da8dSAndroid Build Coastguard Worker        # have the reason in .msg.  It would be good to mark this
1368*cda5da8dSAndroid Build Coastguard Worker        # attribute is deprecated and get then to use info() or
1369*cda5da8dSAndroid Build Coastguard Worker        # .headers.
1370*cda5da8dSAndroid Build Coastguard Worker        r.msg = r.reason
1371*cda5da8dSAndroid Build Coastguard Worker        return r
1372*cda5da8dSAndroid Build Coastguard Worker
1373*cda5da8dSAndroid Build Coastguard Worker
1374*cda5da8dSAndroid Build Coastguard Workerclass HTTPHandler(AbstractHTTPHandler):
1375*cda5da8dSAndroid Build Coastguard Worker
1376*cda5da8dSAndroid Build Coastguard Worker    def http_open(self, req):
1377*cda5da8dSAndroid Build Coastguard Worker        return self.do_open(http.client.HTTPConnection, req)
1378*cda5da8dSAndroid Build Coastguard Worker
1379*cda5da8dSAndroid Build Coastguard Worker    http_request = AbstractHTTPHandler.do_request_
1380*cda5da8dSAndroid Build Coastguard Worker
1381*cda5da8dSAndroid Build Coastguard Workerif hasattr(http.client, 'HTTPSConnection'):
1382*cda5da8dSAndroid Build Coastguard Worker
1383*cda5da8dSAndroid Build Coastguard Worker    class HTTPSHandler(AbstractHTTPHandler):
1384*cda5da8dSAndroid Build Coastguard Worker
1385*cda5da8dSAndroid Build Coastguard Worker        def __init__(self, debuglevel=0, context=None, check_hostname=None):
1386*cda5da8dSAndroid Build Coastguard Worker            AbstractHTTPHandler.__init__(self, debuglevel)
1387*cda5da8dSAndroid Build Coastguard Worker            self._context = context
1388*cda5da8dSAndroid Build Coastguard Worker            self._check_hostname = check_hostname
1389*cda5da8dSAndroid Build Coastguard Worker
1390*cda5da8dSAndroid Build Coastguard Worker        def https_open(self, req):
1391*cda5da8dSAndroid Build Coastguard Worker            return self.do_open(http.client.HTTPSConnection, req,
1392*cda5da8dSAndroid Build Coastguard Worker                context=self._context, check_hostname=self._check_hostname)
1393*cda5da8dSAndroid Build Coastguard Worker
1394*cda5da8dSAndroid Build Coastguard Worker        https_request = AbstractHTTPHandler.do_request_
1395*cda5da8dSAndroid Build Coastguard Worker
1396*cda5da8dSAndroid Build Coastguard Worker    __all__.append('HTTPSHandler')
1397*cda5da8dSAndroid Build Coastguard Worker
1398*cda5da8dSAndroid Build Coastguard Workerclass HTTPCookieProcessor(BaseHandler):
1399*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, cookiejar=None):
1400*cda5da8dSAndroid Build Coastguard Worker        import http.cookiejar
1401*cda5da8dSAndroid Build Coastguard Worker        if cookiejar is None:
1402*cda5da8dSAndroid Build Coastguard Worker            cookiejar = http.cookiejar.CookieJar()
1403*cda5da8dSAndroid Build Coastguard Worker        self.cookiejar = cookiejar
1404*cda5da8dSAndroid Build Coastguard Worker
1405*cda5da8dSAndroid Build Coastguard Worker    def http_request(self, request):
1406*cda5da8dSAndroid Build Coastguard Worker        self.cookiejar.add_cookie_header(request)
1407*cda5da8dSAndroid Build Coastguard Worker        return request
1408*cda5da8dSAndroid Build Coastguard Worker
1409*cda5da8dSAndroid Build Coastguard Worker    def http_response(self, request, response):
1410*cda5da8dSAndroid Build Coastguard Worker        self.cookiejar.extract_cookies(response, request)
1411*cda5da8dSAndroid Build Coastguard Worker        return response
1412*cda5da8dSAndroid Build Coastguard Worker
1413*cda5da8dSAndroid Build Coastguard Worker    https_request = http_request
1414*cda5da8dSAndroid Build Coastguard Worker    https_response = http_response
1415*cda5da8dSAndroid Build Coastguard Worker
1416*cda5da8dSAndroid Build Coastguard Workerclass UnknownHandler(BaseHandler):
1417*cda5da8dSAndroid Build Coastguard Worker    def unknown_open(self, req):
1418*cda5da8dSAndroid Build Coastguard Worker        type = req.type
1419*cda5da8dSAndroid Build Coastguard Worker        raise URLError('unknown url type: %s' % type)
1420*cda5da8dSAndroid Build Coastguard Worker
1421*cda5da8dSAndroid Build Coastguard Workerdef parse_keqv_list(l):
1422*cda5da8dSAndroid Build Coastguard Worker    """Parse list of key=value strings where keys are not duplicated."""
1423*cda5da8dSAndroid Build Coastguard Worker    parsed = {}
1424*cda5da8dSAndroid Build Coastguard Worker    for elt in l:
1425*cda5da8dSAndroid Build Coastguard Worker        k, v = elt.split('=', 1)
1426*cda5da8dSAndroid Build Coastguard Worker        if v[0] == '"' and v[-1] == '"':
1427*cda5da8dSAndroid Build Coastguard Worker            v = v[1:-1]
1428*cda5da8dSAndroid Build Coastguard Worker        parsed[k] = v
1429*cda5da8dSAndroid Build Coastguard Worker    return parsed
1430*cda5da8dSAndroid Build Coastguard Worker
1431*cda5da8dSAndroid Build Coastguard Workerdef parse_http_list(s):
1432*cda5da8dSAndroid Build Coastguard Worker    """Parse lists as described by RFC 2068 Section 2.
1433*cda5da8dSAndroid Build Coastguard Worker
1434*cda5da8dSAndroid Build Coastguard Worker    In particular, parse comma-separated lists where the elements of
1435*cda5da8dSAndroid Build Coastguard Worker    the list may include quoted-strings.  A quoted-string could
1436*cda5da8dSAndroid Build Coastguard Worker    contain a comma.  A non-quoted string could have quotes in the
1437*cda5da8dSAndroid Build Coastguard Worker    middle.  Neither commas nor quotes count if they are escaped.
1438*cda5da8dSAndroid Build Coastguard Worker    Only double-quotes count, not single-quotes.
1439*cda5da8dSAndroid Build Coastguard Worker    """
1440*cda5da8dSAndroid Build Coastguard Worker    res = []
1441*cda5da8dSAndroid Build Coastguard Worker    part = ''
1442*cda5da8dSAndroid Build Coastguard Worker
1443*cda5da8dSAndroid Build Coastguard Worker    escape = quote = False
1444*cda5da8dSAndroid Build Coastguard Worker    for cur in s:
1445*cda5da8dSAndroid Build Coastguard Worker        if escape:
1446*cda5da8dSAndroid Build Coastguard Worker            part += cur
1447*cda5da8dSAndroid Build Coastguard Worker            escape = False
1448*cda5da8dSAndroid Build Coastguard Worker            continue
1449*cda5da8dSAndroid Build Coastguard Worker        if quote:
1450*cda5da8dSAndroid Build Coastguard Worker            if cur == '\\':
1451*cda5da8dSAndroid Build Coastguard Worker                escape = True
1452*cda5da8dSAndroid Build Coastguard Worker                continue
1453*cda5da8dSAndroid Build Coastguard Worker            elif cur == '"':
1454*cda5da8dSAndroid Build Coastguard Worker                quote = False
1455*cda5da8dSAndroid Build Coastguard Worker            part += cur
1456*cda5da8dSAndroid Build Coastguard Worker            continue
1457*cda5da8dSAndroid Build Coastguard Worker
1458*cda5da8dSAndroid Build Coastguard Worker        if cur == ',':
1459*cda5da8dSAndroid Build Coastguard Worker            res.append(part)
1460*cda5da8dSAndroid Build Coastguard Worker            part = ''
1461*cda5da8dSAndroid Build Coastguard Worker            continue
1462*cda5da8dSAndroid Build Coastguard Worker
1463*cda5da8dSAndroid Build Coastguard Worker        if cur == '"':
1464*cda5da8dSAndroid Build Coastguard Worker            quote = True
1465*cda5da8dSAndroid Build Coastguard Worker
1466*cda5da8dSAndroid Build Coastguard Worker        part += cur
1467*cda5da8dSAndroid Build Coastguard Worker
1468*cda5da8dSAndroid Build Coastguard Worker    # append last part
1469*cda5da8dSAndroid Build Coastguard Worker    if part:
1470*cda5da8dSAndroid Build Coastguard Worker        res.append(part)
1471*cda5da8dSAndroid Build Coastguard Worker
1472*cda5da8dSAndroid Build Coastguard Worker    return [part.strip() for part in res]
1473*cda5da8dSAndroid Build Coastguard Worker
1474*cda5da8dSAndroid Build Coastguard Workerclass FileHandler(BaseHandler):
1475*cda5da8dSAndroid Build Coastguard Worker    # Use local file or FTP depending on form of URL
1476*cda5da8dSAndroid Build Coastguard Worker    def file_open(self, req):
1477*cda5da8dSAndroid Build Coastguard Worker        url = req.selector
1478*cda5da8dSAndroid Build Coastguard Worker        if url[:2] == '//' and url[2:3] != '/' and (req.host and
1479*cda5da8dSAndroid Build Coastguard Worker                req.host != 'localhost'):
1480*cda5da8dSAndroid Build Coastguard Worker            if not req.host in self.get_names():
1481*cda5da8dSAndroid Build Coastguard Worker                raise URLError("file:// scheme is supported only on localhost")
1482*cda5da8dSAndroid Build Coastguard Worker        else:
1483*cda5da8dSAndroid Build Coastguard Worker            return self.open_local_file(req)
1484*cda5da8dSAndroid Build Coastguard Worker
1485*cda5da8dSAndroid Build Coastguard Worker    # names for the localhost
1486*cda5da8dSAndroid Build Coastguard Worker    names = None
1487*cda5da8dSAndroid Build Coastguard Worker    def get_names(self):
1488*cda5da8dSAndroid Build Coastguard Worker        if FileHandler.names is None:
1489*cda5da8dSAndroid Build Coastguard Worker            try:
1490*cda5da8dSAndroid Build Coastguard Worker                FileHandler.names = tuple(
1491*cda5da8dSAndroid Build Coastguard Worker                    socket.gethostbyname_ex('localhost')[2] +
1492*cda5da8dSAndroid Build Coastguard Worker                    socket.gethostbyname_ex(socket.gethostname())[2])
1493*cda5da8dSAndroid Build Coastguard Worker            except socket.gaierror:
1494*cda5da8dSAndroid Build Coastguard Worker                FileHandler.names = (socket.gethostbyname('localhost'),)
1495*cda5da8dSAndroid Build Coastguard Worker        return FileHandler.names
1496*cda5da8dSAndroid Build Coastguard Worker
1497*cda5da8dSAndroid Build Coastguard Worker    # not entirely sure what the rules are here
1498*cda5da8dSAndroid Build Coastguard Worker    def open_local_file(self, req):
1499*cda5da8dSAndroid Build Coastguard Worker        import email.utils
1500*cda5da8dSAndroid Build Coastguard Worker        import mimetypes
1501*cda5da8dSAndroid Build Coastguard Worker        host = req.host
1502*cda5da8dSAndroid Build Coastguard Worker        filename = req.selector
1503*cda5da8dSAndroid Build Coastguard Worker        localfile = url2pathname(filename)
1504*cda5da8dSAndroid Build Coastguard Worker        try:
1505*cda5da8dSAndroid Build Coastguard Worker            stats = os.stat(localfile)
1506*cda5da8dSAndroid Build Coastguard Worker            size = stats.st_size
1507*cda5da8dSAndroid Build Coastguard Worker            modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
1508*cda5da8dSAndroid Build Coastguard Worker            mtype = mimetypes.guess_type(filename)[0]
1509*cda5da8dSAndroid Build Coastguard Worker            headers = email.message_from_string(
1510*cda5da8dSAndroid Build Coastguard Worker                'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
1511*cda5da8dSAndroid Build Coastguard Worker                (mtype or 'text/plain', size, modified))
1512*cda5da8dSAndroid Build Coastguard Worker            if host:
1513*cda5da8dSAndroid Build Coastguard Worker                host, port = _splitport(host)
1514*cda5da8dSAndroid Build Coastguard Worker            if not host or \
1515*cda5da8dSAndroid Build Coastguard Worker                (not port and _safe_gethostbyname(host) in self.get_names()):
1516*cda5da8dSAndroid Build Coastguard Worker                if host:
1517*cda5da8dSAndroid Build Coastguard Worker                    origurl = 'file://' + host + filename
1518*cda5da8dSAndroid Build Coastguard Worker                else:
1519*cda5da8dSAndroid Build Coastguard Worker                    origurl = 'file://' + filename
1520*cda5da8dSAndroid Build Coastguard Worker                return addinfourl(open(localfile, 'rb'), headers, origurl)
1521*cda5da8dSAndroid Build Coastguard Worker        except OSError as exp:
1522*cda5da8dSAndroid Build Coastguard Worker            raise URLError(exp)
1523*cda5da8dSAndroid Build Coastguard Worker        raise URLError('file not on local host')
1524*cda5da8dSAndroid Build Coastguard Worker
1525*cda5da8dSAndroid Build Coastguard Workerdef _safe_gethostbyname(host):
1526*cda5da8dSAndroid Build Coastguard Worker    try:
1527*cda5da8dSAndroid Build Coastguard Worker        return socket.gethostbyname(host)
1528*cda5da8dSAndroid Build Coastguard Worker    except socket.gaierror:
1529*cda5da8dSAndroid Build Coastguard Worker        return None
1530*cda5da8dSAndroid Build Coastguard Worker
1531*cda5da8dSAndroid Build Coastguard Workerclass FTPHandler(BaseHandler):
1532*cda5da8dSAndroid Build Coastguard Worker    def ftp_open(self, req):
1533*cda5da8dSAndroid Build Coastguard Worker        import ftplib
1534*cda5da8dSAndroid Build Coastguard Worker        import mimetypes
1535*cda5da8dSAndroid Build Coastguard Worker        host = req.host
1536*cda5da8dSAndroid Build Coastguard Worker        if not host:
1537*cda5da8dSAndroid Build Coastguard Worker            raise URLError('ftp error: no host given')
1538*cda5da8dSAndroid Build Coastguard Worker        host, port = _splitport(host)
1539*cda5da8dSAndroid Build Coastguard Worker        if port is None:
1540*cda5da8dSAndroid Build Coastguard Worker            port = ftplib.FTP_PORT
1541*cda5da8dSAndroid Build Coastguard Worker        else:
1542*cda5da8dSAndroid Build Coastguard Worker            port = int(port)
1543*cda5da8dSAndroid Build Coastguard Worker
1544*cda5da8dSAndroid Build Coastguard Worker        # username/password handling
1545*cda5da8dSAndroid Build Coastguard Worker        user, host = _splituser(host)
1546*cda5da8dSAndroid Build Coastguard Worker        if user:
1547*cda5da8dSAndroid Build Coastguard Worker            user, passwd = _splitpasswd(user)
1548*cda5da8dSAndroid Build Coastguard Worker        else:
1549*cda5da8dSAndroid Build Coastguard Worker            passwd = None
1550*cda5da8dSAndroid Build Coastguard Worker        host = unquote(host)
1551*cda5da8dSAndroid Build Coastguard Worker        user = user or ''
1552*cda5da8dSAndroid Build Coastguard Worker        passwd = passwd or ''
1553*cda5da8dSAndroid Build Coastguard Worker
1554*cda5da8dSAndroid Build Coastguard Worker        try:
1555*cda5da8dSAndroid Build Coastguard Worker            host = socket.gethostbyname(host)
1556*cda5da8dSAndroid Build Coastguard Worker        except OSError as msg:
1557*cda5da8dSAndroid Build Coastguard Worker            raise URLError(msg)
1558*cda5da8dSAndroid Build Coastguard Worker        path, attrs = _splitattr(req.selector)
1559*cda5da8dSAndroid Build Coastguard Worker        dirs = path.split('/')
1560*cda5da8dSAndroid Build Coastguard Worker        dirs = list(map(unquote, dirs))
1561*cda5da8dSAndroid Build Coastguard Worker        dirs, file = dirs[:-1], dirs[-1]
1562*cda5da8dSAndroid Build Coastguard Worker        if dirs and not dirs[0]:
1563*cda5da8dSAndroid Build Coastguard Worker            dirs = dirs[1:]
1564*cda5da8dSAndroid Build Coastguard Worker        try:
1565*cda5da8dSAndroid Build Coastguard Worker            fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout)
1566*cda5da8dSAndroid Build Coastguard Worker            type = file and 'I' or 'D'
1567*cda5da8dSAndroid Build Coastguard Worker            for attr in attrs:
1568*cda5da8dSAndroid Build Coastguard Worker                attr, value = _splitvalue(attr)
1569*cda5da8dSAndroid Build Coastguard Worker                if attr.lower() == 'type' and \
1570*cda5da8dSAndroid Build Coastguard Worker                   value in ('a', 'A', 'i', 'I', 'd', 'D'):
1571*cda5da8dSAndroid Build Coastguard Worker                    type = value.upper()
1572*cda5da8dSAndroid Build Coastguard Worker            fp, retrlen = fw.retrfile(file, type)
1573*cda5da8dSAndroid Build Coastguard Worker            headers = ""
1574*cda5da8dSAndroid Build Coastguard Worker            mtype = mimetypes.guess_type(req.full_url)[0]
1575*cda5da8dSAndroid Build Coastguard Worker            if mtype:
1576*cda5da8dSAndroid Build Coastguard Worker                headers += "Content-type: %s\n" % mtype
1577*cda5da8dSAndroid Build Coastguard Worker            if retrlen is not None and retrlen >= 0:
1578*cda5da8dSAndroid Build Coastguard Worker                headers += "Content-length: %d\n" % retrlen
1579*cda5da8dSAndroid Build Coastguard Worker            headers = email.message_from_string(headers)
1580*cda5da8dSAndroid Build Coastguard Worker            return addinfourl(fp, headers, req.full_url)
1581*cda5da8dSAndroid Build Coastguard Worker        except ftplib.all_errors as exp:
1582*cda5da8dSAndroid Build Coastguard Worker            raise URLError(exp) from exp
1583*cda5da8dSAndroid Build Coastguard Worker
1584*cda5da8dSAndroid Build Coastguard Worker    def connect_ftp(self, user, passwd, host, port, dirs, timeout):
1585*cda5da8dSAndroid Build Coastguard Worker        return ftpwrapper(user, passwd, host, port, dirs, timeout,
1586*cda5da8dSAndroid Build Coastguard Worker                          persistent=False)
1587*cda5da8dSAndroid Build Coastguard Worker
1588*cda5da8dSAndroid Build Coastguard Workerclass CacheFTPHandler(FTPHandler):
1589*cda5da8dSAndroid Build Coastguard Worker    # XXX would be nice to have pluggable cache strategies
1590*cda5da8dSAndroid Build Coastguard Worker    # XXX this stuff is definitely not thread safe
1591*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
1592*cda5da8dSAndroid Build Coastguard Worker        self.cache = {}
1593*cda5da8dSAndroid Build Coastguard Worker        self.timeout = {}
1594*cda5da8dSAndroid Build Coastguard Worker        self.soonest = 0
1595*cda5da8dSAndroid Build Coastguard Worker        self.delay = 60
1596*cda5da8dSAndroid Build Coastguard Worker        self.max_conns = 16
1597*cda5da8dSAndroid Build Coastguard Worker
1598*cda5da8dSAndroid Build Coastguard Worker    def setTimeout(self, t):
1599*cda5da8dSAndroid Build Coastguard Worker        self.delay = t
1600*cda5da8dSAndroid Build Coastguard Worker
1601*cda5da8dSAndroid Build Coastguard Worker    def setMaxConns(self, m):
1602*cda5da8dSAndroid Build Coastguard Worker        self.max_conns = m
1603*cda5da8dSAndroid Build Coastguard Worker
1604*cda5da8dSAndroid Build Coastguard Worker    def connect_ftp(self, user, passwd, host, port, dirs, timeout):
1605*cda5da8dSAndroid Build Coastguard Worker        key = user, host, port, '/'.join(dirs), timeout
1606*cda5da8dSAndroid Build Coastguard Worker        if key in self.cache:
1607*cda5da8dSAndroid Build Coastguard Worker            self.timeout[key] = time.time() + self.delay
1608*cda5da8dSAndroid Build Coastguard Worker        else:
1609*cda5da8dSAndroid Build Coastguard Worker            self.cache[key] = ftpwrapper(user, passwd, host, port,
1610*cda5da8dSAndroid Build Coastguard Worker                                         dirs, timeout)
1611*cda5da8dSAndroid Build Coastguard Worker            self.timeout[key] = time.time() + self.delay
1612*cda5da8dSAndroid Build Coastguard Worker        self.check_cache()
1613*cda5da8dSAndroid Build Coastguard Worker        return self.cache[key]
1614*cda5da8dSAndroid Build Coastguard Worker
1615*cda5da8dSAndroid Build Coastguard Worker    def check_cache(self):
1616*cda5da8dSAndroid Build Coastguard Worker        # first check for old ones
1617*cda5da8dSAndroid Build Coastguard Worker        t = time.time()
1618*cda5da8dSAndroid Build Coastguard Worker        if self.soonest <= t:
1619*cda5da8dSAndroid Build Coastguard Worker            for k, v in list(self.timeout.items()):
1620*cda5da8dSAndroid Build Coastguard Worker                if v < t:
1621*cda5da8dSAndroid Build Coastguard Worker                    self.cache[k].close()
1622*cda5da8dSAndroid Build Coastguard Worker                    del self.cache[k]
1623*cda5da8dSAndroid Build Coastguard Worker                    del self.timeout[k]
1624*cda5da8dSAndroid Build Coastguard Worker        self.soonest = min(list(self.timeout.values()))
1625*cda5da8dSAndroid Build Coastguard Worker
1626*cda5da8dSAndroid Build Coastguard Worker        # then check the size
1627*cda5da8dSAndroid Build Coastguard Worker        if len(self.cache) == self.max_conns:
1628*cda5da8dSAndroid Build Coastguard Worker            for k, v in list(self.timeout.items()):
1629*cda5da8dSAndroid Build Coastguard Worker                if v == self.soonest:
1630*cda5da8dSAndroid Build Coastguard Worker                    del self.cache[k]
1631*cda5da8dSAndroid Build Coastguard Worker                    del self.timeout[k]
1632*cda5da8dSAndroid Build Coastguard Worker                    break
1633*cda5da8dSAndroid Build Coastguard Worker            self.soonest = min(list(self.timeout.values()))
1634*cda5da8dSAndroid Build Coastguard Worker
1635*cda5da8dSAndroid Build Coastguard Worker    def clear_cache(self):
1636*cda5da8dSAndroid Build Coastguard Worker        for conn in self.cache.values():
1637*cda5da8dSAndroid Build Coastguard Worker            conn.close()
1638*cda5da8dSAndroid Build Coastguard Worker        self.cache.clear()
1639*cda5da8dSAndroid Build Coastguard Worker        self.timeout.clear()
1640*cda5da8dSAndroid Build Coastguard Worker
1641*cda5da8dSAndroid Build Coastguard Workerclass DataHandler(BaseHandler):
1642*cda5da8dSAndroid Build Coastguard Worker    def data_open(self, req):
1643*cda5da8dSAndroid Build Coastguard Worker        # data URLs as specified in RFC 2397.
1644*cda5da8dSAndroid Build Coastguard Worker        #
1645*cda5da8dSAndroid Build Coastguard Worker        # ignores POSTed data
1646*cda5da8dSAndroid Build Coastguard Worker        #
1647*cda5da8dSAndroid Build Coastguard Worker        # syntax:
1648*cda5da8dSAndroid Build Coastguard Worker        # dataurl   := "data:" [ mediatype ] [ ";base64" ] "," data
1649*cda5da8dSAndroid Build Coastguard Worker        # mediatype := [ type "/" subtype ] *( ";" parameter )
1650*cda5da8dSAndroid Build Coastguard Worker        # data      := *urlchar
1651*cda5da8dSAndroid Build Coastguard Worker        # parameter := attribute "=" value
1652*cda5da8dSAndroid Build Coastguard Worker        url = req.full_url
1653*cda5da8dSAndroid Build Coastguard Worker
1654*cda5da8dSAndroid Build Coastguard Worker        scheme, data = url.split(":",1)
1655*cda5da8dSAndroid Build Coastguard Worker        mediatype, data = data.split(",",1)
1656*cda5da8dSAndroid Build Coastguard Worker
1657*cda5da8dSAndroid Build Coastguard Worker        # even base64 encoded data URLs might be quoted so unquote in any case:
1658*cda5da8dSAndroid Build Coastguard Worker        data = unquote_to_bytes(data)
1659*cda5da8dSAndroid Build Coastguard Worker        if mediatype.endswith(";base64"):
1660*cda5da8dSAndroid Build Coastguard Worker            data = base64.decodebytes(data)
1661*cda5da8dSAndroid Build Coastguard Worker            mediatype = mediatype[:-7]
1662*cda5da8dSAndroid Build Coastguard Worker
1663*cda5da8dSAndroid Build Coastguard Worker        if not mediatype:
1664*cda5da8dSAndroid Build Coastguard Worker            mediatype = "text/plain;charset=US-ASCII"
1665*cda5da8dSAndroid Build Coastguard Worker
1666*cda5da8dSAndroid Build Coastguard Worker        headers = email.message_from_string("Content-type: %s\nContent-length: %d\n" %
1667*cda5da8dSAndroid Build Coastguard Worker            (mediatype, len(data)))
1668*cda5da8dSAndroid Build Coastguard Worker
1669*cda5da8dSAndroid Build Coastguard Worker        return addinfourl(io.BytesIO(data), headers, url)
1670*cda5da8dSAndroid Build Coastguard Worker
1671*cda5da8dSAndroid Build Coastguard Worker
1672*cda5da8dSAndroid Build Coastguard Worker# Code move from the old urllib module
1673*cda5da8dSAndroid Build Coastguard Worker
1674*cda5da8dSAndroid Build Coastguard WorkerMAXFTPCACHE = 10        # Trim the ftp cache beyond this size
1675*cda5da8dSAndroid Build Coastguard Worker
1676*cda5da8dSAndroid Build Coastguard Worker# Helper for non-unix systems
1677*cda5da8dSAndroid Build Coastguard Workerif os.name == 'nt':
1678*cda5da8dSAndroid Build Coastguard Worker    from nturl2path import url2pathname, pathname2url
1679*cda5da8dSAndroid Build Coastguard Workerelse:
1680*cda5da8dSAndroid Build Coastguard Worker    def url2pathname(pathname):
1681*cda5da8dSAndroid Build Coastguard Worker        """OS-specific conversion from a relative URL of the 'file' scheme
1682*cda5da8dSAndroid Build Coastguard Worker        to a file system path; not recommended for general use."""
1683*cda5da8dSAndroid Build Coastguard Worker        return unquote(pathname)
1684*cda5da8dSAndroid Build Coastguard Worker
1685*cda5da8dSAndroid Build Coastguard Worker    def pathname2url(pathname):
1686*cda5da8dSAndroid Build Coastguard Worker        """OS-specific conversion from a file system path to a relative URL
1687*cda5da8dSAndroid Build Coastguard Worker        of the 'file' scheme; not recommended for general use."""
1688*cda5da8dSAndroid Build Coastguard Worker        return quote(pathname)
1689*cda5da8dSAndroid Build Coastguard Worker
1690*cda5da8dSAndroid Build Coastguard Worker
1691*cda5da8dSAndroid Build Coastguard Workerftpcache = {}
1692*cda5da8dSAndroid Build Coastguard Worker
1693*cda5da8dSAndroid Build Coastguard Worker
1694*cda5da8dSAndroid Build Coastguard Workerclass URLopener:
1695*cda5da8dSAndroid Build Coastguard Worker    """Class to open URLs.
1696*cda5da8dSAndroid Build Coastguard Worker    This is a class rather than just a subroutine because we may need
1697*cda5da8dSAndroid Build Coastguard Worker    more than one set of global protocol-specific options.
1698*cda5da8dSAndroid Build Coastguard Worker    Note -- this is a base class for those who don't want the
1699*cda5da8dSAndroid Build Coastguard Worker    automatic handling of errors type 302 (relocated) and 401
1700*cda5da8dSAndroid Build Coastguard Worker    (authorization needed)."""
1701*cda5da8dSAndroid Build Coastguard Worker
1702*cda5da8dSAndroid Build Coastguard Worker    __tempfiles = None
1703*cda5da8dSAndroid Build Coastguard Worker
1704*cda5da8dSAndroid Build Coastguard Worker    version = "Python-urllib/%s" % __version__
1705*cda5da8dSAndroid Build Coastguard Worker
1706*cda5da8dSAndroid Build Coastguard Worker    # Constructor
1707*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, proxies=None, **x509):
1708*cda5da8dSAndroid Build Coastguard Worker        msg = "%(class)s style of invoking requests is deprecated. " \
1709*cda5da8dSAndroid Build Coastguard Worker              "Use newer urlopen functions/methods" % {'class': self.__class__.__name__}
1710*cda5da8dSAndroid Build Coastguard Worker        warnings.warn(msg, DeprecationWarning, stacklevel=3)
1711*cda5da8dSAndroid Build Coastguard Worker        if proxies is None:
1712*cda5da8dSAndroid Build Coastguard Worker            proxies = getproxies()
1713*cda5da8dSAndroid Build Coastguard Worker        assert hasattr(proxies, 'keys'), "proxies must be a mapping"
1714*cda5da8dSAndroid Build Coastguard Worker        self.proxies = proxies
1715*cda5da8dSAndroid Build Coastguard Worker        self.key_file = x509.get('key_file')
1716*cda5da8dSAndroid Build Coastguard Worker        self.cert_file = x509.get('cert_file')
1717*cda5da8dSAndroid Build Coastguard Worker        self.addheaders = [('User-Agent', self.version), ('Accept', '*/*')]
1718*cda5da8dSAndroid Build Coastguard Worker        self.__tempfiles = []
1719*cda5da8dSAndroid Build Coastguard Worker        self.__unlink = os.unlink # See cleanup()
1720*cda5da8dSAndroid Build Coastguard Worker        self.tempcache = None
1721*cda5da8dSAndroid Build Coastguard Worker        # Undocumented feature: if you assign {} to tempcache,
1722*cda5da8dSAndroid Build Coastguard Worker        # it is used to cache files retrieved with
1723*cda5da8dSAndroid Build Coastguard Worker        # self.retrieve().  This is not enabled by default
1724*cda5da8dSAndroid Build Coastguard Worker        # since it does not work for changing documents (and I
1725*cda5da8dSAndroid Build Coastguard Worker        # haven't got the logic to check expiration headers
1726*cda5da8dSAndroid Build Coastguard Worker        # yet).
1727*cda5da8dSAndroid Build Coastguard Worker        self.ftpcache = ftpcache
1728*cda5da8dSAndroid Build Coastguard Worker        # Undocumented feature: you can use a different
1729*cda5da8dSAndroid Build Coastguard Worker        # ftp cache by assigning to the .ftpcache member;
1730*cda5da8dSAndroid Build Coastguard Worker        # in case you want logically independent URL openers
1731*cda5da8dSAndroid Build Coastguard Worker        # XXX This is not threadsafe.  Bah.
1732*cda5da8dSAndroid Build Coastguard Worker
1733*cda5da8dSAndroid Build Coastguard Worker    def __del__(self):
1734*cda5da8dSAndroid Build Coastguard Worker        self.close()
1735*cda5da8dSAndroid Build Coastguard Worker
1736*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1737*cda5da8dSAndroid Build Coastguard Worker        self.cleanup()
1738*cda5da8dSAndroid Build Coastguard Worker
1739*cda5da8dSAndroid Build Coastguard Worker    def cleanup(self):
1740*cda5da8dSAndroid Build Coastguard Worker        # This code sometimes runs when the rest of this module
1741*cda5da8dSAndroid Build Coastguard Worker        # has already been deleted, so it can't use any globals
1742*cda5da8dSAndroid Build Coastguard Worker        # or import anything.
1743*cda5da8dSAndroid Build Coastguard Worker        if self.__tempfiles:
1744*cda5da8dSAndroid Build Coastguard Worker            for file in self.__tempfiles:
1745*cda5da8dSAndroid Build Coastguard Worker                try:
1746*cda5da8dSAndroid Build Coastguard Worker                    self.__unlink(file)
1747*cda5da8dSAndroid Build Coastguard Worker                except OSError:
1748*cda5da8dSAndroid Build Coastguard Worker                    pass
1749*cda5da8dSAndroid Build Coastguard Worker            del self.__tempfiles[:]
1750*cda5da8dSAndroid Build Coastguard Worker        if self.tempcache:
1751*cda5da8dSAndroid Build Coastguard Worker            self.tempcache.clear()
1752*cda5da8dSAndroid Build Coastguard Worker
1753*cda5da8dSAndroid Build Coastguard Worker    def addheader(self, *args):
1754*cda5da8dSAndroid Build Coastguard Worker        """Add a header to be used by the HTTP interface only
1755*cda5da8dSAndroid Build Coastguard Worker        e.g. u.addheader('Accept', 'sound/basic')"""
1756*cda5da8dSAndroid Build Coastguard Worker        self.addheaders.append(args)
1757*cda5da8dSAndroid Build Coastguard Worker
1758*cda5da8dSAndroid Build Coastguard Worker    # External interface
1759*cda5da8dSAndroid Build Coastguard Worker    def open(self, fullurl, data=None):
1760*cda5da8dSAndroid Build Coastguard Worker        """Use URLopener().open(file) instead of open(file, 'r')."""
1761*cda5da8dSAndroid Build Coastguard Worker        fullurl = unwrap(_to_bytes(fullurl))
1762*cda5da8dSAndroid Build Coastguard Worker        fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]|")
1763*cda5da8dSAndroid Build Coastguard Worker        if self.tempcache and fullurl in self.tempcache:
1764*cda5da8dSAndroid Build Coastguard Worker            filename, headers = self.tempcache[fullurl]
1765*cda5da8dSAndroid Build Coastguard Worker            fp = open(filename, 'rb')
1766*cda5da8dSAndroid Build Coastguard Worker            return addinfourl(fp, headers, fullurl)
1767*cda5da8dSAndroid Build Coastguard Worker        urltype, url = _splittype(fullurl)
1768*cda5da8dSAndroid Build Coastguard Worker        if not urltype:
1769*cda5da8dSAndroid Build Coastguard Worker            urltype = 'file'
1770*cda5da8dSAndroid Build Coastguard Worker        if urltype in self.proxies:
1771*cda5da8dSAndroid Build Coastguard Worker            proxy = self.proxies[urltype]
1772*cda5da8dSAndroid Build Coastguard Worker            urltype, proxyhost = _splittype(proxy)
1773*cda5da8dSAndroid Build Coastguard Worker            host, selector = _splithost(proxyhost)
1774*cda5da8dSAndroid Build Coastguard Worker            url = (host, fullurl) # Signal special case to open_*()
1775*cda5da8dSAndroid Build Coastguard Worker        else:
1776*cda5da8dSAndroid Build Coastguard Worker            proxy = None
1777*cda5da8dSAndroid Build Coastguard Worker        name = 'open_' + urltype
1778*cda5da8dSAndroid Build Coastguard Worker        self.type = urltype
1779*cda5da8dSAndroid Build Coastguard Worker        name = name.replace('-', '_')
1780*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(self, name) or name == 'open_local_file':
1781*cda5da8dSAndroid Build Coastguard Worker            if proxy:
1782*cda5da8dSAndroid Build Coastguard Worker                return self.open_unknown_proxy(proxy, fullurl, data)
1783*cda5da8dSAndroid Build Coastguard Worker            else:
1784*cda5da8dSAndroid Build Coastguard Worker                return self.open_unknown(fullurl, data)
1785*cda5da8dSAndroid Build Coastguard Worker        try:
1786*cda5da8dSAndroid Build Coastguard Worker            if data is None:
1787*cda5da8dSAndroid Build Coastguard Worker                return getattr(self, name)(url)
1788*cda5da8dSAndroid Build Coastguard Worker            else:
1789*cda5da8dSAndroid Build Coastguard Worker                return getattr(self, name)(url, data)
1790*cda5da8dSAndroid Build Coastguard Worker        except (HTTPError, URLError):
1791*cda5da8dSAndroid Build Coastguard Worker            raise
1792*cda5da8dSAndroid Build Coastguard Worker        except OSError as msg:
1793*cda5da8dSAndroid Build Coastguard Worker            raise OSError('socket error', msg) from msg
1794*cda5da8dSAndroid Build Coastguard Worker
1795*cda5da8dSAndroid Build Coastguard Worker    def open_unknown(self, fullurl, data=None):
1796*cda5da8dSAndroid Build Coastguard Worker        """Overridable interface to open unknown URL type."""
1797*cda5da8dSAndroid Build Coastguard Worker        type, url = _splittype(fullurl)
1798*cda5da8dSAndroid Build Coastguard Worker        raise OSError('url error', 'unknown url type', type)
1799*cda5da8dSAndroid Build Coastguard Worker
1800*cda5da8dSAndroid Build Coastguard Worker    def open_unknown_proxy(self, proxy, fullurl, data=None):
1801*cda5da8dSAndroid Build Coastguard Worker        """Overridable interface to open unknown URL type."""
1802*cda5da8dSAndroid Build Coastguard Worker        type, url = _splittype(fullurl)
1803*cda5da8dSAndroid Build Coastguard Worker        raise OSError('url error', 'invalid proxy for %s' % type, proxy)
1804*cda5da8dSAndroid Build Coastguard Worker
1805*cda5da8dSAndroid Build Coastguard Worker    # External interface
1806*cda5da8dSAndroid Build Coastguard Worker    def retrieve(self, url, filename=None, reporthook=None, data=None):
1807*cda5da8dSAndroid Build Coastguard Worker        """retrieve(url) returns (filename, headers) for a local object
1808*cda5da8dSAndroid Build Coastguard Worker        or (tempfilename, headers) for a remote object."""
1809*cda5da8dSAndroid Build Coastguard Worker        url = unwrap(_to_bytes(url))
1810*cda5da8dSAndroid Build Coastguard Worker        if self.tempcache and url in self.tempcache:
1811*cda5da8dSAndroid Build Coastguard Worker            return self.tempcache[url]
1812*cda5da8dSAndroid Build Coastguard Worker        type, url1 = _splittype(url)
1813*cda5da8dSAndroid Build Coastguard Worker        if filename is None and (not type or type == 'file'):
1814*cda5da8dSAndroid Build Coastguard Worker            try:
1815*cda5da8dSAndroid Build Coastguard Worker                fp = self.open_local_file(url1)
1816*cda5da8dSAndroid Build Coastguard Worker                hdrs = fp.info()
1817*cda5da8dSAndroid Build Coastguard Worker                fp.close()
1818*cda5da8dSAndroid Build Coastguard Worker                return url2pathname(_splithost(url1)[1]), hdrs
1819*cda5da8dSAndroid Build Coastguard Worker            except OSError:
1820*cda5da8dSAndroid Build Coastguard Worker                pass
1821*cda5da8dSAndroid Build Coastguard Worker        fp = self.open(url, data)
1822*cda5da8dSAndroid Build Coastguard Worker        try:
1823*cda5da8dSAndroid Build Coastguard Worker            headers = fp.info()
1824*cda5da8dSAndroid Build Coastguard Worker            if filename:
1825*cda5da8dSAndroid Build Coastguard Worker                tfp = open(filename, 'wb')
1826*cda5da8dSAndroid Build Coastguard Worker            else:
1827*cda5da8dSAndroid Build Coastguard Worker                garbage, path = _splittype(url)
1828*cda5da8dSAndroid Build Coastguard Worker                garbage, path = _splithost(path or "")
1829*cda5da8dSAndroid Build Coastguard Worker                path, garbage = _splitquery(path or "")
1830*cda5da8dSAndroid Build Coastguard Worker                path, garbage = _splitattr(path or "")
1831*cda5da8dSAndroid Build Coastguard Worker                suffix = os.path.splitext(path)[1]
1832*cda5da8dSAndroid Build Coastguard Worker                (fd, filename) = tempfile.mkstemp(suffix)
1833*cda5da8dSAndroid Build Coastguard Worker                self.__tempfiles.append(filename)
1834*cda5da8dSAndroid Build Coastguard Worker                tfp = os.fdopen(fd, 'wb')
1835*cda5da8dSAndroid Build Coastguard Worker            try:
1836*cda5da8dSAndroid Build Coastguard Worker                result = filename, headers
1837*cda5da8dSAndroid Build Coastguard Worker                if self.tempcache is not None:
1838*cda5da8dSAndroid Build Coastguard Worker                    self.tempcache[url] = result
1839*cda5da8dSAndroid Build Coastguard Worker                bs = 1024*8
1840*cda5da8dSAndroid Build Coastguard Worker                size = -1
1841*cda5da8dSAndroid Build Coastguard Worker                read = 0
1842*cda5da8dSAndroid Build Coastguard Worker                blocknum = 0
1843*cda5da8dSAndroid Build Coastguard Worker                if "content-length" in headers:
1844*cda5da8dSAndroid Build Coastguard Worker                    size = int(headers["Content-Length"])
1845*cda5da8dSAndroid Build Coastguard Worker                if reporthook:
1846*cda5da8dSAndroid Build Coastguard Worker                    reporthook(blocknum, bs, size)
1847*cda5da8dSAndroid Build Coastguard Worker                while 1:
1848*cda5da8dSAndroid Build Coastguard Worker                    block = fp.read(bs)
1849*cda5da8dSAndroid Build Coastguard Worker                    if not block:
1850*cda5da8dSAndroid Build Coastguard Worker                        break
1851*cda5da8dSAndroid Build Coastguard Worker                    read += len(block)
1852*cda5da8dSAndroid Build Coastguard Worker                    tfp.write(block)
1853*cda5da8dSAndroid Build Coastguard Worker                    blocknum += 1
1854*cda5da8dSAndroid Build Coastguard Worker                    if reporthook:
1855*cda5da8dSAndroid Build Coastguard Worker                        reporthook(blocknum, bs, size)
1856*cda5da8dSAndroid Build Coastguard Worker            finally:
1857*cda5da8dSAndroid Build Coastguard Worker                tfp.close()
1858*cda5da8dSAndroid Build Coastguard Worker        finally:
1859*cda5da8dSAndroid Build Coastguard Worker            fp.close()
1860*cda5da8dSAndroid Build Coastguard Worker
1861*cda5da8dSAndroid Build Coastguard Worker        # raise exception if actual size does not match content-length header
1862*cda5da8dSAndroid Build Coastguard Worker        if size >= 0 and read < size:
1863*cda5da8dSAndroid Build Coastguard Worker            raise ContentTooShortError(
1864*cda5da8dSAndroid Build Coastguard Worker                "retrieval incomplete: got only %i out of %i bytes"
1865*cda5da8dSAndroid Build Coastguard Worker                % (read, size), result)
1866*cda5da8dSAndroid Build Coastguard Worker
1867*cda5da8dSAndroid Build Coastguard Worker        return result
1868*cda5da8dSAndroid Build Coastguard Worker
1869*cda5da8dSAndroid Build Coastguard Worker    # Each method named open_<type> knows how to open that type of URL
1870*cda5da8dSAndroid Build Coastguard Worker
1871*cda5da8dSAndroid Build Coastguard Worker    def _open_generic_http(self, connection_factory, url, data):
1872*cda5da8dSAndroid Build Coastguard Worker        """Make an HTTP connection using connection_class.
1873*cda5da8dSAndroid Build Coastguard Worker
1874*cda5da8dSAndroid Build Coastguard Worker        This is an internal method that should be called from
1875*cda5da8dSAndroid Build Coastguard Worker        open_http() or open_https().
1876*cda5da8dSAndroid Build Coastguard Worker
1877*cda5da8dSAndroid Build Coastguard Worker        Arguments:
1878*cda5da8dSAndroid Build Coastguard Worker        - connection_factory should take a host name and return an
1879*cda5da8dSAndroid Build Coastguard Worker          HTTPConnection instance.
1880*cda5da8dSAndroid Build Coastguard Worker        - url is the url to retrieval or a host, relative-path pair.
1881*cda5da8dSAndroid Build Coastguard Worker        - data is payload for a POST request or None.
1882*cda5da8dSAndroid Build Coastguard Worker        """
1883*cda5da8dSAndroid Build Coastguard Worker
1884*cda5da8dSAndroid Build Coastguard Worker        user_passwd = None
1885*cda5da8dSAndroid Build Coastguard Worker        proxy_passwd= None
1886*cda5da8dSAndroid Build Coastguard Worker        if isinstance(url, str):
1887*cda5da8dSAndroid Build Coastguard Worker            host, selector = _splithost(url)
1888*cda5da8dSAndroid Build Coastguard Worker            if host:
1889*cda5da8dSAndroid Build Coastguard Worker                user_passwd, host = _splituser(host)
1890*cda5da8dSAndroid Build Coastguard Worker                host = unquote(host)
1891*cda5da8dSAndroid Build Coastguard Worker            realhost = host
1892*cda5da8dSAndroid Build Coastguard Worker        else:
1893*cda5da8dSAndroid Build Coastguard Worker            host, selector = url
1894*cda5da8dSAndroid Build Coastguard Worker            # check whether the proxy contains authorization information
1895*cda5da8dSAndroid Build Coastguard Worker            proxy_passwd, host = _splituser(host)
1896*cda5da8dSAndroid Build Coastguard Worker            # now we proceed with the url we want to obtain
1897*cda5da8dSAndroid Build Coastguard Worker            urltype, rest = _splittype(selector)
1898*cda5da8dSAndroid Build Coastguard Worker            url = rest
1899*cda5da8dSAndroid Build Coastguard Worker            user_passwd = None
1900*cda5da8dSAndroid Build Coastguard Worker            if urltype.lower() != 'http':
1901*cda5da8dSAndroid Build Coastguard Worker                realhost = None
1902*cda5da8dSAndroid Build Coastguard Worker            else:
1903*cda5da8dSAndroid Build Coastguard Worker                realhost, rest = _splithost(rest)
1904*cda5da8dSAndroid Build Coastguard Worker                if realhost:
1905*cda5da8dSAndroid Build Coastguard Worker                    user_passwd, realhost = _splituser(realhost)
1906*cda5da8dSAndroid Build Coastguard Worker                if user_passwd:
1907*cda5da8dSAndroid Build Coastguard Worker                    selector = "%s://%s%s" % (urltype, realhost, rest)
1908*cda5da8dSAndroid Build Coastguard Worker                if proxy_bypass(realhost):
1909*cda5da8dSAndroid Build Coastguard Worker                    host = realhost
1910*cda5da8dSAndroid Build Coastguard Worker
1911*cda5da8dSAndroid Build Coastguard Worker        if not host: raise OSError('http error', 'no host given')
1912*cda5da8dSAndroid Build Coastguard Worker
1913*cda5da8dSAndroid Build Coastguard Worker        if proxy_passwd:
1914*cda5da8dSAndroid Build Coastguard Worker            proxy_passwd = unquote(proxy_passwd)
1915*cda5da8dSAndroid Build Coastguard Worker            proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii')
1916*cda5da8dSAndroid Build Coastguard Worker        else:
1917*cda5da8dSAndroid Build Coastguard Worker            proxy_auth = None
1918*cda5da8dSAndroid Build Coastguard Worker
1919*cda5da8dSAndroid Build Coastguard Worker        if user_passwd:
1920*cda5da8dSAndroid Build Coastguard Worker            user_passwd = unquote(user_passwd)
1921*cda5da8dSAndroid Build Coastguard Worker            auth = base64.b64encode(user_passwd.encode()).decode('ascii')
1922*cda5da8dSAndroid Build Coastguard Worker        else:
1923*cda5da8dSAndroid Build Coastguard Worker            auth = None
1924*cda5da8dSAndroid Build Coastguard Worker        http_conn = connection_factory(host)
1925*cda5da8dSAndroid Build Coastguard Worker        headers = {}
1926*cda5da8dSAndroid Build Coastguard Worker        if proxy_auth:
1927*cda5da8dSAndroid Build Coastguard Worker            headers["Proxy-Authorization"] = "Basic %s" % proxy_auth
1928*cda5da8dSAndroid Build Coastguard Worker        if auth:
1929*cda5da8dSAndroid Build Coastguard Worker            headers["Authorization"] =  "Basic %s" % auth
1930*cda5da8dSAndroid Build Coastguard Worker        if realhost:
1931*cda5da8dSAndroid Build Coastguard Worker            headers["Host"] = realhost
1932*cda5da8dSAndroid Build Coastguard Worker
1933*cda5da8dSAndroid Build Coastguard Worker        # Add Connection:close as we don't support persistent connections yet.
1934*cda5da8dSAndroid Build Coastguard Worker        # This helps in closing the socket and avoiding ResourceWarning
1935*cda5da8dSAndroid Build Coastguard Worker
1936*cda5da8dSAndroid Build Coastguard Worker        headers["Connection"] = "close"
1937*cda5da8dSAndroid Build Coastguard Worker
1938*cda5da8dSAndroid Build Coastguard Worker        for header, value in self.addheaders:
1939*cda5da8dSAndroid Build Coastguard Worker            headers[header] = value
1940*cda5da8dSAndroid Build Coastguard Worker
1941*cda5da8dSAndroid Build Coastguard Worker        if data is not None:
1942*cda5da8dSAndroid Build Coastguard Worker            headers["Content-Type"] = "application/x-www-form-urlencoded"
1943*cda5da8dSAndroid Build Coastguard Worker            http_conn.request("POST", selector, data, headers)
1944*cda5da8dSAndroid Build Coastguard Worker        else:
1945*cda5da8dSAndroid Build Coastguard Worker            http_conn.request("GET", selector, headers=headers)
1946*cda5da8dSAndroid Build Coastguard Worker
1947*cda5da8dSAndroid Build Coastguard Worker        try:
1948*cda5da8dSAndroid Build Coastguard Worker            response = http_conn.getresponse()
1949*cda5da8dSAndroid Build Coastguard Worker        except http.client.BadStatusLine:
1950*cda5da8dSAndroid Build Coastguard Worker            # something went wrong with the HTTP status line
1951*cda5da8dSAndroid Build Coastguard Worker            raise URLError("http protocol error: bad status line")
1952*cda5da8dSAndroid Build Coastguard Worker
1953*cda5da8dSAndroid Build Coastguard Worker        # According to RFC 2616, "2xx" code indicates that the client's
1954*cda5da8dSAndroid Build Coastguard Worker        # request was successfully received, understood, and accepted.
1955*cda5da8dSAndroid Build Coastguard Worker        if 200 <= response.status < 300:
1956*cda5da8dSAndroid Build Coastguard Worker            return addinfourl(response, response.msg, "http:" + url,
1957*cda5da8dSAndroid Build Coastguard Worker                              response.status)
1958*cda5da8dSAndroid Build Coastguard Worker        else:
1959*cda5da8dSAndroid Build Coastguard Worker            return self.http_error(
1960*cda5da8dSAndroid Build Coastguard Worker                url, response.fp,
1961*cda5da8dSAndroid Build Coastguard Worker                response.status, response.reason, response.msg, data)
1962*cda5da8dSAndroid Build Coastguard Worker
1963*cda5da8dSAndroid Build Coastguard Worker    def open_http(self, url, data=None):
1964*cda5da8dSAndroid Build Coastguard Worker        """Use HTTP protocol."""
1965*cda5da8dSAndroid Build Coastguard Worker        return self._open_generic_http(http.client.HTTPConnection, url, data)
1966*cda5da8dSAndroid Build Coastguard Worker
1967*cda5da8dSAndroid Build Coastguard Worker    def http_error(self, url, fp, errcode, errmsg, headers, data=None):
1968*cda5da8dSAndroid Build Coastguard Worker        """Handle http errors.
1969*cda5da8dSAndroid Build Coastguard Worker
1970*cda5da8dSAndroid Build Coastguard Worker        Derived class can override this, or provide specific handlers
1971*cda5da8dSAndroid Build Coastguard Worker        named http_error_DDD where DDD is the 3-digit error code."""
1972*cda5da8dSAndroid Build Coastguard Worker        # First check if there's a specific handler for this error
1973*cda5da8dSAndroid Build Coastguard Worker        name = 'http_error_%d' % errcode
1974*cda5da8dSAndroid Build Coastguard Worker        if hasattr(self, name):
1975*cda5da8dSAndroid Build Coastguard Worker            method = getattr(self, name)
1976*cda5da8dSAndroid Build Coastguard Worker            if data is None:
1977*cda5da8dSAndroid Build Coastguard Worker                result = method(url, fp, errcode, errmsg, headers)
1978*cda5da8dSAndroid Build Coastguard Worker            else:
1979*cda5da8dSAndroid Build Coastguard Worker                result = method(url, fp, errcode, errmsg, headers, data)
1980*cda5da8dSAndroid Build Coastguard Worker            if result: return result
1981*cda5da8dSAndroid Build Coastguard Worker        return self.http_error_default(url, fp, errcode, errmsg, headers)
1982*cda5da8dSAndroid Build Coastguard Worker
1983*cda5da8dSAndroid Build Coastguard Worker    def http_error_default(self, url, fp, errcode, errmsg, headers):
1984*cda5da8dSAndroid Build Coastguard Worker        """Default error handler: close the connection and raise OSError."""
1985*cda5da8dSAndroid Build Coastguard Worker        fp.close()
1986*cda5da8dSAndroid Build Coastguard Worker        raise HTTPError(url, errcode, errmsg, headers, None)
1987*cda5da8dSAndroid Build Coastguard Worker
1988*cda5da8dSAndroid Build Coastguard Worker    if _have_ssl:
1989*cda5da8dSAndroid Build Coastguard Worker        def _https_connection(self, host):
1990*cda5da8dSAndroid Build Coastguard Worker            return http.client.HTTPSConnection(host,
1991*cda5da8dSAndroid Build Coastguard Worker                                           key_file=self.key_file,
1992*cda5da8dSAndroid Build Coastguard Worker                                           cert_file=self.cert_file)
1993*cda5da8dSAndroid Build Coastguard Worker
1994*cda5da8dSAndroid Build Coastguard Worker        def open_https(self, url, data=None):
1995*cda5da8dSAndroid Build Coastguard Worker            """Use HTTPS protocol."""
1996*cda5da8dSAndroid Build Coastguard Worker            return self._open_generic_http(self._https_connection, url, data)
1997*cda5da8dSAndroid Build Coastguard Worker
1998*cda5da8dSAndroid Build Coastguard Worker    def open_file(self, url):
1999*cda5da8dSAndroid Build Coastguard Worker        """Use local file or FTP depending on form of URL."""
2000*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(url, str):
2001*cda5da8dSAndroid Build Coastguard Worker            raise URLError('file error: proxy support for file protocol currently not implemented')
2002*cda5da8dSAndroid Build Coastguard Worker        if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/':
2003*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("file:// scheme is supported only on localhost")
2004*cda5da8dSAndroid Build Coastguard Worker        else:
2005*cda5da8dSAndroid Build Coastguard Worker            return self.open_local_file(url)
2006*cda5da8dSAndroid Build Coastguard Worker
2007*cda5da8dSAndroid Build Coastguard Worker    def open_local_file(self, url):
2008*cda5da8dSAndroid Build Coastguard Worker        """Use local file."""
2009*cda5da8dSAndroid Build Coastguard Worker        import email.utils
2010*cda5da8dSAndroid Build Coastguard Worker        import mimetypes
2011*cda5da8dSAndroid Build Coastguard Worker        host, file = _splithost(url)
2012*cda5da8dSAndroid Build Coastguard Worker        localname = url2pathname(file)
2013*cda5da8dSAndroid Build Coastguard Worker        try:
2014*cda5da8dSAndroid Build Coastguard Worker            stats = os.stat(localname)
2015*cda5da8dSAndroid Build Coastguard Worker        except OSError as e:
2016*cda5da8dSAndroid Build Coastguard Worker            raise URLError(e.strerror, e.filename)
2017*cda5da8dSAndroid Build Coastguard Worker        size = stats.st_size
2018*cda5da8dSAndroid Build Coastguard Worker        modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
2019*cda5da8dSAndroid Build Coastguard Worker        mtype = mimetypes.guess_type(url)[0]
2020*cda5da8dSAndroid Build Coastguard Worker        headers = email.message_from_string(
2021*cda5da8dSAndroid Build Coastguard Worker            'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' %
2022*cda5da8dSAndroid Build Coastguard Worker            (mtype or 'text/plain', size, modified))
2023*cda5da8dSAndroid Build Coastguard Worker        if not host:
2024*cda5da8dSAndroid Build Coastguard Worker            urlfile = file
2025*cda5da8dSAndroid Build Coastguard Worker            if file[:1] == '/':
2026*cda5da8dSAndroid Build Coastguard Worker                urlfile = 'file://' + file
2027*cda5da8dSAndroid Build Coastguard Worker            return addinfourl(open(localname, 'rb'), headers, urlfile)
2028*cda5da8dSAndroid Build Coastguard Worker        host, port = _splitport(host)
2029*cda5da8dSAndroid Build Coastguard Worker        if (not port
2030*cda5da8dSAndroid Build Coastguard Worker           and socket.gethostbyname(host) in ((localhost(),) + thishost())):
2031*cda5da8dSAndroid Build Coastguard Worker            urlfile = file
2032*cda5da8dSAndroid Build Coastguard Worker            if file[:1] == '/':
2033*cda5da8dSAndroid Build Coastguard Worker                urlfile = 'file://' + file
2034*cda5da8dSAndroid Build Coastguard Worker            elif file[:2] == './':
2035*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("local file url may start with / or file:. Unknown url of type: %s" % url)
2036*cda5da8dSAndroid Build Coastguard Worker            return addinfourl(open(localname, 'rb'), headers, urlfile)
2037*cda5da8dSAndroid Build Coastguard Worker        raise URLError('local file error: not on local host')
2038*cda5da8dSAndroid Build Coastguard Worker
2039*cda5da8dSAndroid Build Coastguard Worker    def open_ftp(self, url):
2040*cda5da8dSAndroid Build Coastguard Worker        """Use FTP protocol."""
2041*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(url, str):
2042*cda5da8dSAndroid Build Coastguard Worker            raise URLError('ftp error: proxy support for ftp protocol currently not implemented')
2043*cda5da8dSAndroid Build Coastguard Worker        import mimetypes
2044*cda5da8dSAndroid Build Coastguard Worker        host, path = _splithost(url)
2045*cda5da8dSAndroid Build Coastguard Worker        if not host: raise URLError('ftp error: no host given')
2046*cda5da8dSAndroid Build Coastguard Worker        host, port = _splitport(host)
2047*cda5da8dSAndroid Build Coastguard Worker        user, host = _splituser(host)
2048*cda5da8dSAndroid Build Coastguard Worker        if user: user, passwd = _splitpasswd(user)
2049*cda5da8dSAndroid Build Coastguard Worker        else: passwd = None
2050*cda5da8dSAndroid Build Coastguard Worker        host = unquote(host)
2051*cda5da8dSAndroid Build Coastguard Worker        user = unquote(user or '')
2052*cda5da8dSAndroid Build Coastguard Worker        passwd = unquote(passwd or '')
2053*cda5da8dSAndroid Build Coastguard Worker        host = socket.gethostbyname(host)
2054*cda5da8dSAndroid Build Coastguard Worker        if not port:
2055*cda5da8dSAndroid Build Coastguard Worker            import ftplib
2056*cda5da8dSAndroid Build Coastguard Worker            port = ftplib.FTP_PORT
2057*cda5da8dSAndroid Build Coastguard Worker        else:
2058*cda5da8dSAndroid Build Coastguard Worker            port = int(port)
2059*cda5da8dSAndroid Build Coastguard Worker        path, attrs = _splitattr(path)
2060*cda5da8dSAndroid Build Coastguard Worker        path = unquote(path)
2061*cda5da8dSAndroid Build Coastguard Worker        dirs = path.split('/')
2062*cda5da8dSAndroid Build Coastguard Worker        dirs, file = dirs[:-1], dirs[-1]
2063*cda5da8dSAndroid Build Coastguard Worker        if dirs and not dirs[0]: dirs = dirs[1:]
2064*cda5da8dSAndroid Build Coastguard Worker        if dirs and not dirs[0]: dirs[0] = '/'
2065*cda5da8dSAndroid Build Coastguard Worker        key = user, host, port, '/'.join(dirs)
2066*cda5da8dSAndroid Build Coastguard Worker        # XXX thread unsafe!
2067*cda5da8dSAndroid Build Coastguard Worker        if len(self.ftpcache) > MAXFTPCACHE:
2068*cda5da8dSAndroid Build Coastguard Worker            # Prune the cache, rather arbitrarily
2069*cda5da8dSAndroid Build Coastguard Worker            for k in list(self.ftpcache):
2070*cda5da8dSAndroid Build Coastguard Worker                if k != key:
2071*cda5da8dSAndroid Build Coastguard Worker                    v = self.ftpcache[k]
2072*cda5da8dSAndroid Build Coastguard Worker                    del self.ftpcache[k]
2073*cda5da8dSAndroid Build Coastguard Worker                    v.close()
2074*cda5da8dSAndroid Build Coastguard Worker        try:
2075*cda5da8dSAndroid Build Coastguard Worker            if key not in self.ftpcache:
2076*cda5da8dSAndroid Build Coastguard Worker                self.ftpcache[key] = \
2077*cda5da8dSAndroid Build Coastguard Worker                    ftpwrapper(user, passwd, host, port, dirs)
2078*cda5da8dSAndroid Build Coastguard Worker            if not file: type = 'D'
2079*cda5da8dSAndroid Build Coastguard Worker            else: type = 'I'
2080*cda5da8dSAndroid Build Coastguard Worker            for attr in attrs:
2081*cda5da8dSAndroid Build Coastguard Worker                attr, value = _splitvalue(attr)
2082*cda5da8dSAndroid Build Coastguard Worker                if attr.lower() == 'type' and \
2083*cda5da8dSAndroid Build Coastguard Worker                   value in ('a', 'A', 'i', 'I', 'd', 'D'):
2084*cda5da8dSAndroid Build Coastguard Worker                    type = value.upper()
2085*cda5da8dSAndroid Build Coastguard Worker            (fp, retrlen) = self.ftpcache[key].retrfile(file, type)
2086*cda5da8dSAndroid Build Coastguard Worker            mtype = mimetypes.guess_type("ftp:" + url)[0]
2087*cda5da8dSAndroid Build Coastguard Worker            headers = ""
2088*cda5da8dSAndroid Build Coastguard Worker            if mtype:
2089*cda5da8dSAndroid Build Coastguard Worker                headers += "Content-Type: %s\n" % mtype
2090*cda5da8dSAndroid Build Coastguard Worker            if retrlen is not None and retrlen >= 0:
2091*cda5da8dSAndroid Build Coastguard Worker                headers += "Content-Length: %d\n" % retrlen
2092*cda5da8dSAndroid Build Coastguard Worker            headers = email.message_from_string(headers)
2093*cda5da8dSAndroid Build Coastguard Worker            return addinfourl(fp, headers, "ftp:" + url)
2094*cda5da8dSAndroid Build Coastguard Worker        except ftperrors() as exp:
2095*cda5da8dSAndroid Build Coastguard Worker            raise URLError(f'ftp error: {exp}') from exp
2096*cda5da8dSAndroid Build Coastguard Worker
2097*cda5da8dSAndroid Build Coastguard Worker    def open_data(self, url, data=None):
2098*cda5da8dSAndroid Build Coastguard Worker        """Use "data" URL."""
2099*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(url, str):
2100*cda5da8dSAndroid Build Coastguard Worker            raise URLError('data error: proxy support for data protocol currently not implemented')
2101*cda5da8dSAndroid Build Coastguard Worker        # ignore POSTed data
2102*cda5da8dSAndroid Build Coastguard Worker        #
2103*cda5da8dSAndroid Build Coastguard Worker        # syntax of data URLs:
2104*cda5da8dSAndroid Build Coastguard Worker        # dataurl   := "data:" [ mediatype ] [ ";base64" ] "," data
2105*cda5da8dSAndroid Build Coastguard Worker        # mediatype := [ type "/" subtype ] *( ";" parameter )
2106*cda5da8dSAndroid Build Coastguard Worker        # data      := *urlchar
2107*cda5da8dSAndroid Build Coastguard Worker        # parameter := attribute "=" value
2108*cda5da8dSAndroid Build Coastguard Worker        try:
2109*cda5da8dSAndroid Build Coastguard Worker            [type, data] = url.split(',', 1)
2110*cda5da8dSAndroid Build Coastguard Worker        except ValueError:
2111*cda5da8dSAndroid Build Coastguard Worker            raise OSError('data error', 'bad data URL')
2112*cda5da8dSAndroid Build Coastguard Worker        if not type:
2113*cda5da8dSAndroid Build Coastguard Worker            type = 'text/plain;charset=US-ASCII'
2114*cda5da8dSAndroid Build Coastguard Worker        semi = type.rfind(';')
2115*cda5da8dSAndroid Build Coastguard Worker        if semi >= 0 and '=' not in type[semi:]:
2116*cda5da8dSAndroid Build Coastguard Worker            encoding = type[semi+1:]
2117*cda5da8dSAndroid Build Coastguard Worker            type = type[:semi]
2118*cda5da8dSAndroid Build Coastguard Worker        else:
2119*cda5da8dSAndroid Build Coastguard Worker            encoding = ''
2120*cda5da8dSAndroid Build Coastguard Worker        msg = []
2121*cda5da8dSAndroid Build Coastguard Worker        msg.append('Date: %s'%time.strftime('%a, %d %b %Y %H:%M:%S GMT',
2122*cda5da8dSAndroid Build Coastguard Worker                                            time.gmtime(time.time())))
2123*cda5da8dSAndroid Build Coastguard Worker        msg.append('Content-type: %s' % type)
2124*cda5da8dSAndroid Build Coastguard Worker        if encoding == 'base64':
2125*cda5da8dSAndroid Build Coastguard Worker            # XXX is this encoding/decoding ok?
2126*cda5da8dSAndroid Build Coastguard Worker            data = base64.decodebytes(data.encode('ascii')).decode('latin-1')
2127*cda5da8dSAndroid Build Coastguard Worker        else:
2128*cda5da8dSAndroid Build Coastguard Worker            data = unquote(data)
2129*cda5da8dSAndroid Build Coastguard Worker        msg.append('Content-Length: %d' % len(data))
2130*cda5da8dSAndroid Build Coastguard Worker        msg.append('')
2131*cda5da8dSAndroid Build Coastguard Worker        msg.append(data)
2132*cda5da8dSAndroid Build Coastguard Worker        msg = '\n'.join(msg)
2133*cda5da8dSAndroid Build Coastguard Worker        headers = email.message_from_string(msg)
2134*cda5da8dSAndroid Build Coastguard Worker        f = io.StringIO(msg)
2135*cda5da8dSAndroid Build Coastguard Worker        #f.fileno = None     # needed for addinfourl
2136*cda5da8dSAndroid Build Coastguard Worker        return addinfourl(f, headers, url)
2137*cda5da8dSAndroid Build Coastguard Worker
2138*cda5da8dSAndroid Build Coastguard Worker
2139*cda5da8dSAndroid Build Coastguard Workerclass FancyURLopener(URLopener):
2140*cda5da8dSAndroid Build Coastguard Worker    """Derived class with handlers for errors we can handle (perhaps)."""
2141*cda5da8dSAndroid Build Coastguard Worker
2142*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, *args, **kwargs):
2143*cda5da8dSAndroid Build Coastguard Worker        URLopener.__init__(self, *args, **kwargs)
2144*cda5da8dSAndroid Build Coastguard Worker        self.auth_cache = {}
2145*cda5da8dSAndroid Build Coastguard Worker        self.tries = 0
2146*cda5da8dSAndroid Build Coastguard Worker        self.maxtries = 10
2147*cda5da8dSAndroid Build Coastguard Worker
2148*cda5da8dSAndroid Build Coastguard Worker    def http_error_default(self, url, fp, errcode, errmsg, headers):
2149*cda5da8dSAndroid Build Coastguard Worker        """Default error handling -- don't raise an exception."""
2150*cda5da8dSAndroid Build Coastguard Worker        return addinfourl(fp, headers, "http:" + url, errcode)
2151*cda5da8dSAndroid Build Coastguard Worker
2152*cda5da8dSAndroid Build Coastguard Worker    def http_error_302(self, url, fp, errcode, errmsg, headers, data=None):
2153*cda5da8dSAndroid Build Coastguard Worker        """Error 302 -- relocated (temporarily)."""
2154*cda5da8dSAndroid Build Coastguard Worker        self.tries += 1
2155*cda5da8dSAndroid Build Coastguard Worker        try:
2156*cda5da8dSAndroid Build Coastguard Worker            if self.maxtries and self.tries >= self.maxtries:
2157*cda5da8dSAndroid Build Coastguard Worker                if hasattr(self, "http_error_500"):
2158*cda5da8dSAndroid Build Coastguard Worker                    meth = self.http_error_500
2159*cda5da8dSAndroid Build Coastguard Worker                else:
2160*cda5da8dSAndroid Build Coastguard Worker                    meth = self.http_error_default
2161*cda5da8dSAndroid Build Coastguard Worker                return meth(url, fp, 500,
2162*cda5da8dSAndroid Build Coastguard Worker                            "Internal Server Error: Redirect Recursion",
2163*cda5da8dSAndroid Build Coastguard Worker                            headers)
2164*cda5da8dSAndroid Build Coastguard Worker            result = self.redirect_internal(url, fp, errcode, errmsg,
2165*cda5da8dSAndroid Build Coastguard Worker                                            headers, data)
2166*cda5da8dSAndroid Build Coastguard Worker            return result
2167*cda5da8dSAndroid Build Coastguard Worker        finally:
2168*cda5da8dSAndroid Build Coastguard Worker            self.tries = 0
2169*cda5da8dSAndroid Build Coastguard Worker
2170*cda5da8dSAndroid Build Coastguard Worker    def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
2171*cda5da8dSAndroid Build Coastguard Worker        if 'location' in headers:
2172*cda5da8dSAndroid Build Coastguard Worker            newurl = headers['location']
2173*cda5da8dSAndroid Build Coastguard Worker        elif 'uri' in headers:
2174*cda5da8dSAndroid Build Coastguard Worker            newurl = headers['uri']
2175*cda5da8dSAndroid Build Coastguard Worker        else:
2176*cda5da8dSAndroid Build Coastguard Worker            return
2177*cda5da8dSAndroid Build Coastguard Worker        fp.close()
2178*cda5da8dSAndroid Build Coastguard Worker
2179*cda5da8dSAndroid Build Coastguard Worker        # In case the server sent a relative URL, join with original:
2180*cda5da8dSAndroid Build Coastguard Worker        newurl = urljoin(self.type + ":" + url, newurl)
2181*cda5da8dSAndroid Build Coastguard Worker
2182*cda5da8dSAndroid Build Coastguard Worker        urlparts = urlparse(newurl)
2183*cda5da8dSAndroid Build Coastguard Worker
2184*cda5da8dSAndroid Build Coastguard Worker        # For security reasons, we don't allow redirection to anything other
2185*cda5da8dSAndroid Build Coastguard Worker        # than http, https and ftp.
2186*cda5da8dSAndroid Build Coastguard Worker
2187*cda5da8dSAndroid Build Coastguard Worker        # We are using newer HTTPError with older redirect_internal method
2188*cda5da8dSAndroid Build Coastguard Worker        # This older method will get deprecated in 3.3
2189*cda5da8dSAndroid Build Coastguard Worker
2190*cda5da8dSAndroid Build Coastguard Worker        if urlparts.scheme not in ('http', 'https', 'ftp', ''):
2191*cda5da8dSAndroid Build Coastguard Worker            raise HTTPError(newurl, errcode,
2192*cda5da8dSAndroid Build Coastguard Worker                            errmsg +
2193*cda5da8dSAndroid Build Coastguard Worker                            " Redirection to url '%s' is not allowed." % newurl,
2194*cda5da8dSAndroid Build Coastguard Worker                            headers, fp)
2195*cda5da8dSAndroid Build Coastguard Worker
2196*cda5da8dSAndroid Build Coastguard Worker        return self.open(newurl)
2197*cda5da8dSAndroid Build Coastguard Worker
2198*cda5da8dSAndroid Build Coastguard Worker    def http_error_301(self, url, fp, errcode, errmsg, headers, data=None):
2199*cda5da8dSAndroid Build Coastguard Worker        """Error 301 -- also relocated (permanently)."""
2200*cda5da8dSAndroid Build Coastguard Worker        return self.http_error_302(url, fp, errcode, errmsg, headers, data)
2201*cda5da8dSAndroid Build Coastguard Worker
2202*cda5da8dSAndroid Build Coastguard Worker    def http_error_303(self, url, fp, errcode, errmsg, headers, data=None):
2203*cda5da8dSAndroid Build Coastguard Worker        """Error 303 -- also relocated (essentially identical to 302)."""
2204*cda5da8dSAndroid Build Coastguard Worker        return self.http_error_302(url, fp, errcode, errmsg, headers, data)
2205*cda5da8dSAndroid Build Coastguard Worker
2206*cda5da8dSAndroid Build Coastguard Worker    def http_error_307(self, url, fp, errcode, errmsg, headers, data=None):
2207*cda5da8dSAndroid Build Coastguard Worker        """Error 307 -- relocated, but turn POST into error."""
2208*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2209*cda5da8dSAndroid Build Coastguard Worker            return self.http_error_302(url, fp, errcode, errmsg, headers, data)
2210*cda5da8dSAndroid Build Coastguard Worker        else:
2211*cda5da8dSAndroid Build Coastguard Worker            return self.http_error_default(url, fp, errcode, errmsg, headers)
2212*cda5da8dSAndroid Build Coastguard Worker
2213*cda5da8dSAndroid Build Coastguard Worker    def http_error_308(self, url, fp, errcode, errmsg, headers, data=None):
2214*cda5da8dSAndroid Build Coastguard Worker        """Error 308 -- relocated, but turn POST into error."""
2215*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2216*cda5da8dSAndroid Build Coastguard Worker            return self.http_error_301(url, fp, errcode, errmsg, headers, data)
2217*cda5da8dSAndroid Build Coastguard Worker        else:
2218*cda5da8dSAndroid Build Coastguard Worker            return self.http_error_default(url, fp, errcode, errmsg, headers)
2219*cda5da8dSAndroid Build Coastguard Worker
2220*cda5da8dSAndroid Build Coastguard Worker    def http_error_401(self, url, fp, errcode, errmsg, headers, data=None,
2221*cda5da8dSAndroid Build Coastguard Worker            retry=False):
2222*cda5da8dSAndroid Build Coastguard Worker        """Error 401 -- authentication required.
2223*cda5da8dSAndroid Build Coastguard Worker        This function supports Basic authentication only."""
2224*cda5da8dSAndroid Build Coastguard Worker        if 'www-authenticate' not in headers:
2225*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp,
2226*cda5da8dSAndroid Build Coastguard Worker                                         errcode, errmsg, headers)
2227*cda5da8dSAndroid Build Coastguard Worker        stuff = headers['www-authenticate']
2228*cda5da8dSAndroid Build Coastguard Worker        match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff)
2229*cda5da8dSAndroid Build Coastguard Worker        if not match:
2230*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp,
2231*cda5da8dSAndroid Build Coastguard Worker                                         errcode, errmsg, headers)
2232*cda5da8dSAndroid Build Coastguard Worker        scheme, realm = match.groups()
2233*cda5da8dSAndroid Build Coastguard Worker        if scheme.lower() != 'basic':
2234*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp,
2235*cda5da8dSAndroid Build Coastguard Worker                                         errcode, errmsg, headers)
2236*cda5da8dSAndroid Build Coastguard Worker        if not retry:
2237*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp, errcode, errmsg,
2238*cda5da8dSAndroid Build Coastguard Worker                    headers)
2239*cda5da8dSAndroid Build Coastguard Worker        name = 'retry_' + self.type + '_basic_auth'
2240*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2241*cda5da8dSAndroid Build Coastguard Worker            return getattr(self,name)(url, realm)
2242*cda5da8dSAndroid Build Coastguard Worker        else:
2243*cda5da8dSAndroid Build Coastguard Worker            return getattr(self,name)(url, realm, data)
2244*cda5da8dSAndroid Build Coastguard Worker
2245*cda5da8dSAndroid Build Coastguard Worker    def http_error_407(self, url, fp, errcode, errmsg, headers, data=None,
2246*cda5da8dSAndroid Build Coastguard Worker            retry=False):
2247*cda5da8dSAndroid Build Coastguard Worker        """Error 407 -- proxy authentication required.
2248*cda5da8dSAndroid Build Coastguard Worker        This function supports Basic authentication only."""
2249*cda5da8dSAndroid Build Coastguard Worker        if 'proxy-authenticate' not in headers:
2250*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp,
2251*cda5da8dSAndroid Build Coastguard Worker                                         errcode, errmsg, headers)
2252*cda5da8dSAndroid Build Coastguard Worker        stuff = headers['proxy-authenticate']
2253*cda5da8dSAndroid Build Coastguard Worker        match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff)
2254*cda5da8dSAndroid Build Coastguard Worker        if not match:
2255*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp,
2256*cda5da8dSAndroid Build Coastguard Worker                                         errcode, errmsg, headers)
2257*cda5da8dSAndroid Build Coastguard Worker        scheme, realm = match.groups()
2258*cda5da8dSAndroid Build Coastguard Worker        if scheme.lower() != 'basic':
2259*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp,
2260*cda5da8dSAndroid Build Coastguard Worker                                         errcode, errmsg, headers)
2261*cda5da8dSAndroid Build Coastguard Worker        if not retry:
2262*cda5da8dSAndroid Build Coastguard Worker            URLopener.http_error_default(self, url, fp, errcode, errmsg,
2263*cda5da8dSAndroid Build Coastguard Worker                    headers)
2264*cda5da8dSAndroid Build Coastguard Worker        name = 'retry_proxy_' + self.type + '_basic_auth'
2265*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2266*cda5da8dSAndroid Build Coastguard Worker            return getattr(self,name)(url, realm)
2267*cda5da8dSAndroid Build Coastguard Worker        else:
2268*cda5da8dSAndroid Build Coastguard Worker            return getattr(self,name)(url, realm, data)
2269*cda5da8dSAndroid Build Coastguard Worker
2270*cda5da8dSAndroid Build Coastguard Worker    def retry_proxy_http_basic_auth(self, url, realm, data=None):
2271*cda5da8dSAndroid Build Coastguard Worker        host, selector = _splithost(url)
2272*cda5da8dSAndroid Build Coastguard Worker        newurl = 'http://' + host + selector
2273*cda5da8dSAndroid Build Coastguard Worker        proxy = self.proxies['http']
2274*cda5da8dSAndroid Build Coastguard Worker        urltype, proxyhost = _splittype(proxy)
2275*cda5da8dSAndroid Build Coastguard Worker        proxyhost, proxyselector = _splithost(proxyhost)
2276*cda5da8dSAndroid Build Coastguard Worker        i = proxyhost.find('@') + 1
2277*cda5da8dSAndroid Build Coastguard Worker        proxyhost = proxyhost[i:]
2278*cda5da8dSAndroid Build Coastguard Worker        user, passwd = self.get_user_passwd(proxyhost, realm, i)
2279*cda5da8dSAndroid Build Coastguard Worker        if not (user or passwd): return None
2280*cda5da8dSAndroid Build Coastguard Worker        proxyhost = "%s:%s@%s" % (quote(user, safe=''),
2281*cda5da8dSAndroid Build Coastguard Worker                                  quote(passwd, safe=''), proxyhost)
2282*cda5da8dSAndroid Build Coastguard Worker        self.proxies['http'] = 'http://' + proxyhost + proxyselector
2283*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2284*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl)
2285*cda5da8dSAndroid Build Coastguard Worker        else:
2286*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl, data)
2287*cda5da8dSAndroid Build Coastguard Worker
2288*cda5da8dSAndroid Build Coastguard Worker    def retry_proxy_https_basic_auth(self, url, realm, data=None):
2289*cda5da8dSAndroid Build Coastguard Worker        host, selector = _splithost(url)
2290*cda5da8dSAndroid Build Coastguard Worker        newurl = 'https://' + host + selector
2291*cda5da8dSAndroid Build Coastguard Worker        proxy = self.proxies['https']
2292*cda5da8dSAndroid Build Coastguard Worker        urltype, proxyhost = _splittype(proxy)
2293*cda5da8dSAndroid Build Coastguard Worker        proxyhost, proxyselector = _splithost(proxyhost)
2294*cda5da8dSAndroid Build Coastguard Worker        i = proxyhost.find('@') + 1
2295*cda5da8dSAndroid Build Coastguard Worker        proxyhost = proxyhost[i:]
2296*cda5da8dSAndroid Build Coastguard Worker        user, passwd = self.get_user_passwd(proxyhost, realm, i)
2297*cda5da8dSAndroid Build Coastguard Worker        if not (user or passwd): return None
2298*cda5da8dSAndroid Build Coastguard Worker        proxyhost = "%s:%s@%s" % (quote(user, safe=''),
2299*cda5da8dSAndroid Build Coastguard Worker                                  quote(passwd, safe=''), proxyhost)
2300*cda5da8dSAndroid Build Coastguard Worker        self.proxies['https'] = 'https://' + proxyhost + proxyselector
2301*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2302*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl)
2303*cda5da8dSAndroid Build Coastguard Worker        else:
2304*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl, data)
2305*cda5da8dSAndroid Build Coastguard Worker
2306*cda5da8dSAndroid Build Coastguard Worker    def retry_http_basic_auth(self, url, realm, data=None):
2307*cda5da8dSAndroid Build Coastguard Worker        host, selector = _splithost(url)
2308*cda5da8dSAndroid Build Coastguard Worker        i = host.find('@') + 1
2309*cda5da8dSAndroid Build Coastguard Worker        host = host[i:]
2310*cda5da8dSAndroid Build Coastguard Worker        user, passwd = self.get_user_passwd(host, realm, i)
2311*cda5da8dSAndroid Build Coastguard Worker        if not (user or passwd): return None
2312*cda5da8dSAndroid Build Coastguard Worker        host = "%s:%s@%s" % (quote(user, safe=''),
2313*cda5da8dSAndroid Build Coastguard Worker                             quote(passwd, safe=''), host)
2314*cda5da8dSAndroid Build Coastguard Worker        newurl = 'http://' + host + selector
2315*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2316*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl)
2317*cda5da8dSAndroid Build Coastguard Worker        else:
2318*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl, data)
2319*cda5da8dSAndroid Build Coastguard Worker
2320*cda5da8dSAndroid Build Coastguard Worker    def retry_https_basic_auth(self, url, realm, data=None):
2321*cda5da8dSAndroid Build Coastguard Worker        host, selector = _splithost(url)
2322*cda5da8dSAndroid Build Coastguard Worker        i = host.find('@') + 1
2323*cda5da8dSAndroid Build Coastguard Worker        host = host[i:]
2324*cda5da8dSAndroid Build Coastguard Worker        user, passwd = self.get_user_passwd(host, realm, i)
2325*cda5da8dSAndroid Build Coastguard Worker        if not (user or passwd): return None
2326*cda5da8dSAndroid Build Coastguard Worker        host = "%s:%s@%s" % (quote(user, safe=''),
2327*cda5da8dSAndroid Build Coastguard Worker                             quote(passwd, safe=''), host)
2328*cda5da8dSAndroid Build Coastguard Worker        newurl = 'https://' + host + selector
2329*cda5da8dSAndroid Build Coastguard Worker        if data is None:
2330*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl)
2331*cda5da8dSAndroid Build Coastguard Worker        else:
2332*cda5da8dSAndroid Build Coastguard Worker            return self.open(newurl, data)
2333*cda5da8dSAndroid Build Coastguard Worker
2334*cda5da8dSAndroid Build Coastguard Worker    def get_user_passwd(self, host, realm, clear_cache=0):
2335*cda5da8dSAndroid Build Coastguard Worker        key = realm + '@' + host.lower()
2336*cda5da8dSAndroid Build Coastguard Worker        if key in self.auth_cache:
2337*cda5da8dSAndroid Build Coastguard Worker            if clear_cache:
2338*cda5da8dSAndroid Build Coastguard Worker                del self.auth_cache[key]
2339*cda5da8dSAndroid Build Coastguard Worker            else:
2340*cda5da8dSAndroid Build Coastguard Worker                return self.auth_cache[key]
2341*cda5da8dSAndroid Build Coastguard Worker        user, passwd = self.prompt_user_passwd(host, realm)
2342*cda5da8dSAndroid Build Coastguard Worker        if user or passwd: self.auth_cache[key] = (user, passwd)
2343*cda5da8dSAndroid Build Coastguard Worker        return user, passwd
2344*cda5da8dSAndroid Build Coastguard Worker
2345*cda5da8dSAndroid Build Coastguard Worker    def prompt_user_passwd(self, host, realm):
2346*cda5da8dSAndroid Build Coastguard Worker        """Override this in a GUI environment!"""
2347*cda5da8dSAndroid Build Coastguard Worker        import getpass
2348*cda5da8dSAndroid Build Coastguard Worker        try:
2349*cda5da8dSAndroid Build Coastguard Worker            user = input("Enter username for %s at %s: " % (realm, host))
2350*cda5da8dSAndroid Build Coastguard Worker            passwd = getpass.getpass("Enter password for %s in %s at %s: " %
2351*cda5da8dSAndroid Build Coastguard Worker                (user, realm, host))
2352*cda5da8dSAndroid Build Coastguard Worker            return user, passwd
2353*cda5da8dSAndroid Build Coastguard Worker        except KeyboardInterrupt:
2354*cda5da8dSAndroid Build Coastguard Worker            print()
2355*cda5da8dSAndroid Build Coastguard Worker            return None, None
2356*cda5da8dSAndroid Build Coastguard Worker
2357*cda5da8dSAndroid Build Coastguard Worker
2358*cda5da8dSAndroid Build Coastguard Worker# Utility functions
2359*cda5da8dSAndroid Build Coastguard Worker
2360*cda5da8dSAndroid Build Coastguard Worker_localhost = None
2361*cda5da8dSAndroid Build Coastguard Workerdef localhost():
2362*cda5da8dSAndroid Build Coastguard Worker    """Return the IP address of the magic hostname 'localhost'."""
2363*cda5da8dSAndroid Build Coastguard Worker    global _localhost
2364*cda5da8dSAndroid Build Coastguard Worker    if _localhost is None:
2365*cda5da8dSAndroid Build Coastguard Worker        _localhost = socket.gethostbyname('localhost')
2366*cda5da8dSAndroid Build Coastguard Worker    return _localhost
2367*cda5da8dSAndroid Build Coastguard Worker
2368*cda5da8dSAndroid Build Coastguard Worker_thishost = None
2369*cda5da8dSAndroid Build Coastguard Workerdef thishost():
2370*cda5da8dSAndroid Build Coastguard Worker    """Return the IP addresses of the current host."""
2371*cda5da8dSAndroid Build Coastguard Worker    global _thishost
2372*cda5da8dSAndroid Build Coastguard Worker    if _thishost is None:
2373*cda5da8dSAndroid Build Coastguard Worker        try:
2374*cda5da8dSAndroid Build Coastguard Worker            _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2])
2375*cda5da8dSAndroid Build Coastguard Worker        except socket.gaierror:
2376*cda5da8dSAndroid Build Coastguard Worker            _thishost = tuple(socket.gethostbyname_ex('localhost')[2])
2377*cda5da8dSAndroid Build Coastguard Worker    return _thishost
2378*cda5da8dSAndroid Build Coastguard Worker
2379*cda5da8dSAndroid Build Coastguard Worker_ftperrors = None
2380*cda5da8dSAndroid Build Coastguard Workerdef ftperrors():
2381*cda5da8dSAndroid Build Coastguard Worker    """Return the set of errors raised by the FTP class."""
2382*cda5da8dSAndroid Build Coastguard Worker    global _ftperrors
2383*cda5da8dSAndroid Build Coastguard Worker    if _ftperrors is None:
2384*cda5da8dSAndroid Build Coastguard Worker        import ftplib
2385*cda5da8dSAndroid Build Coastguard Worker        _ftperrors = ftplib.all_errors
2386*cda5da8dSAndroid Build Coastguard Worker    return _ftperrors
2387*cda5da8dSAndroid Build Coastguard Worker
2388*cda5da8dSAndroid Build Coastguard Worker_noheaders = None
2389*cda5da8dSAndroid Build Coastguard Workerdef noheaders():
2390*cda5da8dSAndroid Build Coastguard Worker    """Return an empty email Message object."""
2391*cda5da8dSAndroid Build Coastguard Worker    global _noheaders
2392*cda5da8dSAndroid Build Coastguard Worker    if _noheaders is None:
2393*cda5da8dSAndroid Build Coastguard Worker        _noheaders = email.message_from_string("")
2394*cda5da8dSAndroid Build Coastguard Worker    return _noheaders
2395*cda5da8dSAndroid Build Coastguard Worker
2396*cda5da8dSAndroid Build Coastguard Worker
2397*cda5da8dSAndroid Build Coastguard Worker# Utility classes
2398*cda5da8dSAndroid Build Coastguard Worker
2399*cda5da8dSAndroid Build Coastguard Workerclass ftpwrapper:
2400*cda5da8dSAndroid Build Coastguard Worker    """Class used by open_ftp() for cache of open FTP connections."""
2401*cda5da8dSAndroid Build Coastguard Worker
2402*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, user, passwd, host, port, dirs, timeout=None,
2403*cda5da8dSAndroid Build Coastguard Worker                 persistent=True):
2404*cda5da8dSAndroid Build Coastguard Worker        self.user = user
2405*cda5da8dSAndroid Build Coastguard Worker        self.passwd = passwd
2406*cda5da8dSAndroid Build Coastguard Worker        self.host = host
2407*cda5da8dSAndroid Build Coastguard Worker        self.port = port
2408*cda5da8dSAndroid Build Coastguard Worker        self.dirs = dirs
2409*cda5da8dSAndroid Build Coastguard Worker        self.timeout = timeout
2410*cda5da8dSAndroid Build Coastguard Worker        self.refcount = 0
2411*cda5da8dSAndroid Build Coastguard Worker        self.keepalive = persistent
2412*cda5da8dSAndroid Build Coastguard Worker        try:
2413*cda5da8dSAndroid Build Coastguard Worker            self.init()
2414*cda5da8dSAndroid Build Coastguard Worker        except:
2415*cda5da8dSAndroid Build Coastguard Worker            self.close()
2416*cda5da8dSAndroid Build Coastguard Worker            raise
2417*cda5da8dSAndroid Build Coastguard Worker
2418*cda5da8dSAndroid Build Coastguard Worker    def init(self):
2419*cda5da8dSAndroid Build Coastguard Worker        import ftplib
2420*cda5da8dSAndroid Build Coastguard Worker        self.busy = 0
2421*cda5da8dSAndroid Build Coastguard Worker        self.ftp = ftplib.FTP()
2422*cda5da8dSAndroid Build Coastguard Worker        self.ftp.connect(self.host, self.port, self.timeout)
2423*cda5da8dSAndroid Build Coastguard Worker        self.ftp.login(self.user, self.passwd)
2424*cda5da8dSAndroid Build Coastguard Worker        _target = '/'.join(self.dirs)
2425*cda5da8dSAndroid Build Coastguard Worker        self.ftp.cwd(_target)
2426*cda5da8dSAndroid Build Coastguard Worker
2427*cda5da8dSAndroid Build Coastguard Worker    def retrfile(self, file, type):
2428*cda5da8dSAndroid Build Coastguard Worker        import ftplib
2429*cda5da8dSAndroid Build Coastguard Worker        self.endtransfer()
2430*cda5da8dSAndroid Build Coastguard Worker        if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
2431*cda5da8dSAndroid Build Coastguard Worker        else: cmd = 'TYPE ' + type; isdir = 0
2432*cda5da8dSAndroid Build Coastguard Worker        try:
2433*cda5da8dSAndroid Build Coastguard Worker            self.ftp.voidcmd(cmd)
2434*cda5da8dSAndroid Build Coastguard Worker        except ftplib.all_errors:
2435*cda5da8dSAndroid Build Coastguard Worker            self.init()
2436*cda5da8dSAndroid Build Coastguard Worker            self.ftp.voidcmd(cmd)
2437*cda5da8dSAndroid Build Coastguard Worker        conn = None
2438*cda5da8dSAndroid Build Coastguard Worker        if file and not isdir:
2439*cda5da8dSAndroid Build Coastguard Worker            # Try to retrieve as a file
2440*cda5da8dSAndroid Build Coastguard Worker            try:
2441*cda5da8dSAndroid Build Coastguard Worker                cmd = 'RETR ' + file
2442*cda5da8dSAndroid Build Coastguard Worker                conn, retrlen = self.ftp.ntransfercmd(cmd)
2443*cda5da8dSAndroid Build Coastguard Worker            except ftplib.error_perm as reason:
2444*cda5da8dSAndroid Build Coastguard Worker                if str(reason)[:3] != '550':
2445*cda5da8dSAndroid Build Coastguard Worker                    raise URLError(f'ftp error: {reason}') from reason
2446*cda5da8dSAndroid Build Coastguard Worker        if not conn:
2447*cda5da8dSAndroid Build Coastguard Worker            # Set transfer mode to ASCII!
2448*cda5da8dSAndroid Build Coastguard Worker            self.ftp.voidcmd('TYPE A')
2449*cda5da8dSAndroid Build Coastguard Worker            # Try a directory listing. Verify that directory exists.
2450*cda5da8dSAndroid Build Coastguard Worker            if file:
2451*cda5da8dSAndroid Build Coastguard Worker                pwd = self.ftp.pwd()
2452*cda5da8dSAndroid Build Coastguard Worker                try:
2453*cda5da8dSAndroid Build Coastguard Worker                    try:
2454*cda5da8dSAndroid Build Coastguard Worker                        self.ftp.cwd(file)
2455*cda5da8dSAndroid Build Coastguard Worker                    except ftplib.error_perm as reason:
2456*cda5da8dSAndroid Build Coastguard Worker                        raise URLError('ftp error: %r' % reason) from reason
2457*cda5da8dSAndroid Build Coastguard Worker                finally:
2458*cda5da8dSAndroid Build Coastguard Worker                    self.ftp.cwd(pwd)
2459*cda5da8dSAndroid Build Coastguard Worker                cmd = 'LIST ' + file
2460*cda5da8dSAndroid Build Coastguard Worker            else:
2461*cda5da8dSAndroid Build Coastguard Worker                cmd = 'LIST'
2462*cda5da8dSAndroid Build Coastguard Worker            conn, retrlen = self.ftp.ntransfercmd(cmd)
2463*cda5da8dSAndroid Build Coastguard Worker        self.busy = 1
2464*cda5da8dSAndroid Build Coastguard Worker
2465*cda5da8dSAndroid Build Coastguard Worker        ftpobj = addclosehook(conn.makefile('rb'), self.file_close)
2466*cda5da8dSAndroid Build Coastguard Worker        self.refcount += 1
2467*cda5da8dSAndroid Build Coastguard Worker        conn.close()
2468*cda5da8dSAndroid Build Coastguard Worker        # Pass back both a suitably decorated object and a retrieval length
2469*cda5da8dSAndroid Build Coastguard Worker        return (ftpobj, retrlen)
2470*cda5da8dSAndroid Build Coastguard Worker
2471*cda5da8dSAndroid Build Coastguard Worker    def endtransfer(self):
2472*cda5da8dSAndroid Build Coastguard Worker        if not self.busy:
2473*cda5da8dSAndroid Build Coastguard Worker            return
2474*cda5da8dSAndroid Build Coastguard Worker        self.busy = 0
2475*cda5da8dSAndroid Build Coastguard Worker        try:
2476*cda5da8dSAndroid Build Coastguard Worker            self.ftp.voidresp()
2477*cda5da8dSAndroid Build Coastguard Worker        except ftperrors():
2478*cda5da8dSAndroid Build Coastguard Worker            pass
2479*cda5da8dSAndroid Build Coastguard Worker
2480*cda5da8dSAndroid Build Coastguard Worker    def close(self):
2481*cda5da8dSAndroid Build Coastguard Worker        self.keepalive = False
2482*cda5da8dSAndroid Build Coastguard Worker        if self.refcount <= 0:
2483*cda5da8dSAndroid Build Coastguard Worker            self.real_close()
2484*cda5da8dSAndroid Build Coastguard Worker
2485*cda5da8dSAndroid Build Coastguard Worker    def file_close(self):
2486*cda5da8dSAndroid Build Coastguard Worker        self.endtransfer()
2487*cda5da8dSAndroid Build Coastguard Worker        self.refcount -= 1
2488*cda5da8dSAndroid Build Coastguard Worker        if self.refcount <= 0 and not self.keepalive:
2489*cda5da8dSAndroid Build Coastguard Worker            self.real_close()
2490*cda5da8dSAndroid Build Coastguard Worker
2491*cda5da8dSAndroid Build Coastguard Worker    def real_close(self):
2492*cda5da8dSAndroid Build Coastguard Worker        self.endtransfer()
2493*cda5da8dSAndroid Build Coastguard Worker        try:
2494*cda5da8dSAndroid Build Coastguard Worker            self.ftp.close()
2495*cda5da8dSAndroid Build Coastguard Worker        except ftperrors():
2496*cda5da8dSAndroid Build Coastguard Worker            pass
2497*cda5da8dSAndroid Build Coastguard Worker
2498*cda5da8dSAndroid Build Coastguard Worker# Proxy handling
2499*cda5da8dSAndroid Build Coastguard Workerdef getproxies_environment():
2500*cda5da8dSAndroid Build Coastguard Worker    """Return a dictionary of scheme -> proxy server URL mappings.
2501*cda5da8dSAndroid Build Coastguard Worker
2502*cda5da8dSAndroid Build Coastguard Worker    Scan the environment for variables named <scheme>_proxy;
2503*cda5da8dSAndroid Build Coastguard Worker    this seems to be the standard convention.  If you need a
2504*cda5da8dSAndroid Build Coastguard Worker    different way, you can pass a proxies dictionary to the
2505*cda5da8dSAndroid Build Coastguard Worker    [Fancy]URLopener constructor.
2506*cda5da8dSAndroid Build Coastguard Worker
2507*cda5da8dSAndroid Build Coastguard Worker    """
2508*cda5da8dSAndroid Build Coastguard Worker    proxies = {}
2509*cda5da8dSAndroid Build Coastguard Worker    # in order to prefer lowercase variables, process environment in
2510*cda5da8dSAndroid Build Coastguard Worker    # two passes: first matches any, second pass matches lowercase only
2511*cda5da8dSAndroid Build Coastguard Worker    for name, value in os.environ.items():
2512*cda5da8dSAndroid Build Coastguard Worker        name = name.lower()
2513*cda5da8dSAndroid Build Coastguard Worker        if value and name[-6:] == '_proxy':
2514*cda5da8dSAndroid Build Coastguard Worker            proxies[name[:-6]] = value
2515*cda5da8dSAndroid Build Coastguard Worker    # CVE-2016-1000110 - If we are running as CGI script, forget HTTP_PROXY
2516*cda5da8dSAndroid Build Coastguard Worker    # (non-all-lowercase) as it may be set from the web server by a "Proxy:"
2517*cda5da8dSAndroid Build Coastguard Worker    # header from the client
2518*cda5da8dSAndroid Build Coastguard Worker    # If "proxy" is lowercase, it will still be used thanks to the next block
2519*cda5da8dSAndroid Build Coastguard Worker    if 'REQUEST_METHOD' in os.environ:
2520*cda5da8dSAndroid Build Coastguard Worker        proxies.pop('http', None)
2521*cda5da8dSAndroid Build Coastguard Worker    for name, value in os.environ.items():
2522*cda5da8dSAndroid Build Coastguard Worker        if name[-6:] == '_proxy':
2523*cda5da8dSAndroid Build Coastguard Worker            name = name.lower()
2524*cda5da8dSAndroid Build Coastguard Worker            if value:
2525*cda5da8dSAndroid Build Coastguard Worker                proxies[name[:-6]] = value
2526*cda5da8dSAndroid Build Coastguard Worker            else:
2527*cda5da8dSAndroid Build Coastguard Worker                proxies.pop(name[:-6], None)
2528*cda5da8dSAndroid Build Coastguard Worker    return proxies
2529*cda5da8dSAndroid Build Coastguard Worker
2530*cda5da8dSAndroid Build Coastguard Workerdef proxy_bypass_environment(host, proxies=None):
2531*cda5da8dSAndroid Build Coastguard Worker    """Test if proxies should not be used for a particular host.
2532*cda5da8dSAndroid Build Coastguard Worker
2533*cda5da8dSAndroid Build Coastguard Worker    Checks the proxy dict for the value of no_proxy, which should
2534*cda5da8dSAndroid Build Coastguard Worker    be a list of comma separated DNS suffixes, or '*' for all hosts.
2535*cda5da8dSAndroid Build Coastguard Worker
2536*cda5da8dSAndroid Build Coastguard Worker    """
2537*cda5da8dSAndroid Build Coastguard Worker    if proxies is None:
2538*cda5da8dSAndroid Build Coastguard Worker        proxies = getproxies_environment()
2539*cda5da8dSAndroid Build Coastguard Worker    # don't bypass, if no_proxy isn't specified
2540*cda5da8dSAndroid Build Coastguard Worker    try:
2541*cda5da8dSAndroid Build Coastguard Worker        no_proxy = proxies['no']
2542*cda5da8dSAndroid Build Coastguard Worker    except KeyError:
2543*cda5da8dSAndroid Build Coastguard Worker        return False
2544*cda5da8dSAndroid Build Coastguard Worker    # '*' is special case for always bypass
2545*cda5da8dSAndroid Build Coastguard Worker    if no_proxy == '*':
2546*cda5da8dSAndroid Build Coastguard Worker        return True
2547*cda5da8dSAndroid Build Coastguard Worker    host = host.lower()
2548*cda5da8dSAndroid Build Coastguard Worker    # strip port off host
2549*cda5da8dSAndroid Build Coastguard Worker    hostonly, port = _splitport(host)
2550*cda5da8dSAndroid Build Coastguard Worker    # check if the host ends with any of the DNS suffixes
2551*cda5da8dSAndroid Build Coastguard Worker    for name in no_proxy.split(','):
2552*cda5da8dSAndroid Build Coastguard Worker        name = name.strip()
2553*cda5da8dSAndroid Build Coastguard Worker        if name:
2554*cda5da8dSAndroid Build Coastguard Worker            name = name.lstrip('.')  # ignore leading dots
2555*cda5da8dSAndroid Build Coastguard Worker            name = name.lower()
2556*cda5da8dSAndroid Build Coastguard Worker            if hostonly == name or host == name:
2557*cda5da8dSAndroid Build Coastguard Worker                return True
2558*cda5da8dSAndroid Build Coastguard Worker            name = '.' + name
2559*cda5da8dSAndroid Build Coastguard Worker            if hostonly.endswith(name) or host.endswith(name):
2560*cda5da8dSAndroid Build Coastguard Worker                return True
2561*cda5da8dSAndroid Build Coastguard Worker    # otherwise, don't bypass
2562*cda5da8dSAndroid Build Coastguard Worker    return False
2563*cda5da8dSAndroid Build Coastguard Worker
2564*cda5da8dSAndroid Build Coastguard Worker
2565*cda5da8dSAndroid Build Coastguard Worker# This code tests an OSX specific data structure but is testable on all
2566*cda5da8dSAndroid Build Coastguard Worker# platforms
2567*cda5da8dSAndroid Build Coastguard Workerdef _proxy_bypass_macosx_sysconf(host, proxy_settings):
2568*cda5da8dSAndroid Build Coastguard Worker    """
2569*cda5da8dSAndroid Build Coastguard Worker    Return True iff this host shouldn't be accessed using a proxy
2570*cda5da8dSAndroid Build Coastguard Worker
2571*cda5da8dSAndroid Build Coastguard Worker    This function uses the MacOSX framework SystemConfiguration
2572*cda5da8dSAndroid Build Coastguard Worker    to fetch the proxy information.
2573*cda5da8dSAndroid Build Coastguard Worker
2574*cda5da8dSAndroid Build Coastguard Worker    proxy_settings come from _scproxy._get_proxy_settings or get mocked ie:
2575*cda5da8dSAndroid Build Coastguard Worker    { 'exclude_simple': bool,
2576*cda5da8dSAndroid Build Coastguard Worker      'exceptions': ['foo.bar', '*.bar.com', '127.0.0.1', '10.1', '10.0/16']
2577*cda5da8dSAndroid Build Coastguard Worker    }
2578*cda5da8dSAndroid Build Coastguard Worker    """
2579*cda5da8dSAndroid Build Coastguard Worker    from fnmatch import fnmatch
2580*cda5da8dSAndroid Build Coastguard Worker
2581*cda5da8dSAndroid Build Coastguard Worker    hostonly, port = _splitport(host)
2582*cda5da8dSAndroid Build Coastguard Worker
2583*cda5da8dSAndroid Build Coastguard Worker    def ip2num(ipAddr):
2584*cda5da8dSAndroid Build Coastguard Worker        parts = ipAddr.split('.')
2585*cda5da8dSAndroid Build Coastguard Worker        parts = list(map(int, parts))
2586*cda5da8dSAndroid Build Coastguard Worker        if len(parts) != 4:
2587*cda5da8dSAndroid Build Coastguard Worker            parts = (parts + [0, 0, 0, 0])[:4]
2588*cda5da8dSAndroid Build Coastguard Worker        return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]
2589*cda5da8dSAndroid Build Coastguard Worker
2590*cda5da8dSAndroid Build Coastguard Worker    # Check for simple host names:
2591*cda5da8dSAndroid Build Coastguard Worker    if '.' not in host:
2592*cda5da8dSAndroid Build Coastguard Worker        if proxy_settings['exclude_simple']:
2593*cda5da8dSAndroid Build Coastguard Worker            return True
2594*cda5da8dSAndroid Build Coastguard Worker
2595*cda5da8dSAndroid Build Coastguard Worker    hostIP = None
2596*cda5da8dSAndroid Build Coastguard Worker
2597*cda5da8dSAndroid Build Coastguard Worker    for value in proxy_settings.get('exceptions', ()):
2598*cda5da8dSAndroid Build Coastguard Worker        # Items in the list are strings like these: *.local, 169.254/16
2599*cda5da8dSAndroid Build Coastguard Worker        if not value: continue
2600*cda5da8dSAndroid Build Coastguard Worker
2601*cda5da8dSAndroid Build Coastguard Worker        m = re.match(r"(\d+(?:\.\d+)*)(/\d+)?", value)
2602*cda5da8dSAndroid Build Coastguard Worker        if m is not None:
2603*cda5da8dSAndroid Build Coastguard Worker            if hostIP is None:
2604*cda5da8dSAndroid Build Coastguard Worker                try:
2605*cda5da8dSAndroid Build Coastguard Worker                    hostIP = socket.gethostbyname(hostonly)
2606*cda5da8dSAndroid Build Coastguard Worker                    hostIP = ip2num(hostIP)
2607*cda5da8dSAndroid Build Coastguard Worker                except OSError:
2608*cda5da8dSAndroid Build Coastguard Worker                    continue
2609*cda5da8dSAndroid Build Coastguard Worker
2610*cda5da8dSAndroid Build Coastguard Worker            base = ip2num(m.group(1))
2611*cda5da8dSAndroid Build Coastguard Worker            mask = m.group(2)
2612*cda5da8dSAndroid Build Coastguard Worker            if mask is None:
2613*cda5da8dSAndroid Build Coastguard Worker                mask = 8 * (m.group(1).count('.') + 1)
2614*cda5da8dSAndroid Build Coastguard Worker            else:
2615*cda5da8dSAndroid Build Coastguard Worker                mask = int(mask[1:])
2616*cda5da8dSAndroid Build Coastguard Worker
2617*cda5da8dSAndroid Build Coastguard Worker            if mask < 0 or mask > 32:
2618*cda5da8dSAndroid Build Coastguard Worker                # System libraries ignore invalid prefix lengths
2619*cda5da8dSAndroid Build Coastguard Worker                continue
2620*cda5da8dSAndroid Build Coastguard Worker
2621*cda5da8dSAndroid Build Coastguard Worker            mask = 32 - mask
2622*cda5da8dSAndroid Build Coastguard Worker
2623*cda5da8dSAndroid Build Coastguard Worker            if (hostIP >> mask) == (base >> mask):
2624*cda5da8dSAndroid Build Coastguard Worker                return True
2625*cda5da8dSAndroid Build Coastguard Worker
2626*cda5da8dSAndroid Build Coastguard Worker        elif fnmatch(host, value):
2627*cda5da8dSAndroid Build Coastguard Worker            return True
2628*cda5da8dSAndroid Build Coastguard Worker
2629*cda5da8dSAndroid Build Coastguard Worker    return False
2630*cda5da8dSAndroid Build Coastguard Worker
2631*cda5da8dSAndroid Build Coastguard Worker
2632*cda5da8dSAndroid Build Coastguard Workerif sys.platform == 'darwin':
2633*cda5da8dSAndroid Build Coastguard Worker    from _scproxy import _get_proxy_settings, _get_proxies
2634*cda5da8dSAndroid Build Coastguard Worker
2635*cda5da8dSAndroid Build Coastguard Worker    def proxy_bypass_macosx_sysconf(host):
2636*cda5da8dSAndroid Build Coastguard Worker        proxy_settings = _get_proxy_settings()
2637*cda5da8dSAndroid Build Coastguard Worker        return _proxy_bypass_macosx_sysconf(host, proxy_settings)
2638*cda5da8dSAndroid Build Coastguard Worker
2639*cda5da8dSAndroid Build Coastguard Worker    def getproxies_macosx_sysconf():
2640*cda5da8dSAndroid Build Coastguard Worker        """Return a dictionary of scheme -> proxy server URL mappings.
2641*cda5da8dSAndroid Build Coastguard Worker
2642*cda5da8dSAndroid Build Coastguard Worker        This function uses the MacOSX framework SystemConfiguration
2643*cda5da8dSAndroid Build Coastguard Worker        to fetch the proxy information.
2644*cda5da8dSAndroid Build Coastguard Worker        """
2645*cda5da8dSAndroid Build Coastguard Worker        return _get_proxies()
2646*cda5da8dSAndroid Build Coastguard Worker
2647*cda5da8dSAndroid Build Coastguard Worker
2648*cda5da8dSAndroid Build Coastguard Worker
2649*cda5da8dSAndroid Build Coastguard Worker    def proxy_bypass(host):
2650*cda5da8dSAndroid Build Coastguard Worker        """Return True, if host should be bypassed.
2651*cda5da8dSAndroid Build Coastguard Worker
2652*cda5da8dSAndroid Build Coastguard Worker        Checks proxy settings gathered from the environment, if specified,
2653*cda5da8dSAndroid Build Coastguard Worker        or from the MacOSX framework SystemConfiguration.
2654*cda5da8dSAndroid Build Coastguard Worker
2655*cda5da8dSAndroid Build Coastguard Worker        """
2656*cda5da8dSAndroid Build Coastguard Worker        proxies = getproxies_environment()
2657*cda5da8dSAndroid Build Coastguard Worker        if proxies:
2658*cda5da8dSAndroid Build Coastguard Worker            return proxy_bypass_environment(host, proxies)
2659*cda5da8dSAndroid Build Coastguard Worker        else:
2660*cda5da8dSAndroid Build Coastguard Worker            return proxy_bypass_macosx_sysconf(host)
2661*cda5da8dSAndroid Build Coastguard Worker
2662*cda5da8dSAndroid Build Coastguard Worker    def getproxies():
2663*cda5da8dSAndroid Build Coastguard Worker        return getproxies_environment() or getproxies_macosx_sysconf()
2664*cda5da8dSAndroid Build Coastguard Worker
2665*cda5da8dSAndroid Build Coastguard Worker
2666*cda5da8dSAndroid Build Coastguard Workerelif os.name == 'nt':
2667*cda5da8dSAndroid Build Coastguard Worker    def getproxies_registry():
2668*cda5da8dSAndroid Build Coastguard Worker        """Return a dictionary of scheme -> proxy server URL mappings.
2669*cda5da8dSAndroid Build Coastguard Worker
2670*cda5da8dSAndroid Build Coastguard Worker        Win32 uses the registry to store proxies.
2671*cda5da8dSAndroid Build Coastguard Worker
2672*cda5da8dSAndroid Build Coastguard Worker        """
2673*cda5da8dSAndroid Build Coastguard Worker        proxies = {}
2674*cda5da8dSAndroid Build Coastguard Worker        try:
2675*cda5da8dSAndroid Build Coastguard Worker            import winreg
2676*cda5da8dSAndroid Build Coastguard Worker        except ImportError:
2677*cda5da8dSAndroid Build Coastguard Worker            # Std module, so should be around - but you never know!
2678*cda5da8dSAndroid Build Coastguard Worker            return proxies
2679*cda5da8dSAndroid Build Coastguard Worker        try:
2680*cda5da8dSAndroid Build Coastguard Worker            internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
2681*cda5da8dSAndroid Build Coastguard Worker                r'Software\Microsoft\Windows\CurrentVersion\Internet Settings')
2682*cda5da8dSAndroid Build Coastguard Worker            proxyEnable = winreg.QueryValueEx(internetSettings,
2683*cda5da8dSAndroid Build Coastguard Worker                                               'ProxyEnable')[0]
2684*cda5da8dSAndroid Build Coastguard Worker            if proxyEnable:
2685*cda5da8dSAndroid Build Coastguard Worker                # Returned as Unicode but problems if not converted to ASCII
2686*cda5da8dSAndroid Build Coastguard Worker                proxyServer = str(winreg.QueryValueEx(internetSettings,
2687*cda5da8dSAndroid Build Coastguard Worker                                                       'ProxyServer')[0])
2688*cda5da8dSAndroid Build Coastguard Worker                if '=' not in proxyServer and ';' not in proxyServer:
2689*cda5da8dSAndroid Build Coastguard Worker                    # Use one setting for all protocols.
2690*cda5da8dSAndroid Build Coastguard Worker                    proxyServer = 'http={0};https={0};ftp={0}'.format(proxyServer)
2691*cda5da8dSAndroid Build Coastguard Worker                for p in proxyServer.split(';'):
2692*cda5da8dSAndroid Build Coastguard Worker                    protocol, address = p.split('=', 1)
2693*cda5da8dSAndroid Build Coastguard Worker                    # See if address has a type:// prefix
2694*cda5da8dSAndroid Build Coastguard Worker                    if not re.match('(?:[^/:]+)://', address):
2695*cda5da8dSAndroid Build Coastguard Worker                        # Add type:// prefix to address without specifying type
2696*cda5da8dSAndroid Build Coastguard Worker                        if protocol in ('http', 'https', 'ftp'):
2697*cda5da8dSAndroid Build Coastguard Worker                            # The default proxy type of Windows is HTTP
2698*cda5da8dSAndroid Build Coastguard Worker                            address = 'http://' + address
2699*cda5da8dSAndroid Build Coastguard Worker                        elif protocol == 'socks':
2700*cda5da8dSAndroid Build Coastguard Worker                            address = 'socks://' + address
2701*cda5da8dSAndroid Build Coastguard Worker                    proxies[protocol] = address
2702*cda5da8dSAndroid Build Coastguard Worker                # Use SOCKS proxy for HTTP(S) protocols
2703*cda5da8dSAndroid Build Coastguard Worker                if proxies.get('socks'):
2704*cda5da8dSAndroid Build Coastguard Worker                    # The default SOCKS proxy type of Windows is SOCKS4
2705*cda5da8dSAndroid Build Coastguard Worker                    address = re.sub(r'^socks://', 'socks4://', proxies['socks'])
2706*cda5da8dSAndroid Build Coastguard Worker                    proxies['http'] = proxies.get('http') or address
2707*cda5da8dSAndroid Build Coastguard Worker                    proxies['https'] = proxies.get('https') or address
2708*cda5da8dSAndroid Build Coastguard Worker            internetSettings.Close()
2709*cda5da8dSAndroid Build Coastguard Worker        except (OSError, ValueError, TypeError):
2710*cda5da8dSAndroid Build Coastguard Worker            # Either registry key not found etc, or the value in an
2711*cda5da8dSAndroid Build Coastguard Worker            # unexpected format.
2712*cda5da8dSAndroid Build Coastguard Worker            # proxies already set up to be empty so nothing to do
2713*cda5da8dSAndroid Build Coastguard Worker            pass
2714*cda5da8dSAndroid Build Coastguard Worker        return proxies
2715*cda5da8dSAndroid Build Coastguard Worker
2716*cda5da8dSAndroid Build Coastguard Worker    def getproxies():
2717*cda5da8dSAndroid Build Coastguard Worker        """Return a dictionary of scheme -> proxy server URL mappings.
2718*cda5da8dSAndroid Build Coastguard Worker
2719*cda5da8dSAndroid Build Coastguard Worker        Returns settings gathered from the environment, if specified,
2720*cda5da8dSAndroid Build Coastguard Worker        or the registry.
2721*cda5da8dSAndroid Build Coastguard Worker
2722*cda5da8dSAndroid Build Coastguard Worker        """
2723*cda5da8dSAndroid Build Coastguard Worker        return getproxies_environment() or getproxies_registry()
2724*cda5da8dSAndroid Build Coastguard Worker
2725*cda5da8dSAndroid Build Coastguard Worker    def proxy_bypass_registry(host):
2726*cda5da8dSAndroid Build Coastguard Worker        try:
2727*cda5da8dSAndroid Build Coastguard Worker            import winreg
2728*cda5da8dSAndroid Build Coastguard Worker        except ImportError:
2729*cda5da8dSAndroid Build Coastguard Worker            # Std modules, so should be around - but you never know!
2730*cda5da8dSAndroid Build Coastguard Worker            return 0
2731*cda5da8dSAndroid Build Coastguard Worker        try:
2732*cda5da8dSAndroid Build Coastguard Worker            internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
2733*cda5da8dSAndroid Build Coastguard Worker                r'Software\Microsoft\Windows\CurrentVersion\Internet Settings')
2734*cda5da8dSAndroid Build Coastguard Worker            proxyEnable = winreg.QueryValueEx(internetSettings,
2735*cda5da8dSAndroid Build Coastguard Worker                                               'ProxyEnable')[0]
2736*cda5da8dSAndroid Build Coastguard Worker            proxyOverride = str(winreg.QueryValueEx(internetSettings,
2737*cda5da8dSAndroid Build Coastguard Worker                                                     'ProxyOverride')[0])
2738*cda5da8dSAndroid Build Coastguard Worker            # ^^^^ Returned as Unicode but problems if not converted to ASCII
2739*cda5da8dSAndroid Build Coastguard Worker        except OSError:
2740*cda5da8dSAndroid Build Coastguard Worker            return 0
2741*cda5da8dSAndroid Build Coastguard Worker        if not proxyEnable or not proxyOverride:
2742*cda5da8dSAndroid Build Coastguard Worker            return 0
2743*cda5da8dSAndroid Build Coastguard Worker        # try to make a host list from name and IP address.
2744*cda5da8dSAndroid Build Coastguard Worker        rawHost, port = _splitport(host)
2745*cda5da8dSAndroid Build Coastguard Worker        host = [rawHost]
2746*cda5da8dSAndroid Build Coastguard Worker        try:
2747*cda5da8dSAndroid Build Coastguard Worker            addr = socket.gethostbyname(rawHost)
2748*cda5da8dSAndroid Build Coastguard Worker            if addr != rawHost:
2749*cda5da8dSAndroid Build Coastguard Worker                host.append(addr)
2750*cda5da8dSAndroid Build Coastguard Worker        except OSError:
2751*cda5da8dSAndroid Build Coastguard Worker            pass
2752*cda5da8dSAndroid Build Coastguard Worker        try:
2753*cda5da8dSAndroid Build Coastguard Worker            fqdn = socket.getfqdn(rawHost)
2754*cda5da8dSAndroid Build Coastguard Worker            if fqdn != rawHost:
2755*cda5da8dSAndroid Build Coastguard Worker                host.append(fqdn)
2756*cda5da8dSAndroid Build Coastguard Worker        except OSError:
2757*cda5da8dSAndroid Build Coastguard Worker            pass
2758*cda5da8dSAndroid Build Coastguard Worker        # make a check value list from the registry entry: replace the
2759*cda5da8dSAndroid Build Coastguard Worker        # '<local>' string by the localhost entry and the corresponding
2760*cda5da8dSAndroid Build Coastguard Worker        # canonical entry.
2761*cda5da8dSAndroid Build Coastguard Worker        proxyOverride = proxyOverride.split(';')
2762*cda5da8dSAndroid Build Coastguard Worker        # now check if we match one of the registry values.
2763*cda5da8dSAndroid Build Coastguard Worker        for test in proxyOverride:
2764*cda5da8dSAndroid Build Coastguard Worker            if test == '<local>':
2765*cda5da8dSAndroid Build Coastguard Worker                if '.' not in rawHost:
2766*cda5da8dSAndroid Build Coastguard Worker                    return 1
2767*cda5da8dSAndroid Build Coastguard Worker            test = test.replace(".", r"\.")     # mask dots
2768*cda5da8dSAndroid Build Coastguard Worker            test = test.replace("*", r".*")     # change glob sequence
2769*cda5da8dSAndroid Build Coastguard Worker            test = test.replace("?", r".")      # change glob char
2770*cda5da8dSAndroid Build Coastguard Worker            for val in host:
2771*cda5da8dSAndroid Build Coastguard Worker                if re.match(test, val, re.I):
2772*cda5da8dSAndroid Build Coastguard Worker                    return 1
2773*cda5da8dSAndroid Build Coastguard Worker        return 0
2774*cda5da8dSAndroid Build Coastguard Worker
2775*cda5da8dSAndroid Build Coastguard Worker    def proxy_bypass(host):
2776*cda5da8dSAndroid Build Coastguard Worker        """Return True, if host should be bypassed.
2777*cda5da8dSAndroid Build Coastguard Worker
2778*cda5da8dSAndroid Build Coastguard Worker        Checks proxy settings gathered from the environment, if specified,
2779*cda5da8dSAndroid Build Coastguard Worker        or the registry.
2780*cda5da8dSAndroid Build Coastguard Worker
2781*cda5da8dSAndroid Build Coastguard Worker        """
2782*cda5da8dSAndroid Build Coastguard Worker        proxies = getproxies_environment()
2783*cda5da8dSAndroid Build Coastguard Worker        if proxies:
2784*cda5da8dSAndroid Build Coastguard Worker            return proxy_bypass_environment(host, proxies)
2785*cda5da8dSAndroid Build Coastguard Worker        else:
2786*cda5da8dSAndroid Build Coastguard Worker            return proxy_bypass_registry(host)
2787*cda5da8dSAndroid Build Coastguard Worker
2788*cda5da8dSAndroid Build Coastguard Workerelse:
2789*cda5da8dSAndroid Build Coastguard Worker    # By default use environment variables
2790*cda5da8dSAndroid Build Coastguard Worker    getproxies = getproxies_environment
2791*cda5da8dSAndroid Build Coastguard Worker    proxy_bypass = proxy_bypass_environment
2792