From 3b9cc80160bf0658e6b7f908cac325f7c99b9041 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 20 Oct 2021 20:33:16 -0700 Subject: [PATCH] Create a TorConnectionWidget, and use that when testing settings --- .../src/onionshare/tor_connection_dialog.py | 128 ++++++++++++++++++ desktop/src/onionshare/tor_settings_tab.py | 38 +++++- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/desktop/src/onionshare/tor_connection_dialog.py b/desktop/src/onionshare/tor_connection_dialog.py index daf49a32..7ba7c800 100644 --- a/desktop/src/onionshare/tor_connection_dialog.py +++ b/desktop/src/onionshare/tor_connection_dialog.py @@ -157,6 +157,134 @@ class TorConnectionDialog(QtWidgets.QProgressDialog): QtCore.QTimer.singleShot(1, self.cancel) +class TorConnectionWidget(QtWidgets.QWidget): + """ + Connecting to Tor widget, with a progress bar + """ + + open_tor_settings = QtCore.Signal() + success = QtCore.Signal() + fail = QtCore.Signal() + + def __init__(self, common): + super(TorConnectionWidget, self).__init__(None) + self.common = common + self.common.log("TorConnectionWidget", "__init__") + + self.label = QtWidgets.QLabel(strings._("connecting_to_tor")) + self.label.setAlignment(QtCore.Qt.AlignHCenter) + + self.progress = QtWidgets.QProgressBar() + self.progress.setRange(0, 100) + self.cancel_button = QtWidgets.QPushButton( + strings._("gui_settings_button_cancel") + ) + self.cancel_button.clicked.connect(self.cancel_clicked) + + progress_layout = QtWidgets.QHBoxLayout() + progress_layout.addWidget(self.progress) + progress_layout.addWidget(self.cancel_button) + + inner_layout = QtWidgets.QVBoxLayout() + inner_layout.addWidget(self.label) + inner_layout.addLayout(progress_layout) + + layout = QtWidgets.QHBoxLayout() + layout.addStretch() + layout.addLayout(inner_layout) + layout.addStretch() + self.setLayout(layout) + + # Start displaying the status at 0 + self._tor_status_update(0, "") + + def start(self, custom_settings=False, testing_settings=False, onion=None): + self.common.log("TorConnectionWidget", "start") + self.was_canceled = False + + self.testing_settings = testing_settings + + if custom_settings: + self.settings = custom_settings + else: + self.settings = self.common.settings + + if self.testing_settings: + self.onion = onion + else: + self.onion = self.common.gui.onion + + t = TorConnectionThread(self.common, self.settings, self) + t.tor_status_update.connect(self._tor_status_update) + t.connected_to_tor.connect(self._connected_to_tor) + t.canceled_connecting_to_tor.connect(self._canceled_connecting_to_tor) + t.error_connecting_to_tor.connect(self._error_connecting_to_tor) + t.start() + + # The main thread needs to remain active, and checking for Qt events, + # until the thread is finished. Otherwise it won't be able to handle + # accepting signals. + self.active = True + while self.active: + time.sleep(0.1) + self.common.gui.qtapp.processEvents() + + def cancel_clicked(self): + self.was_canceled = True + self.fail.emit() + + def wasCanceled(self): + return self.was_canceled + + def _tor_status_update(self, progress, summary): + self.progress.setValue(int(progress)) + self.label.setText( + f"{strings._('connecting_to_tor')}
{summary}" + ) + + def _connected_to_tor(self): + self.common.log("TorConnectionWidget", "_connected_to_tor") + self.active = False + + # Close the dialog after connecting + self.progress.setValue(self.progress.maximum()) + + self.success.emit() + + def _canceled_connecting_to_tor(self): + self.common.log("TorConnectionWidget", "_canceled_connecting_to_tor") + self.active = False + self.onion.cleanup() + + # Cancel connecting to Tor + QtCore.QTimer.singleShot(1, self.cancel_clicked) + + def _error_connecting_to_tor(self, msg): + self.common.log("TorConnectionWidget", "_error_connecting_to_tor") + self.active = False + + if self.testing_settings: + # If testing, just display the error but don't open settings + def alert(): + Alert(self.common, msg, QtWidgets.QMessageBox.Warning, title=self.title) + + else: + # If not testing, open settings after displaying the error + def alert(): + Alert( + self.common, + f"{msg}\n\n{strings._('gui_tor_connection_error_settings')}", + QtWidgets.QMessageBox.Warning, + title=self.title, + ) + + # Open settings + self.open_tor_settings.emit() + + QtCore.QTimer.singleShot(1, alert) + self.fail.emit() + + class TorConnectionThread(QtCore.QThread): tor_status_update = QtCore.Signal(str, str) connected_to_tor = QtCore.Signal() diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index e46fa729..4b84e923 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -29,7 +29,7 @@ from onionshare_cli.onion import Onion from . import strings from .widgets import Alert -from .tor_connection_dialog import TorConnectionDialog +from .tor_connection_dialog import TorConnectionDialog, TorConnectionWidget from .moat_dialog import MoatDialog from .gui_common import GuiCommon @@ -291,6 +291,7 @@ class TorSettingsTab(QtWidgets.QWidget): connection_type_radio_group_layout.addWidget( self.connection_type_socket_file_radio ) + connection_type_radio_group_layout.addStretch() connection_type_radio_group = QtWidgets.QGroupBox( strings._("gui_settings_connection_type_label") ) @@ -311,6 +312,17 @@ class TorSettingsTab(QtWidgets.QWidget): connection_type_layout = QtWidgets.QVBoxLayout() connection_type_layout.addWidget(self.tor_settings_group) connection_type_layout.addWidget(self.connection_type_bridges_radio_group) + connection_type_layout.addStretch() + + # Settings are in columns + columns_layout = QtWidgets.QHBoxLayout() + columns_layout.addWidget(connection_type_radio_group) + columns_layout.addSpacing(20) + columns_layout.addLayout(connection_type_layout, stretch=1) + + # Tor connection widget + self.tor_con = TorConnectionWidget(self.common) + self.tor_con.hide() # Buttons self.test_tor_button = QtWidgets.QPushButton( @@ -320,16 +332,19 @@ class TorSettingsTab(QtWidgets.QWidget): self.save_button = QtWidgets.QPushButton(strings._("gui_settings_button_save")) self.save_button.clicked.connect(self.save_clicked) buttons_layout = QtWidgets.QHBoxLayout() - buttons_layout.addWidget(self.test_tor_button) buttons_layout.addStretch() + buttons_layout.addWidget(self.test_tor_button) buttons_layout.addWidget(self.save_button) + buttons_layout.addStretch() # Layout layout = QtWidgets.QVBoxLayout() - layout.addWidget(connection_type_radio_group) - layout.addLayout(connection_type_layout) + layout.addStretch() + layout.addLayout(columns_layout) + layout.addWidget(self.tor_con) layout.addStretch() layout.addLayout(buttons_layout) + layout.addStretch() self.setLayout(layout) @@ -566,14 +581,18 @@ class TorSettingsTab(QtWidgets.QWidget): if not settings: return + self.test_tor_button.hide() + onion = Onion( self.common, use_tmp_dir=True, get_tor_paths=self.common.gui.get_tor_paths, ) - tor_con = TorConnectionDialog(self.common, settings, True, onion) - tor_con.start() + self.tor_con.show() + self.tor_con.success.connect(self.test_tor_button_finished) + self.tor_con.fail.connect(self.test_tor_button_finished) + self.tor_con.start(settings, True, onion) # If Tor settings worked, show results if onion.connected_to_tor: @@ -591,6 +610,13 @@ class TorSettingsTab(QtWidgets.QWidget): # Clean up onion.cleanup() + def test_tor_button_finished(self): + """ + Finished testing tor connection. + """ + self.tor_con.hide() + self.test_tor_button.show() + def save_clicked(self): """ Save button clicked. Save current settings to disk.