From 16bd3291e33edd0a9979c52c908cfe63bd1a2348 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 19 Jan 2018 15:31:11 +1100 Subject: [PATCH 001/123] Support for obfs4 in custom bridges --- onionshare/onion.py | 2 ++ onionshare_gui/settings_dialog.py | 2 +- share/locale/en.json | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index bae90f4c..28f03f2d 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -212,6 +212,8 @@ class Onion(object): for line in o: f.write(line) if self.settings.get('tor_bridges_use_custom_bridges'): + if 'obfs4' in self.settings.get('tor_bridges_use_custom_bridges'): + f.write('ClientTransportPlugin obfs4 exec {}\n'.format(self.obfs4proxy_file_path)) f.write(self.settings.get('tor_bridges_use_custom_bridges')) f.write('\nUseBridges 1') diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 18372a47..270fb0e3 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -734,7 +734,7 @@ class SettingsDialog(QtWidgets.QDialog): for bridge in bridges: if bridge != '': # Check the syntax of the custom bridge to make sure it looks legitimate - pattern = re.compile("[0-9.]+:[0-9]+\s[A-Z0-9]+$") + pattern = re.compile("(obfs4\s)?[0-9.]+:[0-9]+\s[A-Z0-9]+(.+)?$") if pattern.match(bridge): new_bridges.append(''.join(['Bridge ', bridge, '\n'])) bridges_valid = True diff --git a/share/locale/en.json b/share/locale/en.json index cdd89c1e..c189a336 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -93,9 +93,9 @@ "gui_settings_cookie_label": "Cookie path", "gui_settings_tor_bridges": "Tor Bridge support", "gui_settings_tor_bridges_no_bridges_radio_option": "Don't use bridges", - "gui_settings_tor_bridges_obfs4_radio_option": "Use obfs4 pluggable transports", - "gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Use obfs4 pluggable transports (requires obfs4proxy)", - "gui_settings_tor_bridges_custom_radio_option": "Use custom bridges (non-pluggable transports)", + "gui_settings_tor_bridges_obfs4_radio_option": "Use built-in obfs4 pluggable transports", + "gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Use built-in obfs4 pluggable transports (requires obfs4proxy)", + "gui_settings_tor_bridges_custom_radio_option": "Use custom bridges", "gui_settings_tor_bridges_custom_label": "You can get bridges from https://bridges.torproject.org", "gui_settings_tor_bridges_invalid": "None of the bridges you supplied seem to be valid, so they've been ignored.\nPlease try again with valid bridges.", "gui_settings_button_save": "Save", From 1fb2116efd0295b2efc756e86169533ec60e94db Mon Sep 17 00:00:00 2001 From: Baccount Date: Thu, 18 Jan 2018 22:36:34 -0800 Subject: [PATCH 002/123] Update en.json --- share/locale/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/locale/en.json b/share/locale/en.json index cdd89c1e..ad8cc1ae 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -96,7 +96,7 @@ "gui_settings_tor_bridges_obfs4_radio_option": "Use obfs4 pluggable transports", "gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Use obfs4 pluggable transports (requires obfs4proxy)", "gui_settings_tor_bridges_custom_radio_option": "Use custom bridges (non-pluggable transports)", - "gui_settings_tor_bridges_custom_label": "You can get bridges from https://bridges.torproject.org", + "gui_settings_tor_bridges_custom_label": "You can get bridges from https://bridges.torproject.org", "gui_settings_tor_bridges_invalid": "None of the bridges you supplied seem to be valid, so they've been ignored.\nPlease try again with valid bridges.", "gui_settings_button_save": "Save", "gui_settings_button_cancel": "Cancel", From 524f8839d269f06922437f736b25d4b099849070 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 21 Jan 2018 13:42:46 +1100 Subject: [PATCH 003/123] Add instruction to update Python 3.6's internal certificate store, to avoid validation error on archive.torproject.org --- BUILD.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BUILD.md b/BUILD.md index 55355aad..e6e54951 100644 --- a/BUILD.md +++ b/BUILD.md @@ -38,6 +38,8 @@ Install Xcode from the Mac App Store. Once it's installed, run it for the first Download and install Python 3.6.4 from https://www.python.org/downloads/release/python-364/. I downloaded `python-3.6.4-macosx10.6.pkg`. +You may also need to run the command `/Applications/Python\ 3.6/Install\ Certificates.command` to update Python 3.6's internal certificate store. Otherwise, you may find that fetching the Tor Browser .dmg file fails later due to a certificate validation error. + Download and install Qt5 from https://www.qt.io/download-open-source/. I downloaded `qt-unified-mac-x64-3.0.2-online.dmg`. There's no need to login to a Qt account during installation. Make sure you install the latest Qt 5.x. I installed Qt 5.10.0 -- all you need is to check `Qt > Qt 5.10.0 > macOS`. Now install some python dependencies with pip (note, there's issues building a .app if you install this in a virtualenv): From 9a3bcb98341d6536df8a6028295b95f81db4f5ae Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 22 Jan 2018 16:24:26 +1100 Subject: [PATCH 004/123] stricter regex of IPv4 as well as IPv6 IPs in custom bridges --- onionshare_gui/settings_dialog.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 270fb0e3..d53ac303 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -734,8 +734,9 @@ class SettingsDialog(QtWidgets.QDialog): for bridge in bridges: if bridge != '': # Check the syntax of the custom bridge to make sure it looks legitimate - pattern = re.compile("(obfs4\s)?[0-9.]+:[0-9]+\s[A-Z0-9]+(.+)?$") - if pattern.match(bridge): + ipv4_pattern = re.compile("(obfs4\s)?(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):[0-9]+\s[A-Z0-9]+(.+)?$") + ipv6_pattern = re.compile("(obfs4\s)?\[(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]:[0-9]+\s[A-Z0-9]+(.+)?$") + if ipv4_pattern.match(bridge) or ipv6_pattern.match(bridge): new_bridges.append(''.join(['Bridge ', bridge, '\n'])) bridges_valid = True if bridges_valid: From 70a624c33188b70e5e861c156a62070214367ad7 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Tue, 23 Jan 2018 16:32:14 +1100 Subject: [PATCH 005/123] #535 do the forced update check via a (non-blocking) QThread --- onionshare_gui/settings_dialog.py | 41 +++++++++++++++++-------------- onionshare_gui/update_checker.py | 5 ++-- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 18372a47..eb109e76 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -557,31 +557,34 @@ class SettingsDialog(QtWidgets.QDialog): self._disable_buttons() self.qtapp.processEvents() + def update_timestamp(): + # Update the last checked label + settings = Settings(self.config) + settings.load() + autoupdate_timestamp = settings.get('autoupdate_timestamp') + self._update_autoupdate_timestamp(autoupdate_timestamp) + # Check for updates def update_available(update_url, installed_version, latest_version): Alert(strings._("update_available", True).format(update_url, installed_version, latest_version)) + forced_update_thread.quit() + # Enable buttons + self._enable_buttons() + # Update timestamp + update_timestamp() + def update_not_available(): Alert(strings._('update_not_available', True)) + forced_update_thread.quit() + # Enable buttons + self._enable_buttons() + # Update timestamp + update_timestamp() - u = UpdateChecker(self.onion) - u.update_available.connect(update_available) - u.update_not_available.connect(update_not_available) - - try: - u.check(force=True) - except UpdateCheckerCheckError: - Alert(strings._('update_error_check_error', True), QtWidgets.QMessageBox.Warning) - except UpdateCheckerInvalidLatestVersion as e: - Alert(strings._('update_error_invalid_latest_version', True).format(e.latest_version), QtWidgets.QMessageBox.Warning) - - # Enable buttons - self._enable_buttons() - - # Update the last checked label - settings = Settings(self.config) - settings.load() - autoupdate_timestamp = settings.get('autoupdate_timestamp') - self._update_autoupdate_timestamp(autoupdate_timestamp) + forced_update_thread = UpdateThread(self.onion, self.config, force=True) + forced_update_thread.update_available.connect(update_available) + forced_update_thread.update_not_available.connect(update_not_available) + forced_update_thread.start() def save_clicked(self): """ diff --git a/onionshare_gui/update_checker.py b/onionshare_gui/update_checker.py index ca2eb48a..c00b36b9 100644 --- a/onionshare_gui/update_checker.py +++ b/onionshare_gui/update_checker.py @@ -149,11 +149,12 @@ class UpdateThread(QtCore.QThread): update_available = QtCore.pyqtSignal(str, str, str) update_not_available = QtCore.pyqtSignal() - def __init__(self, onion, config=False): + def __init__(self, onion, config=False, force=False): super(UpdateThread, self).__init__() common.log('UpdateThread', '__init__') self.onion = onion self.config = config + self.force = force def run(self): common.log('UpdateThread', 'run') @@ -163,7 +164,7 @@ class UpdateThread(QtCore.QThread): u.update_not_available.connect(self._update_not_available) try: - u.check(config=self.config) + u.check(config=self.config,force=self.force) except Exception as e: # If update check fails, silently ignore common.log('UpdateThread', 'run', '{}'.format(e)) From a28f427c0c9f90541db5de49e5a87f09260cd0c8 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Tue, 23 Jan 2018 16:51:13 +1100 Subject: [PATCH 006/123] reinstate the emitted signals for invalid version/error checking for updates --- onionshare_gui/settings_dialog.py | 25 +++++++++++++++++-------- onionshare_gui/update_checker.py | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index eb109e76..2b68b19f 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -564,26 +564,35 @@ class SettingsDialog(QtWidgets.QDialog): autoupdate_timestamp = settings.get('autoupdate_timestamp') self._update_autoupdate_timestamp(autoupdate_timestamp) - # Check for updates - def update_available(update_url, installed_version, latest_version): - Alert(strings._("update_available", True).format(update_url, installed_version, latest_version)) + def close_forced_update_thread(): forced_update_thread.quit() # Enable buttons self._enable_buttons() # Update timestamp update_timestamp() + # Check for updates + def update_available(update_url, installed_version, latest_version): + Alert(strings._("update_available", True).format(update_url, installed_version, latest_version)) + close_forced_update_thread() + def update_not_available(): Alert(strings._('update_not_available', True)) - forced_update_thread.quit() - # Enable buttons - self._enable_buttons() - # Update timestamp - update_timestamp() + close_forced_update_thread() + + def update_error(): + Alert(strings._('update_error_check_error', True), QtWidgets.QMessageBox.Warning) + close_forced_update_thread() + + def update_invalid_version(): + Alert(strings._('update_error_invalid_latest_version', True).format(e.latest_version), QtWidgets.QMessageBox.Warning) + close_forced_update_thread() forced_update_thread = UpdateThread(self.onion, self.config, force=True) forced_update_thread.update_available.connect(update_available) forced_update_thread.update_not_available.connect(update_not_available) + forced_update_thread.update_error.connect(update_error) + forced_update_thread.update_invalid_version.connect(update_invalid_version) forced_update_thread.start() def save_clicked(self): diff --git a/onionshare_gui/update_checker.py b/onionshare_gui/update_checker.py index c00b36b9..16d8b8f5 100644 --- a/onionshare_gui/update_checker.py +++ b/onionshare_gui/update_checker.py @@ -51,6 +51,8 @@ class UpdateChecker(QtCore.QObject): """ update_available = QtCore.pyqtSignal(str, str, str) update_not_available = QtCore.pyqtSignal() + update_error = QtCore.pyqtSignal() + update_invalid_version = QtCore.pyqtSignal() def __init__(self, onion, config=False): super(UpdateChecker, self).__init__() @@ -120,12 +122,14 @@ class UpdateChecker(QtCore.QObject): except Exception as e: common.log('UpdateChecker', 'check', '{}'.format(e)) + self.update_error.emit() raise UpdateCheckerCheckError # Validate that latest_version looks like a version string # This regex is: 1-3 dot-separated numeric components version_re = r"^(\d+\.)?(\d+\.)?(\d+)$" if not re.match(version_re, latest_version): + self.update_invalid_version.emit() raise UpdateCheckerInvalidLatestVersion(latest_version) # Update the last checked timestamp (dropping the seconds and milliseconds) @@ -148,6 +152,8 @@ class UpdateChecker(QtCore.QObject): class UpdateThread(QtCore.QThread): update_available = QtCore.pyqtSignal(str, str, str) update_not_available = QtCore.pyqtSignal() + update_error = QtCore.pyqtSignal() + update_invalid_version = QtCore.pyqtSignal() def __init__(self, onion, config=False, force=False): super(UpdateThread, self).__init__() @@ -162,6 +168,8 @@ class UpdateThread(QtCore.QThread): u = UpdateChecker(self.onion, self.config) u.update_available.connect(self._update_available) u.update_not_available.connect(self._update_not_available) + u.update_error.connect(self._update_error) + u.update_invalid_version.connect(self._update_invalid_version) try: u.check(config=self.config,force=self.force) @@ -179,3 +187,13 @@ class UpdateThread(QtCore.QThread): common.log('UpdateThread', '_update_not_available') self.active = False self.update_not_available.emit() + + def _update_error(self): + common.log('UpdateThread', '_update_error') + self.active = False + self.update_error.emit() + + def _update_invalid_version(self): + common.log('UpdateThread', '_update_invalid_version') + self.active = False + self.update_invalid_version.emit() From 0a66951508954a8d61fcc9247a98b9501c3f56bf Mon Sep 17 00:00:00 2001 From: Baccount Date: Tue, 23 Jan 2018 09:19:53 -0800 Subject: [PATCH 007/123] Update macOS version tor to 0.2.3.9 --- install/get-tor-osx.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/get-tor-osx.py b/install/get-tor-osx.py index f6b04aa4..f6cac62f 100644 --- a/install/get-tor-osx.py +++ b/install/get-tor-osx.py @@ -28,9 +28,9 @@ import inspect, os, sys, hashlib, zipfile, io, shutil, subprocess import urllib.request def main(): - dmg_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.0.11/TorBrowser-7.0.11-osx64_en-US.dmg' - dmg_filename = 'TorBrowser-7.0.11-osx64_en-US.dmg' - expected_dmg_sha256 = '5143e4a2141a69f66869be13eef4bcaac4e6c27c78383fc8a4c38b334759f3a2' + dmg_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.5/TorBrowser-7.5-osx64_en-US.dmg' + dmg_filename = 'TorBrowser-7.5-osx64_en-US.dmg' + expected_dmg_sha256 = '43a8dc0afd0a77e42766311eb54ad9fc8714f67fcd2d3582a3bcb98b22c2e629' # Build paths root_path = os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) From 6fda59551f028dce7978b373063f542ac70f1ab9 Mon Sep 17 00:00:00 2001 From: Baccount Date: Tue, 23 Jan 2018 09:24:27 -0800 Subject: [PATCH 008/123] Update get-tor-windows.py --- install/get-tor-windows.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/get-tor-windows.py b/install/get-tor-windows.py index b1a6665d..f5aeb3f7 100644 --- a/install/get-tor-windows.py +++ b/install/get-tor-windows.py @@ -28,9 +28,9 @@ import inspect, os, sys, hashlib, shutil, subprocess import urllib.request def main(): - exe_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.0.11/torbrowser-install-7.0.11_en-US.exe' - exe_filename = 'torbrowser-install-7.0.11_en-US.exe' - expected_exe_sha256 = 'a033eb9b9ed2ad389169b36a90946a8af8f05bd0c7bbd3e37678041331096624' + exe_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.5/torbrowser-install-7.5_en-US.exe' + exe_filename = 'torbrowser-install-7.5_en-US.exe' + expected_exe_sha256 = '81ccb9456118cf8fa755a3eafb5c514665fc69599cdd41e9eb36baa335ebe233' # Build paths root_path = os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) working_path = os.path.join(os.path.join(root_path, 'build'), 'tor') From 8cc651a370f4925bf94001e89028968d7c8c790a Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 24 Jan 2018 11:37:48 +1100 Subject: [PATCH 009/123] #523 prioritise connecting to the v3 onionshare site when checking for updates, if Tor version is >= 0.3.2.9 --- onionshare_gui/update_checker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/update_checker.py b/onionshare_gui/update_checker.py index ca2eb48a..b9d9d515 100644 --- a/onionshare_gui/update_checker.py +++ b/onionshare_gui/update_checker.py @@ -19,6 +19,7 @@ along with this program. If not, see . """ from PyQt5 import QtCore import datetime, time, socket, re, platform +from distutils.version import LooseVersion as Version from onionshare import socks from onionshare.settings import Settings @@ -96,7 +97,10 @@ class UpdateChecker(QtCore.QObject): if force: path += '?force=1' - onion_domain = 'elx57ue5uyfplgva.onion' + if Version(self.onion.tor_version) >= Version('0.3.2.9'): + onion_domain = 'lldan5gahapx5k7iafb3s4ikijc4ni7gx5iywdflkba5y2ezyg6sjgyd.onion' + else: + onion_domain = 'elx57ue5uyfplgva.onion' common.log('UpdateChecker', 'check', 'loading http://{}{}'.format(onion_domain, path)) From e207db2a6ceaefaa1c2561eaba67da7eb8a1224b Mon Sep 17 00:00:00 2001 From: Freddy Martinez Date: Wed, 31 Jan 2018 10:59:46 -0800 Subject: [PATCH 010/123] link to our mailing list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c83636b0..55487045 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ You can download OnionShare for Windows and macOS from the [OnionShare website]( ## Developing OnionShare -You can set up your development environment to build OnionShare yourself by following [these instructions](/BUILD.md). +You can set up your development environment to build OnionShare yourself by following [these instructions](/BUILD.md). You may also subscribe to our developers mailing list [here](https://lists.riseup.net/www/info/onionshare-dev). # Screenshots From ae2140ceb897aa571096a05b90608e76cf23ddc2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Feb 2018 13:13:38 -0800 Subject: [PATCH 011/123] Update drag and drop look and instructions --- install/onionshare.nsi | 4 ++-- onionshare_gui/file_selection.py | 8 +++++--- share/images/drop_files.png | Bin 2035 -> 0 bytes share/images/logo_transparent.png | Bin 0 -> 3740 bytes share/locale/en.json | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) delete mode 100644 share/images/drop_files.png create mode 100644 share/images/logo_transparent.png diff --git a/install/onionshare.nsi b/install/onionshare.nsi index 9c15de47..d83da6ad 100644 --- a/install/onionshare.nsi +++ b/install/onionshare.nsi @@ -201,8 +201,8 @@ Section "install" File "${BINPATH}\share\html\index.html" SetOutPath "$INSTDIR\share\images" - File "${BINPATH}\share\images\drop_files.png" File "${BINPATH}\share\images\logo.png" + File "${BINPATH}\share\images\logo_transparent.png" File "${BINPATH}\share\images\logo_grayscale.png" File "${BINPATH}\share\images\server_started.png" File "${BINPATH}\share\images\server_stopped.png" @@ -379,8 +379,8 @@ FunctionEnd Delete "$INSTDIR\share\html\404.html" Delete "$INSTDIR\share\html\denied.html" Delete "$INSTDIR\share\html\index.html" - Delete "$INSTDIR\share\images\drop_files.png" Delete "$INSTDIR\share\images\logo.png" + Delete "$INSTDIR\share\images\logo_transparent.png" Delete "$INSTDIR\share\images\logo_grayscale.png" Delete "$INSTDIR\share\images\server_started.png" Delete "$INSTDIR\share\images\server_stopped.png" diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index da03d24d..a8ba92b7 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -50,7 +50,7 @@ class FileList(QtWidgets.QListWidget): self.setAlignment(QtCore.Qt.AlignCenter) if image: - self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(common.get_resource_path('images/drop_files.png')))) + self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(common.get_resource_path('images/logo_transparent.png')))) else: self.setText(strings._('gui_drag_and_drop', True)) self.setStyleSheet('color: #999999;') @@ -64,6 +64,7 @@ class FileList(QtWidgets.QListWidget): self.drop_here_image = DropHereLabel(self, True) self.drop_here_text = DropHereLabel(self, False) + self.resizeEvent(None) self.filenames = [] self.update() @@ -84,8 +85,9 @@ class FileList(QtWidgets.QListWidget): """ When the widget is resized, resize the drop files image and text. """ - self.drop_here_image.setGeometry(0, 0, self.width(), self.height()) - self.drop_here_text.setGeometry(0, 0, self.width(), self.height()) + offset = 70 + self.drop_here_image.setGeometry(0, 0, self.width(), self.height() - offset) + self.drop_here_text.setGeometry(0, offset, self.width(), self.height() - offset) def dragEnterEvent(self, event): """ diff --git a/share/images/drop_files.png b/share/images/drop_files.png deleted file mode 100644 index 1e2ea7d6f1749949ab5787dc830c745ae2902f27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2035 zcmZ`)dpHwn7{_uacS0_?-%2i_j0%f26SZYHZev(1#!93^Va!moOC%<_)uc^wM8si7 z5h`j(8gg=Ii^F8paaQO2asD~q^Stlxd4J#c{+{oT_d@XZ^H!A8krNXWQv~~XofIMM zyU9q2JWw1vD*`Db#M?`Rq9gR|$P|HWl21^In3%lUcM})8adVf5l*WOfAn9ot2}uJ- z%HxD{B1#VpL*P6UF&NZYoG26%^GrrX;!x3ssc2lBp*I-nAH5GHElLdpdmV*cnB-N2 zCaQU>#@dN+pD@$zf5_^5KK*I*AN~#b*d!psUw}A?~22@ zpsZ*h&1(|X!<~Fne?%(|aIcF2_-P8pGV`sJjIcE7Av#&WHaA&xhX~!c8wuA@T8tQ@ zy@Nzjzo0TyEW|9hBLO)iVZHu`>RjwZlO|Jqw?=5A6f;Iheo8;I{9rzEsT=#U@9w0` z!z#qFXX?wu`3T)snf$OuSEu8IjUZ;c-ne|{bEUNVa4_8Gjm`O<44DH+_~mf8lZ7L( z8@O!NH@5577icb_#tK?COJF;yscDBF1_0&0{$6}pjZ3NFHy^I{v)N>orIoaETiCZ$ zHvUT7yEBR|R}TQcCVv!jF}cFazrxI9e{ztX;*lIEI3NBH`I6vH-zwWZDjjF56V9cwCF(kypG`}?zWHWDoo z$q7jXE)za`I1c7|2_{;fiz5IjdZ|X88=MVdJ>LS^=k|KVF9ez0D!O z0sM==Byx@6b?1*x$-h}}_AQ%70W_AypR%zPh%X7_DYt&1D$CmqXxxT< zyPW~}BKNHp`f>W%){dXw*9lc4V;^3=N-C^|#zP8E))HpyJ^lI>)CNDE>hhd=>u8Wn zW1unl6*TrMYPt?f6UZ*8c>KYt0ER|Bx^~Ql1NSJO-F-=zkT%>Fmd zUw3RvaYivCxp* zR6P$1iV8N!Mf9t0eej5TG+?NqIXVYqTlH!){ADkxs0ox=0@Xo*u717gLbtN)C6bz; zk#$dTVDWzSqKf))BaZi0m9Wxv$#fCbd!RM2-Tv|hOq~HRGd5*?-&A$7T;Jr%%=6A3|9b@`Did?{`9Up$hpS z@*k(7y{T<%r|_+ilrR6U2vYWki0JMAss1Nl^ru<;{sr%&l@c0xoF~%juZwD<^)D@6SuK1!Xh~fHScKo{$#qC!2ias{uA=VL z^(I5Jn`-gANbscL4gI1Z81Q)Z37c|s-NiO^5by9VW-b}jD9=@Io0}*- z#Eck42V#dV3BFvzg(NL$dU_CZ32;cj$%aYhMgFLZ3Z z4sG;+)cJet%fLRlgUO1skLKRHjizLi(rXqC+#e*LS!I^2%%;S!fJ5oG{ma>*T&n3@ zb?GOBLiU)BwYcti@r>r1aZs$4`cik;0@W*SJ;4QI74~wp1;NYh!lJws`4R>Rwarq# zmiT~|Jt;&pP1Ytv`hHeW$vfPo8KdxElf1R!W8bOn3j4c6%VvA|QBhFL4TT=J@=gd-Fu(C|7V{gfk!A7qh$aP2gCvfV6g9N0E|Gi@6r~)4m1EY zzHbB22-He;sqRsYL3jk9eGeE5{3Rd)nlPL60AAp0pj@&`xBlM&Xfaw6f#JXeA80)( zUf?uvRkBN$`YeDJqebg8$N3Kdv|wQV7O0WzlDp3UXfaySfmEL_>kZNXlt^}|s8<8% z^KWy2D0-*3fL)RFlR^Z+`z0`tz9IJ|yYx=40MKHzBmw?N6-jpqqK+` z5mAij#C}b(OCLQH04+vKKOonaDX0g`2bO7s3DZauL!KN$!iWUMOdLa;DGt+c6S{sn z0G!QEY*%g6+G;s}>O8lv+~)4}yVx$qSGb^TRz)D(&VIG74%h{tYGQiEJYs^a=b4E3%0w{z|2tJ`nHqX zdL4KGNt2VEx$-J%EBBkT5)fXa4#b3U(Ku(JIOf zG-kb7wmPj>0|Mxy;d208cbezV1PTunVvI8~Ds5Ds(Zs5ARouLIQ)vn~#H=@$Se;gH zAQNs|QVbb90E5xMhf6=C=zvP*Tn3NZgQLkov%Q(dx<;Dnn`mil!R2rvd*z6Oxcbm) zr5Xr-T6*ooiFK6uO3gMP$)?k?`Q^>j+^S*vFQ+RW$l-SmQ+}eH+S|38Il%{?2fpuNN7~j(4%b{wX*W*PH zgf1q~U~gdYhl|O2H7jV)U9h`=olADo($vzLD+}GHi1khNOrJlU8E?+u3G)*X32$Ec zi&v<%)hcBNK51JrZP#71-keP9+O1;EbGzJZFWOE&Lq96dR(5*0POqc%cqxMt2Kk*> zDoZQLP0pn(zYK&{%~jHQK@^DAM|0(mSCD=!@!ieuNSm4#_N00x%^41(^}LHpf5;F z31z}qb#N8wKTB`_J2fknOMkq?&B~jd?upVzQC3h!{P1`ZpH1X2vnlw4XS z*`=czpZU({XwHE&&*gBjY|k>r|8#u7R-c{pEH|reQe9Eq2}DsNQukdQf++Mzx+e$% zE{BWiifYnlq^oY{_O;twI(JD)TsXw)wEjWkD=|y!7!&#%ptHrv<`bJq%kJJ`Pnb4= z+B>yWl~;9XOAv&ht|45hEU#p|Wjuq14pMc0Vsav%{_0ajv7uRSt`KRh&(n0wZ66f7 z-OjQ-%ScLBPE)_mWbw;-w1LUKokUl3&j`wzBDD0Fd{+ey@N%dOD^Q`c-UUJok_DMMQl(@cP}O9pL6`a z*`5H=<#KV|dR^7MVVP&>5Jges(7zsH%Rg+1*s}`v z7qV#5A~YJmcE2c7Es?*-y`&uKMioE4{VLzkzXfYdK z+!&0cy`;#5MN#DL^}DK*^Xc=aKj=GpRueTMhyLvl+vjdq-F9g~DVt_*qMxx}u#m1% zq;<<^w_>|&3$-ApU1q#F1Gn4VBOA*C`b4dU6Ca=8gC!q?+-A{%B9>$>Q4-=bia;%` zb-}>_ykevNo;p)}!u$job24x{dSt&}KoE7&I;3Aqyt`2O>|F6>1)E;lgwbg1F2wNw zwHm9_>M-lg6X~XSTo44B>YEssJxiuicV#{us>_xU+C%-(=DN~<)u{+c$eGwaO_C|H&Adb|w&-e3}+ zO;k0&*a5L*%*mi&SAlBE-EYchZ)DMji>LJxE%2%<8n-H2_wp)o#|C)9J>|3xYsPV+$|LenItMqx4ba zypcn7MKzV>mFT0@?@?@NY{4|r#MTpAJF(`8Nr~uV^b{X0?qrUFAaMPg>(t$=YbQ-U z-}5;eU)<;^Lw|^}dyG{HXGyi12nUN9zJm-m*L{6kovQa-q}d&}cP?8WCBRal72D+qe)g z#u>?dCzs?OCHn>LK{=jxoNfQO4TG_BPA+?8WLfsRe?-B{Y%ebAbd|+uS%reVBZ45X zV*d(Kvm#5pdOcpQTd!08SvfZM6V7^_;+k5~f0Iw-E!y|P^=qplmpoq+Dq&i#eRGX56UT(I5dBDH zRN5$p{P_^d3d%y6FgnU<$u3~$xY4;m6me^J7cE1m@hxnH@tZAVk%Bn^g8e=PFApZ@?td> z5)^}O9e{7s@n40SU5v{f$Ld3?dj)uFbJp_Jv9Htw(q|>R^glsas_t6wHE=Cd!k{$2 zlxYK|QF6TGk@!VfmMJ-2!n6U?D9tZb1Kzdnz*D4w9W?Rrp%ZE}u!H;t1=PPkO{lfi zk{z4P%Bd?U*j3PDz!mH&VCB@6WXEPxYpYcYp2s&yHoK2_L}6M?ZxFU Date: Sun, 4 Feb 2018 19:28:42 -0800 Subject: [PATCH 012/123] When dragging files into the window, highlight the drop area in blue and show a count of files to be added --- onionshare_gui/file_selection.py | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index a8ba92b7..79d1ba20 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -60,10 +60,29 @@ class FileList(QtWidgets.QListWidget): def dragEnterEvent(self, event): self.parent.drop_here_image.hide() self.parent.drop_here_text.hide() - event.ignore() + event.accept() + + class DropCountLabel(QtWidgets.QLabel): + """ + While dragging files over the FileList, this counter displays the + number of files you're dragging. + """ + def __init__(self, parent): + self.parent = parent + super(DropCountLabel, self).__init__(parent=parent) + self.setAcceptDrops(True) + self.setAlignment(QtCore.Qt.AlignCenter) + self.setText(strings._('gui_drag_and_drop', True)) + self.setStyleSheet('color: #ffffff; background-color: #f44449; font-weight: bold; padding: 5px 10px; border-radius: 10px;') + self.hide() + + def dragLeaveEvent(self, event): + self.hide() + event.accept() self.drop_here_image = DropHereLabel(self, True) self.drop_here_text = DropHereLabel(self, False) + self.drop_count = DropCountLabel(self) self.resizeEvent(None) self.filenames = [] @@ -94,6 +113,12 @@ class FileList(QtWidgets.QListWidget): dragEnterEvent for dragging files and directories into the widget. """ if event.mimeData().hasUrls: + self.setStyleSheet('FileList { border: 3px solid #538ad0; }') + count = len(event.mimeData().urls()) + self.drop_count.setText('+{}'.format(count)) + + self.drop_count.setGeometry(self.width() - 60, self.height() - 40, 50, 30) + self.drop_count.show() event.accept() else: event.ignore() @@ -102,6 +127,8 @@ class FileList(QtWidgets.QListWidget): """ dragLeaveEvent for dragging files and directories into the widget. """ + self.setStyleSheet('FileList { border: none; }') + self.drop_count.hide() event.accept() self.update() @@ -127,6 +154,10 @@ class FileList(QtWidgets.QListWidget): self.add_file(filename) else: event.ignore() + + self.setStyleSheet('border: none;') + self.drop_count.hide() + self.files_dropped.emit() def add_file(self, filename): From 52d080d2a347b2d75b4c9a5372c1b4ff5a1366bc Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Feb 2018 20:09:51 -0800 Subject: [PATCH 013/123] Give file items widgets, so they can soon contain working delete buttons --- onionshare_gui/file_selection.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 79d1ba20..74a6674d 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -38,6 +38,8 @@ class FileList(QtWidgets.QListWidget): self.setMinimumHeight(200) self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.filenames = [] + class DropHereLabel(QtWidgets.QLabel): """ When there are no files or folders in the FileList yet, display the @@ -85,9 +87,6 @@ class FileList(QtWidgets.QListWidget): self.drop_count = DropCountLabel(self) self.resizeEvent(None) - self.filenames = [] - self.update() - def update(self): """ Update the GUI elements based on the current state. @@ -108,6 +107,13 @@ class FileList(QtWidgets.QListWidget): self.drop_here_image.setGeometry(0, 0, self.width(), self.height() - offset) self.drop_here_text.setGeometry(0, offset, self.width(), self.height() - offset) + # Add and delete an empty item, to force all items to get redrawn + # This is ugly, but the only way I could figure out how to proceed + item = QtWidgets.QListWidgetItem('fake item') + self.addItem(item) + self.takeItem(self.row(item)) + self.update() + def dragEnterEvent(self, event): """ dragEnterEvent for dragging files and directories into the widget. @@ -183,11 +189,22 @@ class FileList(QtWidgets.QListWidget): else: size = common.human_readable_filesize(common.dir_size(filename)) item_name = '{0:s} ({1:s})'.format(basename, size) + + # Create a new item item = QtWidgets.QListWidgetItem(item_name) item.setToolTip(size) - item.setIcon(icon) + + # Create an item widget to display on the item + item_button = QtWidgets.QPushButton('x') + item_widget_layout = QtWidgets.QHBoxLayout() + item_widget_layout.addStretch() + item_widget_layout.addWidget(item_button) + item_widget = QtWidgets.QWidget() + item_widget.setLayout(item_widget_layout) + self.addItem(item) + self.setItemWidget(item, item_widget) self.files_updated.emit() From d892213db1fb6d3a59d0f821e8b2aaddb9b95d36 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Feb 2018 20:36:34 -0800 Subject: [PATCH 014/123] Move DropHereLabel and DropCountLabel classes out of FileList --- onionshare_gui/file_selection.py | 86 ++++++++++++++++---------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 74a6674d..53937e51 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -23,6 +23,50 @@ from .alert import Alert from onionshare import strings, common +class DropHereLabel(QtWidgets.QLabel): + """ + When there are no files or folders in the FileList yet, display the + 'drop files here' message and graphic. + """ + def __init__(self, parent, image=False): + self.parent = parent + super(DropHereLabel, self).__init__(parent=parent) + self.setAcceptDrops(True) + self.setAlignment(QtCore.Qt.AlignCenter) + + if image: + self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(common.get_resource_path('images/logo_transparent.png')))) + else: + self.setText(strings._('gui_drag_and_drop', True)) + self.setStyleSheet('color: #999999;') + + self.hide() + + def dragEnterEvent(self, event): + self.parent.drop_here_image.hide() + self.parent.drop_here_text.hide() + event.accept() + + +class DropCountLabel(QtWidgets.QLabel): + """ + While dragging files over the FileList, this counter displays the + number of files you're dragging. + """ + def __init__(self, parent): + self.parent = parent + super(DropCountLabel, self).__init__(parent=parent) + self.setAcceptDrops(True) + self.setAlignment(QtCore.Qt.AlignCenter) + self.setText(strings._('gui_drag_and_drop', True)) + self.setStyleSheet('color: #ffffff; background-color: #f44449; font-weight: bold; padding: 5px 10px; border-radius: 10px;') + self.hide() + + def dragLeaveEvent(self, event): + self.hide() + event.accept() + + class FileList(QtWidgets.QListWidget): """ The list of files and folders in the GUI. @@ -40,48 +84,6 @@ class FileList(QtWidgets.QListWidget): self.filenames = [] - class DropHereLabel(QtWidgets.QLabel): - """ - When there are no files or folders in the FileList yet, display the - 'drop files here' message and graphic. - """ - def __init__(self, parent, image=False): - self.parent = parent - super(DropHereLabel, self).__init__(parent=parent) - self.setAcceptDrops(True) - self.setAlignment(QtCore.Qt.AlignCenter) - - if image: - self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(common.get_resource_path('images/logo_transparent.png')))) - else: - self.setText(strings._('gui_drag_and_drop', True)) - self.setStyleSheet('color: #999999;') - - self.hide() - - def dragEnterEvent(self, event): - self.parent.drop_here_image.hide() - self.parent.drop_here_text.hide() - event.accept() - - class DropCountLabel(QtWidgets.QLabel): - """ - While dragging files over the FileList, this counter displays the - number of files you're dragging. - """ - def __init__(self, parent): - self.parent = parent - super(DropCountLabel, self).__init__(parent=parent) - self.setAcceptDrops(True) - self.setAlignment(QtCore.Qt.AlignCenter) - self.setText(strings._('gui_drag_and_drop', True)) - self.setStyleSheet('color: #ffffff; background-color: #f44449; font-weight: bold; padding: 5px 10px; border-radius: 10px;') - self.hide() - - def dragLeaveEvent(self, event): - self.hide() - event.accept() - self.drop_here_image = DropHereLabel(self, True) self.drop_here_text = DropHereLabel(self, False) self.drop_count = DropCountLabel(self) From e17dbd4fddb8a8ca25eb6d7e7ad450fd6de357ad Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Feb 2018 20:50:24 -0800 Subject: [PATCH 015/123] Only do the fake item hack if there are items in the list --- onionshare_gui/file_selection.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 53937e51..5a371d3c 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -62,7 +62,7 @@ class DropCountLabel(QtWidgets.QLabel): self.setStyleSheet('color: #ffffff; background-color: #f44449; font-weight: bold; padding: 5px 10px; border-radius: 10px;') self.hide() - def dragLeaveEvent(self, event): + def dragEnterEvent(self, event): self.hide() event.accept() @@ -109,12 +109,13 @@ class FileList(QtWidgets.QListWidget): self.drop_here_image.setGeometry(0, 0, self.width(), self.height() - offset) self.drop_here_text.setGeometry(0, offset, self.width(), self.height() - offset) - # Add and delete an empty item, to force all items to get redrawn - # This is ugly, but the only way I could figure out how to proceed - item = QtWidgets.QListWidgetItem('fake item') - self.addItem(item) - self.takeItem(self.row(item)) - self.update() + if self.count() > 0: + # Add and delete an empty item, to force all items to get redrawn + # This is ugly, but the only way I could figure out how to proceed + item = QtWidgets.QListWidgetItem('fake item') + self.addItem(item) + self.takeItem(self.row(item)) + self.update() def dragEnterEvent(self, event): """ From 0bb9bf649d0052703730fbd6e4391997f593a878 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Feb 2018 21:18:41 -0800 Subject: [PATCH 016/123] Remove delete button, use delete X's instead --- install/onionshare.nsi | 2 ++ onionshare_gui/file_selection.py | 44 ++++++++++++------------------- share/images/file_delete.png | Bin 0 -> 182 bytes 3 files changed, 19 insertions(+), 27 deletions(-) create mode 100644 share/images/file_delete.png diff --git a/install/onionshare.nsi b/install/onionshare.nsi index d83da6ad..e5f989bb 100644 --- a/install/onionshare.nsi +++ b/install/onionshare.nsi @@ -201,6 +201,7 @@ Section "install" File "${BINPATH}\share\html\index.html" SetOutPath "$INSTDIR\share\images" + File "${BINPATH}\share\images\file_delete.png" File "${BINPATH}\share\images\logo.png" File "${BINPATH}\share\images\logo_transparent.png" File "${BINPATH}\share\images\logo_grayscale.png" @@ -379,6 +380,7 @@ FunctionEnd Delete "$INSTDIR\share\html\404.html" Delete "$INSTDIR\share\html\denied.html" Delete "$INSTDIR\share\html\index.html" + Delete "$INSTDIR\share\images\file_delete.png" Delete "$INSTDIR\share\images\logo.png" Delete "$INSTDIR\share\images\logo_transparent.png" Delete "$INSTDIR\share\images\logo_grayscale.png" diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 5a371d3c..88576e8a 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -80,7 +80,7 @@ class FileList(QtWidgets.QListWidget): self.setIconSize(QtCore.QSize(32, 32)) self.setSortingEnabled(True) self.setMinimumHeight(200) - self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.filenames = [] @@ -198,8 +198,21 @@ class FileList(QtWidgets.QListWidget): item.setToolTip(size) item.setIcon(icon) + # Item's delete button + def delete_item(): + itemrow = self.row(item) + self.filenames.pop(itemrow) + self.takeItem(itemrow) + self.files_updated.emit() + self.update() + + item_button = QtWidgets.QPushButton() + item_button.setDefault(False) + item_button.setFlat(True) + item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) ) + item_button.clicked.connect(delete_item) + # Create an item widget to display on the item - item_button = QtWidgets.QPushButton('x') item_widget_layout = QtWidgets.QHBoxLayout() item_widget_layout.addStretch() item_widget_layout.addWidget(item_button) @@ -214,8 +227,7 @@ class FileList(QtWidgets.QListWidget): class FileSelection(QtWidgets.QVBoxLayout): """ - The list of files and folders in the GUI, as well as buttons to add and - delete the files and folders. + The list of files and folders in the GUI, as well as button to add files and folders. """ def __init__(self): super(FileSelection, self).__init__() @@ -229,11 +241,9 @@ class FileSelection(QtWidgets.QVBoxLayout): # buttons self.add_button = QtWidgets.QPushButton(strings._('gui_add', True)) self.add_button.clicked.connect(self.add) - self.delete_button = QtWidgets.QPushButton(strings._('gui_delete', True)) - self.delete_button.clicked.connect(self.delete) button_layout = QtWidgets.QHBoxLayout() + button_layout.addStretch() button_layout.addWidget(self.add_button) - button_layout.addWidget(self.delete_button) # add the widgets self.addWidget(self.file_list) @@ -248,17 +258,9 @@ class FileSelection(QtWidgets.QVBoxLayout): # all buttons should be disabled if the server is on if self.server_on: self.add_button.setEnabled(False) - self.delete_button.setEnabled(False) else: self.add_button.setEnabled(True) - # delete button should be disabled if item isn't selected - current_item = self.file_list.currentItem() - if not current_item: - self.delete_button.setEnabled(False) - else: - self.delete_button.setEnabled(True) - # update the file list self.file_list.update() @@ -273,18 +275,6 @@ class FileSelection(QtWidgets.QVBoxLayout): self.update() - def delete(self): - """ - Delete button clicked - """ - selected = self.file_list.selectedItems() - for item in selected: - itemrow = self.file_list.row(item) - self.file_list.filenames.pop(itemrow) - self.file_list.takeItem(itemrow) - self.file_list.files_updated.emit() - self.update() - def server_started(self): """ Gets called when the server starts. diff --git a/share/images/file_delete.png b/share/images/file_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..b9057df5d2f0590bb9bdbf6f4b7274cf094eb986 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv`=Osp(cYFe)ylz~Fdo-U3d5|@(`5)yu#KX9O+iP7AG zF_bx7=D?yC47(iJH)z;3oMuSrWUk0@4D4Ch(b>r3Fy*RCHfR6< literal 0 HcmV?d00001 From 8dd9b045cc5e473752d33d938b22741edd4560e2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Feb 2018 21:45:35 -0800 Subject: [PATCH 017/123] Add delete button back, but only show it when files are selected --- onionshare_gui/file_selection.py | 41 ++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 88576e8a..0ba94b51 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -80,7 +80,7 @@ class FileList(QtWidgets.QListWidget): self.setIconSize(QtCore.QSize(32, 32)) self.setSortingEnabled(True) self.setMinimumHeight(200) - self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.filenames = [] @@ -204,7 +204,6 @@ class FileList(QtWidgets.QListWidget): self.filenames.pop(itemrow) self.takeItem(itemrow) self.files_updated.emit() - self.update() item_button = QtWidgets.QPushButton() item_button.setDefault(False) @@ -227,7 +226,8 @@ class FileList(QtWidgets.QListWidget): class FileSelection(QtWidgets.QVBoxLayout): """ - The list of files and folders in the GUI, as well as button to add files and folders. + The list of files and folders in the GUI, as well as buttons to add and + delete the files and folders. """ def __init__(self): super(FileSelection, self).__init__() @@ -241,9 +241,12 @@ class FileSelection(QtWidgets.QVBoxLayout): # buttons self.add_button = QtWidgets.QPushButton(strings._('gui_add', True)) self.add_button.clicked.connect(self.add) + self.delete_button = QtWidgets.QPushButton(strings._('gui_delete', True)) + self.delete_button.clicked.connect(self.delete) button_layout = QtWidgets.QHBoxLayout() button_layout.addStretch() button_layout.addWidget(self.add_button) + button_layout.addWidget(self.delete_button) # add the widgets self.addWidget(self.file_list) @@ -255,13 +258,22 @@ class FileSelection(QtWidgets.QVBoxLayout): """ Update the GUI elements based on the current state. """ - # all buttons should be disabled if the server is on + # All buttons should be hidden if the server is on if self.server_on: - self.add_button.setEnabled(False) + self.add_button.hide() + self.delete_button.hide() else: - self.add_button.setEnabled(True) + self.add_button.show() - # update the file list + # Delete button should be hidden if item isn't selected + current_item = self.file_list.currentItem() + common.log('FileSelection', 'current_item: {}'.format(current_item)) + if not current_item: + self.delete_button.hide() + else: + self.delete_button.show() + + # Update the file list self.file_list.update() def add(self): @@ -273,6 +285,21 @@ class FileSelection(QtWidgets.QVBoxLayout): for filename in file_dialog.selectedFiles(): self.file_list.add_file(filename) + self.file_list.setCurrentItem(None) + self.update() + + def delete(self): + """ + Delete button clicked + """ + selected = self.file_list.selectedItems() + for item in selected: + itemrow = self.file_list.row(item) + self.file_list.filenames.pop(itemrow) + self.file_list.takeItem(itemrow) + self.file_list.files_updated.emit() + + self.file_list.setCurrentItem(None) self.update() def server_started(self): From cf1ff97ce9f3b139ec18a2d0e95af171ebebceae Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 09:24:43 -0800 Subject: [PATCH 018/123] Create a "primary action" layout, and only show it when there are files in the file list --- onionshare_gui/onionshare_gui.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 582ebdb3..47b3761a 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -78,6 +78,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.start_server_finished.connect(self.server_status.start_server_finished) self.stop_server_finished.connect(self.server_status.stop_server_finished) self.file_selection.file_list.files_updated.connect(self.server_status.update) + self.file_selection.file_list.files_updated.connect(self.update_primary_action) self.server_status.url_copied.connect(self.copy_url) self.server_status.hidservauth_copied.connect(self.copy_hidservauth) self.starting_server_step2.connect(self.start_server_step2) @@ -124,13 +125,20 @@ class OnionShareGui(QtWidgets.QMainWindow): self.persistent_url_label.setStyleSheet('padding: 10px 0; font-weight: bold; color: #333333;') self.persistent_url_label.hide() + # Primary action layout + primary_action_layout = QtWidgets.QVBoxLayout() + primary_action_layout.addLayout(self.server_status) + primary_action_layout.addWidget(self.filesize_warning) + primary_action_layout.addWidget(self.persistent_url_label) + primary_action_layout.addWidget(self.downloads_container) + self.primary_action = QtWidgets.QWidget() + self.primary_action.setLayout(primary_action_layout) + self.primary_action.hide() + # Main layout self.layout = QtWidgets.QVBoxLayout() self.layout.addLayout(self.file_selection) - self.layout.addLayout(self.server_status) - self.layout.addWidget(self.filesize_warning) - self.layout.addWidget(self.persistent_url_label) - self.layout.addWidget(self.downloads_container) + self.layout.addWidget(self.primary_action) central_widget = QtWidgets.QWidget() central_widget.setLayout(self.layout) self.setCentralWidget(central_widget) @@ -158,6 +166,12 @@ class OnionShareGui(QtWidgets.QMainWindow): # After connecting to Tor, check for updates self.check_for_updates() + def update_primary_action(self): + if self.file_selection.file_list.count() > 0: + self.primary_action.show() + else: + self.primary_action.hide() + def _initSystemTray(self): system = common.get_platform() From 83c9cd4e00009a2bb07c03755cf4ed14d2df5519 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 09:26:05 -0800 Subject: [PATCH 019/123] Show or hide primary action layout section the first time, in case filenames are passed in from the cli --- onionshare_gui/onionshare_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 47b3761a..ffe9f3cf 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -134,6 +134,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.primary_action = QtWidgets.QWidget() self.primary_action.setLayout(primary_action_layout) self.primary_action.hide() + self.update_primary_action() # Main layout self.layout = QtWidgets.QVBoxLayout() From fd089f88032b950dc6296d587c5a57d0ca8ada6e Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 09:29:28 -0800 Subject: [PATCH 020/123] Move autostop timer checkbox below the start sharing button --- onionshare_gui/server_status.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 442ae440..dee2f1b1 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -49,6 +49,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): # Helper boolean as this is used in a few places self.timer_enabled = False + # Shutdown timeout layout self.server_shutdown_timeout_checkbox = QtWidgets.QCheckBox() self.server_shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Unchecked) @@ -56,8 +57,10 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.server_shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_choice", True)) self.server_shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) self.server_shutdown_timeout = QtWidgets.QDateTimeEdit() + # Set proposed timeout to be 5 minutes into the future self.server_shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) + # Onion services can take a little while to start, so reduce the risk of it expiring too soon by setting the minimum to 2 min from now self.server_shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) self.server_shutdown_timeout.setCurrentSectionIndex(4) @@ -67,7 +70,8 @@ class ServerStatus(QtWidgets.QVBoxLayout): shutdown_timeout_layout_group.addWidget(self.server_shutdown_timeout_checkbox) shutdown_timeout_layout_group.addWidget(self.server_shutdown_timeout_label) shutdown_timeout_layout_group.addWidget(self.server_shutdown_timeout) - # server layout + + # Server layout self.status_image_stopped = QtGui.QImage(common.get_resource_path('images/server_stopped.png')) self.status_image_working = QtGui.QImage(common.get_resource_path('images/server_working.png')) self.status_image_started = QtGui.QImage(common.get_resource_path('images/server_started.png')) @@ -79,7 +83,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): server_layout.addWidget(self.status_image_label) server_layout.addWidget(self.server_button) - # url layout + # URL layout url_font = QtGui.QFont() self.url_label = QtWidgets.QLabel() self.url_label.setFont(url_font) @@ -94,10 +98,10 @@ class ServerStatus(QtWidgets.QVBoxLayout): url_layout.addWidget(self.copy_url_button) url_layout.addWidget(self.copy_hidservauth_button) - # add the widgets - self.addLayout(shutdown_timeout_layout_group) + # Add the widgets self.addLayout(server_layout) self.addLayout(url_layout) + self.addLayout(shutdown_timeout_layout_group) self.update() From 5328a6f7f6004c7c1a9633ad88dbabbc0231f536 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 09:33:31 -0800 Subject: [PATCH 021/123] Resize the window when adding new files --- onionshare_gui/onionshare_gui.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index ffe9f3cf..b7fcdabc 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -56,6 +56,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowTitle('OnionShare') self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png'))) + self.setMinimumWidth(300) # Load settings self.config = config @@ -168,6 +169,10 @@ class OnionShareGui(QtWidgets.QMainWindow): self.check_for_updates() def update_primary_action(self): + # Resize window + self.resize(self.sizeHint()) + + # Show or hide primary action layout if self.file_selection.file_list.count() > 0: self.primary_action.show() else: From 021c1ddc24b7ff2119110bb31b1b2bbb4837488c Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 09:44:17 -0800 Subject: [PATCH 022/123] Stylize the server button --- onionshare_gui/server_status.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index dee2f1b1..7d4576f1 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -133,7 +133,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): """ Update the GUI elements based on the current state. """ - # set the status image + # Set the status image if self.status == self.STATUS_STOPPED: self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_stopped)) elif self.status == self.STATUS_WORKING: @@ -141,7 +141,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): elif self.status == self.STATUS_STARTED: self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_started)) - # set the URL fields + # Set the URL fields if self.status == self.STATUS_STARTED: self.url_label.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) self.url_label.show() @@ -157,7 +157,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): else: self.copy_hidservauth_button.hide() - # resize parent widget + # Resize parent widget p = self.parentWidget() p.resize(p.sizeHint()) else: @@ -165,27 +165,34 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.copy_url_button.hide() self.copy_hidservauth_button.hide() - # button + # Button + button_stopped_style = 'QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px; border: 0; }' + button_working_style = 'QPushButton { background-color: #4c8211; color: #ffffff; padding: 10px; border: 0; font-style: italic; }' + button_started_style = 'QPushButton { background-color: #d0011b; color: #ffffff; padding: 10px; border: 0; }' if self.file_selection.get_num_files() == 0: - self.server_button.setEnabled(False) - self.server_button.setText(strings._('gui_start_server', True)) + self.server_button.hide() else: + self.server_button.show() if self.status == self.STATUS_STOPPED: + self.server_button.setStyleSheet(button_stopped_style) self.server_button.setEnabled(True) self.server_button.setText(strings._('gui_start_server', True)) self.server_shutdown_timeout.setEnabled(True) self.server_shutdown_timeout_checkbox.setEnabled(True) elif self.status == self.STATUS_STARTED: + self.server_button.setStyleSheet(button_started_style) self.server_button.setEnabled(True) self.server_button.setText(strings._('gui_stop_server', True)) self.server_shutdown_timeout.setEnabled(False) self.server_shutdown_timeout_checkbox.setEnabled(False) elif self.status == self.STATUS_WORKING: + self.server_button.setStyleSheet(button_working_style) self.server_button.setEnabled(False) self.server_button.setText(strings._('gui_please_wait')) self.server_shutdown_timeout.setEnabled(False) self.server_shutdown_timeout_checkbox.setEnabled(False) else: + self.server_button.setStyleSheet(button_working_style) self.server_button.setEnabled(False) self.server_button.setText(strings._('gui_please_wait')) self.server_shutdown_timeout.setEnabled(False) From 5dad0e81a33de4080a53615cd6534d225921c151 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 09:46:22 -0800 Subject: [PATCH 023/123] Increase minimum window width --- onionshare_gui/onionshare_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index b7fcdabc..c991a060 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -56,7 +56,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowTitle('OnionShare') self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png'))) - self.setMinimumWidth(300) + self.setMinimumWidth(400) # Load settings self.config = config From 0d8e842e48339e62facf2b550fc3ef3b510ceda6 Mon Sep 17 00:00:00 2001 From: scootergrisen Date: Mon, 5 Feb 2018 19:52:14 +0100 Subject: [PATCH 024/123] Remove a space --- share/locale/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/locale/en.json b/share/locale/en.json index efbbe0cc..421f95ec 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -7,7 +7,7 @@ "wait_for_hs_yup": "Ready!", "give_this_url": "Give this URL to the person you're sending the file to:", "give_this_url_stealth": "Give this URL and HidServAuth line to the person you're sending the file to:", - "ctrlc_to_stop": "Press Ctrl-C to stop server", + "ctrlc_to_stop": "Press Ctrl-C to stop server", "not_a_file": "{0:s} is not a valid file.", "not_a_readable_file": "{0:s} is not a readable file.", "no_available_port": "Could not start the Onion service as there was no available port.", From ec33c9eddcd71c8a876faa64e17f08d12c003a1c Mon Sep 17 00:00:00 2001 From: scootergrisen Date: Mon, 5 Feb 2018 21:38:38 +0100 Subject: [PATCH 025/123] Remove "at least" --- share/locale/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/locale/en.json b/share/locale/en.json index 421f95ec..eff0cae4 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -62,7 +62,7 @@ "error_rate_limit": "An attacker might be trying to guess your URL. To prevent this, OnionShare has automatically stopped the server. To share the files you must start it again and share the new URL.", "zip_progress_bar_format": "Crunching files: %p%", "error_stealth_not_supported": "To create stealth onion services, you need at least Tor 0.2.9.1-alpha (or Tor Browser 6.5) and at least python3-stem 1.5.0.", - "error_ephemeral_not_supported": "OnionShare requires at least at least Tor 0.2.7.1 and at least python3-stem 1.4.0.", + "error_ephemeral_not_supported": "OnionShare requires at least Tor 0.2.7.1 and at least python3-stem 1.4.0.", "gui_settings_window_title": "Settings", "gui_settings_stealth_label": "Stealth (advanced)", "gui_settings_stealth_option": "Create stealth onion services", From 3da58604edace5452ffaa8528cf75bc1629a1e47 Mon Sep 17 00:00:00 2001 From: scootergrisen Date: Mon, 5 Feb 2018 23:59:40 +0100 Subject: [PATCH 026/123] Add da.json (danish translation) --- share/locale/da.json | 136 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 share/locale/da.json diff --git a/share/locale/da.json b/share/locale/da.json new file mode 100644 index 00000000..2c8d0c78 --- /dev/null +++ b/share/locale/da.json @@ -0,0 +1,136 @@ +{ + "config_onion_service": "Konfigurerer onion-tjeneste på port {0:d}.", + "preparing_files": "Forbereder filer som skal deles.", + "wait_for_hs": "Venter på at HS bliver klar:", + "wait_for_hs_trying": "Prøver...", + "wait_for_hs_nope": "Endnu ikke klar.", + "wait_for_hs_yup": "Klar!", + "give_this_url": "Giv denne URL til personen du sender filen til:", + "give_this_url_stealth": "Giv denne URL og HidServAuth-linje til personen du sender filen til:", + "ctrlc_to_stop": "Tryk på Ctrl-C for at stoppe serveren", + "not_a_file": "{0:s} er ikke en gyldig fil.", + "not_a_readable_file": "{0:s} er ikke en læsbar fil.", + "no_available_port": "Kunne ikke starte onion-tjenesten da der ikke var nogen tilgængelig port.", + "download_page_loaded": "Downloadside indlæst", + "other_page_loaded": "URL indlæst", + "close_on_timeout": "Lukker automatisk da timeout er nået", + "closing_automatically": "Lukker automatisk da download er færdig", + "timeout_download_still_running": "Venter på at download skal blive færdig inden automatisk stop", + "large_filesize": "Advarsel: Det kan tage timer at sende store filer", + "error_tails_invalid_port": "Ugyldig værdi, port skal være et heltal", + "error_tails_unknown_root": "Ukendt fejl med Tails-rodproces", + "systray_menu_exit": "Afslut", + "systray_download_started_title": "OnionShare-download startet", + "systray_download_started_message": "En bruger startede download af dine filer", + "systray_download_completed_title": "OnionShare-download færdig", + "systray_download_completed_message": "Brugeren er færdig med at downloade dine filer", + "systray_download_canceled_title": "OnionShare-download annulleret", + "systray_download_canceled_message": "Brugeren annullerede downloaden", + "help_local_only": "Undlad at bruge tor: kun til udvikling", + "help_stay_open": "Hold onion-tjeneste kørende efter download er færdig", + "help_shutdown_timeout": "Luk onion-tjenesten efter N sekunder", + "help_transparent_torification": "Mit system er gennemsigtigt torifiseret", + "help_stealth": "Opret usynlig onion-tjeneste (avanceret)", + "help_debug": "Log programfejl til stdout, og log webfejl til disk", + "help_filename": "Liste over filer eller mapper som skal deles", + "help_config": "Sti til en brugerdefineret JSON-konfigurationsfil (valgfri)", + "gui_drag_and_drop": "Træk og slip\nfiler her", + "gui_add": "Tilføj", + "gui_delete": "Slet", + "gui_choose_items": "Vælg", + "gui_start_server": "Start deling", + "gui_stop_server": "Stop deling", + "gui_copy_url": "Kopiér URL", + "gui_copy_hidservauth": "Kopiér HidServAuth", + "gui_downloads": "Downloads:", + "gui_canceled": "Annulleret", + "gui_copied_url": "Kopierede URL til udklipsholder", + "gui_copied_hidservauth": "Kopierede HidServAuth-linje til udklipsholder", + "gui_starting_server1": "Starter Tor onion-tjeneste...", + "gui_starting_server2": "Databehandler filer...", + "gui_please_wait": "Vent venligst...", + "error_hs_dir_cannot_create": "Kan ikke oprette onion-tjenestens mappe {0:s}", + "error_hs_dir_not_writable": "onion-tjenestens mappe {0:s} er skrivebeskyttet", + "using_ephemeral": "Starter kortvarig Tor onion-tjeneste og afventer udgivelse", + "gui_download_progress_complete": "%p%, tid forløbet: {0:s}", + "gui_download_progress_starting": "{0:s}, %p% (udregner anslået ankomsttid)", + "gui_download_progress_eta": "{0:s}, anslået ankomsttid: {1:s}, %p%", + "version_string": "Onionshare {0:s} | https://onionshare.org/", + "gui_quit_warning": "Er du sikker på, at du vil afslutte?\nURL'en som du deler vil ikke eksistere længere.", + "gui_quit_warning_quit": "Afslut", + "gui_quit_warning_dont_quit": "Afslut ikke", + "error_rate_limit": "En angriber forsøger måske at gætte din URL. For at forhindre det, har OnionShare automatisk stoppet serveren. For at dele filerne skal du starte den igen og dele den nye URL.", + "zip_progress_bar_format": "Databehandler filer: %p%", + "error_stealth_not_supported": "For at oprette usynlige onion-tjenester, skal du mindst have Tor 0.2.9.1-alpha (eller Tor Browser 6.5) og mindst python3-stem 1.5.0.", + "error_ephemeral_not_supported": "OnionShare kræver mindst Tor 0.2.7.1 og mindst python3-stem 1.4.0.", + "gui_settings_window_title": "Indstillinger", + "gui_settings_stealth_label": "Usynlig (avanceret)", + "gui_settings_stealth_option": "Opret usynlige onion-tjenester", + "gui_settings_stealth_option_details": "Det gør OnionShare mere sikker, men også mere besværlig for modtageren at oprette forbindelse til den.
Mere information.", + "gui_settings_stealth_hidservauth_string": "Du har gemt den private nøgle til at blive brugt igen, så din HidServAuth-streng bruges også igen.\nKlik nedenfor, for at kopiere HidServAuth.", + "gui_settings_autoupdate_label": "Søg efter opdateringer", + "gui_settings_autoupdate_option": "Giv mig besked når der findes opdateringer", + "gui_settings_autoupdate_timestamp": "Sidste søgning: {}", + "gui_settings_autoupdate_timestamp_never": "Aldrig", + "gui_settings_autoupdate_check_button": "Søg efter opdateringer", + "gui_settings_sharing_label": "Valgmuligheder for deling", + "gui_settings_close_after_first_download_option": "Stop deling efter første download", + "gui_settings_systray_notifications": "Vis skrivebordsnotifikationer", + "gui_settings_connection_type_label": "Hvordan skal OnionShare oprette forbindelse til Tor?", + "gui_settings_connection_type_bundled_option": "Brug Tor som er bundet med OnionShare", + "gui_settings_connection_type_automatic_option": "Prøv automatisk konfiguration med Tor Browser", + "gui_settings_connection_type_control_port_option": "Opret forbindelse med kontrolport", + "gui_settings_connection_type_socket_file_option": "Opret forbindelse med sokkelfil", + "gui_settings_connection_type_test_button": "Test Tor-indstillinger", + "gui_settings_control_port_label": "Kontrolport", + "gui_settings_socket_file_label": "Sokkelfil", + "gui_settings_socks_label": "SOCKS-port", + "gui_settings_authenticate_label": "Valgmuligheder for Tor-autentifikation", + "gui_settings_authenticate_no_auth_option": "Ingen autentifikation, eller cookieautentifikation", + "gui_settings_authenticate_password_option": "Adgangskode", + "gui_settings_authenticate_cookie_option": "Cookie", + "gui_settings_password_label": "Adgangskode", + "gui_settings_cookie_label": "Cookiesti", + "gui_settings_tor_bridges": "Understøttelse af Tor-bro", + "gui_settings_tor_bridges_no_bridges_radio_option": "Brug ikke broer", + "gui_settings_tor_bridges_obfs4_radio_option": "Brug indbygget obfs4 udskiftelige transporter", + "gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Brug indbygget obfs4 udskiftelige transporter (kræver obfs4proxy)", + "gui_settings_tor_bridges_custom_radio_option": "Brug brugerdefinerede broer", + "gui_settings_tor_bridges_custom_label": "Du kan få broer fra https://bridges.torproject.org", + "gui_settings_tor_bridges_invalid": "Ingen af broerne du leverede ser ud til at være gyldige, så de ignoreres.\nPrøv venligst igen med gyldige broer.", + "gui_settings_button_save": "Gem", + "gui_settings_button_cancel": "Annuller", + "gui_settings_button_help": "Hjælp", + "gui_settings_shutdown_timeout_choice": "Sæt timer til automatisk stop?", + "gui_settings_shutdown_timeout": "Stop delingen ved:", + "settings_saved": "Indstillinger gemt til {}", + "settings_error_unknown": "Kan ikke oprette forbindelse til Tor-kontroller da indstillingerne ikke giver mening.", + "settings_error_automatic": "Kan ikke oprette forbindelse til Tor-kontroller. Kører Tor Browser i baggrunden? Hvis du ikke har den kan du få den fra:\nhttps://www.torproject.org/.", + "settings_error_socket_port": "Kan ikke oprette forbindelse til Tor-kontroller på {}:{}", + "settings_error_socket_file": "Kan ikke oprette forbindelse til Tor-kontroller med sokkelfilen {}", + "settings_error_auth": "Forbundet til {}:{}, men kan ikke autentificere. Er det en Tor-kontroller?", + "settings_error_missing_password": "Forbundet til Tor-kontroller, men den kræver en adgangskode for at autentificere", + "settings_error_unreadable_cookie_file": "Forbundet til Tor-kontroller, men kan ikke autentificere da din adgangskode kan være forkert, og din bruger ikke har tilladelse til at læse cookiefilen.", + "settings_error_bundled_tor_not_supported": "Bundet Tor understøttes ikke når der ikke bruges udviklertilstand i Windows eller MacOS.", + "settings_error_bundled_tor_timeout": "Det tager for længe at oprette forbindelse til Tor. Din computer er måske offline, eller dit ur går forkert.", + "settings_error_bundled_tor_canceled": "Tor-processen lukkede inden den blev færdig med at oprette forbindelse.", + "settings_error_bundled_tor_broken": "Der er noget galt med OnionShare som opretter forbindelse til Tor i baggrunden:\n{}", + "settings_test_success": "Tillykke, OnionShare kan oprette forbindelse til Tor-kontrolleren.\n\nTor version: {}\nUnderstøtter kortvarige onion-tjenester: {}\nUnderstøtter usynlige onion-tjenester: {}", + "error_tor_protocol_error": "Fejl under snak med Tor-kontrolleren.\nHvis du bruger Whonix, så tjek https://www.whonix.org/wiki/onionshare for at få OnionShare til at virke.", + "connecting_to_tor": "Forbundet til Tor-netværket", + "update_available": "Der findes en OnionShare-opdatering. Klik her for at downloade den.

