]> git.lizzy.rs Git - torbrowser-launcher.git/blobdiff - torbrowser_launcher/launcher.py
Add pre-import check that signing keyfile exists; remove asserts
[torbrowser-launcher.git] / torbrowser_launcher / launcher.py
index d250a6e8032086226a6d556c412312d16349d30e..ec87cf87b0d0d07e216d71915a07e1cec550f368 100644 (file)
@@ -2,7 +2,7 @@
 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
@@ -30,7 +30,6 @@ import os, subprocess, time, json, tarfile, hashlib, lzma, threading, re, unicod
 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
 
@@ -48,6 +47,9 @@ class TryStableException(Exception):
 class TryDefaultMirrorException(Exception):
     pass
 
+class TryForcingEnglishException(Exception):
+    pass
+
 class DownloadErrorException(Exception):
     pass
 
@@ -55,16 +57,17 @@ class Launcher:
     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:
@@ -78,9 +81,16 @@ class Launcher:
                     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',
@@ -159,6 +169,8 @@ class Launcher:
                     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)
@@ -260,10 +272,13 @@ class Launcher:
 
         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':
@@ -289,9 +304,11 @@ class Launcher:
 
                 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)
@@ -333,7 +350,7 @@ class Launcher:
             ## 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)
@@ -343,6 +360,10 @@ class Launcher:
             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)
@@ -350,7 +371,7 @@ class Launcher:
         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)
 
@@ -359,7 +380,7 @@ class Launcher:
                 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)
 
@@ -388,7 +409,7 @@ class Launcher:
 
         # 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()
 
@@ -424,6 +445,13 @@ class Launcher:
         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
@@ -435,7 +463,14 @@ class Launcher:
         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):
@@ -447,7 +482,7 @@ class Launcher:
         # 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
@@ -559,6 +594,7 @@ class Launcher:
 
     # 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