]> git.lizzy.rs Git - torbrowser-launcher.git/blobdiff - torbrowser_launcher/common.py
Add 'my' translation
[torbrowser-launcher.git] / torbrowser_launcher / common.py
index 45fd0427fe9b1fdfb1023574f611a43066e1bf51..757f4a124f4d4c5dc5c316b1e3def4f6c96e513a 100644 (file)
@@ -26,140 +26,213 @@ 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, re, gpg
-
-import pygtk
-pygtk.require('2.0')
-import gtk
+import os
+import sys
+import platform
+import subprocess
+import locale
+import pickle
+import json
+import re
+import gettext
+import gpg
 
-SHARE = os.getenv('TBL_SHARE', sys.prefix+'/share/torbrowser-launcher')
+SHARE = os.getenv("TBL_SHARE", sys.prefix + "/share") + "/torbrowser-launcher"
 
-import gettext
-gettext.install('torbrowser-launcher')
+gettext.install("torbrowser-launcher")
 
-from twisted.internet import gtk2reactor
-gtk2reactor.install()
+# 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
+gnupg_import_ok_pattern = re.compile(
+    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']:
-            self.mkdir(self.paths['dirs'][d])
+        for d in self.paths["dirs"]:
+            self.mkdir(self.paths["dirs"][d])
         self.load_mirrors()
         self.load_settings()
-        self.mkdir(self.paths['download_dir'])
-        self.mkdir(self.paths['tbb']['dir'])
+        self.mkdir(self.paths["download_dir"])
+        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'
+        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",
+            "cs",
+            "da",
+            "de",
+            "el",
+            "en-US",
+            "es-AR",
+            "es-ES",
+            "fa",
+            "fr",
+            "ga-IE",
+            "he",
+            "hu",
+            "id",
+            "is",
+            "it",
+            "ja",
+            "ka",
+            "ko",
+            "lt",
+            "mk",
+            "ms",
+            "my",
+            "nb-NO",
+            "nl",
+            "pl",
+            "pt-BR",
+            "ro",
+            "ru",
+            "sv-SE",
+            "th",
+            "tr",
+            "vi",
+            "zh-CN",
+            "zh-TW",
+        ]
+        default_locale = locale.getlocale()[0]
         if default_locale is None:
-            self.language = 'en-US'
+            self.language = "en-US"
         else:
-            self.language = default_locale.replace('_', '-')
+            self.language = default_locale.replace("_", "-")
             if self.language not in available_languages:
-                self.language = self.language.split('-')[0]
+                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'
+                self.language = "en-US"
+
+    # get value of environment variable, if it is not set return the default value
+    @staticmethod
+    def get_env(var_name, default_value):
+        value = os.getenv(var_name)
+        if not value:
+            value = default_value
+        return value
 
     # build all relevant paths
     def build_paths(self, tbb_version=None):
-        homedir = os.getenv('HOME')
+        homedir = os.getenv("HOME")
         if not homedir:
-            homedir = '/tmp/.torbrowser-'+os.getenv('USER')
+            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)
+                    self.set_gui(
+                        "error", _("Error creating {0}").format(homedir), [], False
+                    )
         if not os.access(homedir, os.W_OK):
-            self.set_gui('error', _("{0} is not writable").format(homedir), [], False)
+            self.set_gui("error", _("{0} is not writable").format(homedir), [], False)
 
-        tbb_config = '{0}/.config/torbrowser'.format(homedir)
-        tbb_cache = '{0}/.cache/torbrowser'.format(homedir)
-        tbb_local = '{0}/.local/share/torbrowser'.format(homedir)
+        tbb_config = '{0}/torbrowser'.format(self.get_env('XDG_CONFIG_HOME', '{0}/.config'.format(homedir)))
+        tbb_cache = '{0}/torbrowser'.format(self.get_env('XDG_CACHE_HOME', '{0}/.cache'.format(homedir)))
+        tbb_local = '{0}/torbrowser'.format(self.get_env('XDG_DATA_HOME', '{0}/.local/share'.format(homedir)))
         old_tbb_data = '{0}/.torbrowser'.format(homedir)
 
         if tbb_version:
             # tarball filename
-            if self.architecture == 'x86_64':
-                arch = 'linux64'
+            if self.architecture == "x86_64":
+                arch = "linux64"
             else:
-                arch = 'linux32'
+                arch = "linux32"
 