Installeret version: {}
Seneste version: {}", + "update_error_check_error": "Fejl under søgning efter opdateringer: Du er måske ikke forbundet til Tor, eller måske er OnionShare-webstedet nede.", + "update_error_invalid_latest_version": "Fejl under søgning efter opdateringer: OnionShare-webstedet svarende ved at sige at den seneste version er '{}', men det ser ikke ud til at være en gyldig versionsstreng.", + "update_not_available": "Du kører den seneste version af OnionShare.", + "gui_tor_connection_ask": "Vil du åbne OnionShare-indstillinger for at fejlsøge forbindelsen til Tor?", + "gui_tor_connection_ask_open_settings": "Åbn indstillinger", + "gui_tor_connection_ask_quit": "Afslut", + "gui_tor_connection_error_settings": "Prøv at justere måden hvorpå OnionShare opretter forbindelse til Tor-netværket i Indstillinger.", + "gui_tor_connection_canceled": "OnionShare kan ikke oprette forbindelse til Tor.\n\nSørg for at du har forbindelse til internettet, og åbn herefter OnionShare igen for at konfigurere Tor-forbindelsen.", + "gui_tor_connection_lost": "Afbryder forbindelsen fra Tor.", + "gui_server_started_after_timeout": "Serveren startede efter dit valgte automatiske timeout.\nStart venligst en ny deling.", + "gui_server_timeout_expired": "Den valgte timeout er allerede udløbet.\nOpdater venligst timeouten og herefter kan du starte deling.", + "share_via_onionshare": "Del via OnionShare", + "gui_save_private_key_checkbox": "Brug en vedvarende URL\n(fravalg vil slette gemte URL)", + "persistent_url_in_use": "Denne deling bruger en vedvarende URL" +} From ae494c8e331cc2c945e66c27cc53344de52e549a Mon Sep 17 00:00:00 2001 From: attila Date: Mon, 5 Feb 2018 18:06:37 -0500 Subject: [PATCH 027/123] Second attempt at patches for an OpenBSD port, this time based off of 1.2 and after feedback from the upstream and other contributors at https://github.com/micahflee/onionshare/pull/489 --- onionshare/common.py | 11 +++++++---- onionshare/onion.py | 3 ++- onionshare/web.py | 3 ++- onionshare_gui/__init__.py | 4 ++-- setup.py | 22 ++++++++++++---------- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/onionshare/common.py b/onionshare/common.py index 25b901ee..79d62ca9 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -23,6 +23,7 @@ import inspect import os import platform import random +import re import socket import sys import tempfile @@ -56,8 +57,10 @@ def get_platform(): """ Returns the platform OnionShare is running on. """ - return platform.system() - + plat = platform.system() + if re.match('^.*BSD$', plat): + plat = 'BSD' + return plat def get_resource_path(filename): """ @@ -73,7 +76,7 @@ def get_resource_path(filename): # While running tests during stdeb bdist_deb, look 3 directories up for the share folder prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(prefix)))), 'share') - elif p == 'Linux': + elif p == 'BSD' or p == 'Linux': # Assume OnionShare is installed systemwide in Linux, since we're not running in dev mode prefix = os.path.join(sys.prefix, 'share/onionshare') @@ -107,7 +110,7 @@ def get_tor_paths(): tor_geo_ip_file_path = os.path.join(base_path, 'Resources', 'Tor', 'geoip') tor_geo_ipv6_file_path = os.path.join(base_path, 'Resources', 'Tor', 'geoip6') obfs4proxy_file_path = os.path.join(base_path, 'Resources', 'Tor', 'obfs4proxy') - elif p == 'OpenBSD' or p == 'FreeBSD': + elif p == 'BSD': tor_path = '/usr/local/bin/tor' tor_geo_ip_file_path = '/usr/local/share/tor/geoip' tor_geo_ipv6_file_path = '/usr/local/share/tor/geoip6' diff --git a/onionshare/onion.py b/onionshare/onion.py index bae90f4c..6b2a0e77 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -316,7 +316,8 @@ class Onion(object): # guessing the socket file name next if not found_tor: try: - if self.system == 'Linux': + plat = common.get_platform() + if plat == 'Linux' or plat == 'BSD': socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid()) elif self.system == 'Darwin': socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid()) diff --git a/onionshare/web.py b/onionshare/web.py index 3eef67c7..eec2de89 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -298,7 +298,8 @@ def download(slug_candidate): percent = (1.0 * downloaded_bytes / zip_filesize) * 100 # only output to stdout if running onionshare in CLI mode, or if using Linux (#203, #304) - if not gui_mode or common.get_platform() == 'Linux': + plat = common.get_platform() + if not gui_mode or plat == 'Linux' or plat == 'BSD': sys.stdout.write( "\r{0:s}, {1:.2f}% ".format(common.human_readable_filesize(downloaded_bytes), percent)) sys.stdout.flush() diff --git a/onionshare_gui/__init__.py b/onionshare_gui/__init__.py index 14c76617..24e627bb 100644 --- a/onionshare_gui/__init__.py +++ b/onionshare_gui/__init__.py @@ -35,8 +35,8 @@ class Application(QtWidgets.QApplication): and the quick keyboard shortcut. """ def __init__(self): - system = platform.system() - if system == 'Linux': + system = common.get_platform() + if system == 'Linux' or system == 'BSD': self.setAttribute(QtCore.Qt.AA_X11InitThreads, True) QtWidgets.QApplication.__init__(self, sys.argv) self.installEventFilter(self) diff --git a/setup.py b/setup.py index dcbe42fb..23e1ea17 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,17 @@ author_email = 'micah@micahflee.com' url = 'https://github.com/micahflee/onionshare' license = 'GPL v3' keywords = 'onion, share, onionshare, tor, anonymous, web server' +data_files=[ + (os.path.join(sys.prefix, 'share/applications'), ['install/onionshare.desktop']), + (os.path.join(sys.prefix, 'share/appdata'), ['install/onionshare.appdata.xml']), + (os.path.join(sys.prefix, 'share/pixmaps'), ['install/onionshare80.xpm']), + (os.path.join(sys.prefix, 'share/onionshare'), file_list('share')), + (os.path.join(sys.prefix, 'share/onionshare/images'), file_list('share/images')), + (os.path.join(sys.prefix, 'share/onionshare/locale'), file_list('share/locale')), + (os.path.join(sys.prefix, 'share/onionshare/html'), file_list('share/html')), + ] +if platform.system() != 'OpenBSD': + data_files.append(('/usr/share/nautilus-python/extensions/', ['install/scripts/onionshare-nautilus.py'])) setup( name='onionshare', version=version, @@ -54,14 +65,5 @@ setup( packages=['onionshare', 'onionshare_gui'], include_package_data=True, scripts=['install/scripts/onionshare', 'install/scripts/onionshare-gui'], - data_files=[ - (os.path.join(sys.prefix, 'share/applications'), ['install/onionshare.desktop']), - (os.path.join(sys.prefix, 'share/appdata'), ['install/onionshare.appdata.xml']), - (os.path.join(sys.prefix, 'share/pixmaps'), ['install/onionshare80.xpm']), - (os.path.join(sys.prefix, 'share/onionshare'), file_list('share')), - (os.path.join(sys.prefix, 'share/onionshare/images'), file_list('share/images')), - (os.path.join(sys.prefix, 'share/onionshare/locale'), file_list('share/locale')), - (os.path.join(sys.prefix, 'share/onionshare/html'), file_list('share/html')), - ('/usr/share/nautilus-python/extensions/', ['install/scripts/onionshare-nautilus.py']) - ] + data_files=data_files ) From f23e2a8a9070773f7599f132115f8838a3d5dbdc Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 5 Feb 2018 18:20:09 -0800 Subject: [PATCH 028/123] Progressively hide shutdown timer-related UI, so only relevant widgets are shown at any time --- onionshare_gui/server_status.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 7d4576f1..e4e5431c 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -66,10 +66,16 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.server_shutdown_timeout.setCurrentSectionIndex(4) self.server_shutdown_timeout_label.hide() self.server_shutdown_timeout.hide() - shutdown_timeout_layout_group = QtWidgets.QHBoxLayout() - shutdown_timeout_layout_group.addWidget(self.server_shutdown_timeout_checkbox) - shutdown_timeout_layout_group.addWidget(self.server_shutdown_timeout_label) - shutdown_timeout_layout_group.addWidget(self.server_shutdown_timeout) + shutdown_timeout_layout = QtWidgets.QHBoxLayout() + shutdown_timeout_layout.addWidget(self.server_shutdown_timeout_label) + shutdown_timeout_layout.addWidget(self.server_shutdown_timeout) + + # Shutdown timeout container, so it can all be hidden and shown as a group + shutdown_timeout_container_layout = QtWidgets.QVBoxLayout() + shutdown_timeout_container_layout.addWidget(self.server_shutdown_timeout_checkbox) + shutdown_timeout_container_layout.addLayout(shutdown_timeout_layout) + self.server_shutdown_timeout_container = QtWidgets.QWidget() + self.server_shutdown_timeout_container.setLayout(shutdown_timeout_container_layout) # Server layout self.status_image_stopped = QtGui.QImage(common.get_resource_path('images/server_stopped.png')) @@ -101,7 +107,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): # Add the widgets self.addLayout(server_layout) self.addLayout(url_layout) - self.addLayout(shutdown_timeout_layout_group) + self.addWidget(self.server_shutdown_timeout_container) self.update() @@ -173,6 +179,17 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.server_button.hide() else: self.server_button.show() + + if self.status == self.STATUS_STOPPED: + self.server_shutdown_timeout_checkbox.show() + self.server_shutdown_timeout_container.show() + else: + self.server_shutdown_timeout_checkbox.hide() + if self.server_shutdown_timeout_checkbox.isChecked(): + self.server_shutdown_timeout_container.show() + else: + self.server_shutdown_timeout_container.hide() + if self.status == self.STATUS_STOPPED: self.server_button.setStyleSheet(button_stopped_style) self.server_button.setEnabled(True) From 888523b0ab40e6c0b11fd6f934a7bf9eedf4ff1a Mon Sep 17 00:00:00 2001 From: scootergrisen Date: Tue, 6 Feb 2018 18:26:01 +0100 Subject: [PATCH 029/123] Add [da] translations Perhaps add info about this in https://github.com/micahflee/onionshare/wiki/Translating so translators can know they need to translate this file also. Or even better add the strings to the .json translations files so alle strings are in one place. --- install/onionshare.desktop | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/onionshare.desktop b/install/onionshare.desktop index 256a7b50..fbac3660 100644 --- a/install/onionshare.desktop +++ b/install/onionshare.desktop @@ -1,9 +1,11 @@ [Desktop Entry] Name=OnionShare Comment=Share a file securely and anonymously over Tor +Comment[da]=Del en fil sikkert og anonymt over Tor Exec=/usr/bin/onionshare-gui Terminal=false Type=Application Icon=/usr/share/pixmaps/onionshare80.xpm Categories=Network; Keywords=tor;anonymity;privacy;onion service;file sharing;file hosting; +Keywords[da]=tor;anonymitet;privatliv;onion-tjeneste;fildeling;filhosting; From bc8f233db9ce00480a260e00ad5932cd72df25ab Mon Sep 17 00:00:00 2001 From: attila Date: Tue, 6 Feb 2018 16:39:04 -0500 Subject: [PATCH 030/123] Update patch as per the suggestion by @mig5 on https://github.com/micahflee/onionshare/pull/585 Tested on current snapshot, both gui and cli work --- onionshare/onion.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 6b2a0e77..ce412d79 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -131,7 +131,7 @@ class Onion(object): self.stealth = False self.service_id = None - self.system = platform.system() + self.system = common.get_platform() # Is bundled tor supported? if (self.system == 'Windows' or self.system == 'Darwin') and getattr(sys, 'onionshare_dev_mode', False): @@ -183,7 +183,7 @@ class Onion(object): raise OSError(strings._('no_available_port')) self.tor_torrc = os.path.join(self.tor_data_directory.name, 'torrc') else: - # Linux and Mac can use unix sockets + # Linux, Mac and BSD can use unix sockets with open(common.get_resource_path('torrc_template')) as f: torrc_template = f.read() self.tor_control_port = None @@ -316,8 +316,7 @@ class Onion(object): # guessing the socket file name next if not found_tor: try: - plat = common.get_platform() - if plat == 'Linux' or plat == 'BSD': + if self.system == 'Linux' or self.system == 'BSD': socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid()) elif self.system == 'Darwin': socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid()) From 24a672dac90951bfd002469719580c01987bb0a7 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 16:01:59 -0800 Subject: [PATCH 031/123] Make the server button and URL display much nicer, and replace string "URL" with "address" for usability --- onionshare_gui/onionshare_gui.py | 2 +- onionshare_gui/server_status.py | 51 +++++++++++++++++++++++++------- share/locale/en.json | 21 +++++++------ 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index c991a060..e5aaa439 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -128,7 +128,7 @@ class OnionShareGui(QtWidgets.QMainWindow): # Primary action layout primary_action_layout = QtWidgets.QVBoxLayout() - primary_action_layout.addLayout(self.server_status) + primary_action_layout.addWidget(self.server_status) primary_action_layout.addWidget(self.filesize_warning) primary_action_layout.addWidget(self.persistent_url_label) primary_action_layout.addWidget(self.downloads_container) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index e4e5431c..ccf4864f 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -23,7 +23,7 @@ from PyQt5 import QtCore, QtWidgets, QtGui from onionshare import strings, common, settings -class ServerStatus(QtWidgets.QVBoxLayout): +class ServerStatus(QtWidgets.QWidget): """ The server status chunk of the GUI. """ @@ -91,23 +91,41 @@ class ServerStatus(QtWidgets.QVBoxLayout): # URL layout url_font = QtGui.QFont() + self.url_description = QtWidgets.QLabel(strings._('gui_url_description', True)) + self.url_description.setWordWrap(True) self.url_label = QtWidgets.QLabel() - self.url_label.setFont(url_font) - self.url_label.setWordWrap(False) - self.url_label.setAlignment(QtCore.Qt.AlignCenter) + self.url_label.setStyleSheet('QLabel { color: #666666; font-size: 12px; }') + self.url = QtWidgets.QLabel() + self.url.setFont(url_font) + self.url.setWordWrap(True) + self.url.setStyleSheet('QLabel { background-color: #ffffff; color: #000000; padding: 10px; border: 1px solid #666666; }') + + url_buttons_style = 'QPushButton { color: #3f7fcf; }' self.copy_url_button = QtWidgets.QPushButton(strings._('gui_copy_url', True)) + self.copy_url_button.setFlat(True) + self.copy_url_button.setStyleSheet(url_buttons_style) self.copy_url_button.clicked.connect(self.copy_url) self.copy_hidservauth_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) + self.copy_hidservauth_button.setFlat(True) + self.copy_hidservauth_button.setStyleSheet(url_buttons_style) self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth) - url_layout = QtWidgets.QHBoxLayout() + url_buttons_layout = QtWidgets.QHBoxLayout() + url_buttons_layout.addWidget(self.copy_url_button) + url_buttons_layout.addWidget(self.copy_hidservauth_button) + url_buttons_layout.addStretch() + + url_layout = QtWidgets.QVBoxLayout() + url_layout.addWidget(self.url_description) url_layout.addWidget(self.url_label) - url_layout.addWidget(self.copy_url_button) - url_layout.addWidget(self.copy_hidservauth_button) + url_layout.addWidget(self.url) + url_layout.addLayout(url_buttons_layout) # Add the widgets - self.addLayout(server_layout) - self.addLayout(url_layout) - self.addWidget(self.server_shutdown_timeout_container) + layout = QtWidgets.QVBoxLayout() + layout.addLayout(server_layout) + layout.addLayout(url_layout) + layout.addWidget(self.server_shutdown_timeout_container) + self.setLayout(layout) self.update() @@ -149,8 +167,17 @@ class ServerStatus(QtWidgets.QVBoxLayout): # Set the URL fields if self.status == self.STATUS_STARTED: - self.url_label.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) + self.url_description.show() + + if self.settings.get('close_after_first_download'): + self.url_label.setText(strings._('gui_url_label_one_time', True)) + else: + self.url_label.setText(strings._('gui_url_label', True)) self.url_label.show() + + self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) + self.url.show() + self.copy_url_button.show() if self.settings.get('save_private_key'): @@ -167,7 +194,9 @@ class ServerStatus(QtWidgets.QVBoxLayout): p = self.parentWidget() p.resize(p.sizeHint()) else: + self.url_description.hide() self.url_label.hide() + self.url.hide() self.copy_url_button.hide() self.copy_hidservauth_button.hide() diff --git a/share/locale/en.json b/share/locale/en.json index 83a7a536..a3f577a4 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -5,14 +5,14 @@ "wait_for_hs_trying": "Trying...", "wait_for_hs_nope": "Not ready yet.", "wait_for_hs_yup": "Ready!", - "give_this_url": "Give this URL to the person you're sending the file to:", - "give_this_url_stealth": "Give this URL and HidServAuth line to the person you're sending the file to:", + "give_this_url": "Give this address to the person you're sending the file to:", + "give_this_url_stealth": "Give this address and HidServAuth line to the person you're sending the file to:", "ctrlc_to_stop": "Press Ctrl-C to stop server", "not_a_file": "{0:s} is not a valid file.", "not_a_readable_file": "{0:s} is not a readable file.", "no_available_port": "Could not start the Onion service as there was no available port.", "download_page_loaded": "Download page loaded", - "other_page_loaded": "URL loaded", + "other_page_loaded": "Address loaded", "close_on_timeout": "Closing automatically because timeout was reached", "closing_automatically": "Closing automatically because download finished", "timeout_download_still_running": "Waiting for download to complete before auto-stopping", @@ -40,11 +40,11 @@ "gui_choose_items": "Choose", "gui_start_server": "Start Sharing", "gui_stop_server": "Stop Sharing", - "gui_copy_url": "Copy URL", + "gui_copy_url": "Copy Address", "gui_copy_hidservauth": "Copy HidServAuth", "gui_downloads": "Downloads:", "gui_canceled": "Canceled", - "gui_copied_url": "Copied URL to clipboard", + "gui_copied_url": "Copied address to clipboard", "gui_copied_hidservauth": "Copied HidServAuth line to clipboard", "gui_starting_server1": "Starting Tor onion service...", "gui_starting_server2": "Crunching files...", @@ -56,10 +56,10 @@ "gui_download_progress_starting": "{0:s}, %p% (Computing ETA)", "gui_download_progress_eta": "{0:s}, ETA: {1:s}, %p%", "version_string": "Onionshare {0:s} | https://onionshare.org/", - "gui_quit_warning": "Are you sure you want to quit?\nThe URL you are sharing won't exist anymore.", + "gui_quit_warning": "Are you sure you want to quit?\nThe address you are sharing won't exist anymore.", "gui_quit_warning_quit": "Quit", "gui_quit_warning_dont_quit": "Don't Quit", - "error_rate_limit": "An attacker might be trying to guess your URL. To prevent this, OnionShare has automatically stopped the server. To share the files you must start it again and share the new URL.", + "error_rate_limit": "An attacker might be trying to guess your address. To prevent this, OnionShare has automatically stopped the server. To share the files you must start it again and share the new address.", "zip_progress_bar_format": "Crunching files: %p%", "error_stealth_not_supported": "To create stealth onion services, you need at least Tor 0.2.9.1-alpha (or Tor Browser 6.5) and at least python3-stem 1.5.0.", "error_ephemeral_not_supported": "OnionShare requires at least at least Tor 0.2.7.1 and at least python3-stem 1.4.0.", @@ -131,6 +131,9 @@ "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", - "gui_save_private_key_checkbox": "Use a persistent URL\n(unchecking will delete any saved URL)", - "persistent_url_in_use": "This share is using a persistent URL" + "gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved address)", + "persistent_url_in_use": "This share is using a persistent address", + "gui_url_description": "Anyone with this link can download your files using Tor Browser:", + "gui_url_label": "Your Download Address", + "gui_url_label_one_time": "Your One-Time Download Address" } From 2a23b02f982d31cc4f9faac5370eb81e8211ecda Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 16:11:48 -0800 Subject: [PATCH 032/123] Increase minimum width, and remove word wrap, to fix QLabel squishing problem --- onionshare_gui/onionshare_gui.py | 2 +- onionshare_gui/server_status.py | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index e5aaa439..06629efd 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -56,7 +56,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowTitle('OnionShare') self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png'))) - self.setMinimumWidth(400) + self.setMinimumWidth(500) # Load settings self.config = config diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index ccf4864f..92063602 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -92,12 +92,10 @@ class ServerStatus(QtWidgets.QWidget): # URL layout url_font = QtGui.QFont() self.url_description = QtWidgets.QLabel(strings._('gui_url_description', True)) - self.url_description.setWordWrap(True) self.url_label = QtWidgets.QLabel() self.url_label.setStyleSheet('QLabel { color: #666666; font-size: 12px; }') self.url = QtWidgets.QLabel() self.url.setFont(url_font) - self.url.setWordWrap(True) self.url.setStyleSheet('QLabel { background-color: #ffffff; color: #000000; padding: 10px; border: 1px solid #666666; }') url_buttons_style = 'QPushButton { color: #3f7fcf; }' @@ -189,10 +187,6 @@ class ServerStatus(QtWidgets.QWidget): self.copy_hidservauth_button.show() else: self.copy_hidservauth_button.hide() - - # Resize parent widget - p = self.parentWidget() - p.resize(p.sizeHint()) else: self.url_description.hide() self.url_label.hide() From 5ca4bb0157a238ce2d1135a91c2987547343db9d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 16:14:56 -0800 Subject: [PATCH 033/123] Give server button rounded corners. Change style of persistent URL label --- onionshare_gui/onionshare_gui.py | 2 +- onionshare_gui/server_status.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 06629efd..d4f36452 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -123,7 +123,7 @@ class OnionShareGui(QtWidgets.QMainWindow): # Persistent URL notification self.persistent_url_label = QtWidgets.QLabel(strings._('persistent_url_in_use', True)) - self.persistent_url_label.setStyleSheet('padding: 10px 0; font-weight: bold; color: #333333;') + self.persistent_url_label.setStyleSheet('font-weight: bold; color: #333333;') self.persistent_url_label.hide() # Primary action layout diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 92063602..ce2f586d 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -195,9 +195,9 @@ class ServerStatus(QtWidgets.QWidget): self.copy_hidservauth_button.hide() # Button - button_stopped_style = 'QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px; border: 0; }' - button_working_style = 'QPushButton { background-color: #4c8211; color: #ffffff; padding: 10px; border: 0; font-style: italic; }' - button_started_style = 'QPushButton { background-color: #d0011b; color: #ffffff; padding: 10px; border: 0; }' + button_stopped_style = 'QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; }' + button_working_style = 'QPushButton { background-color: #4c8211; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; font-style: italic; }' + button_started_style = 'QPushButton { background-color: #d0011b; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; }' if self.file_selection.get_num_files() == 0: self.server_button.hide() else: From ab1d6a65dde4713cd36b082a9b70abf44c52e7fe Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 17:10:42 -0800 Subject: [PATCH 034/123] Update color and style of progress bars --- onionshare_gui/downloads.py | 8 +++++--- onionshare_gui/onionshare_gui.py | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/onionshare_gui/downloads.py b/onionshare_gui/downloads.py index 60bd59ac..166f14a4 100644 --- a/onionshare_gui/downloads.py +++ b/onionshare_gui/downloads.py @@ -34,13 +34,15 @@ class Download(object): # make a new progress bar cssStyleData =""" QProgressBar { - border: 2px solid grey; - border-radius: 5px; + border: 1px solid #4e064f; + background-color: #ffffff !important; text-align: center; + color: #9b9b9b; + font-size: 12px; } QProgressBar::chunk { - background: qlineargradient(x1: 0.5, y1: 0, x2: 0.5, y2: 1, stop: 0 #b366ff, stop: 1 #d9b3ff); + background-color: #4e064f; width: 10px; }""" self.progress_bar = QtWidgets.QProgressBar() diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index d4f36452..02e3d625 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -586,14 +586,15 @@ class ZipProgressBar(QtWidgets.QProgressBar): self.setFormat(strings._('zip_progress_bar_format')) cssStyleData =""" QProgressBar { - background-color: rgba(255, 255, 255, 0.0) !important; - border: 0px; + border: 1px solid #4e064f; + background-color: #ffffff !important; text-align: center; + color: #9b9b9b; } QProgressBar::chunk { border: 0px; - background: qlineargradient(x1: 0.5, y1: 0, x2: 0.5, y2: 1, stop: 0 #b366ff, stop: 1 #d9b3ff); + background-color: #4e064f; width: 10px; }""" self.setStyleSheet(cssStyleData) From 8f77603182b27103809991964a7b4efb29047bb5 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 17:47:05 -0800 Subject: [PATCH 035/123] Improve the UI of the quit warning --- onionshare_gui/onionshare_gui.py | 4 +++- share/locale/en.json | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 02e3d625..64807418 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -555,9 +555,11 @@ class OnionShareGui(QtWidgets.QMainWindow): common.log('OnionShareGui', 'closeEvent') try: if self.server_status.status != self.server_status.STATUS_STOPPED: + common.log('OnionShareGui', 'closeEvent, opening warning dialog') dialog = QtWidgets.QMessageBox() - dialog.setWindowTitle("OnionShare") + dialog.setWindowTitle(strings._('gui_quit_title', True)) dialog.setText(strings._('gui_quit_warning', True)) + dialog.setIcon(QtWidgets.QMessageBox.Critical) quit_button = dialog.addButton(strings._('gui_quit_warning_quit', True), QtWidgets.QMessageBox.YesRole) dont_quit_button = dialog.addButton(strings._('gui_quit_warning_dont_quit', True), QtWidgets.QMessageBox.NoRole) dialog.setDefaultButton(dont_quit_button) diff --git a/share/locale/en.json b/share/locale/en.json index a3f577a4..c53311fc 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -56,9 +56,10 @@ "gui_download_progress_starting": "{0:s}, %p% (Computing ETA)", "gui_download_progress_eta": "{0:s}, ETA: {1:s}, %p%", "version_string": "Onionshare {0:s} | https://onionshare.org/", - "gui_quit_warning": "Are you sure you want to quit?\nThe address you are sharing won't exist anymore.", + "gui_quit_title": "Transfer in Progress", + "gui_quit_warning": "You're in the process of sending files. Are you sure you want to quit OnionShare?", "gui_quit_warning_quit": "Quit", - "gui_quit_warning_dont_quit": "Don't Quit", + "gui_quit_warning_dont_quit": "Cancel", "error_rate_limit": "An attacker might be trying to guess your address. To prevent this, OnionShare has automatically stopped the server. To share the files you must start it again and share the new address.", "zip_progress_bar_format": "Crunching files: %p%", "error_stealth_not_supported": "To create stealth onion services, you need at least Tor 0.2.9.1-alpha (or Tor Browser 6.5) and at least python3-stem 1.5.0.", From a1aa25c792975e276fd59e72f4e5db560c04139d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 18:35:35 -0800 Subject: [PATCH 036/123] Start with a narrower width --- onionshare_gui/onionshare_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 64807418..e3c359c6 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -56,7 +56,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowTitle('OnionShare') self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png'))) - self.setMinimumWidth(500) + self.setMinimumWidth(350) # Load settings self.config = config @@ -170,7 +170,7 @@ class OnionShareGui(QtWidgets.QMainWindow): def update_primary_action(self): # Resize window - self.resize(self.sizeHint()) + self.adjustSize() # Show or hide primary action layout if self.file_selection.file_list.count() > 0: From 6fa095a650376d04210fe742157073e8cd371c34 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 18:35:55 -0800 Subject: [PATCH 037/123] Update server status images to have less harsh colors --- share/images/server_started.png | Bin 346 -> 347 bytes share/images/server_stopped.png | Bin 286 -> 342 bytes share/images/server_working.png | Bin 338 -> 349 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/share/images/server_started.png b/share/images/server_started.png index 833387e16b282211dff2227685a79c4239bcb6f9..9c0c317613af73a26b083dcd00fbe66673927963 100644 GIT binary patch delta 275 zcmV+u0qp+T0^0(RQGenB2LcWn5Gs3W0000TX;fHrLvL+uWo~o;00000Lvm$dbY)~9 zcWHEJAV*0}P-HG;2LJ#7zez+vR5;76mB9^zFbn`M-k>v-0T`e=Af)oGGFrs9ND(_^ z00t;CutGnkqEJxV_r+@L&KDQ$Zz=dNH;MZce zl3js2ztOaQ>NdhZWgDqm9P>dQMeC@`JB55rs1!F|Z z$%;X=Hm&Fdh=#%_THMJ%mX?*`4TOGv6g0{vL)Oe~%)EWQ<)JPVU*2ib4$~cW?A$Nl Z31vw}$NyPqgA4!w002ovPDHLkV1h;paP|NI delta 274 zcmV+t0qy?V0@?zQQGebD8x}bXOc7G^0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJ zAV*0}P*;Ht7XSbN!bwCyR5;7Em9Y(jFcd@|ZpjF=6m(v}Mf$WXzyL@LfrQdC025HA zV*s}lv@C#}w2&$hNkl-&=dORc`?Aikpe)D46}7euzGL(qqkpyxaYdcw7`7_QG1@XP zeYds@tKg)hSOdK~DJjHYi_k2`{0e*crT&(iZMmsfx_L^~2c!t84@gDo&TD(VW4s;k zBog{pCWWw6*x{sLVTV&uOCtr0S{fDfoTY$ImgK`$0LKfbf75+1M8{#5`PyY*CBA?= Yy0nP}yjtCC01E&B07*qoM6N<$g0i)B#sB~S diff --git a/share/images/server_stopped.png b/share/images/server_stopped.png index 414ce81e5fcb003488a77b9343dd98f62e93242c..5c5b2ec0855a5dcadbb36a00ee10fb47df580b79 100644 GIT binary patch delta 270 zcmV+p0rCEx0@ebMQGenB2Lc-kIw*}A0000TX;fHrLvL+uWo~o;00000Lvm$dbY)~9 zcWHEJAV*0}P-HG;2LJ#7x=BPqR5;76mB9^zFbn`MUZo7c0QC$Asr(s@e~}_)AOkQ! z*`*&-Q7EWwe7_{h$w_P{aKJc@7oY|zptR`;7=Wm%^QV2`K!4N$SI$X5yA_9x>;~NV zjYd`bo$ycDP8wFne2_<>sp>|vBUVmcOk%a^ z#UMa*6h^V)P7boPu9Vh6*e;KPMcHJ?nx&1ox39N6w1wi!J5APM`qPe``vp7!VzWS{ U)vbL700000Ne4wvM6N<$f(LPMhX4Qo delta 213 zcmV;`04o310-gepQGebD8x}bO+^$bz0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJ zAV*0}P*;Ht7XSbNhDk(0R5;7EmB9(aKnw&Ydb^7&$QWFT8@n3RfO~QN>(3E_aX3Tv z7WSjZAc3@k9}*P7CP=~2oUK-Pss>7dI?gUwus#4?y*gp{&`vg{{&e{UrU97F?mk@x zu;9}Yz-$E0FTnv!Cg3|TD}dd|+XJXZV70G}f3b(0o;@6f#Mh93b-aKRbbOU#q(9J= P00000NkvXXu0mjf;vZPR diff --git a/share/images/server_working.png b/share/images/server_working.png index c6db94ff4e276ed7974eab1153db7d6bc595b37c..e5c8b31853388a4d12344e03ccada68fd057b533 100644 GIT binary patch delta 277 zcmV+w0qXwJ0^I_TQGenB2Lc))kB>zg0000TX;fHrLvL+uWo~o;00000Lvm$dbY)~9 zcWHEJAV*0}P-HG;2LJ#7!AV3xR5;76mAegtFc3hWuVamb0T>`VAf!;xc{I8dK|<`{ z0T>{#M%pw-#3!I6zSr57pU(E(*|Ed(b~*zEkOP@c_do}7D9Z?0IkhqK_O-}ET}Z#Y)1V!uIqcZ6 bU%(5`{zFn)s}6JA00000NkvXXu0mjfKhbff delta 266 zcmV+l0rmdf0@4DIQGebD8x}bvd8GOY0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJ zAV*0}P*;Ht7XSbNx=BPqR5;7El`#r~Fc^hDGq`zxUe5CbdW#;&LCBQJ4yg{3p;MG3 zodhdbrTzQhtsn3GgcsP6v1rrdTp)%-4vFjn(eyZD(VUgBXnz-oIg>xx1>zyN?P!ib zTelsJ7;G16Ec#p6;BK(>n{T<@ddm9s@oRZFQNM~ Q00000NkvXXt^-0~f|f^f1ONa4 From 69fe7f0d982be4df53789e951cd2c217f156d6cc Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 19:05:02 -0800 Subject: [PATCH 038/123] Move version label into settings dialog --- onionshare_gui/onionshare_gui.py | 14 ++++++-------- onionshare_gui/settings_dialog.py | 3 +++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index e3c359c6..192343c7 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -102,19 +102,17 @@ class OnionShareGui(QtWidgets.QMainWindow): self.downloads_container.hide() # downloads start out hidden self.new_download = False - # Status bar - self.status_bar = QtWidgets.QStatusBar() - self.status_bar.setSizeGripEnabled(False) - self.status_bar.setStyleSheet( - "QStatusBar::item { border: 0px; }") - version_label = QtWidgets.QLabel('v{0:s}'.format(common.get_version())) - version_label.setStyleSheet('color: #666666') + # Settings button on the status bar self.settings_button = QtWidgets.QPushButton() self.settings_button.setDefault(False) self.settings_button.setFlat(True) self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) ) self.settings_button.clicked.connect(self.open_settings) - self.status_bar.addPermanentWidget(version_label) + + # Status bar + self.status_bar = QtWidgets.QStatusBar() + self.status_bar.setSizeGripEnabled(False) + self.status_bar.setStyleSheet("QStatusBar::item { border: 0px; }") self.status_bar.addPermanentWidget(self.settings_button) self.setStatusBar(self.status_bar) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index dae3b6c5..c1b1604c 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -295,9 +295,12 @@ class SettingsDialog(QtWidgets.QDialog): self.save_button.clicked.connect(self.save_clicked) self.cancel_button = QtWidgets.QPushButton(strings._('gui_settings_button_cancel', True)) self.cancel_button.clicked.connect(self.cancel_clicked) + version_label = QtWidgets.QLabel('OnionShare {0:s}'.format(common.get_version())) + version_label.setStyleSheet('color: #666666') self.help_button = QtWidgets.QPushButton(strings._('gui_settings_button_help', True)) self.help_button.clicked.connect(self.help_clicked) buttons_layout = QtWidgets.QHBoxLayout() + buttons_layout.addWidget(version_label) buttons_layout.addWidget(self.help_button) buttons_layout.addStretch() buttons_layout.addWidget(self.save_button) From f93ed862830f3cd062828a102139ba194c56b794 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 19:31:02 -0800 Subject: [PATCH 039/123] Move server status indicator to the status bar at the bottom --- onionshare_gui/onionshare_gui.py | 35 ++++++++++++++++++++++++++++++++ onionshare_gui/server_status.py | 18 +--------------- share/locale/en.json | 5 ++++- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 192343c7..b997a8b7 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -73,11 +73,15 @@ class OnionShareGui(QtWidgets.QMainWindow): self.server_status = ServerStatus(self.qtapp, self.app, web, self.file_selection, self.settings) self.server_status.server_started.connect(self.file_selection.server_started) self.server_status.server_started.connect(self.start_server) + self.server_status.server_started.connect(self.update_server_status_indicator) self.server_status.server_stopped.connect(self.file_selection.server_stopped) self.server_status.server_stopped.connect(self.stop_server) + self.server_status.server_stopped.connect(self.update_server_status_indicator) self.start_server_finished.connect(self.clear_message) self.start_server_finished.connect(self.server_status.start_server_finished) + self.start_server_finished.connect(self.update_server_status_indicator) self.stop_server_finished.connect(self.server_status.stop_server_finished) + self.stop_server_finished.connect(self.update_server_status_indicator) self.file_selection.file_list.files_updated.connect(self.server_status.update) self.file_selection.file_list.files_updated.connect(self.update_primary_action) self.server_status.url_copied.connect(self.copy_url) @@ -109,10 +113,26 @@ class OnionShareGui(QtWidgets.QMainWindow): self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) ) self.settings_button.clicked.connect(self.open_settings) + # Server status indicator on the status bar + self.server_status_image_stopped = QtGui.QImage(common.get_resource_path('images/server_stopped.png')) + self.server_status_image_working = QtGui.QImage(common.get_resource_path('images/server_working.png')) + self.server_status_image_started = QtGui.QImage(common.get_resource_path('images/server_started.png')) + self.server_status_image_label = QtWidgets.QLabel() + self.server_status_image_label.setFixedWidth(20) + self.server_status_label = QtWidgets.QLabel() + self.server_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; }') + server_status_indicator_layout = QtWidgets.QHBoxLayout() + server_status_indicator_layout.addWidget(self.server_status_image_label) + server_status_indicator_layout.addWidget(self.server_status_label) + self.server_status_indicator = QtWidgets.QWidget() + self.server_status_indicator.setLayout(server_status_indicator_layout) + self.update_server_status_indicator() + # Status bar self.status_bar = QtWidgets.QStatusBar() self.status_bar.setSizeGripEnabled(False) self.status_bar.setStyleSheet("QStatusBar::item { border: 0px; }") + self.status_bar.addPermanentWidget(self.server_status_indicator) self.status_bar.addPermanentWidget(self.settings_button) self.setStatusBar(self.status_bar) @@ -167,6 +187,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.check_for_updates() def update_primary_action(self): + common.log('OnionShareGui', 'update_primary_action') # Resize window self.adjustSize() @@ -176,6 +197,20 @@ class OnionShareGui(QtWidgets.QMainWindow): else: self.primary_action.hide() + def update_server_status_indicator(self): + common.log('OnionShareGui', 'update_server_status_indicator') + + # Set the status image + if self.server_status.status == self.server_status.STATUS_STOPPED: + self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_stopped)) + self.server_status_label.setText(strings._('gui_status_indicator_stopped', True)) + elif self.server_status.status == self.server_status.STATUS_WORKING: + self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working)) + self.server_status_label.setText(strings._('gui_status_indicator_working', True)) + elif self.server_status.status == self.server_status.STATUS_STARTED: + self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started)) + self.server_status_label.setText(strings._('gui_status_indicator_started', True)) + def _initSystemTray(self): system = common.get_platform() diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index ce2f586d..3f7e390c 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -78,16 +78,8 @@ class ServerStatus(QtWidgets.QWidget): self.server_shutdown_timeout_container.setLayout(shutdown_timeout_container_layout) # Server layout - self.status_image_stopped = QtGui.QImage(common.get_resource_path('images/server_stopped.png')) - self.status_image_working = QtGui.QImage(common.get_resource_path('images/server_working.png')) - self.status_image_started = QtGui.QImage(common.get_resource_path('images/server_started.png')) - self.status_image_label = QtWidgets.QLabel() - self.status_image_label.setFixedWidth(30) self.server_button = QtWidgets.QPushButton() self.server_button.clicked.connect(self.server_button_clicked) - server_layout = QtWidgets.QHBoxLayout() - server_layout.addWidget(self.status_image_label) - server_layout.addWidget(self.server_button) # URL layout url_font = QtGui.QFont() @@ -120,7 +112,7 @@ class ServerStatus(QtWidgets.QWidget): # Add the widgets layout = QtWidgets.QVBoxLayout() - layout.addLayout(server_layout) + layout.addWidget(self.server_button) layout.addLayout(url_layout) layout.addWidget(self.server_shutdown_timeout_container) self.setLayout(layout) @@ -155,14 +147,6 @@ class ServerStatus(QtWidgets.QWidget): """ Update the GUI elements based on the current state. """ - # Set the status image - if self.status == self.STATUS_STOPPED: - self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_stopped)) - elif self.status == self.STATUS_WORKING: - self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_working)) - elif self.status == self.STATUS_STARTED: - self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_started)) - # Set the URL fields if self.status == self.STATUS_STARTED: self.url_description.show() diff --git a/share/locale/en.json b/share/locale/en.json index c53311fc..4f4af3ee 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -136,5 +136,8 @@ "persistent_url_in_use": "This share is using a persistent address", "gui_url_description": "Anyone with this link can download your files using Tor Browser:", "gui_url_label": "Your Download Address", - "gui_url_label_one_time": "Your One-Time Download Address" + "gui_url_label_one_time": "Your One-Time Download Address", + "gui_status_indicator_stopped": "Stopped", + "gui_status_indicator_working": "Working...", + "gui_status_indicator_started": "Running" } From 0fc250fc55e08410b8e9a19ba9fc407ec6281d31 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 6 Feb 2018 19:47:04 -0800 Subject: [PATCH 040/123] Update the status indicator strings --- share/locale/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/locale/en.json b/share/locale/en.json index 4f4af3ee..0c536344 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -137,7 +137,7 @@ "gui_url_description": "Anyone with this link can download your files using Tor Browser:", "gui_url_label": "Your Download Address", "gui_url_label_one_time": "Your One-Time Download Address", - "gui_status_indicator_stopped": "Stopped", - "gui_status_indicator_working": "Working...", - "gui_status_indicator_started": "Running" + "gui_status_indicator_stopped": "Ready to Share", + "gui_status_indicator_working": "Starting...", + "gui_status_indicator_started": "Sharing" } From 6cff5e06b850eca4d84e8f4ffc31734b41193670 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 7 Feb 2018 15:59:13 +1100 Subject: [PATCH 041/123] allow word wrap of URL QLabels, and set MinimumHeight so they don't get shrunk in the layout --- onionshare_gui/server_status.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 3f7e390c..1aff79f4 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -84,10 +84,14 @@ class ServerStatus(QtWidgets.QWidget): # URL layout url_font = QtGui.QFont() self.url_description = QtWidgets.QLabel(strings._('gui_url_description', True)) + self.url_description.setWordWrap(True) + self.url_description.setMinimumHeight(50) self.url_label = QtWidgets.QLabel() self.url_label.setStyleSheet('QLabel { color: #666666; font-size: 12px; }') self.url = QtWidgets.QLabel() self.url.setFont(url_font) + self.url.setWordWrap(True) + self.url.setMinimumHeight(60) self.url.setStyleSheet('QLabel { background-color: #ffffff; color: #000000; padding: 10px; border: 1px solid #666666; }') url_buttons_style = 'QPushButton { color: #3f7fcf; }' From cf6d11816ffeb66782cd9b3ed117e538e61b70c1 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 7 Feb 2018 16:00:48 +1100 Subject: [PATCH 042/123] Call update_primary_action when server is stopped. Because it runs adjustSize(), it will shrink the window back down to a sane size once the URL label widgets get hidden, instead of growing the FileList widget to fill up the space --- onionshare_gui/onionshare_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index b997a8b7..39e43560 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -77,6 +77,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.server_status.server_stopped.connect(self.file_selection.server_stopped) self.server_status.server_stopped.connect(self.stop_server) self.server_status.server_stopped.connect(self.update_server_status_indicator) + self.server_status.server_stopped.connect(self.update_primary_action) self.start_server_finished.connect(self.clear_message) self.start_server_finished.connect(self.server_status.start_server_finished) self.start_server_finished.connect(self.update_server_status_indicator) From 0b18129947478312909822e29029189ec5ed3fb7 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 7 Feb 2018 16:40:41 +1100 Subject: [PATCH 043/123] Set the File List widget to readonly while running, so items can't be deleted mid-share --- onionshare_gui/server_status.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 3f7e390c..32b1aa32 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -178,6 +178,12 @@ class ServerStatus(QtWidgets.QWidget): self.copy_url_button.hide() self.copy_hidservauth_button.hide() + # Set the File List widget to readonly while running, so items can't be deleted mid-share + if self.status == self.STATUS_STARTED or self.status == self.STATUS_WORKING: + self.file_selection.file_list.setEnabled(False) + else: + self.file_selection.file_list.setEnabled(True) + # Button button_stopped_style = 'QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; }' button_working_style = 'QPushButton { background-color: #4c8211; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; font-style: italic; }' From 2b15020e7dd71ebd30a0df709cd1f4b83250c9b6 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 7 Feb 2018 17:34:36 +1100 Subject: [PATCH 044/123] Leave the FileList enabled, but hide the item buttons when the server is working or started --- onionshare_gui/file_selection.py | 12 ++++++------ onionshare_gui/server_status.py | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 0ba94b51..22f9be09 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -205,16 +205,16 @@ class FileList(QtWidgets.QListWidget): self.takeItem(itemrow) self.files_updated.emit() - item_button = QtWidgets.QPushButton() - item_button.setDefault(False) - item_button.setFlat(True) - item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) ) - item_button.clicked.connect(delete_item) + item.item_button = QtWidgets.QPushButton() + item.item_button.setDefault(False) + item.item_button.setFlat(True) + item.item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) ) + item.item_button.clicked.connect(delete_item) # Create an item widget to display on the item item_widget_layout = QtWidgets.QHBoxLayout() item_widget_layout.addStretch() - item_widget_layout.addWidget(item_button) + item_widget_layout.addWidget(item.item_button) item_widget = QtWidgets.QWidget() item_widget.setLayout(item_widget_layout) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 32b1aa32..1ebe4a2a 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -180,9 +180,11 @@ class ServerStatus(QtWidgets.QWidget): # Set the File List widget to readonly while running, so items can't be deleted mid-share if self.status == self.STATUS_STARTED or self.status == self.STATUS_WORKING: - self.file_selection.file_list.setEnabled(False) + for index in range(self.file_selection.file_list.count()): + self.file_selection.file_list.item(index).item_button.hide() else: - self.file_selection.file_list.setEnabled(True) + for index in range(self.file_selection.file_list.count()): + self.file_selection.file_list.item(index).item_button.show() # Button button_stopped_style = 'QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; }' From cdf14e2600823b36fcec50ef7cb3dc4af4b98e44 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 7 Feb 2018 19:18:20 +1100 Subject: [PATCH 045/123] Fix comment to reflect what we're doing with the file list buttons --- onionshare_gui/server_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 1ebe4a2a..036eba35 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -178,7 +178,7 @@ class ServerStatus(QtWidgets.QWidget): self.copy_url_button.hide() self.copy_hidservauth_button.hide() - # Set the File List widget to readonly while running, so items can't be deleted mid-share + # Hide the FileList delete buttons when a share is running if self.status == self.STATUS_STARTED or self.status == self.STATUS_WORKING: for index in range(self.file_selection.file_list.count()): self.file_selection.file_list.item(index).item_button.hide() From 4639420dfcf7a555871d495c901e1edc375cebfb Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 7 Feb 2018 09:16:55 -0800 Subject: [PATCH 046/123] Refactor what happens to FileList when the server starts or stops, and also prevent selections when the server starts --- onionshare_gui/file_selection.py | 26 ++++++++++++++++++++++---- onionshare_gui/server_status.py | 8 -------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 22f9be09..9198ebc6 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -101,6 +101,25 @@ class FileList(QtWidgets.QListWidget): self.drop_here_image.hide() self.drop_here_text.hide() + def server_started(self): + """ + Update the GUI when the server starts, by hiding delete buttons. + """ + self.setAcceptDrops(False) + self.setCurrentItem(None) + self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + for index in range(self.count()): + self.item(index).item_button.hide() + + def server_stopped(self): + """ + Update the GUI when the server stops, by showing delete buttons. + """ + self.file_list.setAcceptDrops(True) + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + for index in range(self.count()): + self.item(index).item_button.show() + def resizeEvent(self, event): """ When the widget is resized, resize the drop files image and text. @@ -267,7 +286,6 @@ class FileSelection(QtWidgets.QVBoxLayout): # Delete button should be hidden if item isn't selected current_item = self.file_list.currentItem() - common.log('FileSelection', 'current_item: {}'.format(current_item)) if not current_item: self.delete_button.hide() else: @@ -298,7 +316,7 @@ class FileSelection(QtWidgets.QVBoxLayout): self.file_list.filenames.pop(itemrow) self.file_list.takeItem(itemrow) self.file_list.files_updated.emit() - + self.file_list.setCurrentItem(None) self.update() @@ -307,7 +325,7 @@ class FileSelection(QtWidgets.QVBoxLayout): Gets called when the server starts. """ self.server_on = True - self.file_list.setAcceptDrops(False) + self.file_list.server_started() self.update() def server_stopped(self): @@ -315,7 +333,7 @@ class FileSelection(QtWidgets.QVBoxLayout): Gets called when the server stops. """ self.server_on = False - self.file_list.setAcceptDrops(True) + self.file_list.server_stopped() self.update() def get_num_files(self): diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 78f171d0..1aff79f4 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -182,14 +182,6 @@ class ServerStatus(QtWidgets.QWidget): self.copy_url_button.hide() self.copy_hidservauth_button.hide() - # Hide the FileList delete buttons when a share is running - if self.status == self.STATUS_STARTED or self.status == self.STATUS_WORKING: - for index in range(self.file_selection.file_list.count()): - self.file_selection.file_list.item(index).item_button.hide() - else: - for index in range(self.file_selection.file_list.count()): - self.file_selection.file_list.item(index).item_button.show() - # Button button_stopped_style = 'QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; }' button_working_style = 'QPushButton { background-color: #4c8211; color: #ffffff; padding: 10px; border: 0; border-radius: 5px; font-style: italic; }' From 23668baa09e44f8c37558659543d4d5ef3a34a93 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 7 Feb 2018 09:48:34 -0800 Subject: [PATCH 047/123] Fix bug when stopping server --- onionshare_gui/file_selection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 9198ebc6..d52fd017 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -115,7 +115,7 @@ class FileList(QtWidgets.QListWidget): """ Update the GUI when the server stops, by showing delete buttons. """ - self.file_list.setAcceptDrops(True) + self.setAcceptDrops(True) self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) for index in range(self.count()): self.item(index).item_button.show() From 05633673fc8a97461c3264e76a3ba753556f064d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 7 Feb 2018 09:55:55 -0800 Subject: [PATCH 048/123] Move auto-stop timer into settings, so its UI does not show up unless the user specifically enables that feature --- onionshare/settings.py | 1 + onionshare_gui/onionshare_gui.py | 3 ++ onionshare_gui/server_status.py | 69 +++++++++++-------------------- onionshare_gui/settings_dialog.py | 13 ++++++ share/locale/en.json | 2 +- 5 files changed, 43 insertions(+), 45 deletions(-) diff --git a/onionshare/settings.py b/onionshare/settings.py index 94b5dde5..8210027d 100644 --- a/onionshare/settings.py +++ b/onionshare/settings.py @@ -58,6 +58,7 @@ class Settings(object): 'auth_password': '', 'close_after_first_download': True, 'systray_notifications': True, + 'shutdown_timeout': False, 'use_stealth': False, 'use_autoupdate': True, 'autoupdate_timestamp': None, diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 39e43560..7fdc3491 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -297,6 +297,9 @@ class OnionShareGui(QtWidgets.QMainWindow): d.settings_saved.connect(reload_settings) d.exec_() + # When settings close, refresh the server status UI + self.server_status.update() + def start_server(self): """ Start the onionshare server. This uses multiple threads to start the Tor onion diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 1aff79f4..786deb64 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -51,31 +51,22 @@ class ServerStatus(QtWidgets.QWidget): self.timer_enabled = False # Shutdown timeout layout - self.server_shutdown_timeout_checkbox = QtWidgets.QCheckBox() - self.server_shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.server_shutdown_timeout_checkbox.toggled.connect(self.shutdown_timeout_toggled) - self.server_shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_choice", True)) - self.server_shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) - self.server_shutdown_timeout = QtWidgets.QDateTimeEdit() - + self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) + self.shutdown_timeout = QtWidgets.QDateTimeEdit() # Set proposed timeout to be 5 minutes into the future - self.server_shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) - + self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) # Onion services can take a little while to start, so reduce the risk of it expiring too soon by setting the minimum to 2 min from now - self.server_shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) - self.server_shutdown_timeout.setCurrentSectionIndex(4) - self.server_shutdown_timeout_label.hide() - self.server_shutdown_timeout.hide() + self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) + self.shutdown_timeout.setCurrentSectionIndex(4) shutdown_timeout_layout = QtWidgets.QHBoxLayout() - shutdown_timeout_layout.addWidget(self.server_shutdown_timeout_label) - shutdown_timeout_layout.addWidget(self.server_shutdown_timeout) + shutdown_timeout_layout.addWidget(self.shutdown_timeout_label) + shutdown_timeout_layout.addWidget(self.shutdown_timeout) # Shutdown timeout container, so it can all be hidden and shown as a group shutdown_timeout_container_layout = QtWidgets.QVBoxLayout() - shutdown_timeout_container_layout.addWidget(self.server_shutdown_timeout_checkbox) shutdown_timeout_container_layout.addLayout(shutdown_timeout_layout) - self.server_shutdown_timeout_container = QtWidgets.QWidget() - self.server_shutdown_timeout_container.setLayout(shutdown_timeout_container_layout) + self.shutdown_timeout_container = QtWidgets.QWidget() + self.shutdown_timeout_container.setLayout(shutdown_timeout_container_layout) # Server layout self.server_button = QtWidgets.QPushButton() @@ -118,7 +109,7 @@ class ServerStatus(QtWidgets.QWidget): layout = QtWidgets.QVBoxLayout() layout.addWidget(self.server_button) layout.addLayout(url_layout) - layout.addWidget(self.server_shutdown_timeout_container) + layout.addWidget(self.shutdown_timeout_container) self.setLayout(layout) self.update() @@ -130,22 +121,21 @@ class ServerStatus(QtWidgets.QWidget): if checked: self.timer_enabled = True # Hide the checkbox, show the options - self.server_shutdown_timeout_label.show() + self.shutdown_timeout_label.show() # Reset the default timer to 5 minutes into the future after toggling the option on - self.server_shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) - self.server_shutdown_timeout.show() + self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) + self.shutdown_timeout.show() else: self.timer_enabled = False - self.server_shutdown_timeout_label.hide() - self.server_shutdown_timeout.hide() + self.shutdown_timeout_label.hide() + self.shutdown_timeout.hide() def shutdown_timeout_reset(self): """ Reset the timeout in the UI after stopping a share """ - self.server_shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.server_shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) - self.server_shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) + self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) + self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) def update(self): """ @@ -191,40 +181,31 @@ class ServerStatus(QtWidgets.QWidget): else: self.server_button.show() - if self.status == self.STATUS_STOPPED: - self.server_shutdown_timeout_checkbox.show() - self.server_shutdown_timeout_container.show() + if self.settings.get('shutdown_timeout'): + self.shutdown_timeout_container.show() else: - self.server_shutdown_timeout_checkbox.hide() - if self.server_shutdown_timeout_checkbox.isChecked(): - self.server_shutdown_timeout_container.show() - else: - self.server_shutdown_timeout_container.hide() + self.shutdown_timeout_container.hide() if self.status == self.STATUS_STOPPED: self.server_button.setStyleSheet(button_stopped_style) self.server_button.setEnabled(True) self.server_button.setText(strings._('gui_start_server', True)) - self.server_shutdown_timeout.setEnabled(True) - self.server_shutdown_timeout_checkbox.setEnabled(True) + self.shutdown_timeout.setEnabled(True) elif self.status == self.STATUS_STARTED: self.server_button.setStyleSheet(button_started_style) self.server_button.setEnabled(True) self.server_button.setText(strings._('gui_stop_server', True)) - self.server_shutdown_timeout.setEnabled(False) - self.server_shutdown_timeout_checkbox.setEnabled(False) + self.shutdown_timeout.setEnabled(False) elif self.status == self.STATUS_WORKING: self.server_button.setStyleSheet(button_working_style) self.server_button.setEnabled(False) self.server_button.setText(strings._('gui_please_wait')) - self.server_shutdown_timeout.setEnabled(False) - self.server_shutdown_timeout_checkbox.setEnabled(False) + self.shutdown_timeout.setEnabled(False) else: self.server_button.setStyleSheet(button_working_style) self.server_button.setEnabled(False) self.server_button.setText(strings._('gui_please_wait')) - self.server_shutdown_timeout.setEnabled(False) - self.server_shutdown_timeout_checkbox.setEnabled(False) + self.shutdown_timeout.setEnabled(False) def server_button_clicked(self): """ @@ -233,7 +214,7 @@ class ServerStatus(QtWidgets.QWidget): if self.status == self.STATUS_STOPPED: if self.timer_enabled: # Get the timeout chosen, stripped of its seconds. This prevents confusion if the share stops at (say) 37 seconds past the minute chosen - self.timeout = self.server_shutdown_timeout.dateTime().toPyDateTime().replace(second=0, microsecond=0) + self.timeout = self.shutdown_timeout.dateTime().toPyDateTime().replace(second=0, microsecond=0) # If the timeout has actually passed already before the user hit Start, refuse to start the server. if QtCore.QDateTime.currentDateTime().toPyDateTime() > self.timeout: Alert(strings._('gui_server_timeout_expired', QtWidgets.QMessageBox.Warning)) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index c1b1604c..d105ea42 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -60,6 +60,11 @@ class SettingsDialog(QtWidgets.QDialog): self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Checked) self.systray_notifications_checkbox.setText(strings._("gui_settings_systray_notifications", True)) + # Whether or not to use a shutdown timer + self.shutdown_timeout_checkbox = QtWidgets.QCheckBox() + self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) + self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True)) + # Whether or not to save the Onion private key for reuse self.save_private_key_checkbox = QtWidgets.QCheckBox() self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) @@ -69,6 +74,7 @@ class SettingsDialog(QtWidgets.QDialog): sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout.addWidget(self.close_after_first_download_checkbox) sharing_group_layout.addWidget(self.systray_notifications_checkbox) + sharing_group_layout.addWidget(self.shutdown_timeout_checkbox) sharing_group_layout.addWidget(self.save_private_key_checkbox) sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True)) sharing_group.setLayout(sharing_group_layout) @@ -352,6 +358,12 @@ class SettingsDialog(QtWidgets.QDialog): else: self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Unchecked) + shutdown_timeout = self.old_settings.get('shutdown_timeout') + if shutdown_timeout: + self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked) + else: + self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Unchecked) + save_private_key = self.old_settings.get('save_private_key') if save_private_key: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) @@ -684,6 +696,7 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked()) settings.set('systray_notifications', self.systray_notifications_checkbox.isChecked()) + settings.set('shutdown_timeout', self.shutdown_timeout_checkbox.isChecked()) if self.save_private_key_checkbox.isChecked(): settings.set('save_private_key', True) settings.set('private_key', self.old_settings.get('private_key')) diff --git a/share/locale/en.json b/share/locale/en.json index 28bc11b9..5473a88f 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -102,7 +102,7 @@ "gui_settings_button_save": "Save", "gui_settings_button_cancel": "Cancel", "gui_settings_button_help": "Help", - "gui_settings_shutdown_timeout_choice": "Set auto-stop timer?", + "gui_settings_shutdown_timeout_checkbox": "Use auto-stop timer", "gui_settings_shutdown_timeout": "Stop the share at:", "settings_saved": "Settings saved to {}", "settings_error_unknown": "Can't connect to Tor controller because the settings don't make sense.", From af1b56e6593f4b58477de2f291f3f1dbc809a6c4 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 7 Feb 2018 10:54:53 -0800 Subject: [PATCH 049/123] Fix settings test, because of adding a new setting --- test/test_onionshare_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_onionshare_settings.py b/test/test_onionshare_settings.py index b17ce30a..9bf0e108 100644 --- a/test/test_onionshare_settings.py +++ b/test/test_onionshare_settings.py @@ -55,6 +55,7 @@ class TestSettings: 'auth_password': '', 'close_after_first_download': True, 'systray_notifications': True, + 'shutdown_timeout': False, 'use_stealth': False, 'use_autoupdate': True, 'autoupdate_timestamp': None, From 749ca6312d74530bb4926a4f725e0295464c78c2 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 8 Feb 2018 11:32:34 +1100 Subject: [PATCH 050/123] Fix shutdown timer and insert larger messages as word-wrapped widgets into the Status Bar --- onionshare_gui/onionshare_gui.py | 33 +++++++++++++++++++++++++++----- onionshare_gui/server_status.py | 21 +------------------- share/locale/en.json | 2 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 7fdc3491..60788c36 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -139,6 +139,10 @@ class OnionShareGui(QtWidgets.QMainWindow): # Status bar, zip progress bar self._zip_progress_bar = None + # Status bar, other larger messages + self._close_on_timeout_label = None + self._closing_automatically_label = None + self._timeout_download_still_running_label = None # Persistent URL notification self.persistent_url_label = QtWidgets.QLabel(strings._('persistent_url_in_use', True)) @@ -315,6 +319,16 @@ class OnionShareGui(QtWidgets.QMainWindow): self.downloads_container.hide() self.downloads.reset_downloads() self.status_bar.clearMessage() + # Remove any other widgets from the statusBar + if self._close_on_timeout_label is not None: + self.status_bar.removeWidget(self._close_on_timeout_label) + self._close_on_timeout_label = None + if self._closing_automatically_label is not None: + self.status_bar.removeWidget(self._closing_automatically_label) + self._closing_automatically_label = None + if self._timeout_download_still_running_label is not None: + self.status_bar.removeWidget(self._timeout_download_still_running_label) + self._timeout_download_still_running_label = None # Reset web counters web.download_count = 0 @@ -396,7 +410,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.filesize_warning.setText(strings._("large_filesize", True)) self.filesize_warning.show() - if self.server_status.timer_enabled: + if self.settings.get('shutdown_timeout'): # Convert the date value to seconds between now and then now = QtCore.QDateTime.currentDateTime() self.timeout = now.secsTo(self.server_status.timeout) @@ -529,7 +543,10 @@ class OnionShareGui(QtWidgets.QMainWindow): # close on finish? if not web.get_stay_open(): self.server_status.stop_server() - self.status_bar.showMessage(strings._('closing_automatically', True)) + self._closing_automatically_label = QtWidgets.QLabel(strings._('closing_automatically', True)) + self._closing_automatically_label.setWordWrap(True) + self.status_bar.clearMessage() + self.status_bar.insertWidget(0, self._closing_automatically_label) else: if self.server_status.status == self.server_status.STATUS_STOPPED: self.downloads.cancel_download(event["data"]["id"]) @@ -544,16 +561,22 @@ class OnionShareGui(QtWidgets.QMainWindow): # If the auto-shutdown timer has stopped, stop the server if self.server_status.status == self.server_status.STATUS_STARTED: - if self.app.shutdown_timer and self.server_status.timer_enabled: + if self.app.shutdown_timer and self.settings.get('shutdown_timeout'): if self.timeout > 0: if not self.app.shutdown_timer.is_alive(): # If there were no attempts to download the share, or all downloads are done, we can stop if web.download_count == 0 or web.done: self.server_status.stop_server() - self.status_bar.showMessage(strings._('close_on_timeout', True)) + self._close_on_timeout_label = QtWidgets.QLabel(strings._('close_on_timeout', True)) + self._close_on_timeout_label.setWordWrap(True) + self.status_bar.clearMessage() + self.status_bar.insertWidget(0, self._close_on_timeout_label) # A download is probably still running - hold off on stopping the share else: - self.status_bar.showMessage(strings._('timeout_download_still_running', True)) + self._timeout_download_still_running_label = QtWidgets.QLabel(strings._('timeout_download_still_running', True)) + self._timeout_download_still_running_label.setWordWrap(True) + self.status_bar.clearMessage() + self.status_bar.insertWidget(0, self._timeout_download_still_running_label) def copy_url(self): """ diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 786deb64..adf064fb 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -47,9 +47,6 @@ class ServerStatus(QtWidgets.QWidget): self.settings = settings - # Helper boolean as this is used in a few places - self.timer_enabled = False - # Shutdown timeout layout self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) self.shutdown_timeout = QtWidgets.QDateTimeEdit() @@ -114,22 +111,6 @@ class ServerStatus(QtWidgets.QWidget): self.update() - def shutdown_timeout_toggled(self, checked): - """ - Shutdown timer option was toggled. If checked, show the timer settings. - """ - if checked: - self.timer_enabled = True - # Hide the checkbox, show the options - self.shutdown_timeout_label.show() - # Reset the default timer to 5 minutes into the future after toggling the option on - self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) - self.shutdown_timeout.show() - else: - self.timer_enabled = False - self.shutdown_timeout_label.hide() - self.shutdown_timeout.hide() - def shutdown_timeout_reset(self): """ Reset the timeout in the UI after stopping a share @@ -212,7 +193,7 @@ class ServerStatus(QtWidgets.QWidget): Toggle starting or stopping the server. """ if self.status == self.STATUS_STOPPED: - if self.timer_enabled: + if self.settings.get('shutdown_timeout'): # Get the timeout chosen, stripped of its seconds. This prevents confusion if the share stops at (say) 37 seconds past the minute chosen self.timeout = self.shutdown_timeout.dateTime().toPyDateTime().replace(second=0, microsecond=0) # If the timeout has actually passed already before the user hit Start, refuse to start the server. diff --git a/share/locale/en.json b/share/locale/en.json index 5473a88f..5e08e57c 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -15,7 +15,7 @@ "other_page_loaded": "Address loaded", "close_on_timeout": "Closing automatically because timeout was reached", "closing_automatically": "Closing automatically because download finished", - "timeout_download_still_running": "Waiting for download to complete before auto-stopping", + "timeout_download_still_running": "Waiting for download to complete before stopping", "large_filesize": "Warning: Sending large files could take hours", "error_tails_invalid_port": "Invalid value, port must be an integer", "error_tails_unknown_root": "Unknown error with Tails root process", From 4d68b1a3dda5f1eb77cb2bde5f0e712b9aeb8f42 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 8 Feb 2018 11:53:56 +1100 Subject: [PATCH 051/123] Friendlier datetime format for the shutdown timer --- onionshare_gui/server_status.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index adf064fb..79fb4d67 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -51,10 +51,11 @@ class ServerStatus(QtWidgets.QWidget): self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) self.shutdown_timeout = QtWidgets.QDateTimeEdit() # Set proposed timeout to be 5 minutes into the future + self.shutdown_timeout.setDisplayFormat("HH:mm A MMM d, yy") self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) # Onion services can take a little while to start, so reduce the risk of it expiring too soon by setting the minimum to 2 min from now self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) - self.shutdown_timeout.setCurrentSectionIndex(4) + self.shutdown_timeout.setCurrentSection(QtWidgets.QDateTimeEdit.MinuteSection) shutdown_timeout_layout = QtWidgets.QHBoxLayout() shutdown_timeout_layout.addWidget(self.shutdown_timeout_label) shutdown_timeout_layout.addWidget(self.shutdown_timeout) From 4751334ab1c46cdccdcb704f709222034001e43a Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 8 Feb 2018 12:03:40 +1100 Subject: [PATCH 052/123] Reduce the size of the closing messages so they only wrap 2 lines --- share/locale/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/locale/en.json b/share/locale/en.json index 5e08e57c..89d31c82 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -13,9 +13,9 @@ "no_available_port": "Could not start the Onion service as there was no available port.", "download_page_loaded": "Download page loaded", "other_page_loaded": "Address loaded", - "close_on_timeout": "Closing automatically because timeout was reached", - "closing_automatically": "Closing automatically because download finished", - "timeout_download_still_running": "Waiting for download to complete before stopping", + "close_on_timeout": "Stopped because timeout was reached", + "closing_automatically": "Stopped because download finished", + "timeout_download_still_running": "Waiting for download to complete", "large_filesize": "Warning: Sending large files could take hours", "error_tails_invalid_port": "Invalid value, port must be an integer", "error_tails_unknown_root": "Unknown error with Tails root process", From 3e5d51a858fff8a04d25ef183b301c6c6f239942 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 8 Feb 2018 12:25:30 +1100 Subject: [PATCH 053/123] Make status bar styling consistent --- onionshare_gui/onionshare_gui.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 60788c36..05e8b224 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -132,7 +132,17 @@ class OnionShareGui(QtWidgets.QMainWindow): # Status bar self.status_bar = QtWidgets.QStatusBar() self.status_bar.setSizeGripEnabled(False) - self.status_bar.setStyleSheet("QStatusBar::item { border: 0px; }") + statusBar_cssStyleData =""" + QStatusBar { + font-style: italic; + color: #666666; + } + + QStatusBar::item { + border: 0px; + }""" + + self.status_bar.setStyleSheet(statusBar_cssStyleData) self.status_bar.addPermanentWidget(self.server_status_indicator) self.status_bar.addPermanentWidget(self.settings_button) self.setStatusBar(self.status_bar) @@ -545,6 +555,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.server_status.stop_server() self._closing_automatically_label = QtWidgets.QLabel(strings._('closing_automatically', True)) self._closing_automatically_label.setWordWrap(True) + self._closing_automatically_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }') self.status_bar.clearMessage() self.status_bar.insertWidget(0, self._closing_automatically_label) else: @@ -568,12 +579,14 @@ class OnionShareGui(QtWidgets.QMainWindow): if web.download_count == 0 or web.done: self.server_status.stop_server() self._close_on_timeout_label = QtWidgets.QLabel(strings._('close_on_timeout', True)) + self._close_on_timeout_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }') self._close_on_timeout_label.setWordWrap(True) self.status_bar.clearMessage() self.status_bar.insertWidget(0, self._close_on_timeout_label) # A download is probably still running - hold off on stopping the share else: self._timeout_download_still_running_label = QtWidgets.QLabel(strings._('timeout_download_still_running', True)) + self._timeout_download_still_running_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }') self._timeout_download_still_running_label.setWordWrap(True) self.status_bar.clearMessage() self.status_bar.insertWidget(0, self._timeout_download_still_running_label) From d0f180fdc5bb2b498f6b7e45b5b27f839dfc321c Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 8 Feb 2018 16:02:00 +1100 Subject: [PATCH 054/123] set MinimumSize on the word-wrapped QLabels in the SettingsDialog, which prevents them getting squished when parent is resized smaller --- onionshare_gui/settings_dialog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index d105ea42..ba9b26ed 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -86,12 +86,14 @@ class SettingsDialog(QtWidgets.QDialog): stealth_details.setWordWrap(True) stealth_details.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) stealth_details.setOpenExternalLinks(True) + stealth_details.setMinimumSize(stealth_details.sizeHint()) self.stealth_checkbox = QtWidgets.QCheckBox() self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) hidservauth_details.setWordWrap(True) + hidservauth_details.setMinimumSize(hidservauth_details.sizeHint()) hidservauth_details.hide() self.hidservauth_copy_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) From bc2417c65e2a51012b5fa0e441e2998d7c6c2a7f Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 9 Feb 2018 07:55:22 +1100 Subject: [PATCH 055/123] Set the URL label to explicitly mention when persistent. Remove older Persistence warning to save clutter. Add ToolTip to explain what Persistence means --- onionshare_gui/onionshare_gui.py | 10 ---------- onionshare_gui/server_status.py | 7 ++++++- share/locale/en.json | 3 ++- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 7fdc3491..75baf3d5 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -140,16 +140,10 @@ class OnionShareGui(QtWidgets.QMainWindow): # Status bar, zip progress bar self._zip_progress_bar = None - # Persistent URL notification - self.persistent_url_label = QtWidgets.QLabel(strings._('persistent_url_in_use', True)) - self.persistent_url_label.setStyleSheet('font-weight: bold; color: #333333;') - self.persistent_url_label.hide() - # Primary action layout primary_action_layout = QtWidgets.QVBoxLayout() primary_action_layout.addWidget(self.server_status) primary_action_layout.addWidget(self.filesize_warning) - primary_action_layout.addWidget(self.persistent_url_label) primary_action_layout.addWidget(self.downloads_container) self.primary_action = QtWidgets.QWidget() self.primary_action.setLayout(primary_action_layout) @@ -409,9 +403,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.stop_server() self.start_server_error(strings._('gui_server_started_after_timeout')) - if self.settings.get('save_private_key'): - self.persistent_url_label.show() - def start_server_error(self, error): """ If there's an error when trying to start the onion service @@ -443,7 +434,6 @@ class OnionShareGui(QtWidgets.QMainWindow): # Remove ephemeral service, but don't disconnect from Tor self.onion.cleanup(stop_tor=False) self.filesize_warning.hide() - self.persistent_url_label.hide() self.stop_server_finished.emit() self.set_server_active(False) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 786deb64..ec046fa6 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -145,10 +145,15 @@ class ServerStatus(QtWidgets.QWidget): if self.status == self.STATUS_STARTED: self.url_description.show() - if self.settings.get('close_after_first_download'): + if self.settings.get('save_private_key'): + self.url_label.setText(strings._('gui_url_label_persistent', True)) + self.url_label.setToolTip(strings._('gui_url_persistence_warning', True)) + elif self.settings.get('close_after_first_download'): self.url_label.setText(strings._('gui_url_label_one_time', True)) + self.url_label.setToolTip('') else: self.url_label.setText(strings._('gui_url_label', True)) + self.url_label.setToolTip('') self.url_label.show() self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) diff --git a/share/locale/en.json b/share/locale/en.json index 5473a88f..4644d21e 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -133,10 +133,11 @@ "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", "gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved address)", - "persistent_url_in_use": "This share is using a persistent address", "gui_url_description": "Anyone with this link can download your files using Tor Browser:", "gui_url_label": "Your Download Address", + "gui_url_label_persistent": "Your Persistent Download Address (what's this?)", "gui_url_label_one_time": "Your One-Time Download Address", + "gui_url_persistence_warning": "Every share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", "gui_status_indicator_stopped": "Ready to Share", "gui_status_indicator_working": "Starting...", "gui_status_indicator_started": "Sharing" From 39954c6ee8435c1762a2679b1325e66be248bebd Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 9 Feb 2018 18:43:57 +1100 Subject: [PATCH 056/123] Allow the user to cancel a share that is still starting up --- onionshare/onion.py | 20 +++++++++------- onionshare_gui/onionshare_gui.py | 39 +++++++++++++++++++++++++++++--- onionshare_gui/server_status.py | 15 +++++++++++- share/locale/en.json | 2 +- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 28f03f2d..5b4ae1bb 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -472,8 +472,8 @@ class Onion(object): auth_cookie = list(res.client_auth.values())[0] self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) - self.settings.save() if onion_host is not None: + self.settings.save() return onion_host else: raise TorErrorProtocolError(strings._('error_tor_protocol_error')) @@ -484,13 +484,17 @@ class Onion(object): """ common.log('Onion', 'cleanup') - # Cleanup the ephemeral onion service - if self.service_id: - try: - self.c.remove_ephemeral_hidden_service(self.service_id) - except: - pass - self.service_id = None + # Cleanup the ephemeral onion services, if we have any + try: + onions = self.c.list_ephemeral_hidden_services() + for onion in onions: + try: + self.c.delete_ephemeral_hidden_service(service_id) + except: + pass + except: + pass + self.service_id = None if stop_tor: # Stop tor process diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 7fdc3491..722ba82f 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -78,6 +78,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.server_status.server_stopped.connect(self.stop_server) self.server_status.server_stopped.connect(self.update_server_status_indicator) self.server_status.server_stopped.connect(self.update_primary_action) + self.server_status.server_canceled.connect(self.cancel_server) self.start_server_finished.connect(self.clear_message) self.start_server_finished.connect(self.server_status.start_server_finished) self.start_server_finished.connect(self.update_server_status_indicator) @@ -341,9 +342,10 @@ class OnionShareGui(QtWidgets.QMainWindow): # wait for modules in thread to load, preventing a thread-related cx_Freeze crash time.sleep(0.2) - t = threading.Thread(target=start_onion_service, kwargs={'self': self}) - t.daemon = True - t.start() + common.log('OnionshareGui', 'start_server', 'Starting an onion thread') + self.t = OnionThread(function=start_onion_service, kwargs={'self': self}) + self.t.daemon = True + self.t.start() def start_server_step2(self): """ @@ -427,6 +429,14 @@ class OnionShareGui(QtWidgets.QMainWindow): self._zip_progress_bar = None self.status_bar.clearMessage() + def cancel_server(self): + """ + Cancel the server while it is preparing to start + """ + if self.t: + self.t.terminate() + self.stop_server() + def stop_server(self): """ Stop the onionshare server. @@ -667,3 +677,26 @@ class ZipProgressBar(QtWidgets.QProgressBar): self.setValue(100) else: self.setValue(0) + + +class OnionThread(QtCore.QThread): + """ + A QThread for starting our Onion Service. + By using QThread rather than threading.Thread, we are able + to call quit() or terminate() on the startup if the user + decided to cancel (in which case do not proceed with obtaining + the Onion address and starting the web server). + """ + def __init__(self, function, kwargs=None): + super(OnionThread, self).__init__() + common.log('OnionThread', '__init__') + self.function = function + if not kwargs: + self.kwargs = {} + else: + self.kwargs = kwargs + + def run(self): + common.log('OnionThread', 'run') + + self.function(**self.kwargs) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 786deb64..2715d57b 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -29,6 +29,7 @@ class ServerStatus(QtWidgets.QWidget): """ server_started = QtCore.pyqtSignal() server_stopped = QtCore.pyqtSignal() + server_canceled = QtCore.pyqtSignal() url_copied = QtCore.pyqtSignal() hidservauth_copied = QtCore.pyqtSignal() @@ -198,7 +199,7 @@ class ServerStatus(QtWidgets.QWidget): self.shutdown_timeout.setEnabled(False) elif self.status == self.STATUS_WORKING: self.server_button.setStyleSheet(button_working_style) - self.server_button.setEnabled(False) + self.server_button.setEnabled(True) self.server_button.setText(strings._('gui_please_wait')) self.shutdown_timeout.setEnabled(False) else: @@ -224,6 +225,8 @@ class ServerStatus(QtWidgets.QWidget): self.start_server() elif self.status == self.STATUS_STARTED: self.stop_server() + elif self.status == self.STATUS_WORKING: + self.cancel_server() def start_server(self): """ @@ -250,6 +253,16 @@ class ServerStatus(QtWidgets.QWidget): self.update() self.server_stopped.emit() + def cancel_server(self): + """ + Cancel the server. + """ + common.log('ServerStatus', 'cancel_server', 'Canceling the server mid-startup') + self.status = self.STATUS_WORKING + self.shutdown_timeout_reset() + self.update() + self.server_canceled.emit() + def stop_server_finished(self): """ The server has finished stopping. diff --git a/share/locale/en.json b/share/locale/en.json index 5473a88f..fd112845 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -48,7 +48,7 @@ "gui_copied_hidservauth": "Copied HidServAuth line to clipboard", "gui_starting_server1": "Starting Tor onion service...", "gui_starting_server2": "Crunching files...", - "gui_please_wait": "Please wait...", + "gui_please_wait": "Starting... Click to cancel", "error_hs_dir_cannot_create": "Cannot create onion service dir {0:s}", "error_hs_dir_not_writable": "onion service dir {0:s} is not writable", "using_ephemeral": "Starting ephemeral Tor onion service and awaiting publication", From 532d5d5cf0c1650b3bc931090ec269a23882c6dd Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 10:59:01 -0800 Subject: [PATCH 057/123] Instead of disabling the settings button when share is active, hide it, and show it again when the share stops --- install/onionshare.nsi | 2 -- onionshare_gui/onionshare_gui.py | 5 ++--- share/images/settings_inactive.png | Bin 513 -> 0 bytes 3 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 share/images/settings_inactive.png diff --git a/install/onionshare.nsi b/install/onionshare.nsi index e5f989bb..151d36d4 100644 --- a/install/onionshare.nsi +++ b/install/onionshare.nsi @@ -209,7 +209,6 @@ Section "install" File "${BINPATH}\share\images\server_stopped.png" File "${BINPATH}\share\images\server_working.png" File "${BINPATH}\share\images\settings.png" - File "${BINPATH}\share\images\settings_inactive.png" SetOutPath "$INSTDIR\share\locale" File "${BINPATH}\share\locale\cs.json" @@ -388,7 +387,6 @@ FunctionEnd Delete "$INSTDIR\share\images\server_stopped.png" Delete "$INSTDIR\share\images\server_working.png" Delete "$INSTDIR\share\images\settings.png" - Delete "$INSTDIR\share\images\settings_inactive.png" Delete "$INSTDIR\share\license.txt" Delete "$INSTDIR\share\locale\cs.json" Delete "$INSTDIR\share\locale\de.json" diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 75baf3d5..05e01530 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -569,11 +569,10 @@ class OnionShareGui(QtWidgets.QMainWindow): """ Disable the Settings button while an OnionShare server is active. """ - self.settings_button.setEnabled(not active) if active: - self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings_inactive.png')) ) + self.settings_button.hide() else: - self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) ) + self.settings_button.show() # Disable settings menu action when server is active self.settingsAction.setEnabled(not active) diff --git a/share/images/settings_inactive.png b/share/images/settings_inactive.png deleted file mode 100644 index 1b35201b804dd4bd20d3272419403735d88c983b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 513 zcmV+c0{;DpP)K z;Kz6j_tlq>CO8tqefA{;k?WlT&%iQJ1IoZcLf&6s6Zit`18hzU#(HdUAc(-oij$nv88`-M|kg zsN@k?cg}yzYz!bUvnk*xVQVUqR-FD-fMUXrerJJE&k8P^*&`G-3N`((veg{^cpby}R+Vgu;0bUW?rGyj$G(JjSOI6ktl}3l00000NkvXXu0mjf Dza!DZ From fe24c543cc1709bca43d57356eb4298dcdf2fd36 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 11:48:14 -0800 Subject: [PATCH 058/123] Added an info label to file selection, to show the total count and size --- onionshare_gui/file_selection.py | 37 ++++++++++++++++++++++++++------ onionshare_gui/onionshare_gui.py | 1 - share/locale/en.json | 3 ++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index d52fd017..317edc1a 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -207,15 +207,18 @@ class FileList(QtWidgets.QListWidget): icon = ip.icon(fileinfo) if os.path.isfile(filename): - size = common.human_readable_filesize(fileinfo.size()) + size_bytes = fileinfo.size() + size_readable = common.human_readable_filesize(size_bytes) else: - size = common.human_readable_filesize(common.dir_size(filename)) - item_name = '{0:s} ({1:s})'.format(basename, size) + size_bytes = common.dir_size(filename) + size_readable = common.human_readable_filesize(size_bytes) + item_name = '{0:s} ({1:s})'.format(basename, size_readable) # Create a new item item = QtWidgets.QListWidgetItem(item_name) - item.setToolTip(size) + item.setToolTip(size_readable) item.setIcon(icon) + item.size_bytes = size_bytes # Item's delete button def delete_item(): @@ -252,12 +255,17 @@ class FileSelection(QtWidgets.QVBoxLayout): super(FileSelection, self).__init__() self.server_on = False - # file list + # Info label + self.info_label = QtWidgets.QLabel() + self.info_label.setStyleSheet('QLabel { font-size: 12px; color: #666666; }') + + # File list self.file_list = FileList() self.file_list.currentItemChanged.connect(self.update) self.file_list.files_dropped.connect(self.update) + self.file_list.files_updated.connect(self.update) - # buttons + # Buttons self.add_button = QtWidgets.QPushButton(strings._('gui_add', True)) self.add_button.clicked.connect(self.add) self.delete_button = QtWidgets.QPushButton(strings._('gui_delete', True)) @@ -267,7 +275,8 @@ class FileSelection(QtWidgets.QVBoxLayout): button_layout.addWidget(self.add_button) button_layout.addWidget(self.delete_button) - # add the widgets + # Add the widgets + self.addWidget(self.info_label) self.addWidget(self.file_list) self.addLayout(button_layout) @@ -277,6 +286,20 @@ class FileSelection(QtWidgets.QVBoxLayout): """ Update the GUI elements based on the current state. """ + # Update the info label + file_count = self.file_list.count() + if file_count == 0: + self.info_label.hide() + else: + total_size_bytes = 0 + for index in range(self.file_list.count()): + item = self.file_list.item(index) + total_size_bytes += item.size_bytes + total_size_readable = common.human_readable_filesize(total_size_bytes) + + self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable)) + self.info_label.show() + # All buttons should be hidden if the server is on if self.server_on: self.add_button.hide() diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 05e01530..5d0999b2 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -182,7 +182,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.check_for_updates() def update_primary_action(self): - common.log('OnionShareGui', 'update_primary_action') # Resize window self.adjustSize() diff --git a/share/locale/en.json b/share/locale/en.json index 4644d21e..2daff998 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -140,5 +140,6 @@ "gui_url_persistence_warning": "Every share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", "gui_status_indicator_stopped": "Ready to Share", "gui_status_indicator_working": "Starting...", - "gui_status_indicator_started": "Sharing" + "gui_status_indicator_started": "Sharing", + "gui_file_info": "{} Files, {}" } From 0d40d7c3b2f3bcf0af4e42d3ee278763dc4263f8 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 12:19:55 -0800 Subject: [PATCH 059/123] Add onionshare favicon to index page --- share/html/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/html/index.html b/share/html/index.html index aa91174e..1d795908 100644 --- a/share/html/index.html +++ b/share/html/index.html @@ -2,6 +2,8 @@ OnionShare + + -

