]> git.lizzy.rs Git - torbrowser-launcher.git/blobdiff - torbrowser-launcher
saves installed_version setting after extracting
[torbrowser-launcher.git] / torbrowser-launcher
index c8372f9636c85f608682e354b7e4a58769dc5294..675b8e62395d4ab0fcd99d7a8d9b398d8bb89d74 100755 (executable)
 #!/usr/bin/env python
 
-import os, sys, subprocess, locale, urllib2, gobject, time
+import os, sys, subprocess, locale, urllib2, gobject, time, pickle, json
 
 import pygtk
 pygtk.require('2.0')
 import gtk
 
 class TorBrowserLauncher:
-  def __init__(self, current_tbb_version):
+  def __init__(self):
     # initialize the app
-    self.current_tbb_version = current_tbb_version
     self.discover_arch_lang()
     self.build_paths()
     self.mkdirs()
 
+    # allow buttons to have icons
+    try:
+      settings = gtk.settings_get_default()
+      settings.props.gtk_button_images = True
+    except:
+      pass
+
     launch_gui = True
 
+    # load settings
+    if self.load_settings():
+      self.build_paths(self.settings['latest_version'])
+
+      # how long was it since the last update check?
+      # 86400 seconds = 24 hours
+      current_timestamp = int(time.time())
+      if current_timestamp - self.settings['last_update_check_timestamp'] >= 86400:
+        # check for update
+        self.set_gui('task', "Checking for Tor Browser update.", 
+          ['download_update_check', 
+           'attempt_update'])
+
+      else:
+        # no need to check for update
+        self.start_launcher()
+
+    else:
+      self.set_gui('error', "Error loading settings. Delete ~/.torbrowser and try again.", [])
+
+    if launch_gui:
+      # set up the window
+      self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+      self.window.set_title("Tor Browser")
+      self.window.set_position(gtk.WIN_POS_CENTER)
+      self.window.set_border_width(10)
+      self.window.connect("delete_event", self.delete_event)
+      self.window.connect("destroy", self.destroy)
+
+      # build the rest of the UI
+      self.build_ui()
+      gtk.main()
+
+  # download or run TBB
+  def start_launcher(self):
     # is TBB already installed?
     if os.path.isfile(self.paths['file']['start']) and os.access(self.paths['file']['start'], os.X_OK):
-      # does the version file exist?
-      if os.path.isfile(self.paths['file']['version']):
-        installed_tbb_version = open(self.paths['file']['version']).read().strip()
-
-        if installed_tbb_version == current_tbb_version:
-          # current version is tbb is installed, launch it
-          self.run(False)
-          launch_gui = False
-        elif installed_tbb_version < self.current_tbb_version:
-          # there is a tbb upgrade available
-          self.set_gui('task', "Your Tor Browser is out of date. Click Start to download the latest version from https://www.torproject.org.", ['download_tarball', 'download_tarball_sig', 'verify', 'extract', 'run'])
-        else:
-          # for some reason the installed tbb is newer than the current version?
-          self.set_gui('error', "Something is wrong. The version of Tor Browser Bundle you have installed is newer than the current version?", [])
-
+      if self.settings['installed_version'] == self.settings['latest_version']:
+        # current version of tbb is installed, launch it
+        self.run(False)
+        launch_gui = False
+      elif self.settings['installed_version'] < self.settings['latest_version']:
+        # there is a tbb upgrade available
+        self.set_gui('task', "Your Tor Browser is out of date.", 
+          ['download_tarball', 
+           'download_tarball_sig', 
+           'verify', 
+           'extract', 
+           'run'])
       else:
-        # if tbb is installed but the version file doesn't exist, something is wrong
-        self.set_gui('error', "Something is wrong. You have the Tor Browser Bundle installed, but the version file is missing.", [])
+        # for some reason the installed tbb is newer than the current version?
+        self.set_gui('error', "Something is wrong. The version of Tor Browser Bundle you have installed is newer than the current version?", [])
 
     # not installed
     else:
