diff --git a/cli/onionshare_cli/settings.py b/cli/onionshare_cli/settings.py index e36c1121..473d8379 100644 --- a/cli/onionshare_cli/settings.py +++ b/cli/onionshare_cli/settings.py @@ -115,7 +115,6 @@ class Settings(object): "persistent_tabs": [], "locale": None, # this gets defined in fill_in_defaults() "theme": 0, - "censorship_circumvention": False, } self._settings = {} self.fill_in_defaults() diff --git a/desktop/src/onionshare/connection_tab.py b/desktop/src/onionshare/connection_tab.py index b7381926..7c97c54a 100644 --- a/desktop/src/onionshare/connection_tab.py +++ b/desktop/src/onionshare/connection_tab.py @@ -20,6 +20,7 @@ along with this program. If not, see . import json import os +import random from PySide2 import QtCore, QtWidgets, QtGui from onionshare_cli.settings import Settings @@ -75,13 +76,15 @@ class AutoConnectTab(QtWidgets.QWidget): # First launch widget self.first_launch_widget = AutoConnectFirstLaunchWidget(self.common) self.first_launch_widget.toggle_auto_connect.connect(self.toggle_auto_connect) - self.first_launch_widget.connect_clicked.connect(self.connect_clicked) + self.first_launch_widget.connect_clicked.connect( + self.first_launch_widget_connect_clicked + ) self.first_launch_widget.open_tor_settings.connect(self.open_tor_settings) self.first_launch_widget.show() # Use bridge widget self.use_bridge_widget = AutoConnectUseBridgeWidget(self.common) - self.use_bridge_widget.connect_clicked.connect(self.connect_clicked) + self.use_bridge_widget.connect_clicked.connect(self.use_bridge_connect_clicked) self.use_bridge_widget.back_clicked.connect(self.back_clicked) self.use_bridge_widget.open_tor_settings.connect(self.open_tor_settings) self.use_bridge_widget.hide() @@ -119,7 +122,7 @@ class AutoConnectTab(QtWidgets.QWidget): self.first_launch_widget.enable_autoconnect_checkbox.setCheckState( QtCore.Qt.Checked ) - self.connect_clicked() + self.first_launch_widget_connect_clicked() def toggle_auto_connect(self): """ @@ -135,17 +138,12 @@ class AutoConnectTab(QtWidgets.QWidget): def open_tor_settings(self): self.parent.open_tor_settings_tab() - def connect_clicked(self): + def first_launch_widget_connect_clicked(self): """ - Connect button clicked. Try to connect to tor. + Connect button in first launch widget clicked. Try to connect to tor. """ - self.common.log("AutoConnectTab", "connect_clicked") - - # Hide the buttons - if self.first_launch_widget.isVisible(): - self.first_launch_widget.hide_buttons() - elif self.use_bridge_widget.isVisible(): - self.use_bridge_widget.hide_buttons() + self.common.log("AutoConnectTab", "first_launch_widget_connect_clicked") + self.first_launch_widget.hide_buttons() if not self.common.gui.local_only: self.tor_con.show() @@ -153,6 +151,33 @@ class AutoConnectTab(QtWidgets.QWidget): else: self.close_this_tab.emit() + def use_bridge_connect_clicked(self): + """ + Connect button in use bridge widget clicked. + """ + self.common.log( + "AutoConnectTab", + "use_bridge_connect_clicked", + "Trying to automatically obtain bridges", + ) + self.use_bridge_widget.hide_buttons() + self.use_bridge_widget.start_autodetecting_location() + + # self.common.gui.meek.start() + # self.censorship_circumvention = CensorshipCircumvention( + # self.common, self.common.gui.meek + # ) + # bridge_settings = self.censorship_circumvention.request_settings(country="tm") + # self.common.gui.meek.cleanup() + + # if bridge_settings and self.censorship_circumvention.save_settings( + # self.settings, bridge_settings + # ): + # # Try and connect again + # self.start() + # else: + # self.fail.emit() + def back_clicked(self): """ Switch from use bridge widget back to first launch widget @@ -174,7 +199,7 @@ class AutoConnectTab(QtWidgets.QWidget): # Close the tab self.close_this_tab.emit() - def tor_con_fail(self, msg): + def tor_con_fail(self): """ Finished testing tor connection. """ @@ -312,6 +337,11 @@ class AutoConnectUseBridgeWidget(QtWidgets.QWidget): for country_code in countries: self.country_combobox.addItem(countries[country_code], country_code) + # Task label + self.task_label = QtWidgets.QLabel() + self.task_label.setStyleSheet(common.gui.css["enable_autoconnect"]) + self.task_label.hide() + # Buttons self.connect_button = QtWidgets.QPushButton( strings._("gui_autoconnect_bridge_start") @@ -346,6 +376,7 @@ class AutoConnectUseBridgeWidget(QtWidgets.QWidget): layout.addWidget(description_label) layout.addLayout(detect_layout) layout.addWidget(self.country_combobox) + layout.addWidget(self.task_label) layout.addWidget(cta_widget) self.setLayout(layout) @@ -353,12 +384,39 @@ class AutoConnectUseBridgeWidget(QtWidgets.QWidget): def hide_buttons(self): self.connect_button.hide() + self.back_button.hide() self.configure_button.hide() def show_buttons(self): self.connect_button.show() + self.back_button.show() self.configure_button.show() + def start_autodetecting_location(self): + self.detect_automatic_radio.setEnabled(False) + self.detect_manual_radio.setEnabled(False) + + self.country_combobox.setEnabled(False) + self.country_combobox.show() + + # If we're automatically detecting it, randomly switch up the country + # dropdown until we detect the location + if self.detect_automatic_radio.isChecked(): + self.task_label.show() + self.task_label.setText(strings._("gui_autoconnect_task_detect_location")) + + self.autodetecting_timer = QtCore.QTimer() + self.autodetecting_timer.timeout.connect(self._autodetecting_timer_callback) + self.autodetecting_timer.start(200) + + def stop_autodetecting_location(self): + self.task_label.hide() + self.autodetecting_timer.stop() + + def _autodetecting_timer_callback(self): + new_index = random.randrange(0, self.country_combobox.count()) + self.country_combobox.setCurrentIndex(new_index) + def _detect_automatic_toggled(self): self.country_combobox.setEnabled(False) self.country_combobox.hide() diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index 0e0ca9d3..7c97d238 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -40,6 +40,7 @@ from onionshare_cli.onion import ( TorTooOldStealth, PortNotAvailable, ) +from onionshare_cli.meek import Meek class GuiCommon: @@ -78,6 +79,9 @@ class GuiCommon: os.makedirs(self.events_dir, 0o700, True) self.events_filename = os.path.join(self.events_dir, "events") + # Instantiate Meek, which is used to bypass censorship + self.meek = Meek(self.common, get_tor_paths=self.get_tor_paths) + self.css = self.get_css(qtapp.color_mode) self.color_mode = qtapp.color_mode @@ -185,6 +189,11 @@ class GuiCommon: color: #666666; } """, + "autoconnect_task_label": """ + QLabel { + font-weight: bold; + } + """, # Common styles between modes and their child widgets "mode_settings_toggle_advanced": """ QPushButton { diff --git a/desktop/src/onionshare/main_window.py b/desktop/src/onionshare/main_window.py index e36ec53b..c7f74e4d 100644 --- a/desktop/src/onionshare/main_window.py +++ b/desktop/src/onionshare/main_window.py @@ -26,10 +26,10 @@ from . import strings from .widgets import Alert from .update_checker import UpdateThread from .tab_widget import TabWidget +from .settings_tab import SettingsTab from .gui_common import GuiCommon from .threads import OnionCleanupThread -from onionshare_cli.meek import Meek class MainWindow(QtWidgets.QMainWindow): """ @@ -53,7 +53,7 @@ class MainWindow(QtWidgets.QMainWindow): self.settings_action = menu.addAction(strings._("gui_settings_window_title")) self.settings_action.triggered.connect(self.open_settings) self.help_action = menu.addAction(strings._("gui_settings_button_help")) - self.help_action.triggered.connect(lambda: SettingsDialog.help_clicked(self)) + self.help_action.triggered.connect(lambda: SettingsTab.open_help()) exit_action = menu.addAction(strings._("systray_menu_exit")) exit_action.triggered.connect(self.close) @@ -160,23 +160,6 @@ class MainWindow(QtWidgets.QMainWindow): self.setCentralWidget(central_widget) self.show() -<<<<<<< HEAD -======= - # Instantiate Meek, which the TorConnectionDialog may use to resolve - # connection issues by automatically obtaining bridges. - self.meek = Meek(self.common, get_tor_paths=self.common.gui.get_tor_paths) - # Start the "Connecting to Tor" dialog, which calls onion.connect() - tor_con = TorConnectionDialog(self.common, self.meek) - tor_con.canceled.connect(self.tor_connection_canceled) - tor_con.open_tor_settings.connect(self.tor_connection_open_tor_settings) - if not self.common.gui.local_only: - tor_con.start() - self.settings_have_changed() - - # After connecting to Tor, check for updates - self.check_for_updates() - ->>>>>>> mig5-censorship_automatically_attempt_and_reconnect # Create the close warning dialog -- the dialog widget needs to be in the constructor # in order to test it self.close_dialog = QtWidgets.QMessageBox() diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index eeb991f4..a4780230 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -45,12 +45,13 @@ "gui_autoconnect_description": "OnionShare relies on the Tor Network, run by thousands of volunteers around the world.", "gui_enable_autoconnect_checkbox": "Enable automatically connecting to Tor", "gui_autoconnect_bridge_description": "Failed connecting to Tor. This could be because your internet is being censored. You might be able to bypass this censorship by using a bridge.", - "gui_autoconnect_bridge_detect_automatic": "Automatically determine my location to bypass country-specific censorship", + "gui_autoconnect_bridge_detect_automatic": "Automatically determine my country to bypass country-specific censorship", "gui_autoconnect_bridge_detect_manual": "Manually select my country", "gui_autoconnect_start": "Connect to Tor", "gui_autoconnect_configure": "Network Settings", "gui_autoconnect_bridge_start": "Use a Bridge", "gui_autoconnect_bridge_back": "Back", + "gui_autoconnect_task_detect_location": "Automatically determining your country...", "gui_settings_window_title": "Settings", "gui_settings_autoupdate_label": "Check for new version", "gui_settings_autoupdate_option": "Notify me when a new version is available", @@ -243,4 +244,4 @@ "moat_captcha_error": "The solution is not correct. Please try again.", "moat_solution_empty_error": "You must enter the characters from the image", "mode_tor_not_connected_label": "OnionShare is not connected to the Tor network" -} +} \ No newline at end of file diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index ae1fe651..db139ea8 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -168,7 +168,7 @@ class TabWidget(QtWidgets.QTabWidget): self.add_tab() else: self.open_connection_tab() - + def check_autoconnect_tab(self): if type(self.tabs[0]) is AutoConnectTab: self.tabs[0].check_autoconnect() @@ -218,7 +218,9 @@ class TabWidget(QtWidgets.QTabWidget): self.setCurrentIndex(self.indexOf(self.tabs[tab_id])) return - connection_tab = AutoConnectTab(self.common, self.current_tab_id, self.status_bar, parent=self) + connection_tab = AutoConnectTab( + self.common, self.current_tab_id, self.status_bar, parent=self + ) connection_tab.close_this_tab.connect(self.close_connection_tab) self.tabs[self.current_tab_id] = connection_tab self.current_tab_id += 1 diff --git a/desktop/src/onionshare/tor_connection.py b/desktop/src/onionshare/tor_connection.py index cb825487..fe3274a6 100644 --- a/desktop/src/onionshare/tor_connection.py +++ b/desktop/src/onionshare/tor_connection.py @@ -39,153 +39,9 @@ from onionshare_cli.onion import ( ) from . import strings -from .gui_common import GuiCommon -from .widgets import Alert from onionshare_cli.censorship import CensorshipCircumvention -class TorConnectionDialog(QtWidgets.QProgressDialog): - """ - Connecting to Tor dialog. - """ - - open_tor_settings = QtCore.Signal() - success = QtCore.Signal() - - def __init__( - self, common, meek, custom_settings=False, testing_settings=False, onion=None - ): - super(TorConnectionDialog, self).__init__(None) - - self.common = common - self.testing_settings = testing_settings - - self.meek = meek - - if custom_settings: - self.settings = custom_settings - else: - self.settings = self.common.settings - - self.common.log("TorConnectionDialog", "__init__") - - if self.testing_settings: - self.title = strings._("gui_settings_connection_type_test_button") - self.onion = onion - else: - self.title = "OnionShare" - self.onion = self.common.gui.onion - - self.setWindowTitle(self.title) - - self.setWindowIcon(QtGui.QIcon(GuiCommon.get_resource_path("images/logo.png"))) - self.setModal(True) - self.setFixedSize(400, 150) - - # Label - self.setLabelText(strings._("connecting_to_tor")) - - # Progress bar ticks from 0 to 100 - self.setRange(0, 100) - # Don't show if connection takes less than 100ms (for non-bundled tor) - self.setMinimumDuration(100) - - # Start displaying the status at 0 - self._tor_status_update(0, "") - - def start(self): - self.common.log("TorConnectionDialog", "start") - - 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 _tor_status_update(self, progress, summary): - self.setValue(int(progress)) - self.setLabelText( - f"{strings._('connecting_to_tor')}
{summary}" - ) - - def _connected_to_tor(self): - self.common.log("TorConnectionDialog", "_connected_to_tor") - self.active = False - # Close the dialog after connecting - self.setValue(self.maximum()) - - def _canceled_connecting_to_tor(self): - self.common.log("TorConnectionDialog", "_canceled_connecting_to_tor") - self.active = False - self.onion.cleanup() - - # Cancel connecting to Tor - QtCore.QTimer.singleShot(1, self.cancel) - - def _error_connecting_to_tor(self, msg): - self.common.log("TorConnectionDialog", "_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) - - # If we are allowed to try automatically resolving connection issues - # (e.g possible censorship) by obtaining bridges for the user, do so - elif self.settings.get("censorship_circumvention"): - - def alert(): - return - - # Automatically try to obtain bridges from the Censorship Circumvention API - self.common.log( - "TorConnectionDialog", - "_error_connecting_to_tor", - "Trying to automatically obtain bridges", - ) - self.meek.start() - self.censorship_circumvention = CensorshipCircumvention( - self.common, self.meek - ) - bridge_settings = self.censorship_circumvention.request_settings( - country="tm" - ) - self.meek.cleanup() - - if bridge_settings and self.censorship_circumvention.save_settings( - self.settings, bridge_settings - ): - # Try and connect again - self.start() - 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) - - # Cancel connecting to Tor - QtCore.QTimer.singleShot(1, self.cancel) - - class TorConnectionWidget(QtWidgets.QWidget): """ Connecting to Tor widget, with a progress bar @@ -193,9 +49,9 @@ class TorConnectionWidget(QtWidgets.QWidget): open_tor_settings = QtCore.Signal() success = QtCore.Signal() - fail = QtCore.Signal(str) + fail = QtCore.Signal() - def __init__(self, common, status_bar, meek): + def __init__(self, common, status_bar): super(TorConnectionWidget, self).__init__(None) self.common = common self.common.log("TorConnectionWidget", "__init__") @@ -211,8 +67,6 @@ class TorConnectionWidget(QtWidgets.QWidget): ) self.cancel_button.clicked.connect(self.cancel_clicked) - self.meek = meek - progress_layout = QtWidgets.QHBoxLayout() progress_layout.addWidget(self.progress) progress_layout.addWidget(self.cancel_button) @@ -259,7 +113,7 @@ class TorConnectionWidget(QtWidgets.QWidget): def cancel_clicked(self): self.was_canceled = True - self.fail.emit("") + self.fail.emit() def wasCanceled(self): return self.was_canceled @@ -288,44 +142,17 @@ class TorConnectionWidget(QtWidgets.QWidget): # Cancel connecting to Tor QtCore.QTimer.singleShot(1, self.cancel_clicked) - def _error_connecting_to_tor(self, msg): + def _error_connecting_to_tor(self): self.common.log("TorConnectionWidget", "_error_connecting_to_tor") self.active = False - - # If we are allowed to try automatically resolving connection issues - # (e.g possible censorship) by obtaining bridges for the user, do so - if self.settings.get("censorship_circumvention"): - # Automatically try to obtain bridges from the Censorship Circumvention API - self.common.log( - "TorConnectionWidget", - "_error_connecting_to_tor", - "Trying to automatically obtain bridges", - ) - self.meek.start() - self.censorship_circumvention = CensorshipCircumvention( - self.common, self.meek - ) - bridge_settings = self.censorship_circumvention.request_settings( - country="tm" - ) - self.meek.cleanup() - - if bridge_settings and self.censorship_circumvention.save_settings( - self.settings, bridge_settings - ): - # Try and connect again - self.start() - else: - self.fail.emit() - else: - self.fail.emit() + self.fail.emit() class TorConnectionThread(QtCore.QThread): tor_status_update = QtCore.Signal(str, str) connected_to_tor = QtCore.Signal() canceled_connecting_to_tor = QtCore.Signal() - error_connecting_to_tor = QtCore.Signal(str) + error_connecting_to_tor = QtCore.Signal() def __init__(self, common, settings, parent): super(TorConnectionThread, self).__init__() @@ -370,7 +197,7 @@ class TorConnectionThread(QtCore.QThread): self.common.log( "TorConnectionThread", "run", f"caught exception: {message}" ) - self.error_connecting_to_tor.emit(message) + self.error_connecting_to_tor.emit() def _tor_status_update(self, progress, summary): self.tor_status_update.emit(progress, summary)