{{ filename }} ▼

-

{{ filesize_human }} (compressed)

-

This zip file contains the following contents:

+ +
+
+ +
+ +

OnionShare

+
+ - - + + + {% for info in file_info.dirs %} - - + + + {% endfor %} {% for info in file_info.files %} - - + + + {% endfor %}
TypeNameFilename Size
{{ info.basename }} + + {{ info.basename }} + {{ info.size_human }}
{{ info.basename }} + + {{ info.basename }} + {{ info.size_human }}
diff --git a/share/images/web_file.png b/share/images/web_file.png new file mode 100644 index 0000000000000000000000000000000000000000..1931aff0cb734d2182bbe14533f7dac1b1389987 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3Y)RhkE)4%caKYZ?lYt_f1s;*b z3=DinK$vl=HlH+5kiEpy*OmPd6E_2o;Ga3Gw*iGZJY5_^JdVGeev#{d0*CAAwEen& z%L3l)6bwu&=6N6O_`SuPd2aBe`n)3&n$P#VnjFUOu$ke3LuA&0t=D4hlNwuHq8?~k z$n;Fya=_VQ&xZz2?mJgi-=b^3?EKbL4AQ5OB5c(23}j zTVDQ3lq3D6XjO0ALFI;Q<&E02uWgyOO4;{y{6Usa32b%@yX#_eWKL{4(;{%puth*e z^oGm1$W%s~(p6{MKj&zDSdw&(h0{XK`efD~)*FBB_U-4^xyP$<`qQI1*7>~K&h3m| zuF;iz*`%sh+pM(zEsuPBOmff)fk&D%M7S7YuSfplIwi4qQ{e9jS!+vew=A8q?0t36 z{bvjlo?GtuuFkMx)vF8JbblrvOxj!eR=g|Nd1}z-IoX|Cu6+$SS`)z4*}Q$iB}W2K31 literal 0 HcmV?d00001 From b41b89add8d607045a26149468bcdd691dc49a22 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 18:15:44 -0800 Subject: [PATCH 064/123] Designed file list items to look better, prevent scrolling horizintally, and ensuring the delete button does not overlap the filename --- onionshare_gui/file_selection.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 317edc1a..72ab9c3b 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -212,14 +212,20 @@ class FileList(QtWidgets.QListWidget): else: size_bytes = common.dir_size(filename) size_readable = common.human_readable_filesize(size_bytes) - item_name = '{0:s} ({1:s})'.format(basename, size_readable) # Create a new item - item = QtWidgets.QListWidgetItem(item_name) - item.setToolTip(size_readable) + item = QtWidgets.QListWidgetItem() item.setIcon(icon) item.size_bytes = size_bytes + # Item's name and size labels + item_name = QtWidgets.QLabel(basename) + item_name.setWordWrap(False) + item_name.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed) + item_name.setStyleSheet('QLabel { color: #000000; font-size: 13px; }') + item_size = QtWidgets.QLabel(size_readable) + item_size.setStyleSheet('QLabel { color: #666666; font-size: 11px; }') + # Item's delete button def delete_item(): itemrow = self.row(item) @@ -232,16 +238,22 @@ class FileList(QtWidgets.QListWidget): item.item_button.setFlat(True) item.item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) ) item.item_button.clicked.connect(delete_item) + item.item_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - # Create an item widget to display on the item - item_widget_layout = QtWidgets.QHBoxLayout() - item_widget_layout.addStretch() - item_widget_layout.addWidget(item.item_button) - item_widget = QtWidgets.QWidget() - item_widget.setLayout(item_widget_layout) + # Create the item's widget and layouts + item_vlayout = QtWidgets.QVBoxLayout() + item_vlayout.addWidget(item_name) + item_vlayout.addWidget(item_size) + item_hlayout = QtWidgets.QHBoxLayout() + item_hlayout.addLayout(item_vlayout) + item_hlayout.addWidget(item.item_button) + widget = QtWidgets.QWidget() + widget.setLayout(item_hlayout) + + item.setSizeHint(widget.sizeHint()) self.addItem(item) - self.setItemWidget(item, item_widget) + self.setItemWidget(item, widget) self.files_updated.emit() From feeb1d75757bdb809b352bc4100274314aed8e6f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 18:25:31 -0800 Subject: [PATCH 065/123] Left-align the client-side file list --- share/html/index.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/share/html/index.html b/share/html/index.html index ad5f342d..3b4d5e70 100644 --- a/share/html/index.html +++ b/share/html/index.html @@ -89,8 +89,8 @@ margin-right: 0.5rem; } - table.file-list td:first-child, table.file-list td:last-child { - width: 50%; + table.file-list td:last-child { + width: 100%; } @@ -111,14 +111,12 @@ - {% for info in file_info.dirs %} - -
Filename Size
{{ info.basename }} @@ -129,7 +127,6 @@ {% endfor %} {% for info in file_info.files %}
{{ info.basename }} From dbe0210104163de0f28f0cadd8b89d20e666d71f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 18:36:38 -0800 Subject: [PATCH 066/123] Make the drop count adjust size based on its sizeHint --- onionshare_gui/file_selection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index 72ab9c3b..b097e501 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -145,7 +145,8 @@ class FileList(QtWidgets.QListWidget): count = len(event.mimeData().urls()) self.drop_count.setText('+{}'.format(count)) - self.drop_count.setGeometry(self.width() - 60, self.height() - 40, 50, 30) + size_hint = self.drop_count.sizeHint() + self.drop_count.setGeometry(self.width() - size_hint.width() - 10, self.height() - size_hint.height() - 10, size_hint.width(), size_hint.height()) self.drop_count.show() event.accept() else: From 2fb8d1f836db234d41e5d131c1f62518db425d05 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 10 Feb 2018 21:27:45 -0800 Subject: [PATCH 067/123] Tweak css --- share/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/html/index.html b/share/html/index.html index 3b4d5e70..b274d003 100644 --- a/share/html/index.html +++ b/share/html/index.html @@ -81,7 +81,7 @@ table.file-list td { white-space: nowrap; - padding: 0.5rem 10rem 0.5rem 0.5rem; + padding: 0.5rem 10rem 0.5rem 0.8rem; } table.file-list td img { From afc9568f30b45a838192f1654f06485c38fb28b7 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 11 Feb 2018 17:16:52 +1100 Subject: [PATCH 068/123] Adjust width of window to fit status bar messages, unwrapped, to avoid squishing widgets. Add pluralisation of filemanager total file(s) Use 'timer expired' rather than 'timeout reached' --- onionshare_gui/file_selection.py | 5 ++++- onionshare_gui/onionshare_gui.py | 12 ++++++------ onionshare_gui/server_status.py | 1 + share/locale/en.json | 5 +++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index b097e501..ba3df69d 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -310,7 +310,10 @@ class FileSelection(QtWidgets.QVBoxLayout): total_size_bytes += item.size_bytes total_size_readable = common.human_readable_filesize(total_size_bytes) - self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable)) + if file_count > 1: + self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable)) + else: + self.info_label.setText(strings._('gui_file_info_single', True).format(file_count, total_size_readable)) self.info_label.show() # All buttons should be hidden if the server is on diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 946000e9..f52281de 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -56,7 +56,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowTitle('OnionShare') self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png'))) - self.setMinimumWidth(350) + self.setMinimumWidth(430) # Load settings self.config = config @@ -152,7 +152,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self._zip_progress_bar = None # Status bar, sharing messages self.server_share_status_label = QtWidgets.QLabel('') - self.server_share_status_label.setWordWrap(True) self.server_share_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }') self.status_bar.insertWidget(0, self.server_share_status_label) @@ -198,15 +197,15 @@ class OnionShareGui(QtWidgets.QMainWindow): self.check_for_updates() def update_primary_action(self): - # Resize window - self.adjustSize() - # Show or hide primary action layout if self.file_selection.file_list.count() > 0: self.primary_action.show() else: self.primary_action.hide() + # Resize window + self.adjustSize() + def update_server_status_indicator(self): common.log('OnionShareGui', 'update_server_status_indicator') @@ -450,9 +449,10 @@ class OnionShareGui(QtWidgets.QMainWindow): # Remove ephemeral service, but don't disconnect from Tor self.onion.cleanup(stop_tor=False) self.filesize_warning.hide() - self.stop_server_finished.emit() + self.file_selection.file_list.adjustSize() self.set_server_active(False) + self.stop_server_finished.emit() def check_for_updates(self): """ diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 5b6fdd52..c292d207 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -81,6 +81,7 @@ class ServerStatus(QtWidgets.QWidget): self.url.setFont(url_font) self.url.setWordWrap(True) self.url.setMinimumHeight(60) + self.url.setMinimumSize(self.url.sizeHint()) self.url.setStyleSheet('QLabel { background-color: #ffffff; color: #000000; padding: 10px; border: 1px solid #666666; }') url_buttons_style = 'QPushButton { color: #3f7fcf; }' diff --git a/share/locale/en.json b/share/locale/en.json index aed92e06..3e3d06c3 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -13,7 +13,7 @@ "no_available_port": "Could not start the Onion service as there was no available port.", "download_page_loaded": "Download page loaded", "other_page_loaded": "Address loaded", - "close_on_timeout": "Stopped because timeout was reached", + "close_on_timeout": "Stopped because timer expired", "closing_automatically": "Stopped because download finished", "timeout_download_still_running": "Waiting for download to complete", "large_filesize": "Warning: Sending large files could take hours", @@ -141,5 +141,6 @@ "gui_status_indicator_stopped": "Ready to Share", "gui_status_indicator_working": "Starting...", "gui_status_indicator_started": "Sharing", - "gui_file_info": "{} Files, {}" + "gui_file_info": "{} Files, {}", + "gui_file_info_single": "{} File, {}" } From 14ece5080755986b706016a156a5adc5a3c76926 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 11 Feb 2018 17:32:45 +1100 Subject: [PATCH 069/123] Clear statusBar messages when the server button is clicked --- onionshare_gui/onionshare_gui.py | 1 + onionshare_gui/server_status.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 5d0999b2..f30bdd2f 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -90,6 +90,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.starting_server_step2.connect(self.start_server_step2) self.starting_server_step3.connect(self.start_server_step3) self.starting_server_error.connect(self.start_server_error) + self.server_status.button_clicked.connect(self.clear_message) # Filesize warning self.filesize_warning = QtWidgets.QLabel() diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index ec046fa6..6f84f751 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -29,6 +29,7 @@ class ServerStatus(QtWidgets.QWidget): """ server_started = QtCore.pyqtSignal() server_stopped = QtCore.pyqtSignal() + button_clicked = QtCore.pyqtSignal() url_copied = QtCore.pyqtSignal() hidservauth_copied = QtCore.pyqtSignal() @@ -229,6 +230,7 @@ class ServerStatus(QtWidgets.QWidget): self.start_server() elif self.status == self.STATUS_STARTED: self.stop_server() + self.button_clicked.emit() def start_server(self): """ From 5e4a4f66131d3a1c16ad91d6ba2c6240be3f09ac Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 11 Feb 2018 17:42:07 +1100 Subject: [PATCH 070/123] Move the copy URL/HidServAuth notifications to desktop notifications instead of statusbar --- onionshare_gui/onionshare_gui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 5d0999b2..372bf719 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -367,7 +367,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.starting_server_error.emit(e.strerror) return - #self.status_bar.showMessage(strings._('gui_starting_server2', True)) t = threading.Thread(target=finish_starting_server, kwargs={'self': self}) t.daemon = True t.start() @@ -549,14 +548,16 @@ class OnionShareGui(QtWidgets.QMainWindow): When the URL gets copied to the clipboard, display this in the status bar. """ common.log('OnionShareGui', 'copy_url') - self.status_bar.showMessage(strings._('gui_copied_url', True), 2000) + if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'): + self.status_bar.showMessage(strings._('gui_copied_url', True), 2000) def copy_hidservauth(self): """ When the stealth onion service HidServAuth gets copied to the clipboard, display this in the status bar. """ common.log('OnionShareGui', 'copy_hidservauth') - self.status_bar.showMessage(strings._('gui_copied_hidservauth', True), 2000) + if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'): + self.status_bar.showMessage(strings._('gui_copied_hidservauth', True), 2000) def clear_message(self): """ From b8cf692cd8d9555a6a0c9a25d867555f7fea02fa Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 11 Feb 2018 17:52:14 +1100 Subject: [PATCH 071/123] actually show those messages in the system tray, not still in the statusbar, and with the mandatory titles --- onionshare_gui/onionshare_gui.py | 4 ++-- share/locale/en.json | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 372bf719..8494c33f 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -549,7 +549,7 @@ class OnionShareGui(QtWidgets.QMainWindow): """ common.log('OnionShareGui', 'copy_url') if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'): - self.status_bar.showMessage(strings._('gui_copied_url', True), 2000) + self.systemTray.showMessage(strings._('gui_copied_url_title', True), strings._('gui_copied_url', True)) def copy_hidservauth(self): """ @@ -557,7 +557,7 @@ class OnionShareGui(QtWidgets.QMainWindow): """ common.log('OnionShareGui', 'copy_hidservauth') if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'): - self.status_bar.showMessage(strings._('gui_copied_hidservauth', True), 2000) + self.systemTray.showMessage(strings._('gui_copied_hidservauth_title', True), strings._('gui_copied_hidservauth', True)) def clear_message(self): """ diff --git a/share/locale/en.json b/share/locale/en.json index 2daff998..2d1590da 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -44,8 +44,10 @@ "gui_copy_hidservauth": "Copy HidServAuth", "gui_downloads": "Downloads:", "gui_canceled": "Canceled", - "gui_copied_url": "Copied address to clipboard", - "gui_copied_hidservauth": "Copied HidServAuth line to clipboard", + "gui_copied_url_title": "Copied Onion address", + "gui_copied_url": "The Onion address has been copied to clipboard", + "gui_copied_hidservauth_title": "Copied HidServAuth", + "gui_copied_hidservauth": "The HidServAuth line has been copied to clipboard", "gui_starting_server1": "Starting Tor onion service...", "gui_starting_server2": "Crunching files...", "gui_please_wait": "Please wait...", From 779ca765530944863b40d8dc5a0ec78e37d2827c Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 11 Feb 2018 18:32:18 +1100 Subject: [PATCH 072/123] Set URL label tooltips --- install/onionshare.nsi | 2 ++ onionshare_gui/server_status.py | 17 ++++++++++------- share/images/info.png | Bin 0 -> 435 bytes share/locale/en.json | 9 +++++---- 4 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 share/images/info.png diff --git a/install/onionshare.nsi b/install/onionshare.nsi index 55ecdbcb..0a9e6cf2 100644 --- a/install/onionshare.nsi +++ b/install/onionshare.nsi @@ -203,6 +203,7 @@ Section "install" SetOutPath "$INSTDIR\share\images" File "${BINPATH}\share\images\favicon.ico" File "${BINPATH}\share\images\file_delete.png" + File "${BINPATH}\share\images\info.png" File "${BINPATH}\share\images\logo.png" File "${BINPATH}\share\images\logo_transparent.png" File "${BINPATH}\share\images\logo_grayscale.png" @@ -384,6 +385,7 @@ FunctionEnd Delete "$INSTDIR\share\html\index.html" Delete "$INSTDIR\share\images\favicon.ico" Delete "$INSTDIR\share\images\file_delete.png" + Delete "$INSTDIR\share\images\info.png" Delete "$INSTDIR\share\images\logo.png" Delete "$INSTDIR\share\images\logo_transparent.png" Delete "$INSTDIR\share\images\logo_grayscale.png" diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index ec046fa6..3dc29c9a 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -145,15 +145,18 @@ class ServerStatus(QtWidgets.QWidget): if self.status == self.STATUS_STARTED: self.url_description.show() + info_image = common.get_resource_path('images/info.png') + self.url_label.setText(strings._('gui_url_label', True).format(info_image)) + # Show a Tool Tip explaining the lifecycle of this URL if self.settings.get('save_private_key'): - self.url_label.setText(strings._('gui_url_label_persistent', True)) - self.url_label.setToolTip(strings._('gui_url_persistence_warning', True)) - elif self.settings.get('close_after_first_download'): - self.url_label.setText(strings._('gui_url_label_one_time', True)) - self.url_label.setToolTip('') + if self.settings.get('close_after_first_download'): + self.url_label.setToolTip(strings._('gui_url_label_onetime_and_persistent', True)) + else: + self.url_label.setToolTip(strings._('gui_url_label_persistent', True)) + if self.settings.get('close_after_first_download'): + self.url_label.setToolTip(strings._('gui_url_label_onetime', True)) else: - self.url_label.setText(strings._('gui_url_label', True)) - self.url_label.setToolTip('') + self.url_label.setToolTip(strings._('gui_url_label_stay_open', True)) self.url_label.show() self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) diff --git a/share/images/info.png b/share/images/info.png new file mode 100644 index 0000000000000000000000000000000000000000..4be4e65e25a9de5a72ab547ea0cb0a88ccb51d49 GIT binary patch literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$0wn*`OvwRKEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4UhtqZsbPuAQPYE0nReaf zHmT@qc)%3H6z;6QpqPFu(@jE>tkQj= z+fpZ;+B-ieOkj@mj*rW(m8Lz5n*8|6fu;Gv>-B7QeR`wE;kL|9*S6wN`^t#|E*E9m z%f99$>2>Wi)Bh&RnaewAN&l^+yqYERzB~gY<1iI9cv$SF6?+x zru*Z*sy5>{^Sh@6%cptHh$g5TZds;>#PL1_n=8KbLh*2~7ZLaFgNy literal 0 HcmV?d00001 diff --git a/share/locale/en.json b/share/locale/en.json index 2daff998..5f7f364d 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -134,10 +134,11 @@ "share_via_onionshare": "Share via OnionShare", "gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved address)", "gui_url_description": "Anyone with this link can download your files using Tor Browser:", - "gui_url_label": "Your Download Address", - "gui_url_label_persistent": "Your Persistent Download Address (what's this?)", - "gui_url_label_one_time": "Your One-Time Download Address", - "gui_url_persistence_warning": "Every share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", + "gui_url_label": "Your Download Address ", + "gui_url_label_persistent": "Every share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", + "gui_url_label_stay_open": "This URL will exist until you stop the share", + "gui_url_label_onetime": "This URL will only exist until the share is stopped or when the first download occurs.", + "gui_url_label_onetime_and_persistent": "This share will stop when first download occurs, but every future share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", "gui_status_indicator_stopped": "Ready to Share", "gui_status_indicator_working": "Starting...", "gui_status_indicator_started": "Sharing", From 145293d3cc3112775df3fb73f6fb5ec97fd50002 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 12 Feb 2018 13:17:28 +1100 Subject: [PATCH 073/123] Fix function name remove_ephemeral_onion_service --- onionshare/onion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 5b4ae1bb..0bf12bb3 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -489,7 +489,7 @@ class Onion(object): onions = self.c.list_ephemeral_hidden_services() for onion in onions: try: - self.c.delete_ephemeral_hidden_service(service_id) + self.c.remove_ephemeral_hidden_service(service_id) except: pass except: From 267aa7dfecab5ffbd6564024d5dfb1dbf21dbcb3 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 12 Feb 2018 13:43:34 +1100 Subject: [PATCH 074/123] Pass the correct parameter to remove_ephemeral_hidden_service() Add debug logs to verify if an onion was successfully deleted or not --- onionshare/onion.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 0bf12bb3..668de051 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -489,8 +489,10 @@ class Onion(object): onions = self.c.list_ephemeral_hidden_services() for onion in onions: try: - self.c.remove_ephemeral_hidden_service(service_id) + common.log('Onion', 'cleanup', 'trying to remove onion {}'.format(onion)) + self.c.remove_ephemeral_hidden_service(onion) except: + common.log('Onion', 'cleanup', 'could not remove onion {}.. moving on anyway'.format(onion)) pass except: pass From ab16afcae8fe2bbcf7c6133ca458f972b13266f9 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Tue, 13 Feb 2018 13:23:25 +1100 Subject: [PATCH 075/123] Replace tab with whitespace in nautilus script --- install/scripts/onionshare-nautilus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/scripts/onionshare-nautilus.py b/install/scripts/onionshare-nautilus.py index 6674dd18..ed50fb23 100644 --- a/install/scripts/onionshare-nautilus.py +++ b/install/scripts/onionshare-nautilus.py @@ -64,7 +64,7 @@ class OnionShareExtension(GObject.GObject, Nautilus.MenuProvider): """ def url2path(self,url): - file_uri = url.get_activation_uri() + file_uri = url.get_activation_uri() arg_uri = file_uri[7:] path = urllib.url2pathname(arg_uri) return path From e20e8d5181f772b77aa05ec94663fdbce5d850de Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 13 Feb 2018 17:20:10 -0800 Subject: [PATCH 076/123] Switch to 12 hour clock --- onionshare_gui/server_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index c292d207..a2701e6a 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -51,7 +51,7 @@ class ServerStatus(QtWidgets.QWidget): self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) self.shutdown_timeout = QtWidgets.QDateTimeEdit() # Set proposed timeout to be 5 minutes into the future - self.shutdown_timeout.setDisplayFormat("HH:mm A MMM d, yy") + self.shutdown_timeout.setDisplayFormat("hh:mm A MMM d, yy") self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) # Onion services can take a little while to start, so reduce the risk of it expiring too soon by setting the minimum to 2 min from now self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) From 24b025419a34bc89cf61f500989ccf6b5e026ef0 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 13 Feb 2018 17:50:10 -0800 Subject: [PATCH 077/123] Fix bug where persistent tooltips were not displaying --- onionshare_gui/server_status.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 654c4c28..1bd18e2f 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -137,10 +137,11 @@ class ServerStatus(QtWidgets.QWidget): self.url_label.setToolTip(strings._('gui_url_label_onetime_and_persistent', True)) else: self.url_label.setToolTip(strings._('gui_url_label_persistent', True)) - if self.settings.get('close_after_first_download'): - self.url_label.setToolTip(strings._('gui_url_label_onetime', True)) else: - self.url_label.setToolTip(strings._('gui_url_label_stay_open', True)) + if self.settings.get('close_after_first_download'): + self.url_label.setToolTip(strings._('gui_url_label_onetime', True)) + else: + self.url_label.setToolTip(strings._('gui_url_label_stay_open', True)) self.url_label.show() self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) From 8db921dfc24eb5276bca9b2465053e57c47cfd99 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 13 Feb 2018 17:58:51 -0800 Subject: [PATCH 078/123] Update tooltip strings, and also copy to clipboard strings --- share/locale/en.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/share/locale/en.json b/share/locale/en.json index 76c36e0f..cda00fcb 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -44,8 +44,8 @@ "gui_copy_hidservauth": "Copy HidServAuth", "gui_downloads": "Downloads:", "gui_canceled": "Canceled", - "gui_copied_url_title": "Copied Onion address", - "gui_copied_url": "The Onion address has been copied to clipboard", + "gui_copied_url_title": "Copied OnionShare address", + "gui_copied_url": "The OnionShare address has been copied to clipboard", "gui_copied_hidservauth_title": "Copied HidServAuth", "gui_copied_hidservauth": "The HidServAuth line has been copied to clipboard", "gui_starting_server1": "Starting Tor onion service...", @@ -137,10 +137,10 @@ "gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved address)", "gui_url_description": "Anyone with this link can download your files using Tor Browser:", "gui_url_label": "Your Download Address ", - "gui_url_label_persistent": "Every share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", - "gui_url_label_stay_open": "This URL will exist until you stop the share", - "gui_url_label_onetime": "This URL will only exist until the share is stopped or when the first download occurs.", - "gui_url_label_onetime_and_persistent": "This share will stop when first download occurs, but every future share will have the same URL.