-      # save the current version to the file
-      open(self.paths['file']['version'], 'w').write(self.current_tbb_version)
-
       # are the tarball and sig already downloaded?
       if os.path.isfile(self.paths['file']['tarball']) and os.path.isfile(self.paths['file']['tarball_sig']):
         # start the gui with verify
-        self.set_gui('task', "You already have Tor Browser Bundle downloaded, but it isn't installed yet.", ['verify', 'extract', 'run'])
+        self.set_gui('task', "Installing Tor Browser.", 
+          ['verify', 
+           'extract', 
+           'run'])
 
       # first run
       else:
-        self.set_gui('task', "The first time you run the Tor Browser Launcher you need to download the Tor Browser Bundle. Click Start to download it now from https://www.torproject.org/.", ['download_tarball', 'download_tarball_sig', 'verify', 'extract', 'run'])
+        self.set_gui('task', "Downloading and installing Tor Browser.", 
+          ['download_tarball', 
+           'download_tarball_sig', 
+           'verify', 
+           'extract', 
+           'run'])
 
-    if launch_gui:
-      self.build_ui()
-      gtk.main()
-  
   # discover the architecture and language
   def discover_arch_lang(self):
     # figure out the architecture
-    self.architecture = subprocess.Popen(['arch'], stdout=subprocess.PIPE, stderr=None).stdout.read().strip('\n')
+    (sysname, nodename, release, version, machine) = os.uname()
+    self.architecture = machine
 
     # figure out the language
     available_languages = ['en-US', 'ar', 'de', 'es-ES', 'fa', 'fr', 'it', 'ko', 'nl', 'pl', 'pt-PT', 'ru', 'vi', 'zh-CN']
-    self.language = locale.getdefaultlocale()[0].replace('_', '-')
-    if self.language not in available_languages:
-      self.language = self.language.split('-')[0]
-      if self.language not in available_languages:
-        for l in available_languages:
-          if l[0:2] == self.language:
-            self.language = l
-    # if language isn't available, default to english
-    if self.language not in available_languages:
+    default_locale = locale.getdefaultlocale()[0]
+    if default_locale == None:
       self.language = 'en-US'
+    else:
+      self.language = default_locale.replace('_', '-')
+      if self.language not in available_languages:
+        self.language = self.language.split('-')[0]
+        if self.language not in available_languages:
+          for l in available_languages:
+            if l[0:2] == self.language:
+              self.language = l
+      # if language isn't available, default to english
+      if self.language not in available_languages:
+        self.language = 'en-US'
 
   # build all relevant paths
-  def build_paths(self):
+  def build_paths(self, tbb_version = None):
     tbb_data = os.getenv('HOME')+'/.torbrowser'
