Tor Browser Launcher
https://github.com/micahflee/torbrowser-launcher/
-Copyright (c) 2013-2014 Micah Lee <micah@micahflee.com>
+Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
from twisted.internet import reactor
from twisted.web.client import Agent, RedirectAgent, ResponseDone, ResponseFailed
from twisted.web.http_headers import Headers
-from twisted.web.iweb import IPolicyForHTTPS
from twisted.internet.protocol import Protocol
from twisted.internet.error import DNSLookupError, ConnectionRefusedError
class TryDefaultMirrorException(Exception):
pass
+class TryForcingEnglishException(Exception):
+ pass
+
class DownloadErrorException(Exception):
pass
def __init__(self, common, url_list):
self.common = common
self.url_list = url_list
+ self.force_redownload = False
# this is the current version of Tor Browser, which should get updated with every release
- self.min_version = '5.5.2'
+ self.min_version = '6.0.2'
# init launcher
self.set_gui(None, '', [])
self.launch_gui = True
# if Tor Browser is not installed, detect latest version, download, and install
- if not self.common.settings['installed']:
+ if not self.common.settings['installed'] or not self.check_min_version():
# if downloading over Tor, include txsocksx
if self.common.settings['download_over_tor']:
try:
self.common.settings['download_over_tor'] = False
self.common.save_settings()
+ # different message if downloading for the first time, or because your installed version is too low
+ download_message = ""
+ if not self.common.settings['installed']:
+ download_message = _("Downloading and installing Tor Browser for the first time.")
+ elif not self.check_min_version():
+ download_message = _("Your version of Tor Browser is out-of-date. Downloading and installing the newest version.")
+
# download and install
- print _("Downloading and installing Tor Browser for the first time.")
- self.set_gui('task', _("Downloading and installing Tor Browser for the first time."),
+ print download_message
+ self.set_gui('task', download_message,
['download_version_check',
'set_version',
'download_sig',
self.yes_button.connect("clicked", self.try_stable, None)
elif self.gui == 'error_try_default_mirror':
self.yes_button.connect("clicked", self.try_default_mirror, None)
+ elif self.gui == 'error_try_forcing_english':
+ self.yes_button.connect("clicked", self.try_forcing_english, None)
elif self.gui == 'error_try_tor':
self.yes_button.connect("clicked", self.try_tor, None)
self.button_box.add(self.yes_button)
# exit button
exit_image = gtk.Image()
exit_image.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_BUTTON)
- self.exit_button = gtk.Button(_("Exit"))
+ self.exit_button = gtk.Button(_("Cancel"))
self.exit_button.set_image(exit_image)
self.exit_button.connect("clicked", self.destroy, None)
self.button_box.add(self.exit_button)
elif task == 'download_tarball':
print _('Downloading'), self.common.paths['tarball_url'].format(self.common.settings['mirror'])
- self.download('tarball', self.common.paths['tarball_url'], self.common.paths['tarball_file'])
+ if not self.force_redownload and os.path.exists(self.common.paths['tarball_file']):
+ self.run_task()
+ else:
+ self.download('tarball', self.common.paths['tarball_url'], self.common.paths['tarball_file'])
elif task == 'verify':
- print _('Verifying signature')
+ print _('Verifying Signature')
self.verify()
elif task == 'extract':
if response.code != 200:
if common.settings['mirror'] != common.default_mirror:
- raise TryDefaultMirrorException(_("Download Error: {0} {1}\n\nYou are currently using a non-default mirror:\n{2}\n\nWould you like to switch back to the default?").format(response.code, response.phrase, common.settings['mirror']))
+ raise TryDefaultMirrorException((_("Download Error:") + " {0} {1}\n\n" + _("You are currently using a non-default mirror") + ":\n{2}\n\n" + _("Would you like to switch back to the default?")).format(response.code, response.phrase, common.settings['mirror']))
+ elif common.language != 'en-US' and not common.settings['force_en-US']:
+ raise TryForcingEnglishException((_("Download Error:") + " {0} {1}\n\n" + _("Would you like to try the English version of Tor Browser instead?")).format(response.code, response.phrase))
else:
- raise DownloadErrorException(_("Download Error: {0} {1}").format(response.code, response.phrase))
+ raise DownloadErrorException((_("Download Error:") + " {0} {1}").format(response.code, response.phrase))
def dataReceived(self, bytes):
self.file.write(bytes)
## FIXME handle errors
def download_error(self, f):
- print _("Download error:"), f.value, type(f.value)
+ print _("Download Error:"), f.value, type(f.value)
if isinstance(f.value, TryStableException):
f.trap(TryStableException)
f.trap(TryDefaultMirrorException)
self.set_gui('error_try_default_mirror', str(f.value), [], False)
+ elif isinstance(f.value, TryForcingEnglishException):
+ f.trap(TryForcingEnglishException)
+ self.set_gui('error_try_forcing_english', str(f.value), [], False)
+
elif isinstance(f.value, DownloadErrorException):
f.trap(DownloadErrorException)
self.set_gui('error', str(f.value), [], False)
elif isinstance(f.value, DNSLookupError):
f.trap(DNSLookupError)
if common.settings['mirror'] != common.default_mirror:
- self.set_gui('error_try_default_mirror', _("DNS Lookup Error\n\nYou are currently using a non-default mirror:\n{0}\n\nWould you like to switch back to the default?").format(common.settings['mirror']), [], False)
+ self.set_gui('error_try_default_mirror', (_("DNS Lookup Error") + "\n\n" + _("You are currently using a non-default mirror") + ":\n{0}\n\n" + _("Would you like to switch back to the default?")).format(common.settings['mirror']), [], False)
else:
self.set_gui('error', str(f.value), [], False)
if isinstance(reason.value, OpenSSL.SSL.Error):
# TODO: add the ability to report attack by posting bug to trac.torproject.org
if not self.common.settings['download_over_tor']:
- self.set_gui('error_try_tor', _('The SSL certificate served by https://www.torproject.org is invalid! You may be under attack. Try the download again using Tor?'), [], False)
+ self.set_gui('error_try_tor', _('The SSL certificate served by https://www.torproject.org is invalid! You may be under attack.') + " " + _('Try the download again using Tor?'), [], False)
else:
self.set_gui('error', _('The SSL certificate served by https://www.torproject.org is invalid! You may be under attack.'), [], False)
# initialize the progress bar
self.progressbar.set_fraction(0)
- self.progressbar.set_text(_('Downloading {0}').format(name))
+ self.progressbar.set_text(_('Downloading') + ' {0}'.format(name))
self.progressbar.show()
self.refresh_gtk()
subprocess.Popen([self.common.paths['tbl_bin']])
self.destroy(False)
+ def try_forcing_english(self, widget, data=None):
+ # change force english to true and relaunch TBL
+ self.common.settings['force_en-US'] = True
+ self.common.save_settings()
+ subprocess.Popen([self.common.paths['tbl_bin']])
+ self.destroy(False)
+
def try_tor(self, widget, data=None):
# set download_over_tor to true and relaunch TBL
self.common.settings['download_over_tor'] = True
tree = ET.parse(self.common.paths['version_check_file'])
for up in tree.getroot():
if up.tag == 'update' and up.attrib['appVersion']:
- return str(up.attrib['appVersion'])
+ version = str(up.attrib['appVersion'])
+
+ # make sure the version does not contain directory traversal attempts
+ # e.g. "5.5.3", "6.0a", "6.0a-hardned" are valid but "../../../../.." is invalid
+ if not re.match(r'^[a-z0-9\.\-]+$', version):
+ return None
+
+ return version
return None
def verify(self):
# verify the PGP signature
verified = False
FNULL = open(os.devnull, 'w')
- p = subprocess.Popen(['/usr/bin/gpg', '--homedir', self.common.paths['gnupg_homedir'], '--verify', self.common.paths['sig_file']], stdout=FNULL, stderr=subprocess.STDOUT)
+ p = subprocess.Popen(['/usr/bin/gpg', '--homedir', self.common.paths['gnupg_homedir'], '--verify', self.common.paths['sig_file'], self.common.paths['tarball_file']], stdout=FNULL, stderr=subprocess.STDOUT)
self.pulse_until_process_exits(p)
if p.returncode == 0:
verified = True
# start over and download TBB again
def start_over(self):
+ self.force_redownload = True # Overwrite any existing file
self.label.set_text(_("Downloading Tor Browser Bundle over again."))
self.gui_tasks = ['download_tarball', 'verify', 'extract', 'run']
self.gui_task_i = 0