From 134611a3253cc61904c10a61d3434c7bed2d2975 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 27 Nov 2020 14:50:07 -0800 Subject: [PATCH] Show a dialog while waiting for Tor rendezvous nodes to close, and let the user quit early --- cli/onionshare_cli/onion.py | 71 ++++++++++--------- desktop/src/onionshare/main_window.py | 26 ++++++- .../src/onionshare/resources/locale/en.json | 4 +- desktop/src/onionshare/threads.py | 15 ++++ 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index 08b3652a..311de936 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -729,7 +729,7 @@ class Onion(object): "Onion", "stop_onion_service", f"failed to remove {onion_host}" ) - def cleanup(self, stop_tor=True): + def cleanup(self, stop_tor=True, wait=True): """ Stop onion services that were created earlier. If there's a tor subprocess running, kill it. """ @@ -756,42 +756,45 @@ class Onion(object): if stop_tor: # Stop tor process if self.tor_proc: - # Wait for Tor rendezvous circuits to close - # Catch exceptions to prevent crash on Ctrl-C - try: - rendevouz_circuit_ids = [] - for c in self.c.get_circuits(): - if ( - c.purpose == "HS_SERVICE_REND" - and c.rend_query in self.graceful_close_onions - ): - rendevouz_circuit_ids.append(c.id) - - symbols = [c for c in "\\|/-"] - symbols_i = 0 - - while True: - num_rend_circuits = 0 + if wait: + # Wait for Tor rendezvous circuits to close + # Catch exceptions to prevent crash on Ctrl-C + try: + rendevouz_circuit_ids = [] for c in self.c.get_circuits(): - if c.id in rendevouz_circuit_ids: - num_rend_circuits += 1 + if ( + c.purpose == "HS_SERVICE_REND" + and c.rend_query in self.graceful_close_onions + ): + rendevouz_circuit_ids.append(c.id) - if num_rend_circuits == 0: - print("\rTor rendezvous circuits have closed" + " " * 20) - break + symbols = [c for c in "\\|/-"] + symbols_i = 0 - if num_rend_circuits == 1: - circuits = "circuit" - else: - circuits = "circuits" - print( - f"\rWaiting for {num_rend_circuits} Tor rendezvous {circuits} to close {symbols[symbols_i]} ", - end="", - ) - symbols_i = (symbols_i + 1) % len(symbols) - time.sleep(1) - except: - pass + while True: + num_rend_circuits = 0 + for c in self.c.get_circuits(): + if c.id in rendevouz_circuit_ids: + num_rend_circuits += 1 + + if num_rend_circuits == 0: + print( + "\rTor rendezvous circuits have closed" + " " * 20 + ) + break + + if num_rend_circuits == 1: + circuits = "circuit" + else: + circuits = "circuits" + print( + f"\rWaiting for {num_rend_circuits} Tor rendezvous {circuits} to close {symbols[symbols_i]} ", + end="", + ) + symbols_i = (symbols_i + 1) % len(symbols) + time.sleep(1) + except: + pass self.tor_proc.terminate() time.sleep(0.2) diff --git a/desktop/src/onionshare/main_window.py b/desktop/src/onionshare/main_window.py index 5a099cbc..a1b44032 100644 --- a/desktop/src/onionshare/main_window.py +++ b/desktop/src/onionshare/main_window.py @@ -30,6 +30,7 @@ from .widgets import Alert from .update_checker import UpdateThread from .tab_widget import TabWidget from .gui_common import GuiCommon +from .threads import OnionCleanupThread class MainWindow(QtWidgets.QMainWindow): @@ -287,7 +288,30 @@ class MainWindow(QtWidgets.QMainWindow): def cleanup(self): self.common.log("MainWindow", "cleanup") self.tabs.cleanup() - self.common.gui.onion.cleanup() + + alert = Alert( + self.common, + strings._("gui_rendezvous_cleanup"), + QtWidgets.QMessageBox.Information, + buttons=QtWidgets.QMessageBox.NoButton, + autostart=False, + ) + quit_early_button = QtWidgets.QPushButton( + strings._("gui_rendezvous_cleanup_quit_early") + ) + alert.addButton(quit_early_button, QtWidgets.QMessageBox.RejectRole) + + self.onion_cleanup_thread = OnionCleanupThread(self.common) + self.onion_cleanup_thread.finished.connect(alert.accept) + self.onion_cleanup_thread.start() + + alert.exec_() + if alert.clickedButton() == quit_early_button: + self.common.log("MainWindow", "cleanup", "quitting early") + if self.onion_cleanup_thread.isRunning(): + self.onion_cleanup_thread.terminate() + self.onion_cleanup_thread.wait() + self.common.gui.onion.cleanup(wait=False) # Wait 1 second for threads to close gracefully, so tests finally pass time.sleep(1) diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index 1f98d7a7..aa34fd82 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -187,5 +187,7 @@ "settings_error_unreadable_cookie_file": "Connected to the Tor controller, but password may be wrong, or your user is not permitted to read the cookie file.", "settings_error_bundled_tor_not_supported": "Using the Tor version that comes with OnionShare does not work in developer mode on Windows or macOS.", "settings_error_bundled_tor_timeout": "Taking too long to connect to Tor. Maybe you aren't connected to the Internet, or have an inaccurate system clock?", - "settings_error_bundled_tor_broken": "OnionShare could not connect to Tor:\n{}" + "settings_error_bundled_tor_broken": "OnionShare could not connect to Tor:\n{}", + "gui_rendezvous_cleanup": "Waiting for Tor rendezvous circuits to close to be sure that the files you shared downloaded successfully.\n\nThis might take a few minutes.", + "gui_rendezvous_cleanup_quit_early": "Quit Early" } \ No newline at end of file diff --git a/desktop/src/onionshare/threads.py b/desktop/src/onionshare/threads.py index d5e2dc6c..22e264e5 100644 --- a/desktop/src/onionshare/threads.py +++ b/desktop/src/onionshare/threads.py @@ -252,3 +252,18 @@ class EventHandlerThread(QtCore.QThread): if self.should_quit: break time.sleep(0.2) + + +class OnionCleanupThread(QtCore.QThread): + """ + Wait for Tor rendezvous circuits to close in a separate thread + """ + + def __init__(self, common): + super(OnionCleanupThread, self).__init__() + self.common = common + self.common.log("OnionCleanupThread", "__init__") + + def run(self): + self.common.log("OnionCleanupThread", "run") + self.common.gui.onion.cleanup()