diff --git a/script.module.httplib2/LICENSE b/script.module.httplib2/LICENSE.txt similarity index 100% rename from script.module.httplib2/LICENSE rename to script.module.httplib2/LICENSE.txt diff --git a/script.module.httplib2/README-py3 b/script.module.httplib2/README-py3 deleted file mode 100644 index 5e09bc5e5d..0000000000 --- a/script.module.httplib2/README-py3 +++ /dev/null @@ -1,68 +0,0 @@ -httplib2 for Python 3 - -This directory contains a port of httplib2 to Python 3. As you may -know, Python 3 is not backward-compatible with Python 2. The biggest -change in Python 3 (that affects httplib2) is the distinction between -bytes and strings. - -To successfully use http2lib for Python 3, you absolutely must -understand the following sentence: - -** THE RESPONSE HEADERS ARE STRINGS, BUT THE CONTENT BODY IS BYTES ** - - -Example: - ->>> import httplib2, pprint ->>> h = httplib2.Http(".cache") ->>> (resp_headers, content) = h.request("http://example.org/", "GET") ->>> pprint.pprint(resp_headers) -{'accept-ranges': 'bytes', - 'connection': 'close', - 'content-length': '438', - 'content-location': 'http://example.org/', - 'content-type': 'text/html; charset=UTF-8', - 'date': 'Fri, 29 May 2009 03:57:29 GMT', - 'etag': '"b80f4-1b6-80bfd280"', - 'last-modified': 'Tue, 15 Nov 2005 13:24:10 GMT', - 'server': 'Apache/2.2.3 (CentOS)', - 'status': '200'} ->>> type(content) - ->>> content[:49] -b'\r\n\r\n Example Web Page' - - -Further reading: - - * http://diveintopython3.org/strings.html - * http://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit - * http://docs.python.org/3.0/howto/unicode.html - - --------------------------------------------------------------------- -Httplib2 Software License - -Copyright (c) 2006 by Joe Gregorio -Copyright (c) 2009 by Mark Pilgrim - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/script.module.httplib2/README.md b/script.module.httplib2/README.md deleted file mode 100644 index 329c4a4892..0000000000 --- a/script.module.httplib2/README.md +++ /dev/null @@ -1,113 +0,0 @@ -Introduction -============ - -httplib2 is a comprehensive HTTP client library, httplib2.py supports many -features left out of other HTTP libraries. - -### HTTP and HTTPS - -HTTPS support is only available if the socket module was -compiled with SSL support. - -### Keep-Alive - -Supports HTTP 1.1 Keep-Alive, keeping the socket open and -performing multiple requests over the same connection if -possible. - -### Authentication - -The following three types of HTTP Authentication are -supported. These can be used over both HTTP and HTTPS. - -* Digest -* Basic -* WSSE - -### Caching - -The module can optionally operate with a private cache that -understands the Cache-Control: header and uses both the ETag -and Last-Modified cache validators. - -### All Methods - -The module can handle any HTTP request method, not just GET -and POST. - -### Redirects - -Automatically follows 3XX redirects on GETs. - -### Compression - -Handles both 'deflate' and 'gzip' types of compression. - -### Lost update support - -Automatically adds back ETags into PUT requests to resources -we have already cached. This implements Section 3.2 of -Detecting the Lost Update Problem Using Unreserved Checkout. - -### Unit Tested - -A large and growing set of unit tests. - - -Installation -============ - - - $ pip install httplib2 - - -Usage -===== - -A simple retrieval: - -```python -import httplib2 -h = httplib2.Http(".cache") -(resp_headers, content) = h.request("http://example.org/", "GET") -``` - -The 'content' is the content retrieved from the URL. The content -is already decompressed or unzipped if necessary. - -To PUT some content to a server that uses SSL and Basic authentication: - -```python -import httplib2 -h = httplib2.Http(".cache") -h.add_credentials('name', 'password') -(resp, content) = h.request("https://example.org/chapter/2", - "PUT", body="This is text", - headers={'content-type':'text/plain'} ) -``` - -Use the Cache-Control: header to control how the caching operates. - -```python -import httplib2 -h = httplib2.Http(".cache") -(resp, content) = h.request("http://bitworking.org/", "GET") -... -(resp, content) = h.request("http://bitworking.org/", "GET", - headers={'cache-control':'no-cache'}) -``` - -The first request will be cached and since this is a request -to bitworking.org it will be set to be cached for two hours, -because that is how I have my server configured. Any subsequent -GET to that URI will return the value from the on-disk cache -and no request will be made to the server. You can use the -Cache-Control: header to change the caches behavior and in -this example the second request adds the Cache-Control: -header with a value of 'no-cache' which tells the library -that the cached copy must not be used when handling this request. - -More example usage can be found at: - - * https://github.com/httplib2/httplib2/wiki/Examples - * https://github.com/httplib2/httplib2/wiki/Examples-Python3 diff --git a/script.module.httplib2/addon.xml b/script.module.httplib2/addon.xml index 12de7a11fc..cd92b0a38f 100644 --- a/script.module.httplib2/addon.xml +++ b/script.module.httplib2/addon.xml @@ -1,18 +1,19 @@ - - + + + - + - httplib2 module - httplib2 module - MIT - all - https://pypi.python.org/pypi/httplib2 - https://github.com/httplib2/httplib2 - - icon.png - - + A comprehensive HTTP client library. + A comprehensive HTTP client library. + MIT + all + https://github.com/httplib2/httplib2 + https://github.com/httplib2/httplib2 + + resources/icon.png + + diff --git a/script.module.httplib2/lib/httplib2/__init__.py b/script.module.httplib2/lib/httplib2/__init__.py index 6467c7905b..723a63c5b8 100644 --- a/script.module.httplib2/lib/httplib2/__init__.py +++ b/script.module.httplib2/lib/httplib2/__init__.py @@ -13,9 +13,10 @@ "Louis Nyffenegger", "Mark Pilgrim", "Alex Yu", + "Lai Han", ] __license__ = "MIT" -__version__ = '0.17.0' +__version__ = "0.22.0" import base64 import calendar @@ -49,6 +50,8 @@ # TODO: remove this fallback and copypasted socksipy module upon py2/3 merge, # idea is to have soft-dependency on any compatible module called socks from . import socks +from . import auth +from .error import * from .iri2uri import iri2uri @@ -79,56 +82,6 @@ def has_timeout(timeout): RETRIES = 2 -# All exceptions raised here derive from HttpLib2Error -class HttpLib2Error(Exception): - pass - - -# Some exceptions can be caught and optionally -# be turned back into responses. -class HttpLib2ErrorWithResponse(HttpLib2Error): - def __init__(self, desc, response, content): - self.response = response - self.content = content - HttpLib2Error.__init__(self, desc) - - -class RedirectMissingLocation(HttpLib2ErrorWithResponse): - pass - - -class RedirectLimit(HttpLib2ErrorWithResponse): - pass - - -class FailedToDecompressContent(HttpLib2ErrorWithResponse): - pass - - -class UnimplementedDigestAuthOptionError(HttpLib2ErrorWithResponse): - pass - - -class UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse): - pass - - -class MalformedHeader(HttpLib2Error): - pass - - -class RelativeURIError(HttpLib2Error): - pass - - -class ServerNotFoundError(HttpLib2Error): - pass - - -class ProxiesUnavailableError(HttpLib2Error): - pass - - # Open Items: # ----------- @@ -169,42 +122,58 @@ class ProxiesUnavailableError(HttpLib2Error): from httplib2 import certs + CA_CERTS = certs.where() # PROTOCOL_TLS is python 3.5.3+. PROTOCOL_SSLv23 is deprecated. # Both PROTOCOL_TLS and PROTOCOL_SSLv23 are equivalent and means: # > Selects the highest protocol version that both the client and server support. # > Despite the name, this option can select “TLS” protocols as well as “SSL”. -# source: https://docs.python.org/3.5/library/ssl.html#ssl.PROTOCOL_TLS -DEFAULT_TLS_VERSION = getattr(ssl, "PROTOCOL_TLS", None) or getattr( - ssl, "PROTOCOL_SSLv23" -) +# source: https://docs.python.org/3.5/library/ssl.html#ssl.PROTOCOL_SSLv23 + +# PROTOCOL_TLS_CLIENT is python 3.10.0+. PROTOCOL_TLS is deprecated. +# > Auto-negotiate the highest protocol version that both the client and server support, and configure the context client-side connections. +# > The protocol enables CERT_REQUIRED and check_hostname by default. +# source: https://docs.python.org/3.10/library/ssl.html#ssl.PROTOCOL_TLS + +DEFAULT_TLS_VERSION = getattr(ssl, "PROTOCOL_TLS_CLIENT", None) or getattr(ssl, "PROTOCOL_TLS", None) or getattr(ssl, "PROTOCOL_SSLv23") + def _build_ssl_context( - disable_ssl_certificate_validation, ca_certs, cert_file=None, key_file=None, - maximum_version=None, minimum_version=None, key_password=None, + disable_ssl_certificate_validation, + ca_certs, + cert_file=None, + key_file=None, + maximum_version=None, + minimum_version=None, + key_password=None, ): if not hasattr(ssl, "SSLContext"): raise RuntimeError("httplib2 requires Python 3.2+ for ssl.SSLContext") context = ssl.SSLContext(DEFAULT_TLS_VERSION) - context.verify_mode = ( - ssl.CERT_NONE if disable_ssl_certificate_validation else ssl.CERT_REQUIRED - ) + # check_hostname and verify_mode should be set in opposite order during disable + # https://bugs.python.org/issue31431 + if disable_ssl_certificate_validation and hasattr(context, "check_hostname"): + context.check_hostname = not disable_ssl_certificate_validation + context.verify_mode = ssl.CERT_NONE if disable_ssl_certificate_validation else ssl.CERT_REQUIRED # SSLContext.maximum_version and SSLContext.minimum_version are python 3.7+. # source: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.maximum_version if maximum_version is not None: if hasattr(context, "maximum_version"): - context.maximum_version = getattr(ssl.TLSVersion, maximum_version) + if isinstance(maximum_version, str): + maximum_version = getattr(ssl.TLSVersion, maximum_version) + context.maximum_version = maximum_version else: raise RuntimeError("setting tls_maximum_version requires Python 3.7 and OpenSSL 1.1 or newer") if minimum_version is not None: if hasattr(context, "minimum_version"): - context.minimum_version = getattr(ssl.TLSVersion, minimum_version) + if isinstance(minimum_version, str): + minimum_version = getattr(ssl.TLSVersion, minimum_version) + context.minimum_version = minimum_version else: raise RuntimeError("setting tls_minimum_version requires Python 3.7 and OpenSSL 1.1 or newer") - # check_hostname requires python 3.4+ # we will perform the equivalent in HTTPSConnectionWithTimeout.connect() by calling ssl.match_hostname # if check_hostname is not supported. @@ -225,6 +194,29 @@ def _get_end2end_headers(response): return [header for header in list(response.keys()) if header not in hopbyhop] +_missing = object() + + +def _errno_from_exception(e): + # TODO python 3.11+ cheap try: return e.errno except AttributeError: pass + errno = getattr(e, "errno", _missing) + if errno is not _missing: + return errno + + # socket.error and common wrap in .args + args = getattr(e, "args", None) + if args: + return _errno_from_exception(args[0]) + + # pysocks.ProxyError wraps in .socket_err + # https://github.com/httplib2/httplib2/pull/202 + socket_err = getattr(e, "socket_err", None) + if socket_err: + return _errno_from_exception(socket_err) + + return None + + URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") @@ -288,10 +280,7 @@ def safename(filename): def _normalize_headers(headers): return dict( [ - ( - _convert_byte_str(key).lower(), - NORMALIZE_SPACE.sub(_convert_byte_str(value), " ").strip(), - ) + (_convert_byte_str(key).lower(), NORMALIZE_SPACE.sub(_convert_byte_str(value), " ").strip(),) for (key, value) in headers.items() ] ) @@ -308,13 +297,9 @@ def _parse_cache_control(headers): if "cache-control" in headers: parts = headers["cache-control"].split(",") parts_with_args = [ - tuple([x.strip().lower() for x in part.split("=", 1)]) - for part in parts - if -1 != part.find("=") - ] - parts_wo_args = [ - (name.strip().lower(), 1) for name in parts if -1 == name.find("=") + tuple([x.strip().lower() for x in part.split("=", 1)]) for part in parts if -1 != part.find("=") ] + parts_wo_args = [(name.strip().lower(), 1) for name in parts if -1 == name.find("=")] retval = dict(parts_with_args + parts_wo_args) return retval @@ -325,53 +310,6 @@ def _parse_cache_control(headers): # Set to true to turn on, useful for testing servers. USE_WWW_AUTH_STRICT_PARSING = 0 -# In regex below: -# [^\0-\x1f\x7f-\xff()<>@,;:\\\"/[\]?={} \t]+ matches a "token" as defined by HTTP -# "(?:[^\0-\x08\x0A-\x1f\x7f-\xff\\\"]|\\[\0-\x7f])*?" matches a "quoted-string" as defined by HTTP, when LWS have already been replaced by a single space -# Actually, as an auth-param value can be either a token or a quoted-string, they are combined in a single pattern which matches both: -# \"?((?<=\")(?:[^\0-\x1f\x7f-\xff\\\"]|\\[\0-\x7f])*?(?=\")|(?@,;:\\\"/[\]?={} \t]+(?!\"))\"? -WWW_AUTH_STRICT = re.compile( - r"^(?:\s*(?:,\s*)?([^\0-\x1f\x7f-\xff()<>@,;:\\\"/[\]?={} \t]+)\s*=\s*\"?((?<=\")(?:[^\0-\x08\x0A-\x1f\x7f-\xff\\\"]|\\[\0-\x7f])*?(?=\")|(?@,;:\\\"/[\]?={} \t]+(?!\"))\"?)(.*)$" -) -WWW_AUTH_RELAXED = re.compile( - r"^(?:\s*(?:,\s*)?([^ \t\r\n=]+)\s*=\s*\"?((?<=\")(?:[^\\\"]|\\.)*?(?=\")|(? 0: # service = "wise" - auth = dict( - Email=credentials[0], - Passwd=credentials[1], - service=service, - source=headers["user-agent"], - ) + auth = dict(Email=credentials[0], Passwd=credentials[1], service=service, source=headers["user-agent"],) resp, content = self.http.request( "https://www.google.com/accounts/ClientLogin", method="POST", @@ -916,9 +787,7 @@ class FileCache(object): be running on the same cache. """ - def __init__( - self, cache, safe=safename - ): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior + def __init__(self, cache, safe=safename): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior self.cache = cache self.safe = safe if not os.path.exists(cache): @@ -966,6 +835,7 @@ def iter(self, domain): class KeyCerts(Credentials): """Identical to Credentials except that name/password are mapped to key/cert.""" + def add(self, key, cert, domain, password): self.credentials.append((domain.lower(), key, cert, password)) @@ -985,14 +855,7 @@ class ProxyInfo(object): bypass_hosts = () def __init__( - self, - proxy_type, - proxy_host, - proxy_port, - proxy_rdns=True, - proxy_user=None, - proxy_pass=None, - proxy_headers=None, + self, proxy_type, proxy_host, proxy_port, proxy_rdns=True, proxy_user=None, proxy_pass=None, proxy_headers=None, ): """Args: @@ -1015,7 +878,15 @@ def __init__( proxy_user = proxy_user.decode() if isinstance(proxy_pass, bytes): proxy_pass = proxy_pass.decode() - self.proxy_type, self.proxy_host, self.proxy_port, self.proxy_rdns, self.proxy_user, self.proxy_pass, self.proxy_headers = ( + ( + self.proxy_type, + self.proxy_host, + self.proxy_port, + self.proxy_rdns, + self.proxy_user, + self.proxy_pass, + self.proxy_headers, + ) = ( proxy_type, proxy_host, proxy_port, @@ -1082,34 +953,14 @@ def proxy_info_from_url(url, method="http", noproxy=None): """Construct a ProxyInfo from a URL (such as http_proxy env var) """ url = urllib.parse.urlparse(url) - username = None - password = None - port = None - if "@" in url[1]: - ident, host_port = url[1].split("@", 1) - if ":" in ident: - username, password = ident.split(":", 1) - else: - password = ident - else: - host_port = url[1] - if ":" in host_port: - host, port = host_port.split(":", 1) - else: - host = host_port - - if port: - port = int(port) - else: - port = dict(https=443, http=80)[method] proxy_type = 3 # socks.PROXY_TYPE_HTTP pi = ProxyInfo( proxy_type=proxy_type, - proxy_host=host, - proxy_port=port, - proxy_user=username or None, - proxy_pass=password or None, + proxy_host=url.hostname, + proxy_port=url.port or dict(https=443, http=80)[method], + proxy_user=url.username or None, + proxy_pass=url.password or None, proxy_headers=None, ) @@ -1149,14 +1000,18 @@ def __init__(self, host, port=None, timeout=None, proxy_info=None): def connect(self): """Connect to the host and port specified in __init__.""" if self.proxy_info and socks is None: - raise ProxiesUnavailableError( - "Proxy support missing but proxy use was requested!" - ) + raise ProxiesUnavailableError("Proxy support missing but proxy use was requested!") if self.proxy_info and self.proxy_info.isgood() and self.proxy_info.applies_to(self.host): use_proxy = True - proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = ( - self.proxy_info.astuple() - ) + ( + proxy_type, + proxy_host, + proxy_port, + proxy_rdns, + proxy_user, + proxy_pass, + proxy_headers, + ) = self.proxy_info.astuple() host = proxy_host port = proxy_port @@ -1175,12 +1030,7 @@ def connect(self): if use_proxy: self.sock = socks.socksocket(af, socktype, proto) self.sock.setproxy( - proxy_type, - proxy_host, - proxy_port, - proxy_rdns, - proxy_user, - proxy_pass, + proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, ) else: self.sock = socket.socket(af, socktype, proto) @@ -1188,22 +1038,11 @@ def connect(self): if has_timeout(self.timeout): self.sock.settimeout(self.timeout) if self.debuglevel > 0: - print( - "connect: ({0}, {1}) ************".format(self.host, self.port) - ) + print("connect: ({0}, {1}) ************".format(self.host, self.port)) if use_proxy: print( "proxy: {0} ************".format( - str( - ( - proxy_host, - proxy_port, - proxy_rdns, - proxy_user, - proxy_pass, - proxy_headers, - ) - ) + str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,)) ) ) @@ -1215,16 +1054,7 @@ def connect(self): if use_proxy: print( "proxy: {0}".format( - str( - ( - proxy_host, - proxy_port, - proxy_rdns, - proxy_user, - proxy_pass, - proxy_headers, - ) - ) + str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,)) ) ) if self.sock: @@ -1268,15 +1098,16 @@ def __init__( self.proxy_info = proxy_info("https") context = _build_ssl_context( - self.disable_ssl_certificate_validation, self.ca_certs, cert_file, key_file, - maximum_version=tls_maximum_version, minimum_version=tls_minimum_version, + self.disable_ssl_certificate_validation, + self.ca_certs, + cert_file, + key_file, + maximum_version=tls_maximum_version, + minimum_version=tls_minimum_version, key_password=key_password, ) super(HTTPSConnectionWithTimeout, self).__init__( - host, - port=port, - timeout=timeout, - context=context, + host, port=port, timeout=timeout, context=context, ) self.key_file = key_file self.cert_file = cert_file @@ -1284,11 +1115,17 @@ def __init__( def connect(self): """Connect to a host on a given (SSL) port.""" - if self.proxy_info and self.proxy_info.isgood(): + if self.proxy_info and self.proxy_info.isgood() and self.proxy_info.applies_to(self.host): use_proxy = True - proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = ( - self.proxy_info.astuple() - ) + ( + proxy_type, + proxy_host, + proxy_port, + proxy_rdns, + proxy_user, + proxy_pass, + proxy_headers, + ) = self.proxy_info.astuple() host = proxy_host port = proxy_port @@ -1309,12 +1146,7 @@ def connect(self): sock = socks.socksocket(family, socktype, proto) sock.setproxy( - proxy_type, - proxy_host, - proxy_port, - proxy_rdns, - proxy_user, - proxy_pass, + proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, ) else: sock = socket.socket(family, socktype, proto) @@ -1326,10 +1158,7 @@ def connect(self): self.sock = self._context.wrap_socket(sock, server_hostname=self.host) # Python 3.3 compatibility: emulate the check_hostname behavior - if ( - not hasattr(self._context, "check_hostname") - and not self.disable_ssl_certificate_validation - ): + if not hasattr(self._context, "check_hostname") and not self.disable_ssl_certificate_validation: try: ssl.match_hostname(self.sock.getpeercert(), self.host) except Exception: @@ -1342,16 +1171,7 @@ def connect(self): if use_proxy: print( "proxy: {0}".format( - str( - ( - proxy_host, - proxy_port, - proxy_rdns, - proxy_user, - proxy_pass, - proxy_headers, - ) - ) + str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,)) ) ) except (ssl.SSLError, ssl.CertificateError) as e: @@ -1366,20 +1186,11 @@ def connect(self): except socket.error as e: socket_err = e if self.debuglevel > 0: - print("connect fail: ({0}, {1})".format((self.host, self.port))) + print("connect fail: ({0}, {1})".format(self.host, self.port)) if use_proxy: print( "proxy: {0}".format( - str( - ( - proxy_host, - proxy_port, - proxy_rdns, - proxy_user, - proxy_pass, - proxy_headers, - ) - ) + str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,)) ) ) if self.sock: @@ -1447,7 +1258,7 @@ def __init__( tls_maximum_version / tls_minimum_version require Python 3.7+ / OpenSSL 1.1.0g+. A value of "TLSv1_3" requires OpenSSL 1.1.1+. -""" + """ self.proxy_info = proxy_info self.ca_certs = ca_certs self.disable_ssl_certificate_validation = disable_ssl_certificate_validation @@ -1523,13 +1334,11 @@ def _auth_from_challenge(self, host, request_uri, headers, response, content): """A generator that creates Authorization objects that can be applied to requests. """ - challenges = _parse_www_authenticate(response, "www-authenticate") + challenges = auth._parse_www_authenticate(response, "www-authenticate") for cred in self.credentials.iter(host): for scheme in AUTH_SCHEME_ORDER: if scheme in challenges: - yield AUTH_SCHEME_CLASSES[scheme]( - cred, host, request_uri, headers, response, content, self - ) + yield AUTH_SCHEME_CLASSES[scheme](cred, host, request_uri, headers, response, content, self) def add_credentials(self, name, password, domain=""): """Add a name and password that will be used @@ -1563,9 +1372,7 @@ def _conn_request(self, conn, request_uri, method, body, headers): conn.close() raise ServerNotFoundError("Unable to find the server at %s" % conn.host) except socket.error as e: - errno_ = ( - e.args[0].errno if isinstance(e.args[0], socket.error) else e.errno - ) + errno_ = _errno_from_exception(e) if errno_ in (errno.ENETUNREACH, errno.EADDRNOTAVAIL) and i < RETRIES: continue # retry on potentially transient errors raise @@ -1624,79 +1431,48 @@ def _conn_request(self, conn, request_uri, method, body, headers): return (response, content) def _request( - self, - conn, - host, - absolute_uri, - request_uri, - method, - body, - headers, - redirections, - cachekey, + self, conn, host, absolute_uri, request_uri, method, body, headers, redirections, cachekey, ): """Do the actual request using the connection object and also follow one level of redirects if necessary""" - auths = [ - (auth.depth(request_uri), auth) - for auth in self.authorizations - if auth.inscope(host, request_uri) - ] + auths = [(auth.depth(request_uri), auth) for auth in self.authorizations if auth.inscope(host, request_uri)] auth = auths and sorted(auths)[0][1] or None if auth: auth.request(method, request_uri, headers, body) - (response, content) = self._conn_request( - conn, request_uri, method, body, headers - ) + (response, content) = self._conn_request(conn, request_uri, method, body, headers) if auth: if auth.response(response, body): auth.request(method, request_uri, headers, body) - (response, content) = self._conn_request( - conn, request_uri, method, body, headers - ) + (response, content) = self._conn_request(conn, request_uri, method, body, headers) response._stale_digest = 1 if response.status == 401: - for authorization in self._auth_from_challenge( - host, request_uri, headers, response, content - ): + for authorization in self._auth_from_challenge(host, request_uri, headers, response, content): authorization.request(method, request_uri, headers, body) - (response, content) = self._conn_request( - conn, request_uri, method, body, headers - ) + (response, content) = self._conn_request(conn, request_uri, method, body, headers) if response.status != 401: self.authorizations.append(authorization) authorization.response(response, body) break - if ( - self.follow_all_redirects - or method in self.safe_methods - or response.status in (303, 308) - ): + if self.follow_all_redirects or method in self.safe_methods or response.status in (303, 308): if self.follow_redirects and response.status in self.redirect_codes: # Pick out the location header and basically start from the beginning # remembering first to strip the ETag header and decrement our 'depth' if redirections: if "location" not in response and response.status != 300: raise RedirectMissingLocation( - _( - "Redirected but the response is missing a Location: header." - ), - response, - content, + _("Redirected but the response is missing a Location: header."), response, content, ) # Fix-up relative redirects (which violate an RFC 2616 MUST) if "location" in response: location = response["location"] (scheme, authority, path, query, fragment) = parse_uri(location) if authority == None: - response["location"] = urllib.parse.urljoin( - absolute_uri, location - ) + response["location"] = urllib.parse.urljoin(absolute_uri, location) if response.status == 308 or (response.status == 301 and (method in self.safe_methods)): response["-x-permanent-redirect-url"] = response["location"] if "content-location" not in response: @@ -1706,10 +1482,7 @@ def _request( del headers["if-none-match"] if "if-modified-since" in headers: del headers["if-modified-since"] - if ( - "authorization" in headers - and not self.forward_authorization_headers - ): + if "authorization" in headers and not self.forward_authorization_headers: del headers["authorization"] if "location" in response: location = response["location"] @@ -1721,18 +1494,12 @@ def _request( redirect_method = "GET" body = None (response, content) = self.request( - location, - method=redirect_method, - body=body, - headers=headers, - redirections=redirections - 1, + location, method=redirect_method, body=body, headers=headers, redirections=redirections - 1, ) response.previous = old_response else: raise RedirectLimit( - "Redirected more times than redirection_limit allows.", - response, - content, + "Redirected more times than redirection_limit allows.", response, content, ) elif response.status in [200, 203] and method in self.safe_methods: # Don't cache 206's since we aren't going to handle byte range requests @@ -1750,13 +1517,7 @@ def _normalize_headers(self, headers): # including all socket.* and httplib.* exceptions. def request( - self, - uri, - method="GET", - body=None, - headers=None, - redirections=DEFAULT_MAX_REDIRECTS, - connection_type=None, + self, uri, method="GET", body=None, headers=None, redirections=DEFAULT_MAX_REDIRECTS, connection_type=None, ): """ Performs a single HTTP request. The 'uri' is the URI of the HTTP resource and can begin @@ -1778,7 +1539,7 @@ def request( being and instance of the 'Response' class, the second being a string that contains the response entity body. """ - conn_key = '' + conn_key = "" try: if headers is None: @@ -1790,6 +1551,9 @@ def request( headers["user-agent"] = "Python-httplib2/%s (gzip)" % __version__ uri = iri2uri(uri) + # Prevent CWE-75 space injection to manipulate request via part of uri. + # Prevent CWE-93 CRLF injection to modify headers via part of uri. + uri = uri.replace(" ", "%20").replace("\r", "%0D").replace("\n", "%0A") (scheme, authority, request_uri, defrag_uri) = urlnorm(uri) @@ -1844,9 +1608,7 @@ def request( info = email.message_from_bytes(info) for k, v in info.items(): if v.startswith("=?") and v.endswith("?="): - info.replace_header( - k, str(*email.header.decode_header(v)[0]) - ) + info.replace_header(k, str(*email.header.decode_header(v)[0])) except (IndexError, ValueError): self.cache.delete(cachekey) cachekey = None @@ -1893,9 +1655,7 @@ def request( # Should cached permanent redirects be counted in our redirection count? For now, yes. if redirections <= 0: raise RedirectLimit( - "Redirected more times than redirection_limit allows.", - {}, - "", + "Redirected more times than redirection_limit allows.", {}, "", ) (response, new_content) = self.request( info["-x-permanent-redirect-url"], @@ -1917,20 +1677,12 @@ def request( entry_disposition = _entry_disposition(info, headers) if entry_disposition == "FRESH": - if not cached_value: - info["status"] = "504" - content = b"" response = Response(info) - if cached_value: - response.fromcache = True + response.fromcache = True return (response, content) if entry_disposition == "STALE": - if ( - "etag" in info - and not self.ignore_etag - and not "if-none-match" in headers - ): + if "etag" in info and not self.ignore_etag and not "if-none-match" in headers: headers["if-none-match"] = info["etag"] if "last-modified" in info and not "last-modified" in headers: headers["if-modified-since"] = info["last-modified"] @@ -1938,15 +1690,7 @@ def request( pass (response, new_content) = self._request( - conn, - authority, - uri, - request_uri, - method, - body, - headers, - redirections, - cachekey, + conn, authority, uri, request_uri, method, body, headers, redirections, cachekey, ) if response.status == 304 and method == "GET": @@ -1960,9 +1704,7 @@ def request( merged_response = Response(info) if hasattr(response, "_stale_digest"): merged_response._stale_digest = response._stale_digest - _updateCache( - headers, merged_response, content, self.cache, cachekey - ) + _updateCache(headers, merged_response, content, self.cache, cachekey) response = merged_response response.status = 200 response.fromcache = True @@ -1980,15 +1722,7 @@ def request( content = b"" else: (response, content) = self._request( - conn, - authority, - uri, - request_uri, - method, - body, - headers, - redirections, - cachekey, + conn, authority, uri, request_uri, method, body, headers, redirections, cachekey, ) except Exception as e: is_timeout = isinstance(e, socket.timeout) @@ -2005,23 +1739,11 @@ def request( response.reason = str(e) elif isinstance(e, socket.timeout): content = b"Request Timeout" - response = Response( - { - "content-type": "text/plain", - "status": "408", - "content-length": len(content), - } - ) + response = Response({"content-type": "text/plain", "status": "408", "content-length": len(content),}) response.reason = "Request Timeout" else: content = str(e).encode("utf-8") - response = Response( - { - "content-type": "text/plain", - "status": "400", - "content-length": len(content), - } - ) + response = Response({"content-type": "text/plain", "status": "400", "content-length": len(content),}) response.reason = "Bad Request" else: raise diff --git a/script.module.httplib2/lib/httplib2/auth.py b/script.module.httplib2/lib/httplib2/auth.py new file mode 100644 index 0000000000..b8028ae2a7 --- /dev/null +++ b/script.module.httplib2/lib/httplib2/auth.py @@ -0,0 +1,69 @@ +import base64 +import re + +import pyparsing as pp + +from .error import * + + +try: # pyparsing>=3.0.0 + downcaseTokens = pp.common.downcaseTokens +except AttributeError: + downcaseTokens = pp.downcaseTokens + +UNQUOTE_PAIRS = re.compile(r"\\(.)") +unquote = lambda s, l, t: UNQUOTE_PAIRS.sub(r"\1", t[0][1:-1]) + +# https://tools.ietf.org/html/rfc7235#section-1.2 +# https://tools.ietf.org/html/rfc7235#appendix-B +tchar = "!#$%&'*+-.^_`|~" + pp.nums + pp.alphas +token = pp.Word(tchar).setName("token") +token68 = pp.Combine(pp.Word("-._~+/" + pp.nums + pp.alphas) + pp.Optional(pp.Word("=").leaveWhitespace())).setName( + "token68" +) + +quoted_string = pp.dblQuotedString.copy().setName("quoted-string").setParseAction(unquote) +auth_param_name = token.copy().setName("auth-param-name").addParseAction(downcaseTokens) +auth_param = auth_param_name + pp.Suppress("=") + (quoted_string | token) +params = pp.Dict(pp.delimitedList(pp.Group(auth_param))) + +scheme = token("scheme") +challenge = scheme + (params("params") | token68("token")) + +authentication_info = params.copy() +www_authenticate = pp.delimitedList(pp.Group(challenge)) + + +def _parse_authentication_info(headers, headername="authentication-info"): + """https://tools.ietf.org/html/rfc7615 + """ + header = headers.get(headername, "").strip() + if not header: + return {} + try: + parsed = authentication_info.parseString(header) + except pp.ParseException as ex: + # print(ex.explain(ex)) + raise MalformedHeader(headername) + + return parsed.asDict() + + +def _parse_www_authenticate(headers, headername="www-authenticate"): + """Returns a dictionary of dictionaries, one dict per auth_scheme.""" + header = headers.get(headername, "").strip() + if not header: + return {} + try: + parsed = www_authenticate.parseString(header) + except pp.ParseException as ex: + # print(ex.explain(ex)) + raise MalformedHeader(headername) + + retval = { + challenge["scheme"].lower(): challenge["params"].asDict() + if "params" in challenge + else {"token": challenge.get("token")} + for challenge in parsed + } + return retval diff --git a/script.module.httplib2/lib/httplib2/cacerts.txt b/script.module.httplib2/lib/httplib2/cacerts.txt index 8020c1b4d5..78a444c43a 100644 --- a/script.module.httplib2/lib/httplib2/cacerts.txt +++ b/script.module.httplib2/lib/httplib2/cacerts.txt @@ -2138,34 +2138,6 @@ hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI -----END CERTIFICATE----- -# Issuer: O=Digital Signature Trust Co., CN=DST Root CA X3 -# Subject: O=Digital Signature Trust Co., CN=DST Root CA X3 -# Label: "IdenTrust DST Root CA X3" -# Serial: 44AFB080D6A327BA893039862EF8406B -# MD5 Fingerprint: 41:03:52:DC:0F:F7:50:1B:16:F0:02:8E:BA:6F:45:C5 -# SHA1 Fingerprint: DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13 -# SHA256 Fingerprint: 06:87:26:03:31:A7:24:03:D9:09:F1:05:E6:9B:CF:0D:32:E1:BD:24:93:FF:C6:D9:20:6D:11:BC:D6:77:07:39 ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow -PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD -Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O -rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq -OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b -xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw -7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD -aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG -SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 -ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr -AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz -R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 -JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo -Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - # Issuer: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US # Subject: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US # Serial: 33af1e6a711a9a0bb2864b11d09fae5 @@ -2195,3 +2167,59 @@ pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl MrY= -----END CERTIFICATE----- +# Issuer: /C=US/O=Internet Security Research Group/CN=ISRG Root X1 +# Subject: /C=US/O=Internet Security Research Group/CN=ISRG Root X1 +# Serial: 8210CFB0D240E3594463E0BB63828B00 +# SHA1 Fingerprint: CA:BD:2A:79:A1:07:6A:31:F2:1D:25:36:35:CB:03:9D:43:29:A5:E8 +# SHA256 Fingerprint: 96:BC:EC:06:26:49:76:F3:74:60:77:9A:CF:28:C5:A7:CF:E8:A3:C0:AA:E1:1A:8F:FC:EE:05:C0:BD:DF:08:C6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: /C=US/O=Internet Security Research Group/CN=ISRG Root X2 +# Subject: /C=US/O=Internet Security Research Group/CN=ISRG Root X2 +# Serial: 41D29DD172EAEEA780C12C6CE92F8752 +# SHA1 Fingerprint: BD:B1:B9:3C:D5:97:8D:45:C6:26:14:55:F8:DB:95:C7:5A:D1:53:AF +# SHA256 Fingerprint: 69:72:9B:8E:15:A8:6E:FC:17:7A:57:AF:B7:17:1D:FC:64:AD:D2:8C:2F:CA:8C:F1:50:7E:34:45:3C:CB:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- diff --git a/script.module.httplib2/lib/httplib2/error.py b/script.module.httplib2/lib/httplib2/error.py new file mode 100644 index 0000000000..0e68c12a85 --- /dev/null +++ b/script.module.httplib2/lib/httplib2/error.py @@ -0,0 +1,48 @@ +# All exceptions raised here derive from HttpLib2Error +class HttpLib2Error(Exception): + pass + + +# Some exceptions can be caught and optionally +# be turned back into responses. +class HttpLib2ErrorWithResponse(HttpLib2Error): + def __init__(self, desc, response, content): + self.response = response + self.content = content + HttpLib2Error.__init__(self, desc) + + +class RedirectMissingLocation(HttpLib2ErrorWithResponse): + pass + + +class RedirectLimit(HttpLib2ErrorWithResponse): + pass + + +class FailedToDecompressContent(HttpLib2ErrorWithResponse): + pass + + +class UnimplementedDigestAuthOptionError(HttpLib2ErrorWithResponse): + pass + + +class UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse): + pass + + +class MalformedHeader(HttpLib2Error): + pass + + +class RelativeURIError(HttpLib2Error): + pass + + +class ServerNotFoundError(HttpLib2Error): + pass + + +class ProxiesUnavailableError(HttpLib2Error): + pass diff --git a/script.module.httplib2/lib/httplib2/test/other_cacerts.txt b/script.module.httplib2/lib/httplib2/test/other_cacerts.txt deleted file mode 100644 index 360954a286..0000000000 --- a/script.module.httplib2/lib/httplib2/test/other_cacerts.txt +++ /dev/null @@ -1,70 +0,0 @@ -# Certifcate Authority certificates for validating SSL connections. -# -# This file contains PEM format certificates generated from -# http://mxr.mozilla.org/seamonkey/source/security/nss/lib/ckfw/builtins/certdata.txt -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is the Netscape security libraries. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1994-2000 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - - -Comodo CA Limited, CN=Trusted Certificate Services -================================================== - ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 -aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla -MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO -BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD -VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW -fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt -TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL -fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW -1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 -kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G -A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v -ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo -dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu -Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ -HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 -pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS -jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ -xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn -dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi ------END CERTIFICATE----- diff --git a/script.module.httplib2/icon.png b/script.module.httplib2/resources/icon.png similarity index 100% rename from script.module.httplib2/icon.png rename to script.module.httplib2/resources/icon.png