Validate the server certificate for HTTPS connections
[zeroinstall/solver.git] / zeroinstall / injector / _download_child.py
blobc58c79daf243ce6c9839a247b008d4c17b3ccf22
1 # Copyright (C) 2011, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 import sys, os, socket, ssl
6 from zeroinstall import _
7 from zeroinstall.injector import download
9 import urllib2, httplib
11 # This works on Debian. It probably needs to be updated to handle other platforms.
12 ca_file = "/etc/ssl/certs/ca-certificates.crt"
14 if os.path.exists(ca_file):
15 class ValidatingHTTPSConnection(httplib.HTTPSConnection):
16 def connect(self):
17 sock = socket.create_connection((self.host, self.port), self.timeout)
18 if self._tunnel_host:
19 self.sock = sock
20 self._tunnel()
21 self.sock = ssl.wrap_socket(sock, cert_reqs = ssl.CERT_REQUIRED, ca_certs = ca_file)
23 class ValidatingHTTPSHandler(urllib2.HTTPSHandler):
24 def https_open(self, req):
25 return self.do_open(self.getConnection, req)
27 def getConnection(self, host, timeout=300):
28 return ValidatingHTTPSConnection(host)
30 urlopener = urllib2.build_opener(ValidatingHTTPSHandler)
32 # Builds an opener that overrides the default HTTPS handler with our one
33 _my_urlopen = urllib2.build_opener(ValidatingHTTPSHandler()).open
34 else:
35 _my_urlopen = urllib2.urlopen
37 def download_in_thread(url, target_file, if_modified_since, notify_done):
38 try:
39 #print "Child downloading", url
40 if url.startswith('http:') or url.startswith('https:') or url.startswith('ftp:'):
41 req = urllib2.Request(url)
42 if url.startswith('http:') and if_modified_since:
43 req.add_header('If-Modified-Since', if_modified_since)
44 src = _my_urlopen(req)
45 else:
46 raise Exception(_('Unsupported URL protocol in: %s') % url)
48 try:
49 sock = src.fp._sock
50 except AttributeError:
51 sock = src.fp.fp._sock # Python 2.5 on FreeBSD
52 while True:
53 data = sock.recv(256)
54 if not data: break
55 target_file.write(data)
56 target_file.flush()
58 notify_done(download.RESULT_OK)
59 except (urllib2.HTTPError, urllib2.URLError, httplib.HTTPException) as ex:
60 if isinstance(ex, urllib2.HTTPError) and ex.code == 304: # Not modified
61 notify_done(download.RESULT_NOT_MODIFIED)
62 else:
63 #print >>sys.stderr, "Error downloading '" + url + "': " + (str(ex) or str(ex.__class__.__name__))
64 __, ex, tb = sys.exc_info()
65 notify_done(download.RESULT_FAILED, (download.DownloadError(unicode(ex)), tb))
66 except Exception as ex:
67 __, ex, tb = sys.exc_info()
68 notify_done(download.RESULT_FAILED, (ex, tb))