]> git.lizzy.rs Git - torbrowser-launcher.git/blobdiff - torbrowser_launcher/common.py
Download key using web key directory from torproject.org instead of keyservers
[torbrowser-launcher.git] / torbrowser_launcher / common.py
index 0dfd8dad6f58bf2e808347041d9b5fd0b13625e0..adb9426aad1b6019dfaf65e9881443195f9f1594 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
@@ -26,37 +26,36 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
 """
 
-import os, sys, platform, subprocess, locale, pickle, json, psutil, re
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-
-SHARE = os.getenv('TBL_SHARE', sys.prefix+'/share/torbrowser-launcher')
-
+import os
+import sys
+import platform
+import subprocess
+import locale
+import pickle
+import json
+import re
 import gettext
-gettext.install('torbrowser-launcher', os.path.join(SHARE, 'locale'))
+import gpg
 
-from twisted.internet import gtk2reactor
-gtk2reactor.install()
+SHARE = os.getenv('TBL_SHARE', sys.prefix + '/share') + '/torbrowser-launcher'
 
+gettext.install('torbrowser-launcher')
 
 # We're looking for output which:
 #
-#   1. The first portion must be `[GNUPG:] IMPORT_OK`
-#   2. The second must be an integer between [0, 15], inclusive
-#   3. The third must be an uppercased hex-encoded 160-bit fingerprint
+#  1. The first portion must be `[GNUPG:] IMPORT_OK`
+#  2. The second must be an integer between [0, 15], inclusive
+#  3. The third must be an uppercased hex-encoded 160-bit fingerprint
 gnupg_import_ok_pattern = re.compile(
-    "(\[GNUPG\:\]) (IMPORT_OK) ([0-9]|[1]?[0-5]) ([A-F0-9]{40})")
-
+    b"(\[GNUPG\:\]) (IMPORT_OK) ([0-9]|[1]?[0-5]) ([A-F0-9]{40})")
 
-class Common:
 
+class Common(object):
     def __init__(self, tbl_version):
         self.tbl_version = tbl_version
 
         # initialize the app
-        self.default_mirror = 'https://www.torproject.org/dist/'
+        self.default_mirror = 'https://dist.torproject.org/'
         self.discover_arch_lang()
         self.build_paths()
         for d in self.paths['dirs']:
@@ -67,21 +66,14 @@ class Common:
         self.mkdir(self.paths['tbb']['dir'])
         self.init_gnupg()
 
-        # allow buttons to have icons
-        try:
-            gtk_settings = gtk.settings_get_default()
-            gtk_settings.props.gtk_button_images = True
-        except:
-            pass
-
     # discover the architecture and language
     def discover_arch_lang(self):
         # figure out the architecture
         self.architecture = 'x86_64' if '64' in platform.architecture()[0] else 'i686'
 
         # figure out the language
-        available_languages = ['en-US', 'ar', 'de', 'es-ES', 'fa', 'fr', 'it', 'ko', 'nl', 'pl', 'pt-PT', 'ru', 'vi', 'zh-CN']
-        default_locale = locale.getlocale(locale.LC_MESSAGES)[0]
+        available_languages = ['ar', 'ca', 'da', 'de', 'en-US', 'es-ES', 'fa', 'fr', 'ga-IE', 'he', 'id', 'is', 'it', 'ja', 'ko', 'nb-NO', 'nl', 'pl', 'pt-BR', 'ru', 'sv-SE', 'tr', 'vi', 'zh-CN', 'zh-TW']
+        default_locale = locale.getlocale()[0]
         if default_locale is None:
             self.language = 'en-US'
         else:
@@ -103,7 +95,7 @@ class Common:
             homedir = '/tmp/.torbrowser-'+os.getenv('USER')
             if not os.path.exists(homedir):
                 try:
-                    os.mkdir(homedir, 0700)
+                    os.mkdir(homedir, 0o700)
                 except:
                     self.set_gui('error', _("Error creating {0}").format(homedir), [], False)
         if not os.access(homedir, os.W_OK):
@@ -125,17 +117,17 @@ class Common:
                 language = 'en-US'
             else:
                 language = self.language
-            tarball_filename = 'tor-browser-'+arch+'-'+tbb_version+'_'+language+'.tar.xz'
+            tarball_filename = 'tor-browser-' + arch + '-' + tbb_version + '_' + language + '.tar.xz'
 
             # tarball
-            self.paths['tarball_url'] = '{0}torbrowser/'+tbb_version+'/'+tarball_filename
-            self.paths['tarball_file'] = tbb_cache+'/download/'+tarball_filename
+            self.paths['tarball_url'] = '{0}torbrowser/' + tbb_version + '/' + tarball_filename
+            self.paths['tarball_file'] = tbb_cache + '/download/' + tarball_filename
             self.paths['tarball_filename'] = tarball_filename
 
             # sig
-            self.paths['sig_url'] = '{0}torbrowser/'+tbb_version+'/'+tarball_filename+'.asc'
-            self.paths['sig_file'] = tbb_cache+'/download/'+tarball_filename+'.asc'
-            self.paths['sig_filename'] = tarball_filename+'.asc'
+            self.paths['sig_url'] = '{0}torbrowser/' + tbb_version + '/' + tarball_filename + '.asc'
+            self.paths['sig_file'] = tbb_cache + '/download/' + tarball_filename + '.asc'
+            self.paths['sig_filename'] = tarball_filename + '.asc'
         else:
             self.paths = {
                 'dirs': {
@@ -151,19 +143,20 @@ class Common:
                     'tor_browser_developers': os.path.join(SHARE, 'tor-browser-developers.asc')
                 },
                 'mirrors_txt': [os.path.join(SHARE, 'mirrors.txt'),
-                                tbb_config+'/mirrors.txt'],
-                'modem_sound': os.path.join(SHARE, 'modem.ogg'),
-                'download_dir': tbb_cache+'/download',
-                'gnupg_homedir': tbb_local+'/gnupg_homedir',
-                'settings_file': tbb_config+'/settings.json',
-                'settings_file_pickle': tbb_config+'/settings',
-                'version_check_url': 'https://dist.torproject.org/torbrowser/update_2/release/Linux_x86_64-gcc3/x/en-US',
-                'version_check_file': tbb_cache+'/download/release.xml',
+                                tbb_config + '/mirrors.txt'],
+                'download_dir': tbb_cache + '/download',
+                'gnupg_homedir': tbb_local + '/gnupg_homedir',
+                'settings_file': tbb_config + '/settings.json',
+                'settings_file_pickle': tbb_config + '/settings',
+                'version_check_url': 'https://aus1.torproject.org/torbrowser/update_3/release/Linux_x86_64-gcc3/x/en-US',
+                'version_check_file': tbb_cache + '/download/release.xml',
                 'tbb': {
-                    'dir': tbb_local+'/tbb/'+self.architecture,
-                    'dir_tbb': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language,
-                    'start': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/start-tor-browser.desktop',
-                    'versions': tbb_local+'/tbb/'+self.architecture+'/tor-browser_'+self.language+'/Browser/TorBrowser/Docs/sources/versions',
+                    'changelog': tbb_local + '/tbb/' + self.architecture + '/tor-browser_' +
+                                 self.language + '/Browser/TorBrowser/Docs/ChangeLog.txt',
+                    'dir': tbb_local + '/tbb/' + self.architecture,
+                    'dir_tbb': tbb_local + '/tbb/' + self.architecture + '/tor-browser_' + self.language,
+                    'start': tbb_local + '/tbb/' + self.architecture + '/tor-browser_' +
+                             self.language + '/start-tor-browser.desktop'
                 },
             }
 
@@ -177,72 +170,91 @@ class Common:
     def mkdir(path):
         try:
             if not os.path.exists(path):
-                os.makedirs(path, 0700)
+                os.makedirs(path, 0o700)
                 return True
         except:
-            print _("Cannot create directory {0}").format(path)
+            print(_("Cannot create directory {0}").format(path))
             return False
         if not os.access(path, os.W_OK):
-            print _("{0} is not writable").format(path)
+            print(_("{0} is not writable").format(path))
             return False
         return True
 
     # if gnupg_homedir isn't set up, set it up
     def init_gnupg(self):
         if not os.path.exists(self.paths['gnupg_homedir']):
-            print _('Creating GnuPG homedir'), self.paths['gnupg_homedir']
+            print(_('Creating GnuPG homedir'), self.paths['gnupg_homedir'])
             self.mkdir(self.paths['gnupg_homedir'])
         self.import_keys()
 
-    def import_key_and_check_status(self, key):
-        """Import a GnuPG key and check that the operation was successful.
-
-        :param str key: A string specifying the key's filepath from
-            ``Common.paths``, as well as its fingerprint in
-            ``Common.fingerprints``.
-        :rtype: bool
-        :returns: ``True`` if the key is now within the keyring (or was
-            previously and hasn't changed). ``False`` otherwise.
-        """
-        success = False
+    def refresh_keyring(self, fingerprint=None):
+        if fingerprint is not None:
+            print('Refreshing local keyring... Missing key: ' + fingerprint)
+        else:
+            print('Refreshing local keyring...')
 
-        p = subprocess.Popen(['/usr/bin/gpg', '--status-fd', '2',
+        # Fetch key from wkd, as per https://support.torproject.org/tbb/how-to-verify-signature/
+        p = subprocess.Popen(['/usr/bin/gpg2', '--status-fd', '2',
                               '--homedir', self.paths['gnupg_homedir'],
-                              '--import', self.paths['signing_keys'][key]],
-                             stderr=subprocess.PIPE)
+                              '--auto-key-locate', 'nodefault,wkd',
+                              '--locate-keys', 'torbrowser@torproject.org'], stderr=subprocess.PIPE)
         p.wait()
 
         for output in p.stderr.readlines():
             match = gnupg_import_ok_pattern.match(output)
-            if match:
-                # The output must match everything in the
-                # ``gnupg_import_ok_pattern``, as well as the expected fingerprint:
-                if match.group().find(self.fingerprints[key]) >= 0:
-                    success = True
-                    break
+            if match and match.group(2) == 'IMPORT_OK':
+                fingerprint = str(match.group(4))
+                if match.group(3) == '0':
+                    print('Keyring refreshed successfully...')
+                    print('  No key updates for key: ' + fingerprint)
+                elif match.group(3) == '4':
+                    print('Keyring refreshed successfully...')
+                    print('  New signatures for key: ' + fingerprint)
+                else:
+                    print('Keyring refreshed successfully...')
 
-        return success
+    def import_key_and_check_status(self, key):
+        """Import a GnuPG key and check that the operation was successful.
+        :param str key: A string specifying the key's filepath from
+            ``Common.paths``
+        :rtype: bool
+        :returns: ``True`` if the key is now within the keyring (or was
+            previously and hasn't changed). ``False`` otherwise.
+        """
+        with gpg.Context() as c:
+            c.set_engine_info(gpg.constants.protocol.OpenPGP, home_dir=self.paths['gnupg_homedir'])
+
+            impkey = self.paths['signing_keys'][key]
+            try:
+                c.op_import(gpg.Data(file=impkey))
+            except:
+                return False
+            else:
+                result = c.op_import_result()
+                if result and self.fingerprints[key] in result.imports[0].fpr:
+                    return True
+                else:
+                    return False
 
     # import gpg keys
     def import_keys(self):
         """Import all GnuPG keys.
-
         :rtype: bool
         :returns: ``True`` if all keys were successfully imported; ``False``
             otherwise.
         """
-        keys = ['tor_browser_developers',]
+        keys = ['tor_browser_developers', ]
         all_imports_succeeded = True
 
         for key in keys:
             imported = self.import_key_and_check_status(key)
             if not imported:
-                print _('Could not import key with fingerprint: %s.'
-                        % self.fingerprints[key])
+                print(_('Could not import key with fingerprint: %s.'
+                        % self.fingerprints[key]))
                 all_imports_succeeded = False
 
         if not all_imports_succeeded:
-            print _('Not all keys were imported successfully!')
+            print(_('Not all keys were imported successfully!'))
 
         return all_imports_succeeded
 
@@ -262,8 +274,7 @@ class Common:
             'tbl_version': self.tbl_version,
             'installed': False,
             'download_over_tor': False,
-            'modem_sound': False,
-            'tor_socks_address': 'tcp:127.0.0.1:9050',
+            'tor_socks_address': '127.0.0.1:9050',
             'mirror': self.default_mirror,
             'force_en-US': False,
         }
@@ -281,6 +292,11 @@ class Common:
                     settings[setting] = default_settings[setting]
                     resave = True
 
+            # make sure tor_socks_address doesn't start with 'tcp:'
+            if settings['tor_socks_address'].startswith('tcp:'):
+                settings['tor_socks_address'] = settings['tor_socks_address'][4:]
+                resave = True
+
             # make sure the version is current
             if settings['tbl_version'] != self.tbl_version:
                 settings['tbl_version'] = self.tbl_version