-    tarball_filename = 'tor-browser-gnu-linux-'+self.architecture+'-'+self.current_tbb_version+'-dev-'+self.language+'.tar.gz'
-
-    self.paths = {
-      'dir': {
-        'data': tbb_data,
-        'download': tbb_data+'/download',
-        'tbb': tbb_data+'/tbb/'+self.architecture,
-        'gpg': tbb_data+'/gpgtmp'
-      },
-      'file': {
-        'version': tbb_data+'/version',
-        'start': tbb_data+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser',
-        'tarball': tbb_data+'/download/'+tarball_filename,
-        'tarball_sig': tbb_data+'/download/'+tarball_filename+'.asc',
-        'verify': '/usr/share/torbrowser-launcher/verify.sh'
-      },
-      'url': {
-        'tarball': 'https://www.torproject.org/dist/torbrowser/linux/'+tarball_filename,
-        'tarball_sig': 'https://www.torproject.org/dist/torbrowser/linux/'+tarball_filename+'.asc'
-      },
-      'filename': {
-        'tarball': tarball_filename,
-        'tarball_sig': tarball_filename+'.asc'
+
+    if tbb_version:
+      tarball_filename = 'tor-browser-gnu-linux-'+self.architecture+'-'+tbb_version+'-dev-'+self.language+'.tar.gz'
+      self.paths['file']['tarball'] = tbb_data+'/download/'+tarball_filename
+      self.paths['file']['tarball_sig'] = tbb_data+'/download/'+tarball_filename+'.asc'
+      self.paths['url']['tarball'] = 'https://www.torproject.org/dist/torbrowser/linux/'+tarball_filename
+      self.paths['url']['tarball_sig'] = 'https://www.torproject.org/dist/torbrowser/linux/'+tarball_filename+'.asc'
+      self.paths['filename']['tarball'] = tarball_filename
+      self.paths['filename']['tarball_sig'] = tarball_filename+'.asc'
+
+    else:
+      self.paths = {
+        'dir': {
+          'data': tbb_data,
+          'download': tbb_data+'/download',
+          'tbb': tbb_data+'/tbb/'+self.architecture,
+          'gpg': tbb_data+'/gpgtmp'
+        },
+        'file': {
+          'settings': tbb_data+'/settings',
+          'version': tbb_data+'/version',
+          'start': tbb_data+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser',
+          'update_check': tbb_data+'/download/RecommendedTBBVersions',
+          'verify': '/usr/share/torbrowser-launcher/verify.sh'
+        },
+        'url': {
+          'update_check': 'https://check.torproject.org/RecommendedTBBVersions'
+        },
+        'filename': {}
       }
-    }
 
   # create directories that don't exist
   def mkdirs(self):
@@ -110,30 +159,33 @@ class TorBrowserLauncher:
       os.makedirs(self.paths['dir']['tbb'])
 
   # there are different GUIs that might appear, this sets which one we want
-  def set_gui(self, gui, message, tasks):
+  def set_gui(self, gui, message, tasks, autostart=True):
     self.gui = gui
     self.gui_message = message
     self.gui_tasks = tasks
+    self.gui_task_i = 0
+    self.gui_autostart = autostart
 
-  # build the application's UI
-  def build_ui(self):
+  # set all gtk variables to False
+  def clear_ui(self):
+    if self.timer:
+      gobject.source_remove(self.timer)
     self.timer = False
 
-    # allow buttons to have icons
-    try:
-      settings = gtk.settings_get_default()
-      settings.props.gtk_button_images = True
-    except:
-      pass
+    if self.box:
+      self.box.destroy()
+    self.box = False
 
-    # set up the window
-    self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-    self.window.set_title("Tor Browser")
-    self.window.set_position(gtk.WIN_POS_CENTER)
-    self.window.set_border_width(10)
-    self.window.connect("delete_event", self.delete_event)
-    self.window.connect("destroy", self.destroy)
+    self.label1 = False
+    self.label2 = False
+    self.label = False
+    self.progressbar = False
+    self.button_box = False
+    self.start_button = False
+    self.exit_button = False
 
+  # build the application's UI
+  def build_ui(self):
     self.box = gtk.VBox(False, 20)
     self.window.add(self.box)
 
@@ -184,7 +236,8 @@ class TorBrowserLauncher:
       self.start_button.set_image(start_image)
       self.start_button.connect("clicked", self.start, None)
       self.button_box.add(self.start_button)
-      self.start_button.show()
+      if not self.gui_autostart:
+        self.start_button.show()
 
       # exit button
       exit_image = gtk.Image()
@@ -198,17 +251,22 @@ class TorBrowserLauncher:
     self.box.show()
     self.window.show()
 
+    if self.gui_autostart:
+      self.start(None)
+
   # start button clicked, begin tasks
   def start(self, widget, data=None):
     # disable the start button
-    self.start_button.set_sensitive(False)
+    if self.start_button:
+      self.start_button.set_sensitive(False)
 
     # start running tasks
-    self.gui_task_i = 0
     self.run_task()
     
   # run the next task in the task list
   def run_task(self):
+    self.refresh_gtk()
+
     if self.gui_task_i >= len(self.gui_tasks):
       self.destroy(False)
       return
@@ -218,7 +276,15 @@ class TorBrowserLauncher:
     # get ready for the next task
     self.gui_task_i += 1
 
-    if task == 'download_tarball':
+    if task == 'download_update_check':
+      print 'Downloading '+self.paths['url']['update_check']
+      self.download('update check', self.paths['url']['update_check'], self.paths['file']['update_check'])
+    
+    if task == 'attempt_update':
+      print 'Checking to see if update it needed'
+      self.attempt_update()
+
+    elif task == 'download_tarball':
       print 'Downloading '+self.paths['url']['tarball']
       self.download('tarball', self.paths['url']['tarball'], self.paths['file']['tarball'])
 
@@ -287,6 +353,36 @@ class TorBrowserLauncher:
 
     return True
 
+  def attempt_update(self):
+    # load the update check file
+    try:
+      versions = json.load(open(self.paths['file']['update_check']))
+      latest_version = None
+
+      end = '-Linux'
+      for version in versions:
+        if str(version).find(end) != -1:
+          latest_version = str(version)
+
+      if latest_version:
+        self.settings['latest_version'] = latest_version[:-len(end)]
+        self.settings['last_update_check_timestamp'] = int(time.time())
+        self.save_settings()
+        self.build_paths(self.settings['latest_version'])
+        self.start_launcher()
+
+      else:
+        # failed to find the latest version
+        self.set_gui('error', "Error checking for updates.", [], False)
+    
+    except:
+      # not a valid JSON object
+      self.set_gui('error', "Error checking for updates.", [], False)
+
+    # now start over
+    self.clear_ui()
+    self.build_ui()
+
   def verify(self):
     # initialize the progress bar
     self.progressbar.set_fraction(0) 
@@ -305,6 +401,7 @@ class TorBrowserLauncher:
       self.label.set_text("SIGNATURE VERIFICATION FAILED!\n\nYou might be under attack, or there might just be a networking problem. Click Start try the download again.")
       self.gui_tasks = ['start_over']
       self.gui_task_i = 0
+      self.start_button.show()
       self.start_button.set_sensitive(True)
 
   def extract(self):
@@ -316,6 +413,10 @@ class TorBrowserLauncher:
     p = subprocess.Popen(['tar', '-xf', self.paths['file']['tarball'], '-C', self.paths['dir']['tbb']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     self.pulse_until_process_exits(p)
 
+    # installation is finished, so save installed_version
+    self.settings['installed_version'] = self.settings['latest_version']
+    self.save_settings()
+
     self.run_task()
 
   def run(self, run_next_task = True):
@@ -336,6 +437,31 @@ class TorBrowserLauncher:
     self.gui_tasks = ['download_tarball', 'download_tarball_sig', 'verify', 'extract', 'run']
     self.gui_task_i = 0
     self.start(None)
+
+  # load settings
+  def load_settings(self):
+    if os.path.isfile(self.paths['file']['settings']):
+      self.settings = pickle.load(open(self.paths['file']['settings']))
+      # sanity checks
+      if not 'installed_version' in self.settings:
+        return False
+      if not 'latest_version' in self.settings:
+        return False
+      if not 'last_update_check_timestamp' in self.settings:
+        return False
+    else:
+      self.settings = {
+        'installed_version': False,
+        'latest_version': '0',
+        'last_update_check_timestamp': 0
+      }
+      self.save_settings()
+    return True
+
+  # save settings
+  def save_settings(self):
+    pickle.dump(self.settings, open(self.paths['file']['settings'], 'w'))
+    return True
   
   # refresh gtk
   def refresh_gtk(self):
@@ -346,19 +472,14 @@ class TorBrowserLauncher:
   def delete_event(self, widget, event, data=None):
     return False
   def destroy(self, widget, data=None):
-    if self.timer:
-      gobject.source_remove(self.timer)
-    self.timer = False
-
     gtk.main_quit()
 
 if __name__ == "__main__":
-  current_tbl_version = '0.1'
-  current_tbb_version = '2.3.25-2'
+  tor_browser_launcher_version = '0.1'
 
   print 'Tor Browser Launcher'
-  print 'version %s' % (current_tbl_version)
+  print 'version %s' % (tor_browser_launcher_version)
   print 'https://github.com/micahflee/torbrowser-launcher'
 
-  app = TorBrowserLauncher(current_tbb_version)
+  app = TorBrowserLauncher()