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