-            if hasattr(self, 'settings') and self.settings['force_en-US']:
-                language = 'en-US'
+            if hasattr(self, "settings") and self.settings["force_en-US"]:
+                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_filename'] = 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': {
-                    'config': tbb_config,
-                    'cache': tbb_cache,
-                    'local': tbb_local,
-                },
-                'old_data_dir': old_tbb_data,
-                'tbl_bin': sys.argv[0],
-                'icon_file': os.path.join(os.path.dirname(SHARE), 'pixmaps/torbrowser.png'),
-                'torproject_pem': os.path.join(SHARE, 'torproject.pem'),
-                'signing_keys': {
-                    'tor_browser_developers': os.path.join(SHARE, 'tor-browser-developers.asc')
+                "dirs": {"config": tbb_config, "cache": tbb_cache, "local": tbb_local,},
+                "old_data_dir": old_tbb_data,
+                "tbl_bin": sys.argv[0],
+                "icon_file": os.path.join(
+                    os.path.dirname(SHARE), "pixmaps/torbrowser.png"
+                ),
+                "torproject_pem": os.path.join(SHARE, "torproject.pem"),
+                "signing_keys": {
+                    "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': {
-                    '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',
+                "mirrors_txt": [
+                    os.path.join(SHARE, "mirrors.txt"),
+                    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": {
+                    "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",
                 },
             }
 
         # Add the expected fingerprint for imported keys:
         self.fingerprints = {
-            'tor_browser_developers': 'EF6E286DDA85EA2A4BA7DE684E2C6E8793298290'
+            "tor_browser_developers": "EF6E286DDA85EA2A4BA7DE684E2C6E8793298290"
         }
 
     # create a directory
@@ -167,23 +240,59 @@ 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']
-            self.mkdir(self.paths['gnupg_homedir'])
+        if not os.path.exists(self.paths["gnupg_homedir"]):
+            print(_("Creating GnuPG homedir"), self.paths["gnupg_homedir"])
+            self.mkdir(self.paths["gnupg_homedir"])
         self.import_keys()
 
+    def refresh_keyring(self, fingerprint=None):
+        if fingerprint is not None:
+            print("Refreshing local keyring... Missing key: " + fingerprint)
+        else:
+            print("Refreshing local keyring...")
+
+        # Fetch key from wkd, as per https://support.torproject.org/tbb/how-to-verify-signature/
+        p = subprocess.Popen(
+            [
+                "gpg2",
+                "--status-fd",
+                "2",
+                "--homedir",
+                self.paths["gnupg_homedir"],
+                "--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 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...")
+
     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
@@ -193,22 +302,21 @@ class Common:
             previously and hasn't changed). ``False`` otherwise.
         """
         with gpg.Context() as c:
-            # change home directory of current gpg context to torbrowser's gpg home
-            c.set_engine_info(gpg.constants.protocol.OpenPGP, home_dir=self.paths['gnupg_homedir'])
-            
-            # try to gpg import key data
-            impkey = self.paths['signing_keys'][key]
-            if os.path.isfile(impkey):
+            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))
-            else:
-                print _("Signing key not found")
-        
-            # store import results, if any then return result
-            result = c.op_import_result()
-            if result:
-                return True
-            else:
+            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):
@@ -217,49 +325,54 @@ class Common:
         :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
 
     # load mirrors
     def load_mirrors(self):
         self.mirrors = []
-        for srcfile in self.paths['mirrors_txt']:
+        for srcfile in self.paths["mirrors_txt"]:
             if not os.path.exists(srcfile):
                 continue
-            for mirror in open(srcfile, 'r').readlines():
+            for mirror in open(srcfile, "r").readlines():
                 if mirror.strip() not in self.mirrors:
                     self.mirrors.append(mirror.strip())
 
     # load settings
     def load_settings(self):
         default_settings = {
-            'tbl_version': self.tbl_version,
-            'installed': False,
-            'download_over_tor': False,
-            'modem_sound': False,
-            'tor_socks_address': 'tcp:127.0.0.1:9050',
-            'mirror': self.default_mirror,
-            'force_en-US': False,
+            "tbl_version": self.tbl_version,
+            "installed": False,
+            "download_over_tor": False,
+            "tor_socks_address": "127.0.0.1:9050",
+            "mirror": self.default_mirror,
+            "force_en-US": False,
         }
 
-        if os.path.isfile(self.paths['settings_file']):
-            settings = json.load(open(self.paths['settings_file']))
+        if os.path.isfile(self.paths["settings_file"]):
+            settings = json.load(open(self.paths["settings_file"]))
             resave = False
 
             # detect installed
-            settings['installed'] = os.path.isfile(self.paths['tbb']['start'])
+            settings["installed"] = os.path.isfile(self.paths["tbb"]["start"])
 
             # make sure settings file is up-to-date
             for setting in default_settings:
@@ -267,9 +380,14 @@ 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
+            if settings["tbl_version"] != self.tbl_version:
+                settings["tbl_version"] = self.tbl_version
                 resave = True
 
             self.settings = settings
@@ -277,10 +395,10 @@ class Common:
                 self.save_settings()
 
         # if settings file is still using old pickle format, convert to json
-        elif os.path.isfile(self.paths['settings_file_pickle']):
-            self.settings = pickle.load(open(self.paths['settings_file_pickle']))
+        elif os.path.isfile(self.paths["settings_file_pickle"]):
+            self.settings = pickle.load(open(self.paths["settings_file_pickle"]))
             self.save_settings()
-            os.remove(self.paths['settings_file_pickle'])
+            os.remove(self.paths["settings_file_pickle"])
             self.load_settings()
 
         else:
@@ -289,5 +407,5 @@ class Common:
 
     # save settings
     def save_settings(self):
-        json.dump(self.settings, open(self.paths['settings_file'], 'w'))
+        json.dump(self.settings, open(self.paths["settings_file"], "w"))
         return True