]> git.lizzy.rs Git - torbrowser-launcher.git/commitdiff
Merge pull request #364 from scootergrisen/patch-1
authorMicah Lee <micah@micahflee.com>
Fri, 18 Jun 2021 22:07:31 +0000 (18:07 -0400)
committerGitHub <noreply@github.com>
Fri, 18 Jun 2021 22:07:31 +0000 (18:07 -0400)
Create da.po

29 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/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/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 045f47922596bc0ee1bcec5dcafb30f8f8854dce..8e66bb14e6569b6d606eadd510583dc3e7f2a349 100644 (file)
@@ -1,5 +1,22 @@
 # 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
+* Fix bug where TBL window stays open after Tor Browser is launched
+
 ## 0.3.0
 
 * Switched from python2 to python3
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 9ca9f3aefd80e45fe368fdd0bfe61ccab54a5007..b293a04ad3ad4ea9191c219e35efa94069239920 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 # 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). 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:
 
 * Downloads and installs the most recent version of Tor Browser in your language and for your computer's architecture, or launches Tor Browser if it's already installed (Tor Browser will automatically update itself)
@@ -14,12 +16,22 @@ You might want to check out the [security design doc](/security_design.md).
 
 ![Tor Browser Launcher screenshot](/screenshot.png)
 
-# Installing in Ubuntu
+# Installing
+
+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:
+
+## Installing in any Linux distro using Flatpak
 
-If you want to always have the latest version of the `torbrowser-launcher` package before your distribution gets it, you can use my PPA:
+Install Flatpak using these [instructions](https://flatpak.org/setup/).
 
-```sh
-sudo add-apt-repository ppa:micahflee/ppa
-sudo apt-get update
-sudo apt-get install torbrowser-launcher
+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/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/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 caf1ac265dc6342b3aab96da7d575c685dc43b90..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..0871c291ad46640f78f767e5b229608eac3215fa 100644 (file)
@@ -1,15 +1,22 @@
 [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 (настройки запуска)
 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 (настройки запуска)
 Exec=torbrowser-launcher --settings
 Terminal=false
index 739be8d252d17580a3fd1fc91b8139faca04e32f..cd4e5aaab1983feb0e4b576822e665deea645ca8 100644 (file)
@@ -1,10 +1,22 @@
 [Desktop Entry]
 Name=Tor Browser
+Name[cs]=Tor Browser
+Name[hr]=Tor preglednik
 Name[hu]=Tor-böngésző
+Name[pt_BR]=Navegador Tor
+GenericName=Tor Browser
+GenericName[hu]=Tor böngésző indító
+Comment=Launch Tor Browser
+Comment[cs]=Spustit Tor Browser
+Name[hu]=Tor-böngésző
+Name[pt_BR]=Navegador Tor
 GenericName=Tor browser
+GenericName[hr]=Tor preglednik
 GenericName[hu]=Tor böngésző indító
 Comment=Launch Tor Browser
+Comment[hr]=Pokreni Tor preglednika
 Comment[hu]=Tor böngésző indító
+Comment[pt_BR]=Navegador Tor
 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 @@ 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
-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
-=/faz
+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
+=ODNf
 -----END PGP PUBLIC KEY BLOCK-----
index 0d91a54c7d439e84e3dd17d3594f1b2b6737f430..1c09c74e221cd58f30240fbcfd9545ed19df54d7 100644 (file)
@@ -1 +1 @@
-0.3.0
+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()