feat(ST2.UtilPackages): bump up all packages
- Refresh PackageCache with latest versions of everything
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import sys
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
import urllib2
|
||||
import httplib
|
||||
|
||||
# Monkey patch AbstractBasicAuthHandler to prevent infinite recursion
|
||||
def non_recursive_http_error_auth_reqed(self, authreq, host, req, headers):
|
||||
authreq = headers.get(authreq, None)
|
||||
|
||||
if not hasattr(self, 'retried'):
|
||||
self.retried = 0
|
||||
|
||||
if self.retried > 5:
|
||||
raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed",
|
||||
headers, None)
|
||||
else:
|
||||
self.retried += 1
|
||||
|
||||
if authreq:
|
||||
mo = urllib2.AbstractBasicAuthHandler.rx.search(authreq)
|
||||
if mo:
|
||||
scheme, quote, realm = mo.groups()
|
||||
if scheme.lower() == 'basic':
|
||||
return self.retry_http_basic_auth(host, req, realm)
|
||||
|
||||
urllib2.AbstractBasicAuthHandler.http_error_auth_reqed = non_recursive_http_error_auth_reqed
|
||||
|
||||
# Money patch urllib2.Request and httplib.HTTPConnection so that
|
||||
# HTTPS proxies work in Python 2.6.1-2
|
||||
if sys.version_info < (2, 6, 3):
|
||||
|
||||
urllib2.Request._tunnel_host = None
|
||||
|
||||
def py268_set_proxy(self, host, type):
|
||||
if self.type == 'https' and not self._tunnel_host:
|
||||
self._tunnel_host = self.host
|
||||
else:
|
||||
self.type = type
|
||||
# The _Request prefix is to handle python private name mangling
|
||||
self._Request__r_host = self._Request__original
|
||||
self.host = host
|
||||
urllib2.Request.set_proxy = py268_set_proxy
|
||||
|
||||
if sys.version_info < (2, 6, 5):
|
||||
|
||||
def py268_set_tunnel(self, host, port=None, headers=None):
|
||||
""" Sets up the host and the port for the HTTP CONNECT Tunnelling.
|
||||
|
||||
The headers argument should be a mapping of extra HTTP headers
|
||||
to send with the CONNECT request.
|
||||
"""
|
||||
self._tunnel_host = host
|
||||
self._tunnel_port = port
|
||||
if headers:
|
||||
self._tunnel_headers = headers
|
||||
else:
|
||||
self._tunnel_headers.clear()
|
||||
httplib.HTTPConnection._set_tunnel = py268_set_tunnel
|
||||
|
||||
|
||||
except (ImportError):
|
||||
# Python 3 does not need to be patched
|
||||
pass
|
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
try:
|
||||
# Python 3
|
||||
from http.client import HTTPConnection
|
||||
from urllib.error import URLError
|
||||
except (ImportError):
|
||||
# Python 2
|
||||
from httplib import HTTPConnection
|
||||
from urllib2 import URLError
|
||||
|
||||
from ..console_write import console_write
|
||||
from .debuggable_http_response import DebuggableHTTPResponse
|
||||
|
||||
|
||||
class DebuggableHTTPConnection(HTTPConnection):
|
||||
"""
|
||||
A custom HTTPConnection that formats debugging info for Sublime Text
|
||||
"""
|
||||
|
||||
response_class = DebuggableHTTPResponse
|
||||
_debug_protocol = 'HTTP'
|
||||
|
||||
def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
**kwargs):
|
||||
self.passwd = kwargs.get('passwd')
|
||||
|
||||
# Python 2.6.1 on OS X 10.6 does not include these
|
||||
self._tunnel_host = None
|
||||
self._tunnel_port = None
|
||||
self._tunnel_headers = {}
|
||||
if 'debug' in kwargs and kwargs['debug']:
|
||||
self.debuglevel = 5
|
||||
elif 'debuglevel' in kwargs:
|
||||
self.debuglevel = kwargs['debuglevel']
|
||||
|
||||
HTTPConnection.__init__(self, host, port=port, timeout=timeout)
|
||||
|
||||
def connect(self):
|
||||
if self.debuglevel == -1:
|
||||
console_write(u'Urllib %s Debug General' % self._debug_protocol, True)
|
||||
console_write(u" Connecting to %s on port %s" % (self.host, self.port))
|
||||
HTTPConnection.connect(self)
|
||||
|
||||
def send(self, string):
|
||||
# We have to use a positive debuglevel to get it passed to the
|
||||
# HTTPResponse object, however we don't want to use it because by
|
||||
# default debugging prints to the stdout and we can't capture it, so
|
||||
# we temporarily set it to -1 for the standard httplib code
|
||||
reset_debug = False
|
||||
if self.debuglevel == 5:
|
||||
reset_debug = 5
|
||||
self.debuglevel = -1
|
||||
HTTPConnection.send(self, string)
|
||||
if reset_debug or self.debuglevel == -1:
|
||||
if len(string.strip()) > 0:
|
||||
console_write(u'Urllib %s Debug Write' % self._debug_protocol, True)
|
||||
for line in string.strip().splitlines():
|
||||
console_write(u' ' + line.decode('iso-8859-1'))
|
||||
if reset_debug:
|
||||
self.debuglevel = reset_debug
|
||||
|
||||
def request(self, method, url, body=None, headers={}):
|
||||
original_headers = headers.copy()
|
||||
|
||||
# By default urllib2 and urllib.request override the Connection header,
|
||||
# however, it is preferred to be able to re-use it
|
||||
original_headers['Connection'] = 'Keep-Alive'
|
||||
|
||||
HTTPConnection.request(self, method, url, body, original_headers)
|
@@ -0,0 +1,35 @@
|
||||
import sys
|
||||
|
||||
try:
|
||||
# Python 3
|
||||
from urllib.request import HTTPHandler
|
||||
except (ImportError):
|
||||
# Python 2
|
||||
from urllib2 import HTTPHandler
|
||||
|
||||
from .debuggable_http_connection import DebuggableHTTPConnection
|
||||
from .persistent_handler import PersistentHandler
|
||||
|
||||
|
||||
class DebuggableHTTPHandler(PersistentHandler, HTTPHandler):
|
||||
"""
|
||||
A custom HTTPHandler that formats debugging info for Sublime Text
|
||||
"""
|
||||
|
||||
def __init__(self, debuglevel=0, debug=False, **kwargs):
|
||||
# This is a special value that will not trigger the standard debug
|
||||
# functionality, but custom code where we can format the output
|
||||
if debug:
|
||||
self._debuglevel = 5
|
||||
else:
|
||||
self._debuglevel = debuglevel
|
||||
self.passwd = kwargs.get('passwd')
|
||||
|
||||
def http_open(self, req):
|
||||
def http_class_wrapper(host, **kwargs):
|
||||
kwargs['passwd'] = self.passwd
|
||||
if 'debuglevel' not in kwargs:
|
||||
kwargs['debuglevel'] = self._debuglevel
|
||||
return DebuggableHTTPConnection(host, **kwargs)
|
||||
|
||||
return self.do_open(http_class_wrapper, req)
|
@@ -0,0 +1,66 @@
|
||||
try:
|
||||
# Python 3
|
||||
from http.client import HTTPResponse, IncompleteRead
|
||||
except (ImportError):
|
||||
# Python 2
|
||||
from httplib import HTTPResponse, IncompleteRead
|
||||
|
||||
from ..console_write import console_write
|
||||
|
||||
|
||||
class DebuggableHTTPResponse(HTTPResponse):
|
||||
"""
|
||||
A custom HTTPResponse that formats debugging info for Sublime Text
|
||||
"""
|
||||
|
||||
_debug_protocol = 'HTTP'
|
||||
|
||||
def __init__(self, sock, debuglevel=0, method=None, **kwargs):
|
||||
# We have to use a positive debuglevel to get it passed to here,
|
||||
# however we don't want to use it because by default debugging prints
|
||||
# to the stdout and we can't capture it, so we use a special -1 value
|
||||
if debuglevel == 5:
|
||||
debuglevel = -1
|
||||
HTTPResponse.__init__(self, sock, debuglevel=debuglevel, method=method)
|
||||
|
||||
def begin(self):
|
||||
return_value = HTTPResponse.begin(self)
|
||||
if self.debuglevel == -1:
|
||||
console_write(u'Urllib %s Debug Read' % self._debug_protocol, True)
|
||||
|
||||
# Python 2
|
||||
if hasattr(self.msg, 'headers'):
|
||||
headers = self.msg.headers
|
||||
# Python 3
|
||||
else:
|
||||
headers = []
|
||||
for header in self.msg:
|
||||
headers.append("%s: %s" % (header, self.msg[header]))
|
||||
|
||||
versions = {
|
||||
9: 'HTTP/0.9',
|
||||
10: 'HTTP/1.0',
|
||||
11: 'HTTP/1.1'
|
||||
}
|
||||
status_line = versions[self.version] + ' ' + str(self.status) + ' ' + self.reason
|
||||
headers.insert(0, status_line)
|
||||
for line in headers:
|
||||
console_write(u" %s" % line.rstrip())
|
||||
return return_value
|
||||
|
||||
def is_keep_alive(self):
|
||||
# Python 2
|
||||
if hasattr(self.msg, 'headers'):
|
||||
connection = self.msg.getheader('connection')
|
||||
# Python 3
|
||||
else:
|
||||
connection = self.msg['connection']
|
||||
if connection and connection.lower() == 'keep-alive':
|
||||
return True
|
||||
return False
|
||||
|
||||
def read(self, *args):
|
||||
try:
|
||||
return HTTPResponse.read(self, *args)
|
||||
except (IncompleteRead) as e:
|
||||
return e.partial
|
@@ -0,0 +1,9 @@
|
||||
from .debuggable_http_response import DebuggableHTTPResponse
|
||||
|
||||
|
||||
class DebuggableHTTPSResponse(DebuggableHTTPResponse):
|
||||
"""
|
||||
A version of DebuggableHTTPResponse that sets the debug protocol to HTTPS
|
||||
"""
|
||||
|
||||
_debug_protocol = 'HTTPS'
|
@@ -0,0 +1,25 @@
|
||||
try:
|
||||
# Python 3
|
||||
from http.client import HTTPException
|
||||
from urllib.error import URLError
|
||||
except (ImportError):
|
||||
# Python 2
|
||||
from httplib import HTTPException
|
||||
from urllib2 import URLError
|
||||
|
||||
|
||||
class InvalidCertificateException(HTTPException, URLError):
|
||||
"""
|
||||
An exception for when an SSL certification is not valid for the URL
|
||||
it was presented for.
|
||||
"""
|
||||
|
||||
def __init__(self, host, cert, reason):
|
||||
HTTPException.__init__(self)
|
||||
self.host = host
|
||||
self.cert = cert
|
||||
self.reason = reason
|
||||
|
||||
def __str__(self):
|
||||
return ('Host %s returned an invalid certificate (%s) %s\n' %
|
||||
(self.host, self.reason, self.cert))
|
@@ -0,0 +1,116 @@
|
||||
import sys
|
||||
import socket
|
||||
|
||||
try:
|
||||
# Python 3
|
||||
from urllib.error import URLError
|
||||
except ImportError:
|
||||
# Python 2
|
||||
from urllib2 import URLError
|
||||
from urllib import addinfourl
|
||||
|
||||
from ..console_write import console_write
|
||||
|
||||
|
||||
class PersistentHandler:
|
||||
connection = None
|
||||
use_count = 0
|
||||
|
||||
def close(self):
|
||||
if self.connection:
|
||||
if self._debuglevel == 5:
|
||||
s = '' if self.use_count == 1 else 's'
|
||||
console_write(u"Urllib %s Debug General" % self.connection._debug_protocol, True)
|
||||
console_write(u" Closing connection to %s on port %s after %s request%s" % (
|
||||
self.connection.host, self.connection.port, self.use_count, s))
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
self.use_count = 0
|
||||
|
||||
def do_open(self, http_class, req):
|
||||
# Large portions from Python 3.3 Lib/urllib/request.py and
|
||||
# Python 2.6 Lib/urllib2.py
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
host = req.host
|
||||
else:
|
||||
host = req.get_host()
|
||||
|
||||
if not host:
|
||||
raise URLError('no host given')
|
||||
|
||||
if self.connection and self.connection.host != host:
|
||||
self.close()
|
||||
|
||||
# Re-use the connection if possible
|
||||
self.use_count += 1
|
||||
if not self.connection:
|
||||
h = http_class(host, timeout=req.timeout)
|
||||
else:
|
||||
h = self.connection
|
||||
if self._debuglevel == 5:
|
||||
console_write(u"Urllib %s Debug General" % h._debug_protocol, True)
|
||||
console_write(u" Re-using connection to %s on port %s for request #%s" % (
|
||||
h.host, h.port, self.use_count))
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
headers = dict(req.unredirected_hdrs)
|
||||
headers.update(dict((k, v) for k, v in req.headers.items()
|
||||
if k not in headers))
|
||||
headers = dict((name.title(), val) for name, val in headers.items())
|
||||
|
||||
else:
|
||||
h.set_debuglevel(self._debuglevel)
|
||||
|
||||
headers = dict(req.headers)
|
||||
headers.update(req.unredirected_hdrs)
|
||||
headers = dict(
|
||||
(name.title(), val) for name, val in headers.items())
|
||||
|
||||
if req._tunnel_host and not self.connection:
|
||||
tunnel_headers = {}
|
||||
proxy_auth_hdr = "Proxy-Authorization"
|
||||
if proxy_auth_hdr in headers:
|
||||
tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr]
|
||||
del headers[proxy_auth_hdr]
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
|
||||
else:
|
||||
h._set_tunnel(req._tunnel_host, headers=tunnel_headers)
|
||||
|
||||
try:
|
||||
if sys.version_info >= (3,):
|
||||
h.request(req.get_method(), req.selector, req.data, headers)
|
||||
else:
|
||||
h.request(req.get_method(), req.get_selector(), req.data, headers)
|
||||
except socket.error as err: # timeout error
|
||||
h.close()
|
||||
raise URLError(err)
|
||||
else:
|
||||
r = h.getresponse()
|
||||
|
||||
# Keep the connection around for re-use
|
||||
if r.is_keep_alive():
|
||||
self.connection = h
|
||||
else:
|
||||
if self._debuglevel == 5:
|
||||
s = '' if self.use_count == 1 else 's'
|
||||
console_write(u"Urllib %s Debug General" % h._debug_protocol, True)
|
||||
console_write(u" Closing connection to %s on port %s after %s request%s" % (
|
||||
h.host, h.port, self.use_count, s))
|
||||
self.use_count = 0
|
||||
self.connection = None
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
r.url = req.get_full_url()
|
||||
r.msg = r.reason
|
||||
return r
|
||||
|
||||
r.recv = r.read
|
||||
fp = socket._fileobject(r, close=True)
|
||||
|
||||
resp = addinfourl(fp, r.msg, req.get_full_url())
|
||||
resp.code = r.status
|
||||
resp.msg = r.reason
|
||||
return resp
|
@@ -0,0 +1,345 @@
|
||||
import re
|
||||
import socket
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
# Python 3
|
||||
from http.client import HTTPS_PORT
|
||||
from urllib.request import parse_keqv_list, parse_http_list
|
||||
except (ImportError):
|
||||
# Python 2
|
||||
from httplib import HTTPS_PORT
|
||||
from urllib2 import parse_keqv_list, parse_http_list
|
||||
|
||||
from ..console_write import console_write
|
||||
from .debuggable_https_response import DebuggableHTTPSResponse
|
||||
from .debuggable_http_connection import DebuggableHTTPConnection
|
||||
from .invalid_certificate_exception import InvalidCertificateException
|
||||
|
||||
|
||||
# The following code is wrapped in a try because the Linux versions of Sublime
|
||||
# Text do not include the ssl module due to the fact that different distros
|
||||
# have different versions
|
||||
try:
|
||||
import ssl
|
||||
|
||||
class ValidatingHTTPSConnection(DebuggableHTTPConnection):
|
||||
"""
|
||||
A custom HTTPConnection class that validates SSL certificates, and
|
||||
allows proxy authentication for HTTPS connections.
|
||||
"""
|
||||
|
||||
default_port = HTTPS_PORT
|
||||
|
||||
response_class = DebuggableHTTPSResponse
|
||||
_debug_protocol = 'HTTPS'
|
||||
|
||||
def __init__(self, host, port=None, key_file=None, cert_file=None,
|
||||
ca_certs=None, **kwargs):
|
||||
passed_args = {}
|
||||
if 'timeout' in kwargs:
|
||||
passed_args['timeout'] = kwargs['timeout']
|
||||
if 'debug' in kwargs:
|
||||
passed_args['debug'] = kwargs['debug']
|
||||
DebuggableHTTPConnection.__init__(self, host, port, **passed_args)
|
||||
|
||||
self.passwd = kwargs.get('passwd')
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.ca_certs = ca_certs
|
||||
if 'user_agent' in kwargs:
|
||||
self.user_agent = kwargs['user_agent']
|
||||
if self.ca_certs:
|
||||
self.cert_reqs = ssl.CERT_REQUIRED
|
||||
else:
|
||||
self.cert_reqs = ssl.CERT_NONE
|
||||
|
||||
def get_valid_hosts_for_cert(self, cert):
|
||||
"""
|
||||
Returns a list of valid hostnames for an SSL certificate
|
||||
|
||||
:param cert: A dict from SSLSocket.getpeercert()
|
||||
|
||||
:return: An array of hostnames
|
||||
"""
|
||||
|
||||
if 'subjectAltName' in cert:
|
||||
return [x[1] for x in cert['subjectAltName']
|
||||
if x[0].lower() == 'dns']
|
||||
else:
|
||||
return [x[0][1] for x in cert['subject']
|
||||
if x[0][0].lower() == 'commonname']
|
||||
|
||||
def validate_cert_host(self, cert, hostname):
|
||||
"""
|
||||
Checks if the cert is valid for the hostname
|
||||
|
||||
:param cert: A dict from SSLSocket.getpeercert()
|
||||
|
||||
:param hostname: A string hostname to check
|
||||
|
||||
:return: A boolean if the cert is valid for the hostname
|
||||
"""
|
||||
|
||||
hosts = self.get_valid_hosts_for_cert(cert)
|
||||
for host in hosts:
|
||||
host_re = host.replace('.', '\.').replace('*', '[^.]*')
|
||||
if re.search('^%s$' % (host_re,), hostname, re.I):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _tunnel(self):
|
||||
"""
|
||||
This custom _tunnel method allows us to read and print the debug
|
||||
log for the whole response before throwing an error, and adds
|
||||
support for proxy authentication
|
||||
"""
|
||||
|
||||
self._proxy_host = self.host
|
||||
self._proxy_port = self.port
|
||||
self._set_hostport(self._tunnel_host, self._tunnel_port)
|
||||
|
||||
self._tunnel_headers['Host'] = u"%s:%s" % (self.host, self.port)
|
||||
self._tunnel_headers['User-Agent'] = self.user_agent
|
||||
self._tunnel_headers['Proxy-Connection'] = 'Keep-Alive'
|
||||
|
||||
request = "CONNECT %s:%d HTTP/1.1\r\n" % (self.host, self.port)
|
||||
for header, value in self._tunnel_headers.items():
|
||||
request += "%s: %s\r\n" % (header, value)
|
||||
request += "\r\n"
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
request = bytes(request, 'iso-8859-1')
|
||||
|
||||
self.send(request)
|
||||
|
||||
response = self.response_class(self.sock, method=self._method)
|
||||
(version, code, message) = response._read_status()
|
||||
|
||||
status_line = u"%s %s %s" % (version, code, message.rstrip())
|
||||
headers = [status_line]
|
||||
|
||||
if self.debuglevel in [-1, 5]:
|
||||
console_write(u'Urllib %s Debug Read' % self._debug_protocol, True)
|
||||
console_write(u" %s" % status_line)
|
||||
|
||||
content_length = 0
|
||||
close_connection = False
|
||||
while True:
|
||||
line = response.fp.readline()
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
line = str(line, encoding='iso-8859-1')
|
||||
|
||||
if line == '\r\n':
|
||||
break
|
||||
|
||||
headers.append(line.rstrip())
|
||||
|
||||
parts = line.rstrip().split(': ', 1)
|
||||
name = parts[0].lower()
|
||||
value = parts[1].lower().strip()
|
||||
if name == 'content-length':
|
||||
content_length = int(value)
|
||||
|
||||
if name in ['connection', 'proxy-connection'] and value == 'close':
|
||||
close_connection = True
|
||||
|
||||
if self.debuglevel in [-1, 5]:
|
||||
console_write(u" %s" % line.rstrip())
|
||||
|
||||
# Handle proxy auth for SSL connections since regular urllib punts on this
|
||||
if code == 407 and self.passwd and 'Proxy-Authorization' not in self._tunnel_headers:
|
||||
if content_length:
|
||||
response._safe_read(content_length)
|
||||
|
||||
supported_auth_methods = {}
|
||||
for line in headers:
|
||||
parts = line.split(': ', 1)
|
||||
if parts[0].lower() != 'proxy-authenticate':
|
||||
continue
|
||||
details = parts[1].split(' ', 1)
|
||||
supported_auth_methods[details[0].lower()] = details[1] if len(details) > 1 else ''
|
||||
|
||||
username, password = self.passwd.find_user_password(None, "%s:%s" % (
|
||||
self._proxy_host, self._proxy_port))
|
||||
|
||||
if 'digest' in supported_auth_methods:
|
||||
response_value = self.build_digest_response(
|
||||
supported_auth_methods['digest'], username, password)
|
||||
if response_value:
|
||||
self._tunnel_headers['Proxy-Authorization'] = u"Digest %s" % response_value
|
||||
|
||||
elif 'basic' in supported_auth_methods:
|
||||
response_value = u"%s:%s" % (username, password)
|
||||
response_value = base64.b64encode(response_value).strip()
|
||||
self._tunnel_headers['Proxy-Authorization'] = u"Basic %s" % response_value
|
||||
|
||||
if 'Proxy-Authorization' in self._tunnel_headers:
|
||||
self.host = self._proxy_host
|
||||
self.port = self._proxy_port
|
||||
|
||||
# If the proxy wanted the connection closed, we need to make a new connection
|
||||
if close_connection:
|
||||
self.sock.close()
|
||||
self.sock = socket.create_connection((self.host, self.port), self.timeout)
|
||||
|
||||
return self._tunnel()
|
||||
|
||||
if code != 200:
|
||||
self.close()
|
||||
raise socket.error("Tunnel connection failed: %d %s" % (code,
|
||||
message.strip()))
|
||||
|
||||
def build_digest_response(self, fields, username, password):
|
||||
"""
|
||||
Takes a Proxy-Authenticate: Digest header and creates a response
|
||||
header
|
||||
|
||||
:param fields:
|
||||
The string portion of the Proxy-Authenticate header after
|
||||
"Digest "
|
||||
|
||||
:param username:
|
||||
The username to use for the response
|
||||
|
||||
:param password:
|
||||
The password to use for the response
|
||||
|
||||
:return:
|
||||
None if invalid Proxy-Authenticate header, otherwise the
|
||||
string of fields for the Proxy-Authorization: Digest header
|
||||
"""
|
||||
|
||||
fields = parse_keqv_list(parse_http_list(fields))
|
||||
|
||||
realm = fields.get('realm')
|
||||
nonce = fields.get('nonce')
|
||||
qop = fields.get('qop')
|
||||
algorithm = fields.get('algorithm')
|
||||
if algorithm:
|
||||
algorithm = algorithm.lower()
|
||||
opaque = fields.get('opaque')
|
||||
|
||||
if algorithm in ['md5', None]:
|
||||
def md5hash(string):
|
||||
return hashlib.md5(string).hexdigest()
|
||||
hash = md5hash
|
||||
|
||||
elif algorithm == 'sha':
|
||||
def sha1hash(string):
|
||||
return hashlib.sha1(string).hexdigest()
|
||||
hash = sha1hash
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
host_port = u"%s:%s" % (self.host, self.port)
|
||||
|
||||
a1 = "%s:%s:%s" % (username, realm, password)
|
||||
a2 = "CONNECT:%s" % host_port
|
||||
ha1 = hash(a1)
|
||||
ha2 = hash(a2)
|
||||
|
||||
if qop == None:
|
||||
response = hash(u"%s:%s:%s" % (ha1, nonce, ha2))
|
||||
elif qop == 'auth':
|
||||
nc = '00000001'
|
||||
cnonce = hash(os.urandom(8))[:8]
|
||||
response = hash(u"%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2))
|
||||
else:
|
||||
return None
|
||||
|
||||
response_fields = {
|
||||
'username': username,
|
||||
'realm': realm,
|
||||
'nonce': nonce,
|
||||
'response': response,
|
||||
'uri': host_port
|
||||
}
|
||||
if algorithm:
|
||||
response_fields['algorithm'] = algorithm
|
||||
if qop == 'auth':
|
||||
response_fields['nc'] = nc
|
||||
response_fields['cnonce'] = cnonce
|
||||
response_fields['qop'] = qop
|
||||
if opaque:
|
||||
response_fields['opaque'] = opaque
|
||||
|
||||
return ', '.join([u"%s=\"%s\"" % (field, response_fields[field]) for field in response_fields])
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Adds debugging and SSL certification validation
|
||||
"""
|
||||
|
||||
if self.debuglevel == -1:
|
||||
console_write(u"Urllib HTTPS Debug General", True)
|
||||
console_write(u" Connecting to %s on port %s" % (self.host, self.port))
|
||||
|
||||
self.sock = socket.create_connection((self.host, self.port), self.timeout)
|
||||
if self._tunnel_host:
|
||||
self._tunnel()
|
||||
|
||||
if self.debuglevel == -1:
|
||||
console_write(u"Urllib HTTPS Debug General", True)
|
||||
console_write(u" Connecting to %s on port %s" % (self.host, self.port))
|
||||
console_write(u" CA certs file at %s" % (self.ca_certs.decode(sys.getfilesystemencoding())))
|
||||
|
||||
self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file,
|
||||
certfile=self.cert_file, cert_reqs=self.cert_reqs,
|
||||
ca_certs=self.ca_certs)
|
||||
|
||||
if self.debuglevel == -1:
|
||||
console_write(u" Successfully upgraded connection to %s:%s with SSL" % (
|
||||
self.host, self.port))
|
||||
|
||||
# This debugs and validates the SSL certificate
|
||||
if self.cert_reqs & ssl.CERT_REQUIRED:
|
||||
cert = self.sock.getpeercert()
|
||||
|
||||
if self.debuglevel == -1:
|
||||
subjectMap = {
|
||||
'organizationName': 'O',
|
||||
'commonName': 'CN',
|
||||
'organizationalUnitName': 'OU',
|
||||
'countryName': 'C',
|
||||
'serialNumber': 'serialNumber',
|
||||
'commonName': 'CN',
|
||||
'localityName': 'L',
|
||||
'stateOrProvinceName': 'S'
|
||||
}
|
||||
subject_list = list(cert['subject'])
|
||||
subject_list.reverse()
|
||||
subject_parts = []
|
||||
for pair in subject_list:
|
||||
if pair[0][0] in subjectMap:
|
||||
field_name = subjectMap[pair[0][0]]
|
||||
else:
|
||||
field_name = pair[0][0]
|
||||
subject_parts.append(field_name + '=' + pair[0][1])
|
||||
|
||||
console_write(u" Server SSL certificate:")
|
||||
console_write(u" subject: " + ','.join(subject_parts))
|
||||
if 'subjectAltName' in cert:
|
||||
console_write(u" common name: " + cert['subjectAltName'][0][1])
|
||||
if 'notAfter' in cert:
|
||||
console_write(u" expire date: " + cert['notAfter'])
|
||||
|
||||
hostname = self.host.split(':', 0)[0]
|
||||
|
||||
if not self.validate_cert_host(cert, hostname):
|
||||
if self.debuglevel == -1:
|
||||
console_write(u" Certificate INVALID")
|
||||
|
||||
raise InvalidCertificateException(hostname, cert,
|
||||
'hostname mismatch')
|
||||
|
||||
if self.debuglevel == -1:
|
||||
console_write(u" Certificate validated for %s" % hostname)
|
||||
|
||||
except (ImportError):
|
||||
pass
|
@@ -0,0 +1,59 @@
|
||||
try:
|
||||
# Python 3
|
||||
from urllib.error import URLError
|
||||
import urllib.request as urllib_compat
|
||||
except (ImportError):
|
||||
# Python 2
|
||||
from urllib2 import URLError
|
||||
import urllib2 as urllib_compat
|
||||
|
||||
|
||||
# The following code is wrapped in a try because the Linux versions of Sublime
|
||||
# Text do not include the ssl module due to the fact that different distros
|
||||
# have different versions
|
||||
try:
|
||||
import ssl
|
||||
|
||||
from .validating_https_connection import ValidatingHTTPSConnection
|
||||
from .invalid_certificate_exception import InvalidCertificateException
|
||||
from .persistent_handler import PersistentHandler
|
||||
|
||||
if hasattr(urllib_compat, 'HTTPSHandler'):
|
||||
class ValidatingHTTPSHandler(PersistentHandler, urllib_compat.HTTPSHandler):
|
||||
"""
|
||||
A urllib handler that validates SSL certificates for HTTPS requests
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# This is a special value that will not trigger the standard debug
|
||||
# functionality, but custom code where we can format the output
|
||||
self._debuglevel = 0
|
||||
if 'debug' in kwargs and kwargs['debug']:
|
||||
self._debuglevel = 5
|
||||
elif 'debuglevel' in kwargs:
|
||||
self._debuglevel = kwargs['debuglevel']
|
||||
self._connection_args = kwargs
|
||||
|
||||
def https_open(self, req):
|
||||
def http_class_wrapper(host, **kwargs):
|
||||
full_kwargs = dict(self._connection_args)
|
||||
full_kwargs.update(kwargs)
|
||||
return ValidatingHTTPSConnection(host, **full_kwargs)
|
||||
|
||||
try:
|
||||
return self.do_open(http_class_wrapper, req)
|
||||
except URLError as e:
|
||||
if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1:
|
||||
raise InvalidCertificateException(req.host, '',
|
||||
e.reason.args[1])
|
||||
raise
|
||||
|
||||
https_request = urllib_compat.AbstractHTTPHandler.do_request_
|
||||
else:
|
||||
raise ImportError()
|
||||
|
||||
except (ImportError) as e:
|
||||
|
||||
class ValidatingHTTPSHandler():
|
||||
def __init__(self, **kwargs):
|
||||
raise e
|
Reference in New Issue
Block a user