X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=torbrowser_launcher%2Fcommon.py;h=e7af753a4218ad7abc1fafe017badb8b4a5694da;hb=fae91193ca7fc04e144ba1ec256a607471a60852;hp=a3047868a7092ff65a65184f415e0cc7d6692003;hpb=914d200d72f0d0c34725dd2d565cf5e0e296085a;p=torbrowser-launcher.git diff --git a/torbrowser_launcher/common.py b/torbrowser_launcher/common.py index a304786..e7af753 100644 --- a/torbrowser_launcher/common.py +++ b/torbrowser_launcher/common.py @@ -26,8 +26,6 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from __future__ import print_function - import os import sys import platform @@ -36,24 +34,12 @@ import locale import pickle import json import re - -try: - import gpg - gpgme_support = True -except ImportError: - gpgme_support = False - -import pygtk -pygtk.require('2.0') -import gtk - -SHARE = os.getenv('TBL_SHARE', sys.prefix+'/share/torbrowser-launcher') - import gettext -gettext.install('torbrowser-launcher') +import gpg + +SHARE = os.getenv("TBL_SHARE", sys.prefix + "/share") + "/torbrowser-launcher" -from twisted.internet import gtk2reactor -gtk2reactor.install() +gettext.install("torbrowser-launcher") # We're looking for output which: # @@ -61,130 +47,181 @@ gtk2reactor.install() # 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://dist.torproject.org/' + 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", + "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' + 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, 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'), - 'keyserver_ca': os.path.join(SHARE, 'sks-keyservers.netCA.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://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', + "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 @@ -204,37 +241,46 @@ class Common: # 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) + print("Refreshing local keyring... Missing key: " + fingerprint) else: - print('Refreshing local keyring...') - - p = subprocess.Popen(['/usr/bin/gpg', '--status-fd', '2', - '--homedir', self.paths['gnupg_homedir'], - '--keyserver', 'hkps://hkps.pool.sks-keyservers.net', - '--keyserver-options', 'ca-cert-file=' + self.paths['keyserver_ca'] - + ',include-revoked,no-honor-keyserver-url,no-honor-pka-record', - '--refresh-keys'], stderr=subprocess.PIPE) + print("Refreshing local keyring...") + + # 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"], + "--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': + 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) + 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...') + print("Keyring refreshed successfully...") def import_key_and_check_status(self, key): """Import a GnuPG key and check that the operation was successful. @@ -244,38 +290,22 @@ class Common: :returns: ``True`` if the key is now within the keyring (or was previously and hasn't changed). ``False`` otherwise. """ - if gpgme_support: - 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 + 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: - result = c.op_import_result() - if result and self.fingerprints[key] in result.imports[0].fpr: - return True - else: - return False - else: - success = False - - p = subprocess.Popen(['/usr/bin/gpg', '--status-fd', '2', - '--homedir', self.paths['gnupg_homedir'], - '--import', self.paths['signing_keys'][key]], - stderr=subprocess.PIPE) - p.wait() - - for output in p.stderr.readlines(): - match = gnupg_import_ok_pattern.match(output) - if match: - if match.group().find(self.fingerprints[key]) >= 0: - success = True - break - - return success + return False # import gpg keys def import_keys(self): @@ -284,50 +314,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!")) - self.refresh_keyring() 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: @@ -335,9 +369,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 @@ -345,10 +384,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: @@ -357,5 +396,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