If you want to go back to using one-time URLs, turn off persistence in the Settings.", + "gui_url_label_persistent": "This share will not stop automatically

Every share will have the same address (to use one-time addresses, disable persistence in the Settings)", + "gui_url_label_stay_open": "This share will not stop automatically", + "gui_url_label_onetime": "This share will stop after the first download", + "gui_url_label_onetime_and_persistent": "This share will stop after the first download

Every share will have the same address (to use one-time addresses, disable persistence in the Settings)", "gui_status_indicator_stopped": "Ready to Share", "gui_status_indicator_working": "Starting...", "gui_status_indicator_started": "Sharing", From 062101fcbfd81f33280e311937793ce48192720e Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 14 Feb 2018 16:56:03 +1100 Subject: [PATCH 079/123] Make table sortable with javascript (if enabled) --- onionshare/web.py | 2 +- share/html/index.html | 74 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/onionshare/web.py b/onionshare/web.py index 7d1b72e8..0fedb29b 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -59,7 +59,7 @@ zip_filename = None zip_filesize = None security_headers = [ - ('Content-Security-Policy', 'default-src \'self\'; style-src \'unsafe-inline\'; img-src \'self\' data:;'), + ('Content-Security-Policy', 'default-src \'self\'; style-src \'unsafe-inline\'; script-src \'unsafe-inline\'; img-src \'self\' data:;'), ('X-Frame-Options', 'DENY'), ('X-Xss-Protection', '1; mode=block'), ('X-Content-Type-Options', 'nosniff'), diff --git a/share/html/index.html b/share/html/index.html index b274d003..d5d36055 100644 --- a/share/html/index.html +++ b/share/html/index.html @@ -109,10 +109,10 @@

