]> git.lizzy.rs Git - torbrowser-launcher.git/commitdiff
Merge branch 'patch-2' of https://github.com/scootergrisen/torbrowser-launcher into...
authorMicah Lee <micah@micahflee.com>
Fri, 18 Jun 2021 22:15:15 +0000 (18:15 -0400)
committerMicah Lee <micah@micahflee.com>
Fri, 18 Jun 2021 22:15:15 +0000 (18:15 -0400)
31 files changed:
BUILD.md
CHANGELOG.md
LICENSE
README.md
apparmor/local/torbrowser.Browser.plugin-container [deleted file]
apparmor/torbrowser.Browser.firefox
apparmor/torbrowser.Browser.plugin-container [deleted file]
apparmor/torbrowser.Tor.tor
build_rpm.sh
po/da.po [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/fr.po
po/hr.po [new file with mode: 0644]
po/pt_BR.po [new file with mode: 0644]
po/sv.po [new file with mode: 0644]
po/zh_TW.po [new file with mode: 0644]
screenshot.png
setup.py
share/applications/torbrowser-settings.desktop
share/applications/torbrowser.desktop
share/icons/hicolor/128x128/apps/torbrowser.png [new file with mode: 0644]
share/metainfo/torbrowser.appdata.xml
share/pixmaps/torbrowser.png [deleted file]
share/torbrowser-launcher/sks-keyservers.netCA.pem [deleted file]
share/torbrowser-launcher/tor-browser-developers.asc
share/torbrowser-launcher/version
stdeb.cfg
torbrowser_launcher/__init__.py
torbrowser_launcher/common.py
torbrowser_launcher/launcher.py
torbrowser_launcher/settings.py

index 6155893a362aeea7e9e5b85a01f86f6ed3467c1f..88c610979be24890c9b262b0371b457978403020 100644 (file)
--- a/BUILD.md
+++ b/BUILD.md
@@ -12,7 +12,7 @@ Then install dependencies, build a package, and install:
 ### Debian, Ubuntu, Linux Mint, etc.
 
 ```sh
-sudo apt install build-essential dh-python python3-all python3-stdeb python3-pyqt5 python3-gpg python3-requests python3-socks gnupg2 tor
+sudo apt install build-essential dh-python python3-all python3-stdeb python3-pyqt5 python3-gpg python3-requests python3-socks python3-packaging gnupg2 tor
 ./build_deb.sh
 sudo dpkg -i deb_dist/torbrowser-launcher_*.deb
 ```
@@ -20,7 +20,7 @@ sudo dpkg -i deb_dist/torbrowser-launcher_*.deb
 ### Red Hat, Fedora, CentOS, etc.
 
 ```sh
-sudo dnf install rpm-build python3-qt5 python3-gpg python3-requests python3-pysocks gnupg2 tor
+sudo dnf install rpm-build python3-qt5 python3-gpg python3-requests python3-pysocks python3-packaging gnupg2 tor
 ./build_rpm.sh
 sudo yum install dist/torbrowser-launcher-*.rpm
 ```
index cee562ac627746376edbb883352ce7a1e7039474..8e66bb14e6569b6d606eadd510583dc3e7f2a349 100644 (file)
@@ -1,5 +1,17 @@
 # Tor Browser Launcher Changelog
 
+## 0.3.3
+
+* Switch to Web Key Directory to refresh signing key from torproject.org, because everything is broken
+* Use proper version comparison now that Tor Browser 10.0 is out
+* Fix DNS leak when downloading over Tor
+* Various bug fixes, as well as AppData and AppArmor fixes
+
+## 0.3.2
+
+* Switch to keys.openpgp.org when refreshing signing key, because SKS keyservers are broken
+* Use new Tor Browser logo
+
 ## 0.3.1
 
 * Ship with latest version of the Tor Browser Developers OpenPGP public key
diff --git a/LICENSE b/LICENSE
index e25643d8872cca29c2b6a9af7eeec6a860578283..d279732e6c8861beb553989fac943ff715a10edb 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
 Tor Browser Launcher
 https://github.com/micahflee/torbrowser-launcher/
 
-Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
+Copyright (c) 2013-2021 Micah Lee <micah@micahflee.com>
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
index 440576aac8c48adfab8d60ef84cd983e468924d0..b293a04ad3ad4ea9191c219e35efa94069239920 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Tor Browser Launcher
 
-_**Are you getting an error?** Sometimes updates in Tor Browser itself will break Tor Browser Launcher. There's a good chance that the problem you're experiencing has already been fixed in the [newest version](https://github.com/micahflee/torbrowser-launcher/releases), but Linux distributions can be slow to provide up-to-date packages. In this case, you can install from the PPA (instructions below), or [build from source](/BUILD.md)._
+_**Are you getting an error?** Sometimes updates in Tor Browser itself will break Tor Browser Launcher. There's a good chance that the problem you're experiencing has already been fixed in the [newest version](https://github.com/micahflee/torbrowser-launcher/releases). Try installing from Flatpak (instructions below), or [build from source](/BUILD.md)._
 
 Tor Browser Launcher is intended to make Tor Browser easier to install and use for GNU/Linux users. You install ```torbrowser-launcher``` from your distribution's package manager and it handles everything else:
 
@@ -16,12 +16,22 @@ You might want to check out the [security design doc](/security_design.md).
 
 ![Tor Browser Launcher screenshot](/screenshot.png)
 
-# Installing from the PPA
+# Installing
 
-If you want to always have the latest version of the `torbrowser-launcher` package before your distribution gets it, you can use my PPA:
+You can install `torbrowser-launcher` from your operating system's package manager, but it might be out-of-date and have issues working. If you want to make sure you always have the latest version, use Flatpak:
 
-```sh
-sudo add-apt-repository ppa:micahflee/ppa
-sudo apt-get update
-sudo apt-get install torbrowser-launcher
+## Installing in any Linux distro using Flatpak
+
+Install Flatpak using these [instructions](https://flatpak.org/setup/).
+
+Then install `torbrowser-launcher` like this:
+
+```
+flatpak install flathub com.github.micahflee.torbrowser-launcher -y
+```
+
+Run `torbrowser-launcher` either by using the GUI desktop launcher, or by running:
+
+```
+flatpak run com.github.micahflee.torbrowser-launcher
 ```
diff --git a/apparmor/local/torbrowser.Browser.plugin-container b/apparmor/local/torbrowser.Browser.plugin-container
deleted file mode 100644 (file)
index e69de29..0000000
index 9f269e1702416ae99ab61c7efb46e025af522922..6882f9d9ae186c129e4faa55529953c5ba81137b 100644 (file)
@@ -4,19 +4,25 @@
 @{torbrowser_firefox_executable} = /home/*/.local/share/torbrowser/tbb/{i686,x86_64}/tor-browser_*/Browser/firefox.real
 
 profile torbrowser_firefox @{torbrowser_firefox_executable} {
+  #include <abstractions/audio>
   #include <abstractions/gnome>
+  #include <abstractions/ibus>
+  #include if exists <abstractions/vulkan>
 
   # Uncomment the following lines if you want to give the Tor Browser read-write
   # access to most of your personal files.
   # #include <abstractions/user-download>
   # @{HOME}/ r,
 
+  # Audio support
+  /{,usr/}bin/pulseaudio Pixr,
+
   #dbus,
   network netlink raw,
   network tcp,
 
-  ptrace (trace) peer=torbrowser_plugin_container,
-  signal (send) set=("term") peer=torbrowser_plugin_container,
+  ptrace (trace) peer=@{profile_name},
+  signal (receive, send) set=("term") peer=@{profile_name},
 
   deny /etc/host.conf r,
   deny /etc/hosts r,
@@ -26,12 +32,14 @@ profile torbrowser_firefox @{torbrowser_firefox_executable} {
   deny /etc/group r,
   deny /etc/mailcap r,
 
-  deny /etc/machine-id r,
-  deny /var/lib/dbus/machine-id r,
+  /etc/machine-id r,
+  /var/lib/dbus/machine-id r,
 
   /dev/ r,
   /dev/shm/ r,
 
+  owner @{PROC}/@{pid}/cgroup r,
+  owner @{PROC}/@{pid}/environ r,
   owner @{PROC}/@{pid}/fd/ r,
   owner @{PROC}/@{pid}/mountinfo r,
   owner @{PROC}/@{pid}/stat r,
@@ -51,20 +59,28 @@ profile torbrowser_firefox @{torbrowser_firefox_executable} {
   owner @{torbrowser_home_dir}/*.so mr,
   owner @{torbrowser_home_dir}/.cache/fontconfig/ rwk,
   owner @{torbrowser_home_dir}/.cache/fontconfig/** rwkl,
-  owner @{torbrowser_home_dir}/components/*.so mr,
-  owner @{torbrowser_home_dir}/browser/components/*.so mr,
+  owner @{torbrowser_home_dir}/browser/** r,
+  owner @{torbrowser_home_dir}/{,browser/}components/*.so mr,
+  owner @{torbrowser_home_dir}/Downloads/ rwk,
+  owner @{torbrowser_home_dir}/Downloads/** rwk,
   owner @{torbrowser_home_dir}/firefox rix,
-  owner @{torbrowser_home_dir}/{,TorBrowser/UpdateInfo/}updates/[0-9]*/updater ix,
-  owner @{torbrowser_home_dir}/{,TorBrowser/UpdateInfo/}updates/0/MozUpdater/bgupdate/updater ix,
+  owner @{torbrowser_home_dir}/{,TorBrowser/UpdateInfo/}updates/[0-9]*/* rw,
+  owner @{torbrowser_home_dir}/{,TorBrowser/UpdateInfo/}updates/[0-9]*/{,MozUpdater/bgupdate/}updater ix,
+  owner @{torbrowser_home_dir}/updater ix,
+  owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/.parentwritetest rw,
   owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profiles.ini r,
-  owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/ r,
+  owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/{,**} rwk,
+  owner @{torbrowser_home_dir}/TorBrowser/Data/fontconfig/fonts.conf r,
+  owner @{torbrowser_home_dir}/fonts/* l,
   owner @{torbrowser_home_dir}/TorBrowser/Tor/tor px,
   owner @{torbrowser_home_dir}/TorBrowser/Tor/ r,
   owner @{torbrowser_home_dir}/TorBrowser/Tor/*.so mr,
   owner @{torbrowser_home_dir}/TorBrowser/Tor/*.so.* mr,
+  owner @{torbrowser_home_dir}/TorBrowser/Tor/libstdc++/*.so mr,
+  owner @{torbrowser_home_dir}/TorBrowser/Tor/libstdc++/*.so.* mr,
 
-  # Web Content processes
-  owner @{torbrowser_firefox_executable} px -> torbrowser_plugin_container,
+  # parent Firefox process when restarting after upgrade, Web Content processes
+  owner @{torbrowser_firefox_executable} pxmr -> torbrowser_firefox,
 
   /etc/mailcap r,
   /etc/mime.types r,
@@ -86,6 +102,7 @@ profile torbrowser_firefox @{torbrowser_firefox_executable} {
   /sys/devices/system/cpu/present r,
   /sys/devices/system/node/ r,
   /sys/devices/system/node/node[0-9]*/meminfo r,
+  /sys/fs/cgroup/cpu,cpuacct/{,user.slice/}cpu.cfs_quota_us r,
   deny /sys/devices/virtual/block/*/uevent r,
 
   # Should use abstractions/gstreamer instead once merged upstream
@@ -96,6 +113,7 @@ profile torbrowser_firefox @{torbrowser_firefox_executable} {
 
   # Required for multiprocess Firefox (aka Electrolysis, i.e. e10s)
   owner /{dev,run}/shm/org.chromium.* rw,
+  owner /dev/shm/org.mozilla.ipc.[0-9]*.[0-9]* rw, # for Chromium IPC
 
   # Deny access to DRM nodes, that's granted by the X abstraction, which is
   # sourced by the gnome abstraction, that we include.
@@ -110,6 +128,8 @@ profile torbrowser_firefox @{torbrowser_firefox_executable} {
   deny @{PROC}/@{pid}/net/route r,
   deny /sys/devices/system/cpu/cpufreq/policy[0-9]*/cpuinfo_max_freq r,
   deny /sys/devices/system/cpu/*/cache/index[0-9]*/size r,
+  deny /run/user/[0-9]*/dconf/user rw,
+  deny /usr/bin/lsb_release x,
 
   # Silence denial logs about PulseAudio
   deny /etc/pulse/client.conf r,
@@ -122,5 +142,20 @@ profile torbrowser_firefox @{torbrowser_firefox_executable} {
   /etc/xfce4/defaults.list r,
   /usr/share/xfce4/applications/ r,
 
+  # u2f (tested with Yubikey 4)
+  /sys/class/ r,
+  /sys/bus/ r,
+  /sys/class/hidraw/ r,
+  /run/udev/data/c24{7,9}:* r,
+  /dev/hidraw* rw,
+  # Yubikey NEO also needs this:
+  /sys/devices/**/hidraw/hidraw*/uevent r,
+
+  # Needed for Firefox sandboxing via unprivileged user namespaces
+  capability sys_admin,
+  capability sys_chroot,
+  owner @{PROC}/@{pid}/{gid,uid}_map w,
+  owner @{PROC}/@{pid}/setgroups w,
+
   #include <local/torbrowser.Browser.firefox>
 }
diff --git a/apparmor/torbrowser.Browser.plugin-container b/apparmor/torbrowser.Browser.plugin-container
deleted file mode 100644 (file)
index 7ec8a00..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#include <tunables/global>
-#include <tunables/torbrowser>
-
-@{torbrowser_firefox_executable} = /home/*/.local/share/torbrowser/tbb/{i686,x86_64}/tor-browser_*/Browser/firefox.real
-
-profile torbrowser_plugin_container {
-  #include <abstractions/gnome>
-
-  # Uncomment the following lines if you want Tor Browser
-  # to have direct access to your sound hardware. You will also
-  # need to remove, further bellow:
-  #  - the "deny" word in the machine-id lines
-  #  - the rules that deny reading /etc/pulse/client.conf
-  #    and executing /usr/bin/pulseaudio
-  # #include <abstractions/audio>
-  # /etc/asound.conf r,
-  # owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/tmp/mozilla-temp-* rw,
-
-  signal (receive) set=("term") peer=torbrowser_firefox,
-
-  deny /etc/host.conf r,
-  deny /etc/hosts r,
-  deny /etc/nsswitch.conf r,
-  deny /etc/resolv.conf r,
-  deny /etc/passwd r,
-  deny /etc/group r,
-  deny /etc/mailcap r,
-
-  deny /etc/machine-id r,
-  deny /var/lib/dbus/machine-id r,
-
-  /etc/mime.types r,
-  /usr/share/applications/gnome-mimeapps.list r,
-
-  /dev/shm/ r,
-
-  owner @{PROC}/@{pid}/environ r,
-  owner @{PROC}/@{pid}/fd/ r,
-  owner @{PROC}/@{pid}/mountinfo r,
-  owner @{PROC}/@{pid}/stat r,
-  owner @{PROC}/@{pid}/status r,
-  owner @{PROC}/@{pid}/task/*/stat r,
-  @{PROC}/sys/kernel/random/uuid r,
-
-  owner @{torbrowser_home_dir}/*.dat r,
-  owner @{torbrowser_home_dir}/*.manifest r,
-  owner @{torbrowser_home_dir}/*.so mr,
-  owner @{torbrowser_home_dir}/.cache/fontconfig/   rw,
-  owner @{torbrowser_home_dir}/.cache/fontconfig/** rw,
-  owner @{torbrowser_home_dir}/browser/** r,
-  owner @{torbrowser_home_dir}/components/*.so mr,
-  owner @{torbrowser_home_dir}/browser/components/*.so mr,
-  owner @{torbrowser_home_dir}/defaults/pref/     r,
-  owner @{torbrowser_home_dir}/defaults/pref/*.js r,
-  owner @{torbrowser_home_dir}/dependentlibs.list r,
-  owner @{torbrowser_home_dir}/fonts/   r,
-  owner @{torbrowser_home_dir}/fonts/** r,
-  owner @{torbrowser_home_dir}/omni.ja r,
-  owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/extensions/*.xpi r,
-  owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/startupCache/* r,
-  owner @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/tmp/* rw,
-  owner @{torbrowser_home_dir}/TorBrowser/Data/fontconfig/fonts.conf r,
-  owner @{torbrowser_home_dir}/TorBrowser/Tor/ r,
-  owner @{torbrowser_home_dir}/TorBrowser/Tor/*.so mr,
-  owner @{torbrowser_home_dir}/TorBrowser/Tor/*.so.* mr,
-  owner @{torbrowser_home_dir}/Downloads/ rwk,
-  owner @{torbrowser_home_dir}/Downloads/** rwk,
-
-  owner @{torbrowser_firefox_executable} ixmr -> torbrowser_plugin_container,
-
-  /sys/devices/system/cpu/ r,
-  /sys/devices/system/cpu/present r,
-  /sys/devices/system/node/ r,
-  /sys/devices/system/node/node[0-9]*/meminfo r,
-  deny /sys/devices/virtual/block/*/uevent r,
-
-  # Should use abstractions/gstreamer instead once merged upstream
-  /etc/udev/udev.conf r,
-  /run/udev/data/+pci:* r,
-  /sys/devices/pci[0-9]*/**/uevent r,
-  owner /{dev,run}/shm/shmfd-* rw,
-
-  # Required for multiprocess Firefox (aka Electrolysis, i.e. e10s)
-  owner /{dev,run}/shm/org.chromium.* rw,
-
-  # Deny access to DRM nodes, that's granted by the X abstraction, which is
-  # sourced by the gnome abstraction, that we include.
-  deny /dev/dri/** rwklx,
-
-  # Silence denial logs about permissions we don't need
-  deny /dev/dri/   rwklx,
-  deny @{PROC}/@{pid}/net/route r,
-  deny /sys/devices/system/cpu/cpufreq/policy[0-9]*/cpuinfo_max_freq r,
-  deny /sys/devices/system/cpu/*/cache/index[0-9]*/size r,
-
-  # Silence denial logs about PulseAudio
-  deny /etc/pulse/client.conf r,
-  deny /usr/bin/pulseaudio x,
-
-  #include <local/torbrowser.Browser.plugin-container>
-}
index b0bfce057e4130bf7044c1847929dbfa6be6f55e..f5b8177908d8e5e69855ec5dad2e6e6ba1717e77 100644 (file)
@@ -24,6 +24,7 @@ profile torbrowser_tor @{torbrowser_tor_executable} {
   # Support some of the included pluggable transports
   owner @{torbrowser_home_dir}/TorBrowser/Tor/PluggableTransports/** rix,
   @{PROC}/sys/net/core/somaxconn r,
+  #include <abstractions/ssl_certs>
 
   # Silence file_inherit logs
   deny @{torbrowser_home_dir}/{browser/,}omni.ja r,
@@ -31,6 +32,9 @@ profile torbrowser_tor @{torbrowser_tor_executable} {
   deny @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/.parentlock rw,
   deny @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/extensions/*.xpi r,
   deny @{torbrowser_home_dir}/TorBrowser/Data/Browser/profile.default/startupCache/* r,
+  # Silence logs from included pluggable transports
+  deny /etc/hosts r,
+  deny /etc/services r,
 
   @{PROC}/sys/kernel/random/uuid r,
   /sys/devices/system/cpu/ r,
index 07e8692e0b3427872ed6bb35d02e8a3040390bf9..30a79cbd31730218bfa8d53b34ed106a4f738c66 100755 (executable)
@@ -6,7 +6,7 @@ VERSION=`cat share/torbrowser-launcher/version`
 rm -r build dist
 
 # build binary package
-python3 setup.py bdist_rpm --requires="python3-qt5, python3-gpg, python3-requests, python3-pysocks, gnupg2"
+python3 setup.py bdist_rpm --requires="python3-qt5, python3-gpg, python3-requests, python3-pysocks, python3-packaging, gnupg2"
 
 # install it
 echo ""
diff --git a/po/da.po b/po/da.po
new file mode 100644 (file)
index 0000000..3680945
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,254 @@
+# Danish translation for Tor Browser Launcher.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# scootergrisen, 2018.
+msgid ""
+msgstr ""
+"Project-Id-Version: torbrowser-launcher\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-23 15:47-0700\n"
+"PO-Revision-Date: 2018-09-30 16:24+0200\n"
+"Last-Translator: scootergrisen\n"
+"Language-Team: Danish\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: __init__.py:65 launcher.py:470
+msgid "Tor Browser Launcher"
+msgstr "Tor Browser-opstarter"
+
+#: __init__.py:66
+msgid "By Micah Lee, licensed under MIT"
+msgstr "Af Micah Lee, licenseret under MIT"
+
+#: __init__.py:67
+#, python-brace-format
+msgid "version {0}"
+msgstr "version {0}"
+
+#: common.py:100
+#, python-brace-format
+msgid "Error creating {0}"
+msgstr "Fejl ved oprettelse af {0}"
+
+#: common.py:102 common.py:180
+#, python-brace-format
+msgid "{0} is not writable"
+msgstr "{0} er skrivebeskyttet"
+
+#: common.py:177
+#, python-brace-format
+msgid "Cannot create directory {0}"
+msgstr "Kan ikke oprette mappen {0}"
+
+#: common.py:187
+msgid "Creating GnuPG homedir"
+msgstr "Opretter GnuPG-hjemmemappe"
+
+#: common.py:254
+#, python-format
+msgid "Could not import key with fingerprint: %s."
+msgstr "Kunne ikke importere nøgle med fingeraftryk: %s."
+
+#: common.py:259
+msgid "Not all keys were imported successfully!"
+msgstr "Det lykkedes ikke at importere alle nøgler!"
+
+#: launcher.py:83
+msgid "Downloading Tor Browser for the first time."
+msgstr "Downloader Tor Browser for første gang."
+
+#: launcher.py:85
+msgid ""
+"Your version of Tor Browser is out-of-date. Downloading the newest version."
+msgstr "Din version af Tor Browser er forældet. Downloader den nyeste version."
+
+#: launcher.py:100
+msgid "Downloading over Tor"
+msgstr "Downloader over Tor"
+
+#: launcher.py:111
+msgid "Tor Browser"
+msgstr "Tor Browser"
+
+#: launcher.py:128
+msgid "Start"
+msgstr "Start"
+
+#: launcher.py:174
+msgid "Yes"
+msgstr "Ja"
+
+#: launcher.py:178
+msgid "Exit"
+msgstr "Afslut"
+
+#: launcher.py:192 settings.py:136
+msgid "Cancel"
+msgstr "Annuller"
+
+#: launcher.py:231 launcher.py:245 launcher.py:249 launcher.py:279
+#: launcher.py:281
+msgid "Downloading"
+msgstr "Downloader"
+
+#: launcher.py:238
+msgid "Latest version: {}"
+msgstr "Seneste version: {}"
+
+#: launcher.py:241
+msgid "Error detecting Tor Browser version."
+msgstr "Fejl ved registrering af Tor Browser-version."
+
+#: launcher.py:256 launcher.py:357
+msgid "Verifying Signature"
+msgstr "Verificerer signatur"
+
+#: launcher.py:260
+msgid "Extracting"
+msgstr "Udpakker"
+
+#: launcher.py:264
+msgid "Running"
+msgstr "Kører"
+
+#: launcher.py:268
+msgid "Starting download over again"
+msgstr "Starter download på ny"
+
+#: launcher.py:279 launcher.py:295
+msgid "(over Tor)"
+msgstr "(over Tor)"
+
+#: launcher.py:293
+msgid "Downloaded"
+msgstr "Downloadet"
+
+#: launcher.py:393
+msgid "Installing"
+msgstr "Installerer"
+
+#: launcher.py:401
+#, python-brace-format
+msgid "Tor Browser Launcher doesn't understand the file format of {0}"
+msgstr "Tor Browser-opstarter forstår ikke filformatet af {0}"
+
+#: launcher.py:427
+msgid ""
+"The version of Tor Browser you have installed is earlier than it should be, "
+"which could be a sign of an attack!"
+msgstr ""
+"Den version af Tor Browser som du har installeret er tidligere end den bør "
+"være, hvilket kan være tegn på angreb!"
+
+#: launcher.py:446
+msgid "Downloading Tor Browser over again."
+msgstr "Downloader Tor Browser på ny."
+
+#: launcher.py:516 launcher.py:525 launcher.py:533
+msgid "Download Error:"
+msgstr "Fejl ved download:"
+
+#: launcher.py:517
+msgid "You are currently using a non-default mirror"
+msgstr "Du bruger i øjeblikket et spejl som ikke er standard"
+
+#: launcher.py:518
+msgid "Would you like to switch back to the default?"
+msgstr "Vil du skifte tilbage til standarden?"
+
+#: launcher.py:527
+msgid "Would you like to try the English version of Tor Browser instead?"
+msgstr "Vil du i stedet prøve den engelske version af Tor Browser?"
+
+#: launcher.py:548
+#, python-brace-format
+msgid ""
+"Invalid SSL certificate for:\n"
+"{0}\n"
+"\n"
+"You may be under attack."
+msgstr ""
+"Ugyldigt SSL-certifikat for:\n"
+"{0}\n"
+"\n"
+"Du er måske under angreb."
+
+#: launcher.py:550
+msgid "Try the download again using Tor?"
+msgstr "Prøv downloaden igen med Tor?"
+
+#: launcher.py:559
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Trying to download over Tor. Are you sure Tor is configured correctly and "
+"running?"
+msgstr ""
+"Fejl ved start af download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Prøver at downloade over Tor. Er du sikker på, at Tor er konfigureret "
+"korrekt og kører?"
+
+#: launcher.py:563
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Are you connected to the internet?"
+msgstr ""
+"Fejl ved start af download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Har du forbindelse til internettet?"
+
+#: settings.py:46
+msgid "Tor Browser Launcher Settings"
+msgstr "Indstillinger for Tor Browser-opstarter"
+
+#: settings.py:50
+msgid "Download over system Tor"
+msgstr "Download over systemets Tor"
+
+#: settings.py:57
+msgid "Force downloading English version of Tor Browser"
+msgstr "Tving download af den engelske version af Tor Browser"
+
+#: settings.py:66
+msgid "Tor server"
+msgstr "Tor-server"
+
+#: settings.py:82
+msgid "Status: Installed"
+msgstr "Status: Installeret"
+
+#: settings.py:84
+msgid "Status: Not Installed"
+msgstr "Status: Ikke installeret"
+
+#: settings.py:87
+msgid "Install Tor Browser"
+msgstr "Installer Tor Browser"
+
+#: settings.py:92
+msgid "Reinstall Tor Browser"
+msgstr "Geninstaller Tor Browser"
+
+#: settings.py:115
+msgid "Mirror"
+msgstr "Spejl"
+
+#: settings.py:131
+msgid "Save && Exit"
+msgstr "Gem og afslut"
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..81317cd
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,255 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-23 15:47-0700\n"
+"PO-Revision-Date: 2019-12-15 21:28+0100\n"
+"Last-Translator: René Mario Baumgartner <ned84@protonmail.com>\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.4\n"
+"X-Poedit-Basepath: .\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: __init__.py:65 launcher.py:470
+msgid "Tor Browser Launcher"
+msgstr "Tor Browser Launcher"
+
+#: __init__.py:66
+msgid "By Micah Lee, licensed under MIT"
+msgstr "Von Micah Lee, lizensiert unter MIT"
+
+#: __init__.py:67
+#, python-brace-format
+msgid "version {0}"
+msgstr "Version {0}"
+
+#: common.py:100
+#, python-brace-format
+msgid "Error creating {0}"
+msgstr "Fehler erzeugt {0}"
+
+#: common.py:102 common.py:180
+#, python-brace-format
+msgid "{0} is not writable"
+msgstr "{0} ist nicht schreibbar"
+
+#: common.py:177
+#, python-brace-format
+msgid "Cannot create directory {0}"
+msgstr "Kann Verzeichnis {0} nicht erzeugen"
+
+#: common.py:187
+msgid "Creating GnuPG homedir"
+msgstr "Erzeuge GnuPG Verzeichnis"
+
+#: common.py:254
+#, python-format
+msgid "Could not import key with fingerprint: %s."
+msgstr "Konnte Schlüssel mit folgendem Fingerabdruck nicht importieren: %s."
+
+#: common.py:259
+msgid "Not all keys were imported successfully!"
+msgstr "Nicht alle Schlüssel konnten erfolgreich importiert werden!"
+
+#: launcher.py:83
+msgid "Downloading Tor Browser for the first time."
+msgstr "Lade Tor Browser das erste mal herunter."
+
+#: launcher.py:85
+msgid ""
+"Your version of Tor Browser is out-of-date. Downloading the newest version."
+msgstr ""
+"Ihre Version des Tor Browsers ist nicht mehr aktuell. Lade die neueste "
+"Version herunter."
+
+#: launcher.py:100
+msgid "Downloading over Tor"
+msgstr "Lade über Tor herunter"
+
+#: launcher.py:111
+msgid "Tor Browser"
+msgstr "Tor Browser"
+
+#: launcher.py:128
+msgid "Start"
+msgstr "Start"
+
+#: launcher.py:174
+msgid "Yes"
+msgstr "Ja"
+
+#: launcher.py:178
+msgid "Exit"
+msgstr "Exit"
+
+#: launcher.py:192 settings.py:136
+msgid "Cancel"
+msgstr "Abbruch"
+
+#: launcher.py:231 launcher.py:245 launcher.py:249 launcher.py:279
+#: launcher.py:281
+msgid "Downloading"
+msgstr "Herunterladen"
+
+#: launcher.py:238
+msgid "Latest version: {}"
+msgstr "Aktuelle Version: {}"
+
+#: launcher.py:241
+msgid "Error detecting Tor Browser version."
+msgstr "Fehler bei dem Versuch die Tor Browser Version zu finden."
+
+#: launcher.py:256 launcher.py:357
+msgid "Verifying Signature"
+msgstr "Prüfe Signatur"
+
+#: launcher.py:260
+msgid "Extracting"
+msgstr "Extrahieren"
+
+#: launcher.py:264
+msgid "Running"
+msgstr "Läuft"
+
+#: launcher.py:268
+msgid "Starting download over again"
+msgstr "Starte download nochmals"
+
+#: launcher.py:279 launcher.py:295
+msgid "(over Tor)"
+msgstr "(über Tor)"
+
+#: launcher.py:293
+msgid "Downloaded"
+msgstr "Heruntergeladen"
+
+#: launcher.py:393
+msgid "Installing"
+msgstr "Installiere"
+
+#: launcher.py:401
+#, python-brace-format
+msgid "Tor Browser Launcher doesn't understand the file format of {0}"
+msgstr "Tor Browser versteht folgendes Datei Format nicht: {0}"
+
+#: launcher.py:427
+msgid ""
+"The version of Tor Browser you have installed is earlier than it should be, "
+"which could be a sign of an attack!"
+msgstr ""
+"Die Version ihres Tor Browsers ist neuer als sie sein sollte, was auf eine "
+"Attacke schließen lassen könnte!"
+
+#: launcher.py:446
+msgid "Downloading Tor Browser over again."
+msgstr "Lade Tor Browser nochmals herunter."
+
+#: launcher.py:516 launcher.py:525 launcher.py:533
+msgid "Download Error:"
+msgstr "Fehler beim herunterladen:"
+
+#: launcher.py:517
+msgid "You are currently using a non-default mirror"
+msgstr "Sie verwenden zur Zeit einen nicht voreingestellten Mirror."
+
+#: launcher.py:518
+msgid "Would you like to switch back to the default?"
+msgstr "Wollen sie zur Voreinstellung wechseln?"
+
+#: launcher.py:527
+msgid "Would you like to try the English version of Tor Browser instead?"
+msgstr ""
+"Wollen sie die englische Version des Tor Browsers stattdessen versuchen?"
+
+#: launcher.py:548
+#, python-brace-format
+msgid ""
+"Invalid SSL certificate for:\n"
+"{0}\n"
+"\n"
+"You may be under attack."
+msgstr ""
+"Ungültiges SSL Zertifikat für:\n"
+"{0}\n"
+"\n"
+"Möglicherweise werden sie angegriffen."
+
+#: launcher.py:550
+msgid "Try the download again using Tor?"
+msgstr "Wollen sie das herunterladen nochmals über Tor versuchen?"
+
+#: launcher.py:559
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Trying to download over Tor. Are you sure Tor is configured correctly and "
+"running?"
+msgstr ""
+"Fehler beim starten des Downloads:\n"
+"\n"
+"{0}\n"
+"\n"
+"Versuche über Tor herunterzuladen. Sind sie sicher das Tor richtig "
+"konfiguriert und aktiv ist?"
+
+#: launcher.py:563
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Are you connected to the internet?"
+msgstr ""
+"Fehler beim starten des Downloads:\n"
+"\n"
+"{0}\n"
+"\n"
+"Sind sie mit dem Internet verbunden?"
+
+#: settings.py:46
+msgid "Tor Browser Launcher Settings"
+msgstr "Tor Browser Launcher Einstellungen"
+
+#: settings.py:50
+msgid "Download over system Tor"
+msgstr "Herunterladen über system Tor"
+
+#: settings.py:57
+msgid "Force downloading English version of Tor Browser"
+msgstr "Herunterladen der englischen Version des Tor Browsers erzwingen"
+
+#: settings.py:66
+msgid "Tor server"
+msgstr "Tor server"
+
+#: settings.py:82
+msgid "Status: Installed"
+msgstr "Status: Installiert"
+
+#: settings.py:84
+msgid "Status: Not Installed"
+msgstr "Status: Nicht installiert"
+
+#: settings.py:87
+msgid "Install Tor Browser"
+msgstr "Tor Browser installieren"
+
+#: settings.py:92
+msgid "Reinstall Tor Browser"
+msgstr "Tor Browser nochmals installieren"
+
+#: settings.py:115
+msgid "Mirror"
+msgstr "Mirror"
+
+#: settings.py:131
+msgid "Save && Exit"
+msgstr "Speichern && Exit"
index 6e1e8b568528ac780fa7f47f4236b821e74d34b1..7b0d9b13e56451836f74c7d7b62227963e48fa5a 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -24,12 +24,12 @@ msgstr "Tor Browser Launcher"
 
 #: __init__.py:66
 msgid "By Micah Lee, licensed under MIT"
-msgstr "Par Micah Lee, sous license MIT"
+msgstr "Par Micah Lee, sous licence MIT"
 
 #: __init__.py:67
 #, python-brace-format
 msgid "version {0}"
-msgstr ""
+msgstr "version {0}"
 
 #: common.py:100
 #, python-brace-format
@@ -48,29 +48,27 @@ msgstr "Impossible de créer le dossier {0}"
 
 #: common.py:187
 msgid "Creating GnuPG homedir"
-msgstr "Creation du dossier GnuPG"
+msgstr "Création du dossier GnuPG"
 
 #: common.py:254
 #, python-format
 msgid "Could not import key with fingerprint: %s."
-msgstr "Impossible d'importer la clé: %s"
+msgstr "Impossible d'importer la clé avec l'empreinte : %s."
 
 #: common.py:259
 msgid "Not all keys were imported successfully!"
-msgstr "Certaines clés n'ont pas pu être importées"
+msgstr "Certaines clés n'ont pas pu être importées !"
 
 #: launcher.py:83
-#, fuzzy
 msgid "Downloading Tor Browser for the first time."
-msgstr "Premier téléchargement et installation du Tor Browser"
+msgstr "Téléchargement du Navigateur Tor pour la première fois."
 
 #: launcher.py:85
-#, fuzzy
 msgid ""
 "Your version of Tor Browser is out-of-date. Downloading the newest version."
 msgstr ""
-"Votre version du Tor Browser est obsolète. Téléchargement et installation de "
-"la nouvelle version."
+"Votre version du Navigateur Tor est obsolète. Téléchargement de la nouvelle "
+"version."
 
 #: launcher.py:100
 msgid "Downloading over Tor"
@@ -78,7 +76,7 @@ msgstr "Télécharger à travers Tor"
 
 #: launcher.py:111
 msgid "Tor Browser"
-msgstr "Tor Browser"
+msgstr "Navigateur Tor"
 
 #: launcher.py:128
 msgid "Start"
@@ -86,11 +84,11 @@ msgstr "Démarrer"
 
 #: launcher.py:174
 msgid "Yes"
-msgstr ""
+msgstr "Oui"
 
 #: launcher.py:178
 msgid "Exit"
-msgstr ""
+msgstr "Quitter"
 
 #: launcher.py:192 settings.py:136
 msgid "Cancel"
@@ -103,11 +101,11 @@ msgstr "Téléchargement"
 
 #: launcher.py:238
 msgid "Latest version: {}"
-msgstr "Dernière version: {}"
+msgstr "Dernière version : {}"
 
 #: launcher.py:241
 msgid "Error detecting Tor Browser version."
-msgstr "Impossible de détecter la version du Tor Browser."
+msgstr "Impossible de détecter la version du Navigateur Tor."
 
 #: launcher.py:256 launcher.py:357
 msgid "Verifying Signature"
@@ -127,7 +125,7 @@ msgstr "Télécharger à nouveau"
 
 #: launcher.py:279 launcher.py:295
 msgid "(over Tor)"
-msgstr ""
+msgstr "(à travers Tor)"
 
 #: launcher.py:293
 msgid "Downloaded"
@@ -147,29 +145,29 @@ msgid ""
 "The version of Tor Browser you have installed is earlier than it should be, "
 "which could be a sign of an attack!"
 msgstr ""
-"La version du Tor Browser installée est antérieure à l'actuelle, ce qui peut "
-"être la signature d'une attaque!"
+"La version du Navigateur Tor que vous avez installé est antérieure à ce "
+"qu'elle devrait, ce qui peut être le signe d'une attaque !"
 
 #: launcher.py:446
-#, fuzzy
 msgid "Downloading Tor Browser over again."
-msgstr "Nouveau téléchargement du Tor Browser Bundle."
+msgstr "Télécharger le Navigateur Tor à nouveau."
 
 #: launcher.py:516 launcher.py:525 launcher.py:533
 msgid "Download Error:"
-msgstr "Erreur de téléchargement:"
+msgstr "Erreur de téléchargement :"
 
 #: launcher.py:517
 msgid "You are currently using a non-default mirror"
-msgstr "Vous utilisez actuellement un miroir non-défaut"
+msgstr "Vous utilisez actuellement un miroir n'étant pas celui par défaut"
 
 #: launcher.py:518
 msgid "Would you like to switch back to the default?"
-msgstr "Voulez-vous revenir à la valeur par défaut?"
+msgstr "Voulez-vous revenir à la valeur par défaut ?"
 
 #: launcher.py:527
 msgid "Would you like to try the English version of Tor Browser instead?"
-msgstr "Voulez-vous essayer la version anglophone du Tor Browser à la place?"
+msgstr ""
+"Voulez-vous essayer la version anglophone du Navigateur Tor à la place ?"
 
 #: launcher.py:548
 #, python-brace-format
@@ -179,10 +177,14 @@ msgid ""
 "\n"
 "You may be under attack."
 msgstr ""
+"Certificat SSL invalide pour :\n"
+"{0}\n"
+"\n"
+"Vous pourriez être attaqué."
 
 #: launcher.py:550
 msgid "Try the download again using Tor?"
-msgstr "Essayez de télécharger à nouveau à travers Tor?"
+msgstr "Essayer de télécharger à nouveau à travers Tor ?"
 
 #: launcher.py:559
 #, python-brace-format
@@ -194,6 +196,12 @@ msgid ""
 "Trying to download over Tor. Are you sure Tor is configured correctly and "
 "running?"
 msgstr ""
+"Impossible de démarrer le téléchargement :\n"
+"\n"
+"{0}\n"
+"\n"
+"Essayez de télécharger à travers Tor. Êtes-vous sûr que Tor est configuré "
+"correctement et fonctionne ?"
 
 #: launcher.py:563
 #, python-brace-format
@@ -204,11 +212,11 @@ msgid ""
 "\n"
 "Are you connected to the internet?"
 msgstr ""
-"Impossible de démarrer le téléchargement:\n"
+"Impossible de démarrer le téléchargement :\n"
 "\n"
 "{0}\n"
 "\n"
-"Êtes-vous connecté à Internet?"
+"Êtes-vous connecté à Internet ?"
 
 #: settings.py:46
 msgid "Tor Browser Launcher Settings"
@@ -220,7 +228,7 @@ msgstr "Télécharger à travers le Tor installé sur le système"
 
 #: settings.py:57
 msgid "Force downloading English version of Tor Browser"
-msgstr "Forcer le téléchargement de la version anglophone du Tor Browser"
+msgstr "Forcer le téléchargement de la version anglophone du Navigateur Tor"
 
 #: settings.py:66
 msgid "Tor server"
@@ -228,26 +236,25 @@ msgstr "Serveur Tor"
 
 #: settings.py:82
 msgid "Status: Installed"
-msgstr "Status: Installé"
+msgstr "Statu: Installé"
 
 #: settings.py:84
 msgid "Status: Not Installed"
-msgstr "Status: Pas installé"
+msgstr "Statu: Pas installé"
 
 #: settings.py:87
 msgid "Install Tor Browser"
-msgstr "Installer le Tor Browser"
+msgstr "Installer le Navigateur Tor"
 
 #: settings.py:92
 msgid "Reinstall Tor Browser"
-msgstr "Réinstaller le Tor Browser"
+msgstr "Réinstaller le Navigateur Tor"
 
 #: settings.py:115
 msgid "Mirror"
 msgstr "Miroir"
 
 #: settings.py:131
-#, fuzzy
 msgid "Save && Exit"
 msgstr "Enregistrer et quitter"
 
diff --git a/po/hr.po b/po/hr.po
new file mode 100644 (file)
index 0000000..f1dc9e6
--- /dev/null
+++ b/po/hr.po
@@ -0,0 +1,258 @@
+# Croatian translation of Tor Browser Launcher.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Milo Ivir <mail@milotype.de>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Tor Browser Launcher\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-23 15:47-0700\n"
+"PO-Revision-Date: 2020-01-22 13:20+0100\n"
+"Last-Translator: Milo Ivir <mail@milotype.de>\n"
+"Language-Team: \n"
+"Language: hr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.12\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: __init__.py:65 launcher.py:470
+msgid "Tor Browser Launcher"
+msgstr "Pokretač Tor preglednika"
+
+#: __init__.py:66
+msgid "By Micah Lee, licensed under MIT"
+msgstr "Autor Micah Lee, MIT licenca"
+
+#: __init__.py:67
+#, python-brace-format
+msgid "version {0}"
+msgstr "verzija {0}"
+
+#: common.py:100
+#, python-brace-format
+msgid "Error creating {0}"
+msgstr "Greška prilikom stvaranja {0}"
+
+#: common.py:102 common.py:180
+#, python-brace-format
+msgid "{0} is not writable"
+msgstr "Nije moguće pisati u {0}"
+
+#: common.py:177
+#, python-brace-format
+msgid "Cannot create directory {0}"
+msgstr "Nije moguće stvoriti mapu {0}"
+
+#: common.py:187
+msgid "Creating GnuPG homedir"
+msgstr "Izrada početne mape za GnuPG"
+
+#: common.py:254
+#, python-format
+msgid "Could not import key with fingerprint: %s."
+msgstr "Nije bilo moguće uvesti ključ s otiskom prsta: %s."
+
+#: common.py:259
+msgid "Not all keys were imported successfully!"
+msgstr "Nisu svi ključevi uspješno uvezeni!"
+
+#: launcher.py:83
+msgid "Downloading Tor Browser for the first time."
+msgstr "Preuzimanje Tor preglednika po prvi put."
+
+#: launcher.py:85
+msgid ""
+"Your version of Tor Browser is out-of-date. Downloading the newest version."
+msgstr ""
+"Tvoja verzija Tor preglednika je zastarjela. Preuzima se najnovija verzija."
+
+#: launcher.py:100
+msgid "Downloading over Tor"
+msgstr "Preuzimanje preko Tora"
+
+#: launcher.py:111
+msgid "Tor Browser"
+msgstr "Tor preglednik"
+
+#: launcher.py:128
+msgid "Start"
+msgstr "Pokreni"
+
+#: launcher.py:174
+msgid "Yes"
+msgstr "Da"
+
+#: launcher.py:178
+msgid "Exit"
+msgstr "Zatvori"
+
+#: launcher.py:192 settings.py:136
+msgid "Cancel"
+msgstr "Odustani"
+
+#: launcher.py:231 launcher.py:245 launcher.py:249 launcher.py:279
+#: launcher.py:281
+msgid "Downloading"
+msgstr "Preuzimanje"
+
+#: launcher.py:238
+msgid "Latest version: {}"
+msgstr "Zadnja verzija: {}"
+
+#: launcher.py:241
+msgid "Error detecting Tor Browser version."
+msgstr "Greška prilikom otkrivanja verzije Tor preglednika."
+
+#: launcher.py:256 launcher.py:357
+msgid "Verifying Signature"
+msgstr "Potvrđivanje potpisa"
+
+#: launcher.py:260
+msgid "Extracting"
+msgstr "Otpakiravanje"
+
+#: launcher.py:264
+msgid "Running"
+msgstr "Izvršava se"
+
+#: launcher.py:268
+msgid "Starting download over again"
+msgstr "Pokreće se ponovno preuzimanje"
+
+#: launcher.py:279 launcher.py:295
+msgid "(over Tor)"
+msgstr "(preko Tora)"
+
+#: launcher.py:293
+msgid "Downloaded"
+msgstr "Preuzeto"
+
+#: launcher.py:393
+msgid "Installing"
+msgstr "Instaliranje"
+
+#: launcher.py:401
+#, python-brace-format
+msgid "Tor Browser Launcher doesn't understand the file format of {0}"
+msgstr "Pokretač Tor preglednika ne razumije format datoteke {0}"
+
+#: launcher.py:427
+msgid ""
+"The version of Tor Browser you have installed is earlier than it should be, "
+"which could be a sign of an attack!"
+msgstr ""
+"Tvoja verzija Tor preglednika je starija nego što bi trebala biti, što može "
+"biti znak napada!"
+
+#: launcher.py:446
+msgid "Downloading Tor Browser over again."
+msgstr "Ponovno preuzimanje Tor preglednika."
+
+#: launcher.py:516 launcher.py:525 launcher.py:533
+msgid "Download Error:"
+msgstr "Greška prilikom preuzimanja:"
+
+#: launcher.py:517
+msgid "You are currently using a non-default mirror"
+msgstr "Trenutačno koristiš nestandardno zrcalo"
+
+#: launcher.py:518
+msgid "Would you like to switch back to the default?"
+msgstr "Želiš li vratiti na standardno?"
+
+#: launcher.py:527
+msgid "Would you like to try the English version of Tor Browser instead?"
+msgstr "Želiš li umjesto toga isprobati englesku verziju Tor preglednika?"
+
+#: launcher.py:548
+#, python-brace-format
+msgid ""
+"Invalid SSL certificate for:\n"
+"{0}\n"
+"\n"
+"You may be under attack."
+msgstr ""
+"Neispravni SSL certifikat za:\n"
+"{0}\n"
+"\n"
+"Možda si napadnut/a."
+
+#: launcher.py:550
+msgid "Try the download again using Tor?"
+msgstr "Ponovo pokušati preuzeti pomoću Tora?"
+
+#: launcher.py:559
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Trying to download over Tor. Are you sure Tor is configured correctly and "
+"running?"
+msgstr ""
+"Greška prilikom pokretanja preuzimanja:\n"
+"\n"
+"{0}\n"
+"\n"
+"Pokušava se preuzeti preko Tora. Je li je Tor ispravno konfiguriran i "
+"pokrenut?"
+
+#: launcher.py:563
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Are you connected to the internet?"
+msgstr ""
+"Greška prilikom pokretanja preuzimanja:\n"
+"\n"
+"{0}\n"
+"\n"
+"Jesi li spojen/a na internet?"
+
+#: settings.py:46
+msgid "Tor Browser Launcher Settings"
+msgstr "Postavke pokretača Tor preglednika"
+
+#: settings.py:50
+msgid "Download over system Tor"
+msgstr "Preuzmi preko Tora sustava"
+
+#: settings.py:57
+msgid "Force downloading English version of Tor Browser"
+msgstr "Prisili preuzimanje engleske verzije Tor preglednika"
+
+#: settings.py:66
+msgid "Tor server"
+msgstr "Tor poslužitelj"
+
+#: settings.py:82
+msgid "Status: Installed"
+msgstr "Stanje: Instalirano"
+
+#: settings.py:84
+msgid "Status: Not Installed"
+msgstr "Stanje: Nije instalirano"
+
+#: settings.py:87
+msgid "Install Tor Browser"
+msgstr "Instaliraj Tor preglednik"
+
+#: settings.py:92
+msgid "Reinstall Tor Browser"
+msgstr "Ponovo instaliraj Tor preglednik"
+
+#: settings.py:115
+msgid "Mirror"
+msgstr "Zrcalo"
+
+#: settings.py:131
+msgid "Save && Exit"
+msgstr "Spremi i zatvori"
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644 (file)
index 0000000..735429d
--- /dev/null
@@ -0,0 +1,308 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-23 15:47-0700\n"
+"PO-Revision-Date: 2020-02-09 16:35-0300\n"
+"Language: pt_BR\n"
+"Language-Team: \n"
+"Last-Translator: João Vítor S. F. <jjoaovitor7@outlook.com.br>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.4\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: __init__.py:65 launcher.py:470
+msgid "Tor Browser Launcher"
+msgstr "Lançador do Navegador Tor"
+
+#: __init__.py:66
+msgid "By Micah Lee, licensed under MIT"
+msgstr "Por Micah Lee, licenciado sob o MIT"
+
+#: __init__.py:67
+#, python-brace-format
+msgid "version {0}"
+msgstr "versão {0}"
+
+#: common.py:100
+#, python-brace-format
+msgid "Error creating {0}"
+msgstr "Erro ao criar {0}"
+
+#: common.py:102 common.py:180
+#, python-brace-format
+msgid "{0} is not writable"
+msgstr "{0} não é gravável"
+
+#: common.py:177
+#, python-brace-format
+msgid "Cannot create directory {0}"
+msgstr "Não é possível criar o diretório {0}"
+
+#: common.py:187
+msgid "Creating GnuPG homedir"
+msgstr "Criando o diretório inicial do GnuPG"
+
+#: common.py:254
+#, python-format
+msgid "Could not import key with fingerprint: %s."
+msgstr "Não foi possível importar a chave com a impressão digital: %s."
+
+#: common.py:259
+msgid "Not all keys were imported successfully!"
+msgstr "Nem todas as chaves foram importadas com sucesso!"
+
+#: launcher.py:83
+#, fuzzy
+msgid "Downloading Tor Browser for the first time."
+msgstr "Baixando o Navegador Tor pela primeira vez."
+
+#: launcher.py:85
+#, fuzzy
+msgid "Your version of Tor Browser is out-of-date. Downloading the newest version."
+msgstr "Sua versão do Navegador Tor está desatualizada. Baixando a versão mais recente."
+
+#: launcher.py:100
+msgid "Downloading over Tor"
+msgstr "Baixando pelo Tor"
+
+#: launcher.py:111
+msgid "Tor Browser"
+msgstr "Navegador Tor"
+
+#: launcher.py:128
+msgid "Start"
+msgstr "Iniciar"
+
+#: launcher.py:174
+msgid "Yes"
+msgstr "Sim"
+
+#: launcher.py:178
+msgid "Exit"
+msgstr "Sair"
+
+#: launcher.py:192 settings.py:136
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: launcher.py:231 launcher.py:245 launcher.py:249 launcher.py:279
+#: launcher.py:281
+msgid "Downloading"
+msgstr "Baixando"
+
+#: launcher.py:238
+msgid "Latest version: {}"
+msgstr "Versão mais recente: {}"
+
+#: launcher.py:241
+msgid "Error detecting Tor Browser version."
+msgstr "Erro ao detectar a versão do Navegador Tor."
+
+#: launcher.py:256 launcher.py:357
+msgid "Verifying Signature"
+msgstr "Verificando Assinatura"
+
+#: launcher.py:260
+msgid "Extracting"
+msgstr "Extraindo"
+
+#: launcher.py:264
+msgid "Running"
+msgstr "Rodando"
+
+#: launcher.py:268
+msgid "Starting download over again"
+msgstr "Iniciando o download novamente"
+
+#: launcher.py:279 launcher.py:295
+msgid "(over Tor)"
+msgstr "(pelo Tor)"
+
+#: launcher.py:293
+msgid "Downloaded"
+msgstr "Baixado"
+
+#: launcher.py:393
+msgid "Installing"
+msgstr "Instalando"
+
+#: launcher.py:401
+#, python-brace-format
+msgid "Tor Browser Launcher doesn't understand the file format of {0}"
+msgstr "O Lançador do Navegador Tor não entende o formato do arquivo de {0}"
+
+#: launcher.py:427
+msgid ""
+"The version of Tor Browser you have installed is earlier than it should be, "
+"which could be a sign of an attack!"
+msgstr ""
+"A versão do Navegador Tor que você instalou é anterior ao que deveria ser,
+"o que poderia ser um sinal de ataque!"
+
+#: launcher.py:446
+#, fuzzy
+msgid "Downloading Tor Browser over again."
+msgstr "Baixando o Navegador Tor novamente.."
+
+#: launcher.py:516 launcher.py:525 launcher.py:533
+msgid "Download Error:"
+msgstr "Erro de Download:"
+
+#: launcher.py:517
+msgid "You are currently using a non-default mirror"
+msgstr "No momento você está usando espelho não-padrão"
+
+#: launcher.py:518
+msgid "Would you like to switch back to the default?"
+msgstr "Você gostaria de retornar para o padrão?"
+
+#: launcher.py:527
+msgid "Would you like to try the English version of Tor Browser instead?"
+msgstr "Você gostaria de experimentar a versão em inglês do Navegador Tor?"
+
+#: launcher.py:548
+#, python-brace-format
+msgid ""
+"Invalid SSL certificate for:\n"
+"{0}\n"
+"\n"
+"You may be under attack."
+msgstr ""
+"Certificado SSL inválido para:\n"
+"{0}\n"
+"\n"
+"Você pode estar sob ataque."
+
+#: launcher.py:550
+msgid "Try the download again using Tor?"
+msgstr "Tentar o download novamente usando o Tor?"
+
+#: launcher.py:559
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Trying to download over Tor. Are you sure Tor is configured correctly and "
+"running?"
+msgstr ""
+"Erro ao iniciar o download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Tentando fazer o download pelo Tor. Você tem certeza de que o Tor está configurado corretamente e "
+"e funcionando?"
+
+#: launcher.py:563
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Are you connected to the internet?"
+msgstr ""
+"Erro ao iniciar o download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Você está conectado à internet?"
+
+#: settings.py:46
+msgid "Tor Browser Launcher Settings"
+msgstr "Configurações do Lançador do Navegador Tor"
+
+#: settings.py:50
+msgid "Download over system Tor"
+msgstr "Download pelo sistema Tor"
+
+#: settings.py:57
+msgid "Force downloading English version of Tor Browser"
+msgstr "Forçar o download da versão em Inglês do Navegador Tor"
+
+#: settings.py:66
+msgid "Tor server"
+msgstr "Servidor Tor"
+
+#: settings.py:82
+msgid "Status: Installed"
+msgstr "Estado: Instalado"
+
+#: settings.py:84
+msgid "Status: Not Installed"
+msgstr "Estado: Não Instalado"
+
+#: settings.py:87
+msgid "Install Tor Browser"
+msgstr "Instalar o Navegador Tor"
+
+#: settings.py:92
+msgid "Reinstall Tor Browser"
+msgstr "Reinstalar o Navegador Tor"
+
+#: settings.py:115
+msgid "Mirror"
+msgstr "Espelho"
+
+#: settings.py:131
+#, fuzzy
+msgid "Save && Exit"
+msgstr "Salvar && Sair"
+
+#~ msgid ""
+#~ "The python-txsocksx package is missing, downloads will not happen over tor"
+#~ msgstr
+#~ "O pacote python-txsocksx está faltando, os downloads não acontecerão pelo tor"
+
+#~ msgid "DNS Lookup Error"
+#~ msgstr "Erro de Pesquisa de DNS"
+
+#~ msgid ""
+#~ "The SSL certificate served by https://www.torproject.org is invalid! You "
+#~ "may be under attack."
+#~ msgstr ""
+#~ "O certificado SSL fornecido por https://www.torproject.org é inválido! Você "
+#~ "pode estar sob ataque."
+
+#~ msgid "Error connecting to Tor at {0}"
+#~ msgstr "Erro ao conectar ao Tor em {0}"
+
+#~ msgid ""
+#~ "SIGNATURE VERIFICATION FAILED!\n"
+#~ "\n"
+#~ "You might be under attack, or there might just be a networking problem. "
+#~ "Click Start try the download again."
+#~ msgstr ""
+#~ "FALHA NA VERIFICAÇÃO DA ASSINATURA!\n"
+#~ "\n"
+#~ "Você pode estar sob ataque, ou pode haver apenas um problema de rede. "
+#~ "Clique em Iniciar para tentar baixar novamente."
+
+#~ msgid ""
+#~ "The python-pygame package is missing, the modem sound is unavailable."
+#~ msgstr ""
+#~ "O pacote python-pygame está faltando, o som do modem não está disponível."
+
+#~ msgid ""
+#~ "This option is only available when using a system wide Tor installation."
+#~ msgstr ""
+#~ "Esta opção está apenas disponível ao usar uma instalação Tor do sistema."
+
+#~ msgid "This option requires the python-txsocksx package."
+#~ msgstr "Esta opção precisa do pacote python-txsocksx"
+
+#~ msgid "Play modem sound, because Tor is slow :]"
+#~ msgstr "Reproduzir som do modem, porque o Tor é lento :]"
+
+#~ msgid "This option requires python-pygame to be installed"
+#~ msgstr "Esta opção precisa do pacote python-pygame"
diff --git a/po/sv.po b/po/sv.po
new file mode 100644 (file)
index 0000000..385e2be
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,306 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-23 15:47-0700\n"
+"PO-Revision-Date: 2018-11-10 11:36+0100\n"
+"Last-Translator: Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>\n"
+"Language-Team: Swedish\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: __init__.py:65 launcher.py:470
+msgid "Tor Browser Launcher"
+msgstr "Tor Browser Launcher"
+
+#: __init__.py:66
+msgid "By Micah Lee, licensed under MIT"
+msgstr "Av Mika Lee, licensierad under MIT"
+
+#: __init__.py:67
+#, python-brace-format
+msgid "version {0}"
+msgstr "version {0}"
+
+#: common.py:100
+#, python-brace-format
+msgid "Error creating {0}"
+msgstr "Ett fel uppstod vid skapande {0}"
+
+#: common.py:102 common.py:180
+#, python-brace-format
+msgid "{0} is not writable"
+msgstr "{0} är inte skrivbar"
+
+#: common.py:177
+#, python-brace-format
+msgid "Cannot create directory {0}"
+msgstr "Kan inte att skapa katalog {0}"
+
+#: common.py:187
+msgid "Creating GnuPG homedir"
+msgstr "Skapa GnuPG-homedir"
+
+#: common.py:254
+#, python-format
+msgid "Could not import key with fingerprint: %s."
+msgstr "Kunde inte importera nyckel med fingeravtryck: %s."
+
+#: common.py:259
+msgid "Not all keys were imported successfully!"
+msgstr "Inte alla nycklar importerades!"
+
+#: launcher.py:83
+msgid "Downloading Tor Browser for the first time."
+msgstr "Hämtar Tor Browser för första gången."
+
+#: launcher.py:85
+msgid ""
+"Your version of Tor Browser is out-of-date. Downloading the newest version."
+msgstr "Din version av Tor Browser är föråldrad. Hämta den senaste versionen."
+
+#: launcher.py:100
+msgid "Downloading over Tor"
+msgstr "Hämtar över Tor"
+
+#: launcher.py:111
+msgid "Tor Browser"
+msgstr "Tor Browser"
+
+#: launcher.py:128
+msgid "Start"
+msgstr "Börja"
+
+#: launcher.py:174
+msgid "Yes"
+msgstr "Ja"
+
+#: launcher.py:178
+msgid "Exit"
+msgstr "Avsluta"
+
+#: launcher.py:192 settings.py:136
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: launcher.py:231 launcher.py:245 launcher.py:249 launcher.py:279
+#: launcher.py:281
+msgid "Downloading"
+msgstr "Hämtar"
+
+#: launcher.py:238
+msgid "Latest version: {}"
+msgstr "Senaste versionen: {}"
+
+#: launcher.py:241
+msgid "Error detecting Tor Browser version."
+msgstr "Fel vid detektering av Tor Browser-versionen."
+
+#: launcher.py:256 launcher.py:357
+msgid "Verifying Signature"
+msgstr "Verifierar signaturen"
+
+#: launcher.py:260
+msgid "Extracting"
+msgstr "Packar upp"
+
+#: launcher.py:264
+msgid "Running"
+msgstr "Körs"
+
+#: launcher.py:268
+msgid "Starting download over again"
+msgstr "Börja hämtningen igen"
+
+#: launcher.py:279 launcher.py:295
+msgid "(over Tor)"
+msgstr "(över Tor)"
+
+#: launcher.py:293
+msgid "Downloaded"
+msgstr "Hämtad"
+
+#: launcher.py:393
+msgid "Installing"
+msgstr "Installerar"
+
+#: launcher.py:401
+#, python-brace-format
+msgid "Tor Browser Launcher doesn't understand the file format of {0}"
+msgstr "Tor Browser Launcher förstår inte filformatet {0}"
+
+#: launcher.py:427
+msgid ""
+"The version of Tor Browser you have installed is earlier than it should be, "
+"which could be a sign of an attack!"
+msgstr ""
+"Den version av Tor Browser du har installerat är tidigare än den borde vara, "
+"vilket kan vara ett tecken på en attack!"
+
+#: launcher.py:446
+msgid "Downloading Tor Browser over again."
+msgstr "Hämtar Tor Browser igen."
+
+#: launcher.py:516 launcher.py:525 launcher.py:533
+msgid "Download Error:"
+msgstr "Hämtningsfel:"
+
+#: launcher.py:517
+msgid "You are currently using a non-default mirror"
+msgstr "Du använder för närvarande en icke-standardspegel"
+
+#: launcher.py:518
+msgid "Would you like to switch back to the default?"
+msgstr "Vill du byta tillbaka till standard?"
+
+#: launcher.py:527
+msgid "Would you like to try the English version of Tor Browser instead?"
+msgstr "Vill du prova den engelska versionen av Tor Browser istället?"
+
+#: launcher.py:548
+#, python-brace-format
+msgid ""
+"Invalid SSL certificate for:\n"
+"{0}\n"
+"\n"
+"You may be under attack."
+msgstr ""
+"Ogiltigt SSL-certifikat för:\n"
+"{0}\n"
+"\n"
+"Du kan vara under attack."
+
+#: launcher.py:550
+msgid "Try the download again using Tor?"
+msgstr "Försök att hämta igen med Tor?"
+
+#: launcher.py:559
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Trying to download over Tor. Are you sure Tor is configured correctly and "
+"running?"
+msgstr ""
+"Fel vid start av hämtning:\n"
+"\n"
+"{0}\n"
+"\n"
+"Försöker att hämta över Tor. Är du säker på att Tor är korrekt konfigurerad "
+"och körs?"
+
+#: launcher.py:563
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Are you connected to the internet?"
+msgstr ""
+"Fel vid start av hämtning:\n"
+"\n"
+"{0}\n"
+"\n"
+"Är du ansluten till internet?"
+
+#: settings.py:46
+msgid "Tor Browser Launcher Settings"
+msgstr "Inställningar för Tor Browser Launcher"
+
+#: settings.py:50
+msgid "Download over system Tor"
+msgstr "Hämta över Tor-system"
+
+#: settings.py:57
+msgid "Force downloading English version of Tor Browser"
+msgstr "Tvinga att hämta engelsk version av Tor Browser"
+
+#: settings.py:66
+msgid "Tor server"
+msgstr "Tor-server"
+
+#: settings.py:82
+msgid "Status: Installed"
+msgstr "Tillstånd: Installerad"
+
+#: settings.py:84
+msgid "Status: Not Installed"
+msgstr "Tillstånd: Inte installerad"
+
+#: settings.py:87
+msgid "Install Tor Browser"
+msgstr "Installera Tor Browser"
+
+#: settings.py:92
+msgid "Reinstall Tor Browser"
+msgstr "Installera om Tor Browser"
+
+#: settings.py:115
+msgid "Mirror"
+msgstr "Spegel"
+
+#: settings.py:131
+msgid "Save && Exit"
+msgstr "Spara && avsluta"
+
+#~ msgid ""
+#~ "The python-txsocksx package is missing, downloads will not happen over tor"
+#~ msgstr ""
+#~ ""
+#~ "Python-txsocksx-paketet saknas, hämtningar kommer inte att hända över Tor."
+
+#~ msgid "DNS Lookup Error"
+#~ msgstr "DNS-uppsökningsfel"
+
+#~ msgid ""
+#~ "The SSL certificate served by https://www.torproject.org is invalid! You "
+#~ "may be under attack."
+#~ msgstr ""
+#~ "SSL-certifikatet som betjänas av https://www.torproject.org är ogiltig!"
+#~ "Du kan vara under attack."
+
+#~ msgid "Error connecting to Tor at {0}"
+#~ msgstr "Fel vid anslutning till Tor på {0}"
+
+#~ msgid ""
+#~ "SIGNATURE VERIFICATION FAILED!\n"
+#~ "\n"
+#~ "You might be under attack, or there might just be a networking problem. "
+#~ "Click Start try the download again."
+#~ msgstr ""
+#~ "SIGNATUR MISSLYCKADES!\n"
+#~ "\n"
+#~ "Du kan vara under attack, eller det kan bara vara ett nätverksproblem. "
+#~ "Klicka på Start för försöka hämta igen."
+
+#~ msgid ""
+#~ "The python-pygame package is missing, the modem sound is unavailable."
+#~ msgstr ""
+#~ "Python-pygame-paketet saknas, modemljudet är otillgängligt."
+
+#~ msgid ""
+#~ "This option is only available when using a system wide Tor installation."
+#~ msgstr ""
+#~ "Det här alternativet är endast tillgängligt när du använder en "
+#~ "systembredd Tor-installation."
+
+#~ msgid "This option requires the python-txsocksx package."
+#~ msgstr "Det här alternativet kräver python-txsocksx-paketet."
+
+#~ msgid "Play modem sound, because Tor is slow :]"
+#~ msgstr "Spela modemljud, eftersom Tor är långsamt :]"
+
+#~ msgid "This option requires python-pygame to be installed"
+#~ msgstr "Det här alternativet kräver att python-pygame installeras"
diff --git a/po/zh_TW.po b/po/zh_TW.po
new file mode 100644 (file)
index 0000000..aae7f26
--- /dev/null
@@ -0,0 +1,253 @@
+# Chinese translations for PACKAGE package
+# PACKAGE 套件的正體中文翻譯.
+# Copyright (C) 2020 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Koala Yeung <koalay@gmail.com>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-23 15:47-0700\n"
+"PO-Revision-Date: 2020-04-22 23:31+0800\n"
+"Last-Translator: Koala Yeung <koalay@gmail.com>\n"
+"Language-Team: Chinese (traditional)\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.3\n"
+
+#: __init__.py:65 launcher.py:470
+msgid "Tor Browser Launcher"
+msgstr "Tor 瀏覽器啟動程式"
+
+#: __init__.py:66
+msgid "By Micah Lee, licensed under MIT"
+msgstr "由 Micah Lee 編寫,以 MIT 特許授權發佈"
+
+#: __init__.py:67
+#, python-brace-format
+msgid "version {0}"
+msgstr "{0} 版"
+
+#: common.py:100
+#, python-brace-format
+msgid "Error creating {0}"
+msgstr "生成 {0} 時發生問題"
+
+#: common.py:102 common.py:180
+#, python-brace-format
+msgid "{0} is not writable"
+msgstr "無法寫入 {0}"
+
+#: common.py:177
+#, python-brace-format
+msgid "Cannot create directory {0}"
+msgstr "無法生成 {0} 目錄"
+
+#: common.py:187
+msgid "Creating GnuPG homedir"
+msgstr "生成 GnuPG 目錄"
+
+#: common.py:254
+#, python-format
+msgid "Could not import key with fingerprint: %s."
+msgstr "無法以指紋匯入加密金鑰︰%s."
+
+#: common.py:259
+msgid "Not all keys were imported successfully!"
+msgstr "只有部份加密金鑰加入成功!"
+
+#: launcher.py:83
+msgid "Downloading Tor Browser for the first time."
+msgstr "第一次下載 Tor 瀏覽器。"
+
+#: launcher.py:85
+msgid ""
+"Your version of Tor Browser is out-of-date. Downloading the newest version."
+msgstr "你目前的 Tor 瀏覽器不是最新的,正下載最新版本。"
+
+#: launcher.py:100
+msgid "Downloading over Tor"
+msgstr "透過 Tor 網絡下載"
+
+#: launcher.py:111
+msgid "Tor Browser"
+msgstr "Tor 瀏覽器"
+
+#: launcher.py:128
+msgid "Start"
+msgstr "開始"
+
+#: launcher.py:174
+msgid "Yes"
+msgstr "是"
+
+#: launcher.py:178
+msgid "Exit"
+msgstr "離開"
+
+#: launcher.py:192 settings.py:136
+msgid "Cancel"
+msgstr "取消"
+
+#: launcher.py:231 launcher.py:245 launcher.py:249 launcher.py:279
+#: launcher.py:281
+msgid "Downloading"
+msgstr "下載中"
+
+#: launcher.py:238
+msgid "Latest version: {}"
+msgstr "最新版本︰{}"
+
+#: launcher.py:241
+msgid "Error detecting Tor Browser version."
+msgstr "偵測 Tor 瀏覽器版本時發生錯誤。"
+
+#: launcher.py:256 launcher.py:357
+msgid "Verifying Signature"
+msgstr "正在檢查數位簽章"
+
+#: launcher.py:260
+msgid "Extracting"
+msgstr "正在解開"
+
+#: launcher.py:264
+msgid "Running"
+msgstr "執行中"
+
+#: launcher.py:268
+msgid "Starting download over again"
+msgstr "從新開始下載"
+
+#: launcher.py:279 launcher.py:295
+msgid "(over Tor)"
+msgstr "(透過 Tor 網絡)"
+
+#: launcher.py:293
+msgid "Downloaded"
+msgstr "已下載"
+
+#: launcher.py:393
+msgid "Installing"
+msgstr "正在安裝"
+
+#: launcher.py:401
+#, python-brace-format
+msgid "Tor Browser Launcher doesn't understand the file format of {0}"
+msgstr "Tor 瀏覽器啟動程式不了解 {0} 的檔案格式"
+
+#: launcher.py:427
+msgid ""
+"The version of Tor Browser you have installed is earlier than it should be, "
+"which could be a sign of an attack!"
+msgstr "你已安裝的 Tor 瀏覽器版本比預期的版本早期,這表示你的電腦可能被入侵!"
+
+#: launcher.py:446
+msgid "Downloading Tor Browser over again."
+msgstr "重新下載 Tor 瀏覽器。"
+
+#: launcher.py:516 launcher.py:525 launcher.py:533
+msgid "Download Error:"
+msgstr "下載錯誤:"
+
+#: launcher.py:517
+msgid "You are currently using a non-default mirror"
+msgstr "你目前正在使用非預設的鏡像"
+
+#: launcher.py:518
+msgid "Would you like to switch back to the default?"
+msgstr "你想改為使用預設的嗎?"
+
+#: launcher.py:527
+msgid "Would you like to try the English version of Tor Browser instead?"
+msgstr "你要試試改為下載英文版 Tor 瀏覽器嗎?"
+
+#: launcher.py:548
+#, python-brace-format
+msgid ""
+"Invalid SSL certificate for:\n"
+"{0}\n"
+"\n"
+"You may be under attack."
+msgstr ""
+"以下網址的 SSL 證書不正確︰\n"
+"{0}\n"
+"\n"
+"你可能正被入侵。"
+
+#: launcher.py:550
+msgid "Try the download again using Tor?"
+msgstr "要試試改用 Tor 網絡重新下載嗎?"
+
+#: launcher.py:559
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Trying to download over Tor. Are you sure Tor is configured correctly and "
+"running?"
+msgstr ""
+"無法開始下載︰\n"
+"\n"
+"{0}\n"
+"\n"
+"請試試經由 Tor 網絡下載。你肯定你的 Tor 有正確被下載和執行嗎?"
+
+#: launcher.py:563
+#, python-brace-format
+msgid ""
+"Error starting download:\n"
+"\n"
+"{0}\n"
+"\n"
+"Are you connected to the internet?"
+msgstr ""
+"無法開始下載︰\n"
+"\n"
+"{0}\n"
+"\n"
+"請問你是否有連上互聯網?"
+
+#: settings.py:46
+msgid "Tor Browser Launcher Settings"
+msgstr "Tor 瀏覽器啟動程式設定"
+
+#: settings.py:50
+msgid "Download over system Tor"
+msgstr "透過系統的 Tor 網絡下載"
+
+#: settings.py:57
+msgid "Force downloading English version of Tor Browser"
+msgstr "強制下載英文版本的 Tor 瀏覽器"
+
+#: settings.py:66
+msgid "Tor server"
+msgstr "Tor 伺服器"
+
+#: settings.py:82
+msgid "Status: Installed"
+msgstr "狀態︰已安裝"
+
+#: settings.py:84
+msgid "Status: Not Installed"
+msgstr "狀態︰未安裝"
+
+#: settings.py:87
+msgid "Install Tor Browser"
+msgstr "安裝 Tor 瀏覽器"
+
+#: settings.py:92
+msgid "Reinstall Tor Browser"
+msgstr "重新安裝 Tor 瀏覽器"
+
+#: settings.py:115
+msgid "Mirror"
+msgstr "鏡像"
+
+#: settings.py:131
+msgid "Save && Exit"
+msgstr "儲存及離開"
index 5955d7ff2b2d68326799eba18d8809fd889ce300..56218d9585c6f5726f21451779acbba3c48773fd 100644 (file)
Binary files a/screenshot.png and b/screenshot.png differ
index 37452ba06fcef82a943a81bdd162a874cdacbcd8..67e126e20a8b3c9e66b700d02f66f396dfffaeec 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -28,79 +28,82 @@ OTHER DEALINGS IN THE SOFTWARE.
 
 import os
 import sys
-import platform
+import distro
 import subprocess
 from distutils.core import setup
 
-SHARE = 'share'
+SHARE = "share"
 
 # detect linux distribution
-distro = platform.dist()[0]
+distro = distro.linux_distribution()[0]
 
 
 def file_list(path):
     files = []
     for filename in os.listdir(path):
-        if os.path.isfile(path+'/'+filename):
-            files.append(path+'/'+filename)
+        if os.path.isfile(path + "/" + filename):
+            files.append(path + "/" + filename)
     return files
 
 
 def create_mo_files():
-    po_dir = 'po/'
+    po_dir = "po/"
     if not os.path.exists(po_dir):
         return []
-    domain = 'torbrowser-launcher'
+    domain = "torbrowser-launcher"
     mo_files = []
-    po_files = [f
-                for f in next(os.walk(po_dir))[2]
-                if os.path.splitext(f)[1] == '.po']
+    po_files = sorted(
+        [f for f in next(os.walk(po_dir))[2] if os.path.splitext(f)[1] == ".po"]
+    )
     for po_file in po_files:
         filename, extension = os.path.splitext(po_file)
-        mo_file = domain + '.mo'
-        mo_dir = 'share/locale/' + filename + '/LC_MESSAGES/'
-        subprocess.call('mkdir -p ' + mo_dir, shell=True)
-        msgfmt_cmd = 'msgfmt {} -o {}'.format(po_dir + po_file, mo_dir + mo_file)
+        mo_file = domain + ".mo"
+        mo_dir = "share/locale/" + filename + "/LC_MESSAGES/"
+        subprocess.call("mkdir -p " + mo_dir, shell=True)
+        msgfmt_cmd = "msgfmt {} -o {}".format(po_dir + po_file, mo_dir + mo_file)
         subprocess.call(msgfmt_cmd, shell=True)
         mo_files.append(mo_dir + mo_file)
     return mo_files
 
 
-with open(os.path.join(SHARE, 'torbrowser-launcher/version')) as buf:
+with open(os.path.join(SHARE, "torbrowser-launcher/version")) as buf:
     version = buf.read().strip()
 
 datafiles = []
 for root, dirs, files in os.walk(SHARE):
-    datafiles.append((os.path.join(sys.prefix, root),
-                      [os.path.join(root, f) for f in files]))
+    if files:
+        datafiles.append((root, [os.path.join(root, f) for f in files]))
 
 # disable shipping apparmor profiles until they work in ubuntu (#128)
-if distro != 'Ubuntu':
-    if not hasattr(sys, 'real_prefix'):
+if distro != "Ubuntu":
+    if not hasattr(sys, "real_prefix"):
         # we're not in a virtualenv, so we can probably write to /etc
         datafiles += [
-            ('/etc/apparmor.d/', [
-                'apparmor/torbrowser.Browser.firefox',
-                'apparmor/torbrowser.Browser.plugin-container',
-                'apparmor/torbrowser.Tor.tor']),
-            ('/etc/apparmor.d/local/', [
-                'apparmor/local/torbrowser.Browser.firefox',
-                'apparmor/local/torbrowser.Browser.plugin-container',
-                'apparmor/local/torbrowser.Tor.tor']),
-            ('/etc/apparmor.d/tunables/', ['apparmor/tunables/torbrowser'])
+            (
+                "/etc/apparmor.d/",
+                ["apparmor/torbrowser.Browser.firefox", "apparmor/torbrowser.Tor.tor"],
+            ),
+            (
+                "/etc/apparmor.d/local/",
+                [
+                    "apparmor/local/torbrowser.Browser.firefox",
+                    "apparmor/local/torbrowser.Tor.tor",
+                ],
+            ),
+            ("/etc/apparmor.d/tunables/", ["apparmor/tunables/torbrowser"]),
         ]
 
-datafiles += [('/usr/share/locale/', create_mo_files())]
+datafiles += [(os.path.dirname(f), [f]) for f in create_mo_files()]
 
 setup(
-    name='torbrowser-launcher',
+    name="torbrowser-launcher",
     version=version,
-    author='Micah Lee',
-    author_email='micah@micahflee.com',
-    url='https://www.github.com/micahflee/torbrowser-launcher',
-    platforms=['GNU/Linux'],
-    license='MIT',
-    description='A program to help you securely download and run Tor Browser',
+    author="Micah Lee",
+    author_email="micah@micahflee.com",
+    url="https://www.github.com/micahflee/torbrowser-launcher",
+    platforms=["GNU/Linux"],
+    license="MIT",
+    description="A program to help you securely download and run Tor Browser",
     long_description="""
 Tor Browser Launcher is intended to make Tor Browser easier to install and use
 for GNU/Linux users. You install torbrowser-launcher from your distribution's
@@ -112,7 +115,7 @@ https://www.torproject.org/, verify the PGP signature, extract it in your home
 directory, and launch it. When you run it after that it will just launch Tor
 Browser.
 """,
-    packages=['torbrowser_launcher'],
-    scripts=['torbrowser-launcher'],
-    data_files=datafiles
+    packages=["torbrowser_launcher"],
+    scripts=["torbrowser-launcher"],
+    data_files=datafiles,
 )
index c2c18d1b481c90f985c74ab5551fe804caecb658..4e649892ae414de65bf23881600fbe5c664a9898 100644 (file)
@@ -1,16 +1,25 @@
 [Desktop Entry]
 Name=Tor Browser Launcher Settings
+Name[cs]=Tor Browser Launcher nastavení
 Name[fr]=Tor Browser Launcher configurations
+Name[hr]=Tor preglednik – postavke pokretanja
 Name[hu]=Tor-böngésző indító beállításai
 Name[nl]=Tor Browser Launcher Instellingen
+Name[pt_BR]=Navegador Tor (Configurações do Lançador)
 Name[ru]=Tor Browser (настройки запуска)
+Name[sv]=Inställningar för Tor Browser Launcher
 GenericName=Tor Browser Launcher Settings
+GenericName[hr]=Tor preglednik – postavke pokretanja
 GenericName[hu]=Tor-böngésző indító beállításai
 Comment=Tor Browser Launcher Settings
+Comment[cs]=Tor Browser Launcher nastavení
 Comment[fr]=Tor Browser Launcher configurations
+Comment[hr]=Postavke za pokretanje Tor preglednika
 Comment[hu]=Tor-böngésző indító beállításai
 Comment[nl]=Tor Browser Launcher Instellingen
+Comment[pt_BR]=Navegador Tor (Configurações do Lançador)
 Comment[ru]=Tor Browser (настройки запуска)
+Comment[sv]=Inställningar för Tor Browser Launcher
 Exec=torbrowser-launcher --settings
 Terminal=false
 Type=Application
index ee0d9fc175053715c706285279801e276a92d2e6..d96f37d43080f8640e1ddb4e96b11a2c6994b289 100644 (file)
@@ -1,13 +1,22 @@
 [Desktop Entry]
 Name=Tor Browser
+Name[cs]=Tor Browser
 Name[da]=Tor Browser
+Name[hr]=Tor preglednik
 Name[hu]=Tor-böngésző
+Name[pt_BR]=Navegador Tor
+Name[sv]=Tor Browser
 GenericName=Tor browser
 GenericName[da]=Tor Browser
+GenericName[hr]=Tor preglednik
 GenericName[hu]=Tor böngésző indító
 Comment=Launch Tor Browser
+Comment[cs]=Spustit Tor Browser
+Comment[hr]=Pokreni Tor preglednika
 Comment[da]=Start Tor Browser
 Comment[hu]=Tor böngésző indító
+Comment[pt_BR]=Navegador Tor
+Comment[sv]=Starta Tor Browser
 Exec=torbrowser-launcher %u
 Terminal=false
 Type=Application
diff --git a/share/icons/hicolor/128x128/apps/torbrowser.png b/share/icons/hicolor/128x128/apps/torbrowser.png
new file mode 100644 (file)
index 0000000..18f3572
Binary files /dev/null and b/share/icons/hicolor/128x128/apps/torbrowser.png differ
index 5577868c61158c849a363c8ca1a37771efed2f8e..95c494300aea86e1b92054e66b31afefd35d0ef7 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Copyright 2014 Micah Lee <micah@micahflee.com> -->
-<component>
- <id>org.torproject.torbrowser.desktop</id>
+<component type="desktop-application">
+ <id>torbrowser.desktop</id>
  <metadata_license>CC0-1.0</metadata_license>
  <project_license>MIT</project_license>
  <name>Tor Browser Launcher</name>
@@ -27,4 +27,5 @@
  </screenshots>
  <url type="homepage">https://github.com/micahflee/torbrowser-launcher</url>
  <update_contact>micah@micahflee.com</update_contact>
+ <content_rating type="oars-1.1"/>
 </component>
diff --git a/share/pixmaps/torbrowser.png b/share/pixmaps/torbrowser.png
deleted file mode 100644 (file)
index 8fc3530..0000000
Binary files a/share/pixmaps/torbrowser.png and /dev/null differ
diff --git a/share/torbrowser-launcher/sks-keyservers.netCA.pem b/share/torbrowser-launcher/sks-keyservers.netCA.pem
deleted file mode 100644 (file)
index 24a2ad2..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFizCCA3OgAwIBAgIJAK9zyLTPn4CPMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV
-BAYTAk5PMQ0wCwYDVQQIDARPc2xvMR4wHAYDVQQKDBVza3Mta2V5c2VydmVycy5u
-ZXQgQ0ExHjAcBgNVBAMMFXNrcy1rZXlzZXJ2ZXJzLm5ldCBDQTAeFw0xMjEwMDkw
-MDMzMzdaFw0yMjEwMDcwMDMzMzdaMFwxCzAJBgNVBAYTAk5PMQ0wCwYDVQQIDARP
-c2xvMR4wHAYDVQQKDBVza3Mta2V5c2VydmVycy5uZXQgQ0ExHjAcBgNVBAMMFXNr
-cy1rZXlzZXJ2ZXJzLm5ldCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
-ggIBANdsWy4PXWNUCkS3L//nrd0GqN3dVwoBGZ6w94Tw2jPDPifegwxQozFXkG6I
-6A4TK1CJLXPvfz0UP0aBYyPmTNadDinaB9T4jIwd4rnxl+59GiEmqkN3IfPsv5Jj
-MkKUmJnvOT0DEVlEaO1UZIwx5WpfprB3mR81/qm4XkAgmYrmgnLXd/pJDAMk7y1F
-45b5zWofiD5l677lplcIPRbFhpJ6kDTODXh/XEdtF71EAeaOdEGOvyGDmCO0GWqS
-FDkMMPTlieLA/0rgFTcz4xwUYj/cD5e0ZBuSkYsYFAU3hd1cGfBue0cPZaQH2HYx
-Qk4zXD8S3F4690fRhr+tki5gyG6JDR67aKp3BIGLqm7f45WkX1hYp+YXywmEziM4
-aSbGYhx8hoFGfq9UcfPEvp2aoc8u5sdqjDslhyUzM1v3m3ZGbhwEOnVjljY6JJLx
-MxagxnZZSAY424ZZ3t71E/Mn27dm2w+xFRuoy8JEjv1d+BT3eChM5KaNwrj0IO/y
-u8kFIgWYA1vZ/15qMT+tyJTfyrNVV/7Df7TNeWyNqjJ5rBmt0M6NpHG7CrUSkBy9
-p8JhimgjP5r0FlEkgg+lyD+V79H98gQfVgP3pbJICz0SpBQf2F/2tyS4rLm+49rP
-fcOajiXEuyhpcmzgusAj/1FjrtlynH1r9mnNaX4e+rLWzvU5AgMBAAGjUDBOMB0G
-A1UdDgQWBBTkwyoJFGfYTVISTpM8E+igjdq28zAfBgNVHSMEGDAWgBTkwyoJFGfY
-TVISTpM8E+igjdq28zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQAR
-OXnYwu3g1ZjHyley3fZI5aLPsaE17cOImVTehC8DcIphm2HOMR/hYTTL+V0G4P+u
-gH+6xeRLKSHMHZTtSBIa6GDL03434y9CBuwGvAFCMU2GV8w92/Z7apkAhdLToZA/
-X/iWP2jeaVJhxgEcH8uPrnSlqoPBcKC9PrgUzQYfSZJkLmB+3jEa3HKruy1abJP5
-gAdQvwvcPpvYRnIzUc9fZODsVmlHVFBCl2dlu/iHh2h4GmL4Da2rRkUMlbVTdioB
-UYIvMycdOkpH5wJftzw7cpjsudGas0PARDXCFfGyKhwBRFY7Xp7lbjtU5Rz0Gc04
-lPrhDf0pFE98Aw4jJRpFeWMjpXUEaG1cq7D641RpgcMfPFvOHY47rvDTS7XJOaUT
-BwRjmDt896s6vMDcaG/uXJbQjuzmmx3W2Idyh3s5SI0GTHb0IwMKYb4eBUIpQOnB
-cE77VnCYqKvN1NVYAqhWjXbY7XasZvszCRcOG+W3FqNaHOK/n/0ueb0uijdLan+U
-f4p1bjbAox8eAOQS/8a3bzkJzdyBNUKGx1BIK2IBL9bn/HravSDOiNRSnZ/R3l9G
-ZauX0tu7IIDlRCILXSyeazu0aj/vdT3YFQXPcvt5Fkf5wiNTo53f72/jYEJd6qph
-WrpoKqrwGwTpRUCMhYIUt65hsTxCiJJ5nKe39h46sg==
------END CERTIFICATE-----
index d6987b5a06baa0b5107f9859d448268d186511e0..796eda23f16306f3c4aacc8e0f3c9a523876afd9 100644 (file)
@@ -12,199 +12,98 @@ G/k7yOQWWNY7RPU1cV+E9QKNwqS4Zj2VyU6s6ikaPuUnjW59iMkSGUuS+gJUR2hp
 jOKjNzu8vxbotBgZ01upDUdl69OnR1dv9X+bMzGWUyOjAjK6SP8rFtWFBjWgWcED
 OHu51YpicSdN3uf7lppEXGx91n45xVMhL9d2KNp3DhWkKDuWhdliWC/r1wARAQAB
 tEBUb3IgQnJvd3NlciBEZXZlbG9wZXJzIChzaWduaW5nIGtleSkgPHRvcmJyb3dz
-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
-=/faz
+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
+=ODNf
 -----END PGP PUBLIC KEY BLOCK-----
index 9e11b32fcaa96816319e5d0dcff9fb2873f04061..1c09c74e221cd58f30240fbcfd9545ed19df54d7 100644 (file)
@@ -1 +1 @@
-0.3.1
+0.3.3
index 4bf5e73046b845874a93c8b96db62bdd1e10dd6f..3653de23d35bc62651c5ab6a6fb73228a2c00f85 100644 (file)
--- a/stdeb.cfg
+++ b/stdeb.cfg
@@ -1,6 +1,6 @@
 [DEFAULT]
 Package3: torbrowser-launcher
-Depends3: python3-pyqt5, python3-gpg, python3-requests, python3-socks, gnupg2
-Build-Depends: dh-python, python3-pyqt5, python3-gpg, python3-requests, python3-socks, gnupg2
+Depends3: python3-pyqt5, python3-gpg, python3-requests, python3-socks, python3-packaging, gnupg2
+Build-Depends: dh-python, python3-pyqt5, python3-gpg, python3-requests, python3-socks, python3-packaging, gnupg2
 Recommends: tor
 Suite: bionic
index ce542a472181b49327839122218d7f4aaba566e9..bff3e8ea7f8974afc978cec50329743b2d6ee42f 100644 (file)
@@ -2,7 +2,7 @@
 Tor Browser Launcher
 https://github.com/micahflee/torbrowser-launcher/
 
-Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
+Copyright (c) 2013-2021 Micah Lee <micah@micahflee.com>
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
@@ -42,6 +42,7 @@ class Application(QtWidgets.QApplication):
     """
     Qt's QApplication class. It has been overridden to support threads.
     """
+
     def __init__(self):
         self.setAttribute(QtCore.Qt.AA_X11InitThreads, True)
         QtWidgets.QApplication.__init__(self, sys.argv)
@@ -51,21 +52,26 @@ class Application(QtWidgets.QApplication):
 def main():
     # Parse arguments
     parser = argparse.ArgumentParser()
-    parser.add_argument('--settings', action='store_true', dest='settings', help='Open Tor Browser Launcher settings')
-    parser.add_argument('url', nargs='*', help='URL to load')
+    parser.add_argument(
+        "--settings",
+        action="store_true",
+        dest="settings",
+        help="Open Tor Browser Launcher settings",
+    )
+    parser.add_argument("url", nargs="*", help="URL to load")
     args = parser.parse_args()
 
     settings = bool(args.settings)
     url_list = args.url
 
     # Load the version and print the banner
-    with open(os.path.join(SHARE, 'version')) as buf:
+    with open(os.path.join(SHARE, "version")) as buf:
         tor_browser_launcher_version = buf.read().strip()
 
-    print(_('Tor Browser Launcher'))
-    print(_('By Micah Lee, licensed under MIT'))
-    print(_('version {0}').format(tor_browser_launcher_version))
-    print('https://github.com/micahflee/torbrowser-launcher')
+    print(_("Tor Browser Launcher"))
+    print(_("By Micah Lee, licensed under MIT"))
+    print(_("version {0}").format(tor_browser_launcher_version))
+    print("https://github.com/micahflee/torbrowser-launcher")
 
     common = Common(tor_browser_launcher_version)
     app = Application()
@@ -85,7 +91,7 @@ def main():
     window_size = gui.size()
     gui.move(
         (desktop.width() - window_size.width()) / 2,
-        (desktop.height() - window_size.height()) / 2
+        (desktop.height() - window_size.height()) / 2,
     )
     gui.show()
 
index 4e3a30a6db3430bd96348d0358fa667227e347f3..be4f554c586c8ccf217fb80e5e9d1691eee84830 100644 (file)
@@ -2,7 +2,7 @@
 Tor Browser Launcher
 https://github.com/micahflee/torbrowser-launcher/
 
-Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
+Copyright (c) 2013-2021 Micah Lee <micah@micahflee.com>
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
@@ -37,9 +37,9 @@ 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"
 
-gettext.install('torbrowser-launcher')
+gettext.install("torbrowser-launcher")
 
 # We're looking for output which:
 #
@@ -47,7 +47,8 @@ gettext.install('torbrowser-launcher')
 #  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})")
+    b"(\[GNUPG\:\]) (IMPORT_OK) ([0-9]|[1]?[0-5]) ([A-F0-9]{40})"
+)
 
 
 class Common(object):
@@ -55,115 +56,199 @@ class Common(object):
         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()
 
     # 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 = ['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']
+        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",
+        ]
+
+        # a list of manually configured language fallback overriding
+        language_overrides = {
+            "zh-HK": "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 in language_overrides:
+                self.language = language_overrides[self.language]
             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)
-        if not os.access(homedir, os.W_OK):
-            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)
-        old_tbb_data = '{0}/.torbrowser'.format(homedir)
+                    self.set_gui(
+                        "error", _("Error creating {0}").format(homedir), [], False
+                    )
+
+        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,
+                "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')
+                "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'],
-                '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
@@ -183,37 +268,46 @@ class Common(object):
 
     # 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/gpg2', '--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(
+            [
+                "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.
@@ -224,9 +318,11 @@ class Common(object):
             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'])
+            c.set_engine_info(
+                gpg.constants.protocol.OpenPGP, home_dir=self.paths["gnupg_homedir"]
+            )
 
-            impkey = self.paths['signing_keys'][key]
+            impkey = self.paths["signing_keys"][key]
             try:
                 c.op_import(gpg.Data(file=impkey))
             except:
@@ -245,48 +341,54 @@ class Common(object):
         :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,
-            'tor_socks_address': '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:
@@ -295,13 +397,13 @@ class Common(object):
                     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:]
+            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
@@ -309,10 +411,10 @@ class Common(object):
                 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:
@@ -321,5 +423,5 @@ class Common(object):
 
     # 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
index efcd1c847861ef1eba89658114f2d85f0f6016b5..3d0898ee9c75aa69c1e7588c57d061461e622cac 100644 (file)
@@ -2,7 +2,7 @@
 Tor Browser Launcher
 https://github.com/micahflee/torbrowser-launcher/
 
-Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
+Copyright (c) 2013-2021 Micah Lee <micah@micahflee.com>
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
@@ -37,6 +37,7 @@ import requests
 import gpg
 import shutil
 import xml.etree.ElementTree as ET
+from packaging import version
 
 from PyQt5 import QtCore, QtWidgets, QtGui
 
@@ -61,6 +62,7 @@ class Launcher(QtWidgets.QMainWindow):
     """
     Launcher window.
     """
+
     def __init__(self, common, app, url_list):
         super(Launcher, self).__init__()
         self.common = common
@@ -70,47 +72,54 @@ class Launcher(QtWidgets.QMainWindow):
         self.force_redownload = False
 
         # This is the current version of Tor Browser, which should get updated with every release
-        self.min_version = '7.5.2'
+        self.min_version = "7.5.2"
 
         # Init launcher
-        self.set_state(None, '', [])
+        self.set_state(None, "", [])
         self.launch_gui = True
 
         # If Tor Browser is not installed, detect latest version, download, and install
-        if not self.common.settings['installed'] or not self.check_min_version():
+        if not self.common.settings["installed"] or not self.check_min_version():
             # Different message if downloading for the first time, or because your installed version is too low
             download_message = ""
-            if not self.common.settings['installed']:
+            if not self.common.settings["installed"]:
                 download_message = _("Downloading Tor Browser for the first time.")
             elif not self.check_min_version():
-                download_message = _("Your version of Tor Browser is out-of-date. "
-                                     "Downloading the newest version.")
+                download_message = _(
+                    "Your version of Tor Browser is out-of-date. "
+                    "Downloading the newest version."
+                )
 
             # Download and install
             print(download_message)
-            self.set_state('task', download_message,
-                           ['download_version_check',
-                            'set_version',
-                            'download_sig',
-                            'download_tarball',
-                            'verify',
-                            'extract',
-                            'run'])
-
-            if self.common.settings['download_over_tor']:
-                print(_('Downloading over Tor'))
+            self.set_state(
+                "task",
+                download_message,
+                [
+                    "download_version_check",
+                    "set_version",
+                    "download_sig",
+                    "download_tarball",
+                    "verify",
+                    "extract",
+                    "run",
+                ],
+            )
+
+            if self.common.settings["download_over_tor"]:
+                print(_("Downloading over Tor"))
 
         else:
             # Tor Browser is already installed, so run
             launch_message = "Launching Tor Browser."
             print(launch_message)
-            self.set_state('task', launch_message, ['run'])
+            self.set_state("task", launch_message, ["run"])
 
         # Build the rest of the UI
 
         # Set up the window
         self.setWindowTitle(_("Tor Browser"))
-        self.setWindowIcon(QtGui.QIcon(self.common.paths['icon_file']))
+        self.setWindowIcon(QtGui.QIcon(self.common.paths["icon_file"]))
 
         # Label
         self.label = QtWidgets.QLabel()
@@ -124,13 +133,19 @@ class Launcher(QtWidgets.QMainWindow):
 
         # Buttons
         self.yes_button = QtWidgets.QPushButton()
-        self.yes_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton))
+        self.yes_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton)
+        )
         self.yes_button.clicked.connect(self.yes_clicked)
-        self.start_button = QtWidgets.QPushButton(_('Start'))
-        self.start_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton))
+        self.start_button = QtWidgets.QPushButton(_("Start"))
+        self.start_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton)
+        )
         self.start_button.clicked.connect(self.start)
         self.cancel_button = QtWidgets.QPushButton()
-        self.cancel_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogCancelButton))
+        self.cancel_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogCancelButton)
+        )
         self.cancel_button.clicked.connect(self.close)
         buttons_layout = QtWidgets.QHBoxLayout()
         buttons_layout.addStretch()
@@ -166,19 +181,19 @@ class Launcher(QtWidgets.QMainWindow):
         self.yes_button.hide()
         self.start_button.hide()
 
-        if 'error' in self.gui:
+        if "error" in self.gui:
             # Label
             self.label.setText(self.gui_message)
 
             # Yes button
-            if self.gui != 'error':
-                self.yes_button.setText(_('Yes'))
+            if self.gui != "error":
+                self.yes_button.setText(_("Yes"))
                 self.yes_button.show()
 
             # Exit button
-            self.cancel_button.setText(_('Exit'))
+            self.cancel_button.setText(_("Exit"))
 
-        elif self.gui == 'task':
+        elif self.gui == "task":
             # Label
             self.label.setText(self.gui_message)
 
@@ -190,7 +205,7 @@ class Launcher(QtWidgets.QMainWindow):
                 self.start_button.show()
 
             # Cancel button
-            self.cancel_button.setText(_('Cancel'))
+            self.cancel_button.setText(_("Cancel"))
 
         # Resize the window
         self.adjustSize()
@@ -200,13 +215,13 @@ class Launcher(QtWidgets.QMainWindow):
 
     # Yes button clicked, based on the state decide what to do
     def yes_clicked(self):
-        if self.gui == 'error_try_stable':
+        if self.gui == "error_try_stable":
             self.try_stable()
-        elif self.gui == 'error_try_default_mirror':
+        elif self.gui == "error_try_default_mirror":
             self.try_default_mirror()
-        elif self.gui == 'error_try_forcing_english':
+        elif self.gui == "error_try_forcing_english":
             self.try_forcing_english()
-        elif self.gui == 'error_try_tor':
+        elif self.gui == "error_try_tor":
             self.try_tor()
 
     # Start button clicked, begin tasks
@@ -228,58 +243,80 @@ class Launcher(QtWidgets.QMainWindow):
         # Get ready for the next task
         self.gui_task_i += 1
 
-        if task == 'download_version_check':
-            print(_('Downloading'), self.common.paths['version_check_url'])
-            self.download('version check', self.common.paths['version_check_url'], self.common.paths['version_check_file'])
+        if task == "download_version_check":
+            print(_("Downloading"), self.common.paths["version_check_url"])
+            self.download(
+                "version check",
+                self.common.paths["version_check_url"],
+                self.common.paths["version_check_file"],
+            )
 
-        if task == 'set_version':
+        if task == "set_version":
             version = self.get_stable_version()
             if version:
                 self.common.build_paths(self.get_stable_version())
-                print(_('Latest version: {}').format(version))
+                print(_("Latest version: {}").format(version))
                 self.run_task()
             else:
-                self.set_state('error', _("Error detecting Tor Browser version."), [], False)
+                self.set_state(
+                    "error", _("Error detecting Tor Browser version."), [], False
+                )
                 self.update()
 
-        elif task == 'download_sig':
-            print(_('Downloading'), self.common.paths['sig_url'].format(self.common.settings['mirror']))
-            self.download('signature', self.common.paths['sig_url'], self.common.paths['sig_file'])
+        elif task == "download_sig":
+            print(
+                _("Downloading"),
+                self.common.paths["sig_url"].format(self.common.settings["mirror"]),
+            )
+            self.download(
+                "signature", self.common.paths["sig_url"], self.common.paths["sig_file"]
+            )
 
-        elif task == 'download_tarball':
-            print(_('Downloading'), self.common.paths['tarball_url'].format(self.common.settings['mirror']))
-            if not self.force_redownload and os.path.exists(self.common.paths['tarball_file']):
+        elif task == "download_tarball":
+            print(
+                _("Downloading"),
+                self.common.paths["tarball_url"].format(self.common.settings["mirror"]),
+            )
+            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'))
+                self.download(
+                    "tarball",
+                    self.common.paths["tarball_url"],
+                    self.common.paths["tarball_file"],
+                )
+
+        elif task == "verify":
+            print(_("Verifying Signature"))
             self.verify()
 
-        elif task == 'extract':
-            print(_('Extracting'), self.common.paths['tarball_filename'])
+        elif task == "extract":
+            print(_("Extracting"), self.common.paths["tarball_filename"])
             self.extract()
 
-        elif task == 'run':
-            print(_('Running'), self.common.paths['tbb']['start'])
+        elif task == "run":
+            print(_("Running"), self.common.paths["tbb"]["start"])
             self.run()
 
-        elif task == 'start_over':
-            print(_('Starting download over again'))
+        elif task == "start_over":
+            print(_("Starting download over again"))
             self.start_over()
 
     def download(self, name, url, path):
         # Download from the selected mirror
-        mirror_url = url.format(self.common.settings['mirror']).encode()
+        mirror_url = url.format(self.common.settings["mirror"]).encode()
 
         # Initialize the progress bar
         self.progress_bar.setValue(0)
         self.progress_bar.setMaximum(100)
-        if self.common.settings['download_over_tor']:
-            self.progress_bar.setFormat(_('Downloading') + ' {0} '.format(name) + _('(over Tor)') + ', %p%')
+        if self.common.settings["download_over_tor"]:
+            self.progress_bar.setFormat(
+                _("Downloading") + " {0} ".format(name) + _("(over Tor)") + ", %p%"
+            )
         else:
-            self.progress_bar.setFormat(_('Downloading') + ' {0}, %p%'.format(name))
+            self.progress_bar.setFormat(_("Downloading") + " {0}, %p%".format(name))
 
         def progress_update(total_bytes, bytes_so_far):
             percent = float(bytes_so_far) / float(total_bytes)
@@ -291,9 +328,11 @@ class Launcher(QtWidgets.QMainWindow):
                     amount /= float(size)
                     break
 
-            message = _('Downloaded') + (' %2.1f%% (%2.1f %s)' % ((percent * 100.0), amount, units))
-            if self.common.settings['download_over_tor']:
-                message += ' ' + _('(over Tor)')
+            message = _("Downloaded") + (
+                " %2.1f%% (%2.1f %s)" % ((percent * 100.0), amount, units)
+            )
+            if self.common.settings["download_over_tor"]:
+                message += " " + _("(over Tor)")
 
             self.progress_bar.setMaximum(total_bytes)
             self.progress_bar.setValue(bytes_so_far)
@@ -317,34 +356,34 @@ class Launcher(QtWidgets.QMainWindow):
 
     def try_default_mirror(self):
         # change mirror to default and relaunch TBL
-        self.common.settings['mirror'] = self.common.default_mirror
+        self.common.settings["mirror"] = self.common.default_mirror
         self.common.save_settings()
-        subprocess.Popen([self.common.paths['tbl_bin']])
+        subprocess.Popen([self.common.paths["tbl_bin"]])
         self.close()
 
     def try_forcing_english(self):
         # change force english to true and relaunch TBL
-        self.common.settings['force_en-US'] = True
+        self.common.settings["force_en-US"] = True
         self.common.save_settings()
-        subprocess.Popen([self.common.paths['tbl_bin']])
+        subprocess.Popen([self.common.paths["tbl_bin"]])
         self.close()
 
     def try_tor(self):
         # set download_over_tor to true and relaunch TBL
-        self.common.settings['download_over_tor'] = True
+        self.common.settings["download_over_tor"] = True
         self.common.save_settings()
-        subprocess.Popen([self.common.paths['tbl_bin']])
+        subprocess.Popen([self.common.paths["tbl_bin"]])
         self.close()
 
     def get_stable_version(self):
-        tree = ET.parse(self.common.paths['version_check_file'])
+        tree = ET.parse(self.common.paths["version_check_file"])
         for up in tree.getroot():
-            if up.tag == 'update' and up.attrib['appVersion']:
-                version = str(up.attrib['appVersion'])
+            if up.tag == "update" and 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-hardened" are valid but "../../../../.." is invalid
-                if not re.match(r'^[a-z0-9\.\-]+$', version):
+                if not re.match(r"^[a-z0-9\.\-]+$", version):
                     return None
 
                 return version
@@ -355,29 +394,35 @@ class Launcher(QtWidgets.QMainWindow):
         self.progress_bar.setMaximum(0)
         self.progress_bar.show()
 
-        self.label.setText(_('Verifying Signature'))
+        self.label.setText(_("Verifying Signature"))
 
         def success():
             self.run_task()
 
         def error(message):
             # Make backup of tarball and sig
-            backup_tarball_filename = self.common.paths['tarball_file'] + '.verification_failed'
-            backup_sig_filename = self.common.paths['sig_file'] + '.verification_failed'
-            shutil.copyfile(self.common.paths['tarball_file'], backup_tarball_filename)
-            shutil.copyfile(self.common.paths['sig_file'], backup_sig_filename)
-
-            sigerror = 'SIGNATURE VERIFICATION FAILED!\n\n' \
-                       'Error Code: {0}\n\n' \
-                       'You might be under attack, there might be a network problem, or you may be missing a ' \
-                       'recently added Tor Browser verification key.\n\n' \
-                       'A copy of the Tor Browser files you downloaded have been saved here:\n' \
-                       '{1}\n{2}\n\n' \
-                       'Click Start to refresh the keyring and try again. If the message persists report the above ' \
-                       'error code here:\nhttps://github.com/micahflee/torbrowser-launcher/issues'
-            sigerror = sigerror.format(message, backup_tarball_filename, backup_sig_filename)
-
-            self.set_state('task', sigerror, ['start_over'], False)
+            backup_tarball_filename = (
+                self.common.paths["tarball_file"] + ".verification_failed"
+            )
+            backup_sig_filename = self.common.paths["sig_file"] + ".verification_failed"
+            shutil.copyfile(self.common.paths["tarball_file"], backup_tarball_filename)
+            shutil.copyfile(self.common.paths["sig_file"], backup_sig_filename)
+
+            sigerror = (
+                "SIGNATURE VERIFICATION FAILED!\n\n"
+                "Error Code: {0}\n\n"
+                "You might be under attack, there might be a network problem, or you may be missing a "
+                "recently added Tor Browser verification key.\n\n"
+                "A copy of the Tor Browser files you downloaded have been saved here:\n"
+                "{1}\n{2}\n\n"
+                "Click Start to refresh the keyring and try again. If the message persists report the above "
+                "error code here:\nhttps://github.com/micahflee/torbrowser-launcher/issues"
+            )
+            sigerror = sigerror.format(
+                message, backup_tarball_filename, backup_sig_filename
+            )
+
+            self.set_state("task", sigerror, ["start_over"], False)
             self.update()
 
         t = VerifyThread(self.common)
@@ -391,16 +436,21 @@ class Launcher(QtWidgets.QMainWindow):
         self.progress_bar.setMaximum(0)
         self.progress_bar.show()
 
-        self.label.setText(_('Installing'))
+        self.label.setText(_("Installing"))
 
         def success():
             self.run_task()
 
         def error(message):
             self.set_state(
-                'task',
-                _("Tor Browser Launcher doesn't understand the file format of {0}".format(self.common.paths['tarball_file'])),
-                ['start_over'], False
+                "task",
+                _(
+                    "Tor Browser Launcher doesn't understand the file format of {0}".format(
+                        self.common.paths["tarball_file"]
+                    )
+                ),
+                ["start_over"],
+                False,
             )
             self.update()
 
@@ -412,12 +462,12 @@ class Launcher(QtWidgets.QMainWindow):
 
     def check_min_version(self):
         installed_version = None
-        for line in open(self.common.paths['tbb']['changelog'],'rb').readlines():
-            if line.startswith(b'Tor Browser '):
+        for line in open(self.common.paths["tbb"]["changelog"], "rb").readlines():
+            if line.startswith(b"Tor Browser "):
                 installed_version = line.split()[2].decode()
                 break
 
-        if self.min_version <= installed_version:
+        if version.parse(self.min_version) <= version.parse(installed_version):
             return True
 
         return False
@@ -425,31 +475,35 @@ class Launcher(QtWidgets.QMainWindow):
     def run(self):
         # Don't run if it isn't at least the minimum version
         if not self.check_min_version():
-            message = _("The version of Tor Browser you have installed is earlier than it should be, which could be a "
-                        "sign of an attack!")
+            message = _(
+                "The version of Tor Browser you have installed is earlier than it should be, which could be a "
+                "sign of an attack!"
+            )
             print(message)
 
             Alert(self.common, message)
             return
 
         # Run Tor Browser
-        subprocess.call([self.common.paths['tbb']['start']], cwd=self.common.paths['tbb']['dir_tbb'])
+        subprocess.call(
+            [self.common.paths["tbb"]["start"]], cwd=self.common.paths["tbb"]["dir_tbb"]
+        )
         sys.exit(0)
 
     # Start over and download TBB again
     def start_over(self):
         self.force_redownload = True  # Overwrite any existing file
         self.label.setText(_("Downloading Tor Browser over again."))
-        self.gui_tasks = ['download_tarball', 'verify', 'extract', 'run']
+        self.gui_tasks = ["download_tarball", "verify", "extract", "run"]
         self.gui_task_i = 0
         self.start(None)
 
     def closeEvent(self, event):
         # Clear the download cache
         try:
-            os.remove(self.common.paths['version_check_file'])
-            os.remove(self.common.paths['sig_file'])
-            os.remove(self.common.paths['tarball_file'])
+            os.remove(self.common.paths["version_check_file"])
+            os.remove(self.common.paths["sig_file"])
+            os.remove(self.common.paths["tarball_file"])
         except:
             pass
 
@@ -460,11 +514,19 @@ class Alert(QtWidgets.QMessageBox):
     """
     An alert box dialog.
     """
-    def __init__(self, common, message, icon=QtWidgets.QMessageBox.NoIcon, buttons=QtWidgets.QMessageBox.Ok, autostart=True):
+
+    def __init__(
+        self,
+        common,
+        message,
+        icon=QtWidgets.QMessageBox.NoIcon,
+        buttons=QtWidgets.QMessageBox.Ok,
+        autostart=True,
+    ):
         super(Alert, self).__init__(None)
 
         self.setWindowTitle(_("Tor Browser Launcher"))
-        self.setWindowIcon(QtGui.QIcon(common.paths['icon_file']))
+        self.setWindowIcon(QtGui.QIcon(common.paths["icon_file"]))
         self.setText(message)
         self.setIcon(icon)
         self.setStandardButtons(buttons)
@@ -477,6 +539,7 @@ class DownloadThread(QtCore.QThread):
     """
     Download a file in a separate thread.
     """
+
     progress_update = QtCore.pyqtSignal(int, int)
     download_complete = QtCore.pyqtSignal()
     download_error = QtCore.pyqtSignal(str, str)
@@ -489,7 +552,7 @@ class DownloadThread(QtCore.QThread):
 
         # Use tor socks5 proxy, if enabled
         if self.common.settings['download_over_tor']:
-            socks5_address = 'socks5://{}'.format(self.common.settings['tor_socks_address'])
+            socks5_address = 'socks5h://{}'.format(self.common.settings['tor_socks_address'])
             self.proxies = {
                 'https': socks5_address,
                 'http': socks5_address
@@ -501,39 +564,49 @@ class DownloadThread(QtCore.QThread):
         with open(self.path, "wb") as f:
             try:
                 # Start the request
-                r = requests.get(self.url,
-                                 headers={'User-Agent': 'torbrowser-launcher'},
-                                 stream=True, proxies=self.proxies)
+                r = requests.get(
+                    self.url,
+                    headers={"User-Agent": "torbrowser-launcher"},
+                    stream=True,
+                    proxies=self.proxies,
+                )
 
                 # If status code isn't 200, something went wrong
                 if r.status_code != 200:
                     # Should we use the default mirror?
-                    if self.common.settings['mirror'] != self.common.default_mirror:
-                        message = (_("Download Error:") +
-                                   " {0}\n\n" + _("You are currently using a non-default mirror") +
-                                   ":\n{1}\n\n" + _("Would you like to switch back to the default?")).format(
-                                       r.status_code, self.common.settings['mirror']
-                                   )
-                        self.download_error.emit('error_try_default_mirror', message)
+                    if self.common.settings["mirror"] != self.common.default_mirror:
+                        message = (
+                            _("Download Error:")
+                            + " {0}\n\n"
+                            + _("You are currently using a non-default mirror")
+                            + ":\n{1}\n\n"
+                            + _("Would you like to switch back to the default?")
+                        ).format(r.status_code, self.common.settings["mirror"])
+                        self.download_error.emit("error_try_default_mirror", message)
 
                     # Should we switch to English?
-                    elif self.common.language != 'en-US' and not self.common.settings['force_en-US']:
-                        message = (_("Download Error:") +
-                                   " {0}\n\n" +
-                                   _("Would you like to try the English version of Tor Browser instead?")).format(
-                                       r.status_code
-                                   )
-                        self.download_error.emit('error_try_forcing_english', message)
+                    elif (
+                        self.common.language != "en-US"
+                        and not self.common.settings["force_en-US"]
+                    ):
+                        message = (
+                            _("Download Error:")
+                            + " {0}\n\n"
+                            + _(
+                                "Would you like to try the English version of Tor Browser instead?"
+                            )
+                        ).format(r.status_code)
+                        self.download_error.emit("error_try_forcing_english", message)
 
                     else:
                         message = (_("Download Error:") + " {0}").format(r.status_code)
-                        self.download_error.emit('error', message)
+                        self.download_error.emit("error", message)
 
                     r.close()
                     return
 
                 # Start streaming the download
-                total_bytes = int(r.headers.get('content-length'))
+                total_bytes = int(r.headers.get("content-length"))
                 bytes_so_far = 0
                 for data in r.iter_content(chunk_size=4096):
                     bytes_so_far += len(data)
@@ -541,25 +614,29 @@ class DownloadThread(QtCore.QThread):
                     self.progress_update.emit(total_bytes, bytes_so_far)
 
             except requests.exceptions.SSLError:
-                message = _('Invalid SSL certificate for:\n{0}\n\nYou may be under attack.').format(self.url.decode())
-                if not self.common.settings['download_over_tor']:
-                    message += "\n\n" + _('Try the download again using Tor?')
-                    self.download_error.emit('error_try_tor', message)
+                message = _(
+                    "Invalid SSL certificate for:\n{0}\n\nYou may be under attack."
+                ).format(self.url.decode())
+                if not self.common.settings["download_over_tor"]:
+                    message += "\n\n" + _("Try the download again using Tor?")
+                    self.download_error.emit("error_try_tor", message)
                 else:
-                    self.download_error.emit('error', message)
+                    self.download_error.emit("error", message)
                 return
 
             except requests.exceptions.ConnectionError:
                 # Connection error
-                if self.common.settings['download_over_tor']:
-                    message = _("Error starting download:\n\n{0}\n\nTrying to download over Tor. "
-                                "Are you sure Tor is configured correctly and running?").format(self.url.decode())
-                    self.download_error.emit('error', message)
+                if self.common.settings["download_over_tor"]:
+                    message = _(
+                        "Error starting download:\n\n{0}\n\nTrying to download over Tor. "
+                        "Are you sure Tor is configured correctly and running?"
+                    ).format(self.url.decode())
+                    self.download_error.emit("error", message)
                 else:
-                    message = _("Error starting download:\n\n{0}\n\nAre you connected to the internet?").format(
-                        self.url.decode()
-                    )
-                    self.download_error.emit('error', message)
+                    message = _(
+                        "Error starting download:\n\n{0}\n\nAre you connected to the internet?"
+                    ).format(self.url.decode())
+                    self.download_error.emit("error", message)
 
                 return
 
@@ -570,6 +647,7 @@ class VerifyThread(QtCore.QThread):
     """
     Verify the signature in a separate thread
     """
+
     success = QtCore.pyqtSignal()
     error = QtCore.pyqtSignal(str)
 
@@ -580,10 +658,13 @@ class VerifyThread(QtCore.QThread):
     def run(self):
         def verify(second_try=False):
             with gpg.Context() as c:
-                c.set_engine_info(gpg.constants.protocol.OpenPGP, home_dir=self.common.paths['gnupg_homedir'])
+                c.set_engine_info(
+                    gpg.constants.protocol.OpenPGP,
+                    home_dir=self.common.paths["gnupg_homedir"],
+                )
 
-                sig = gpg.Data(file=self.common.paths['sig_file'])
-                signed = gpg.Data(file=self.common.paths['tarball_file'])
+                sig = gpg.Data(file=self.common.paths["sig_file"])
+                signed = gpg.Data(file=self.common.paths["tarball_file"])
 
                 try:
                     c.verify(signature=sig, signed_data=signed)
@@ -608,6 +689,7 @@ class ExtractThread(QtCore.QThread):
     """
     Extract the tarball in a separate thread
     """
+
     success = QtCore.pyqtSignal()
     error = QtCore.pyqtSignal()
 
@@ -618,17 +700,17 @@ class ExtractThread(QtCore.QThread):
     def run(self):
         extracted = False
         try:
-            if self.common.paths['tarball_file'][-2:] == 'xz':
+            if self.common.paths["tarball_file"][-2:] == "xz":
                 # if tarball is .tar.xz
-                xz = lzma.LZMAFile(self.common.paths['tarball_file'])
+                xz = lzma.LZMAFile(self.common.paths["tarball_file"])
                 tf = tarfile.open(fileobj=xz)
-                tf.extractall(self.common.paths['tbb']['dir'])
+                tf.extractall(self.common.paths["tbb"]["dir"])
                 extracted = True
             else:
                 # if tarball is .tar.gz
-                if tarfile.is_tarfile(self.common.paths['tarball_file']):
-                    tf = tarfile.open(self.common.paths['tarball_file'])
-                    tf.extractall(self.common.paths['tbb']['dir'])
+                if tarfile.is_tarfile(self.common.paths["tarball_file"]):
+                    tf = tarfile.open(self.common.paths["tarball_file"])
+                    tf.extractall(self.common.paths["tbb"]["dir"])
                     extracted = True
         except:
             pass
index 545fab505dda4c17a44d8589aa42f673b4b73953..11ab18a63f5a9ccada8253d8894d8675f6011473 100644 (file)
@@ -2,7 +2,7 @@
 Tor Browser Launcher
 https://github.com/micahflee/torbrowser-launcher/
 
-Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
+Copyright (c) 2013-2021 Micah Lee <micah@micahflee.com>
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
@@ -36,6 +36,7 @@ class Settings(QtWidgets.QMainWindow):
     """
     Settings window.
     """
+
     def __init__(self, common, app):
         super(Settings, self).__init__()
 
@@ -44,28 +45,30 @@ class Settings(QtWidgets.QMainWindow):
 
         # Set up the window
         self.setWindowTitle(_("Tor Browser Launcher Settings"))
-        self.setWindowIcon(QtGui.QIcon(self.common.paths['icon_file']))
+        self.setWindowIcon(QtGui.QIcon(self.common.paths["icon_file"]))
 
         # Download over system tor
         self.tor_download_checkbox = QtWidgets.QCheckBox(_("Download over system Tor"))
-        if self.common.settings['download_over_tor']:
+        if self.common.settings["download_over_tor"]:
             self.tor_download_checkbox.setCheckState(QtCore.Qt.Checked)
         else:
             self.tor_download_checkbox.setCheckState(QtCore.Qt.Unchecked)
 
         # Force en-US, only display if language isn't already en-US
-        self.force_en_checkbox = QtWidgets.QCheckBox(_("Force downloading English version of Tor Browser"))
-        if self.common.settings['force_en-US']:
+        self.force_en_checkbox = QtWidgets.QCheckBox(
+            _("Force downloading English version of Tor Browser")
+        )
+        if self.common.settings["force_en-US"]:
             self.force_en_checkbox.setCheckState(QtCore.Qt.Checked)
         else:
             self.force_en_checkbox.setCheckState(QtCore.Qt.Unchecked)
-        if self.common.language == 'en-US':
+        if self.common.language == "en-US":
             self.force_en_checkbox.hide()
 
         # Tor SOCKS address
-        tor_addr_label = QtWidgets.QLabel(_('Tor server'))
+        tor_addr_label = QtWidgets.QLabel(_("Tor server"))
         self.tor_addr = QtWidgets.QLineEdit()
-        self.tor_addr.setText(self.common.settings['tor_socks_address'])
+        self.tor_addr.setText(self.common.settings["tor_socks_address"])
         tor_addr_layout = QtWidgets.QHBoxLayout()
         tor_addr_layout.addWidget(tor_addr_label)
         tor_addr_layout.addWidget(self.tor_addr)
@@ -78,22 +81,26 @@ class Settings(QtWidgets.QMainWindow):
 
         # Status
         status_label = QtWidgets.QLabel()
-        if(self.common.settings['installed']):
-            status_label.setText(_('Status: Installed'))
+        if self.common.settings["installed"]:
+            status_label.setText(_("Status: Installed"))
         else:
-            status_label.setText(_('Status: Not Installed'))
+            status_label.setText(_("Status: Not Installed"))
 
         # Install button
         install_button = QtWidgets.QPushButton(_("Install Tor Browser"))
-        install_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton))
+        install_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton)
+        )
         install_button.clicked.connect(self.install)
 
         # Reinstall buttons
         reinstall_button = QtWidgets.QPushButton(_("Reinstall Tor Browser"))
-        reinstall_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton))
+        reinstall_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton)
+        )
         reinstall_button.clicked.connect(self.reinstall)
 
-        if(self.common.settings['installed']):
+        if self.common.settings["installed"]:
             install_button.hide()
             reinstall_button.show()
         else:
@@ -112,14 +119,16 @@ class Settings(QtWidgets.QMainWindow):
         top_layout.addLayout(status_layout)
 
         # Mirror
-        mirror_label = QtWidgets.QLabel(_('Mirror'))
+        mirror_label = QtWidgets.QLabel(_("Mirror"))
 
         self.mirror = QtWidgets.QComboBox()
         for mirror in self.common.mirrors:
             self.mirror.addItem(mirror)
 
-        if self.common.settings['mirror'] in self.common.mirrors:
-            self.mirror.setCurrentIndex(self.mirror.findText(self.common.settings['mirror']))
+        if self.common.settings["mirror"] in self.common.mirrors:
+            self.mirror.setCurrentIndex(
+                self.mirror.findText(self.common.settings["mirror"])
+            )
         else:
             self.mirror.setCurrentIndex(0)
 
@@ -129,12 +138,16 @@ class Settings(QtWidgets.QMainWindow):
 
         # Save & Exit button
         self.save_exit_button = QtWidgets.QPushButton(_("Save && Exit"))
-        self.save_exit_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton))
+        self.save_exit_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogApplyButton)
+        )
         self.save_exit_button.clicked.connect(self.save_exit)
 
         # Cancel button
         self.cancel_button = QtWidgets.QPushButton(_("Cancel"))
-        self.cancel_button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogCancelButton))
+        self.cancel_button.setIcon(
+            self.style().standardIcon(QtWidgets.QStyle.SP_DialogCancelButton)
+        )
         self.cancel_button.clicked.connect(self.close)
 
         # Buttons layout
@@ -155,14 +168,14 @@ class Settings(QtWidgets.QMainWindow):
     # Install
     def install(self):
         self.save()
-        subprocess.Popen([self.common.paths['tbl_bin']])
+        subprocess.Popen([self.common.paths["tbl_bin"]])
         self.close()
 
     # Reinstall
     def reinstall(self):
         self.save()
-        shutil.rmtree(self.common.paths['tbb']['dir'])
-        subprocess.Popen([self.common.paths['tbl_bin']])
+        shutil.rmtree(self.common.paths["tbb"]["dir"])
+        subprocess.Popen([self.common.paths["tbl_bin"]])
         self.close()
 
     # Save & Exit
@@ -173,12 +186,14 @@ class Settings(QtWidgets.QMainWindow):
     # Save settings
     def save(self):
         # Checkbox options
-        self.common.settings['download_over_tor'] = self.tor_download_checkbox.isChecked()
-        self.common.settings['force_en-US'] = self.force_en_checkbox.isChecked()
-        self.common.settings['tor_socks_address'] = self.tor_addr.text()
+        self.common.settings[
+            "download_over_tor"
+        ] = self.tor_download_checkbox.isChecked()
+        self.common.settings["force_en-US"] = self.force_en_checkbox.isChecked()
+        self.common.settings["tor_socks_address"] = self.tor_addr.text()
 
         # Figure out the selected mirror
-        self.common.settings['mirror'] = self.mirror.currentText()
+        self.common.settings["mirror"] = self.mirror.currentText()
 
         # Save them
         self.common.save_settings()