OnionShare

- +
- - + + {% for info in file_info.dirs %} @@ -136,5 +136,73 @@ {% endfor %}
FilenameSizeFilenameSize
+ From 7a8e828cd56768eb11e48e1dcf12eebb648dde69 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 14 Feb 2018 17:04:08 +1100 Subject: [PATCH 080/123] Fix indentation --- share/html/index.html | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/share/html/index.html b/share/html/index.html index d5d36055..f3fe8247 100644 --- a/share/html/index.html +++ b/share/html/index.html @@ -182,27 +182,27 @@ if (unhumanize(x.innerHTML.toLowerCase()) < unhumanize(y.innerHTML.toLowerCase())) { // If so, mark as a switch and break the loop: shouldSwitch= true; - break; + break; + } + } + } + if (shouldSwitch) { + /* If a switch has been marked, make the switch + and mark that a switch has been done: */ + rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); + switching = true; + // Each time a switch is done, increase this count by 1: + switchcount ++; + } else { + /* If no switching has been done AND the direction is "asc", + set the direction to "desc" and run the while loop again. */ + if (switchcount == 0 && dir == "asc") { + dir = "desc"; + switching = true; } } } - if (shouldSwitch) { - /* If a switch has been marked, make the switch - and mark that a switch has been done: */ - rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); - switching = true; - // Each time a switch is done, increase this count by 1: - switchcount ++; - } else { - /* If no switching has been done AND the direction is "asc", - set the direction to "desc" and run the while loop again. */ - if (switchcount == 0 && dir == "asc") { - dir = "desc"; - switching = true; - } - } } - } - + From 5172294dfa7156ac990c202c78cd614b6d9a15d9 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 14 Feb 2018 17:20:00 +1100 Subject: [PATCH 081/123] Account for files that are in bytes --- share/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/html/index.html b/share/html/index.html index f3fe8247..57711e02 100644 --- a/share/html/index.html +++ b/share/html/index.html @@ -139,7 +139,7 @@