diff --git a/onionshare/common.py b/onionshare/common.py index ffa6529f..250972f9 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -373,6 +373,11 @@ class Common(object): 'settings_whats_this': """ QLabel { font-size: 12px; + }""", + + 'settings_connect_to_tor': """ + QLabel { + font-style: italic; }""" } diff --git a/onionshare/onion.py b/onionshare/onion.py index 6066f059..3d7b4514 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -146,6 +146,9 @@ class Onion(object): # The tor process self.tor_proc = None + # The Tor controller + self.c = None + # Start out not connected to Tor self.connected_to_tor = False @@ -387,6 +390,7 @@ class Onion(object): # Get the tor version self.tor_version = self.c.get_version().version_str + self.common.log('Onion', 'connect', 'Connected to tor {}'.format(self.tor_version)) # Do the versions of stem and tor that I'm using support ephemeral onion services? list_ephemeral_hidden_services = getattr(self.c, "list_ephemeral_hidden_services", None) @@ -403,7 +407,9 @@ class Onion(object): self.supports_stealth = False # Does this version of Tor support next-gen ('v3') onions? - self.supports_next_gen_onions = self.tor_version > Version('0.3.3.1') + # Note, this is the version of Tor where this bug was fixed: + # https://trac.torproject.org/projects/tor/ticket/28619 + self.supports_v3_onions = self.tor_version >= Version('0.4.0.0') def is_authenticated(self): """ @@ -461,7 +467,7 @@ class Onion(object): else: key_type = "NEW" # Work out if we can support v3 onion services, which are preferred - if Version(self.tor_version) >= Version('0.3.3.1') and not self.settings.get('use_legacy_v2_onions'): + if self.supports_v3_onions and not self.settings.get('use_legacy_v2_onions'): key_content = "ED25519-V3" else: # fall back to v2 onion services diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 958d49fd..92c84262 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -88,6 +88,10 @@ class SettingsDialog(QtWidgets.QDialog): self.shutdown_timeout_widget = QtWidgets.QWidget() self.shutdown_timeout_widget.setLayout(shutdown_timeout_layout) + # Label telling user to connect to Tor for onion service settings + self.connect_to_tor_label = QtWidgets.QLabel(strings._("gui_connect_to_tor_for_onion_settings")) + self.connect_to_tor_label.setStyleSheet(self.common.css['settings_connect_to_tor']) + # Whether or not to use legacy v2 onions self.use_legacy_v2_onions_checkbox = QtWidgets.QCheckBox() self.use_legacy_v2_onions_checkbox.setCheckState(QtCore.Qt.Unchecked) @@ -149,16 +153,23 @@ class SettingsDialog(QtWidgets.QDialog): self.hidservauth_copy_button.clicked.connect(self.hidservauth_copy_button_clicked) self.hidservauth_copy_button.hide() + # Onion settings widget + onion_settings_layout = QtWidgets.QVBoxLayout() + onion_settings_layout.setContentsMargins(0, 0, 0, 0) + onion_settings_layout.addWidget(self.use_legacy_v2_onions_widget) + onion_settings_layout.addWidget(self.save_private_key_widget) + onion_settings_layout.addWidget(self.use_stealth_widget) + onion_settings_layout.addWidget(hidservauth_details) + onion_settings_layout.addWidget(self.hidservauth_copy_button) + self.onion_settings_widget = QtWidgets.QWidget() + self.onion_settings_widget.setLayout(onion_settings_layout) + # General options layout general_group_layout = QtWidgets.QVBoxLayout() general_group_layout.addWidget(self.public_mode_widget) general_group_layout.addWidget(self.shutdown_timeout_widget) - general_group_layout.addWidget(self.use_legacy_v2_onions_widget) - general_group_layout.addWidget(self.save_private_key_widget) - general_group_layout.addWidget(self.use_stealth_widget) - general_group_layout.addWidget(hidservauth_details) - general_group_layout.addWidget(self.hidservauth_copy_button) - + general_group_layout.addWidget(self.connect_to_tor_label) + general_group_layout.addWidget(self.onion_settings_widget) general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label")) general_group.setLayout(general_group_layout) @@ -460,6 +471,9 @@ class SettingsDialog(QtWidgets.QDialog): self.setLayout(layout) self.cancel_button.setFocus() + self.reload_settings() + + def reload_settings(self): # Load settings, and fill them in self.old_settings = Settings(self.common, self.config) self.old_settings.load() @@ -579,6 +593,24 @@ class SettingsDialog(QtWidgets.QDialog): new_bridges = ''.join(new_bridges) self.tor_bridges_use_custom_textbox.setPlainText(new_bridges) + # If we're connected to Tor, show onion service settings, show label if not + if self.onion.is_authenticated(): + self.connect_to_tor_label.hide() + self.onion_settings_widget.show() + + # If v3 onion services are supported, allow using legacy mode + if self.onion.supports_v3_onions: + self.common.log('SettingsDialog', '__init__', 'v3 onions are supported') + self.use_legacy_v2_onions_checkbox.show() + else: + self.common.log('SettingsDialog', '__init__', 'v3 onions are not supported') + self.use_legacy_v2_onions_widget.hide() + self.use_legacy_v2_onions_checkbox_clicked(True) + else: + self.connect_to_tor_label.show() + self.onion_settings_widget.hide() + + def connection_type_bundled_toggled(self, checked): """ Connection type bundled was toggled. If checked, hide authentication fields. @@ -754,7 +786,7 @@ class SettingsDialog(QtWidgets.QDialog): onion.connect(custom_settings=settings, config=self.config, tor_status_update_func=tor_status_update_func) # If an exception hasn't been raised yet, the Tor settings work - Alert(self.common, strings._('settings_test_success').format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth, onion.supports_next_gen_onions)) + Alert(self.common, strings._('settings_test_success').format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth, onion.supports_v3_onions)) # Clean up onion.cleanup() diff --git a/share/locale/en.json b/share/locale/en.json index 7fb30df8..43c7cfe3 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -69,7 +69,7 @@ "error_ephemeral_not_supported": "OnionShare requires at least both Tor 0.2.7.1 and python3-stem 1.4.0.", "gui_settings_window_title": "Settings", "gui_settings_whats_this": "What's this?", - "gui_settings_stealth_option": "Use client authorization (legacy)", + "gui_settings_stealth_option": "Use client authorization", "gui_settings_stealth_hidservauth_string": "Having saved your private key for reuse, means you can now\nclick to copy your HidServAuth.", "gui_settings_autoupdate_label": "Check for new version", "gui_settings_autoupdate_option": "Notify me when a new version is available", @@ -135,8 +135,9 @@ "gui_server_started_after_timeout": "The auto-stop timer ran out before the server started.\nPlease make a new share.", "gui_server_timeout_expired": "The auto-stop timer already ran out.\nPlease update it to start sharing.", "share_via_onionshare": "OnionShare it", + "gui_connect_to_tor_for_onion_settings": "Connect to Tor to see onion service settings", "gui_use_legacy_v2_onions_checkbox": "Use legacy addresses", - "gui_save_private_key_checkbox": "Use a persistent address (legacy)", + "gui_save_private_key_checkbox": "Use a persistent address", "gui_share_url_description": "Anyone with this OnionShare address can download your files using the Tor Browser: ", "gui_receive_url_description": "Anyone with this OnionShare address can upload files to your computer using the Tor Browser: ", "gui_url_label_persistent": "This share will not auto-stop.

Every subsequent share reuses the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)", diff --git a/tests/SettingsGuiBaseTest.py b/tests/SettingsGuiBaseTest.py index e59a58a8..47d698f3 100644 --- a/tests/SettingsGuiBaseTest.py +++ b/tests/SettingsGuiBaseTest.py @@ -1,5 +1,7 @@ import json import os +import unittest +from PyQt5 import QtCore, QtTest from onionshare import strings from onionshare.common import Common @@ -8,10 +10,27 @@ from onionshare.onion import Onion from onionshare_gui import Application, OnionShare from onionshare_gui.settings_dialog import SettingsDialog + +class OnionStub(object): + def __init__(self, is_authenticated, supports_v3_onions): + self._is_authenticated = is_authenticated + self.supports_v3_onions = supports_v3_onions + + def is_authenticated(self): + return self._is_authenticated + + class SettingsGuiBaseTest(object): @staticmethod - def set_up(test_settings): + def set_up(): '''Create the GUI''' + + # Default settings for the settings GUI tests + test_settings = { + "no_bridges": False, + "tor_bridges_use_custom_bridges": "Bridge 1.2.3.4:56 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 5.6.7.8:910 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 11.12.13.14:1516 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n", + } + # Create our test file testfile = open('/tmp/test.txt', 'w') testfile.write('onionshare') @@ -37,8 +56,186 @@ class SettingsGuiBaseTest(object): gui = SettingsDialog(common, testonion, qtapp, '/tmp/settings.json', True) return gui - @staticmethod def tear_down(): '''Clean up after tests''' os.remove('/tmp/settings.json') + + def run_settings_gui_tests(self): + self.gui.show() + + # Window is shown + self.assertTrue(self.gui.isVisible()) + self.assertEqual(self.gui.windowTitle(), strings._('gui_settings_window_title')) + + # Check for updates button is hidden + self.assertFalse(self.gui.check_for_updates_button.isVisible()) + + # public mode is off + self.assertFalse(self.gui.public_mode_checkbox.isChecked()) + # enable public mode + QtTest.QTest.mouseClick(self.gui.public_mode_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.public_mode_checkbox.height()/2)) + self.assertTrue(self.gui.public_mode_checkbox.isChecked()) + + # shutdown timer is off + self.assertFalse(self.gui.shutdown_timeout_checkbox.isChecked()) + # enable shutdown timer + QtTest.QTest.mouseClick(self.gui.shutdown_timeout_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.shutdown_timeout_checkbox.height()/2)) + self.assertTrue(self.gui.shutdown_timeout_checkbox.isChecked()) + + # legacy mode checkbox and related widgets + if self.gui.onion.is_authenticated(): + if self.gui.onion.supports_v3_onions: + # legacy mode is off + self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isChecked()) + # persistence, stealth is hidden and disabled + self.assertFalse(self.gui.save_private_key_widget.isVisible()) + self.assertFalse(self.gui.save_private_key_checkbox.isChecked()) + self.assertFalse(self.gui.use_stealth_widget.isVisible()) + self.assertFalse(self.gui.stealth_checkbox.isChecked()) + self.assertFalse(self.gui.hidservauth_copy_button.isVisible()) + + # enable legacy mode + QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2)) + self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isChecked()) + self.assertTrue(self.gui.save_private_key_checkbox.isVisible()) + self.assertTrue(self.gui.use_stealth_widget.isVisible()) + + # enable persistent mode + QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2)) + self.assertTrue(self.gui.save_private_key_checkbox.isChecked()) + # enable stealth mode + QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2)) + self.assertTrue(self.gui.stealth_checkbox.isChecked()) + # now that stealth, persistence are enabled, we can't turn off legacy mode + self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isEnabled()) + # disable stealth, persistence + QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2)) + QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2)) + # legacy mode checkbox is enabled again + self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isEnabled()) + # uncheck legacy mode + QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2)) + # legacy options hidden again + self.assertFalse(self.gui.save_private_key_widget.isVisible()) + self.assertFalse(self.gui.use_stealth_widget.isVisible()) + + # re-enable legacy mode + QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2)) + + else: + # legacy mode setting is hidden + self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isVisible()) + # legacy options are showing + self.assertTrue(self.gui.save_private_key_widget.isVisible()) + self.assertTrue(self.gui.use_stealth_widget.isVisible()) + + # enable them all again so that we can see the setting stick in settings.json + QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2)) + QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2)) + else: + # None of the onion settings should appear + self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isVisible()) + self.assertFalse(self.gui.save_private_key_widget.isVisible()) + self.assertFalse(self.gui.save_private_key_checkbox.isChecked()) + self.assertFalse(self.gui.use_stealth_widget.isVisible()) + self.assertFalse(self.gui.stealth_checkbox.isChecked()) + self.assertFalse(self.gui.hidservauth_copy_button.isVisible()) + + # stay open toggled off, on + self.assertTrue(self.gui.close_after_first_download_checkbox.isChecked()) + QtTest.QTest.mouseClick(self.gui.close_after_first_download_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.close_after_first_download_checkbox.height()/2)) + self.assertFalse(self.gui.close_after_first_download_checkbox.isChecked()) + + # receive mode + self.gui.downloads_dir_lineedit.setText('/tmp/OnionShareSettingsTest') + + + # bundled mode is enabled + self.assertTrue(self.gui.connection_type_bundled_radio.isEnabled()) + self.assertTrue(self.gui.connection_type_bundled_radio.isChecked()) + # bridge options are shown + self.assertTrue(self.gui.connection_type_bridges_radio_group.isVisible()) + # bridges are set to custom + self.assertFalse(self.gui.tor_bridges_no_bridges_radio.isChecked()) + self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked()) + + # switch to obfs4 + QtTest.QTest.mouseClick(self.gui.tor_bridges_use_obfs4_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_obfs4_radio.height()/2)) + self.assertTrue(self.gui.tor_bridges_use_obfs4_radio.isChecked()) + + # custom bridges are hidden + self.assertFalse(self.gui.tor_bridges_use_custom_textbox_options.isVisible()) + # other modes are unchecked but enabled + self.assertTrue(self.gui.connection_type_automatic_radio.isEnabled()) + self.assertTrue(self.gui.connection_type_control_port_radio.isEnabled()) + self.assertTrue(self.gui.connection_type_socket_file_radio.isEnabled()) + self.assertFalse(self.gui.connection_type_automatic_radio.isChecked()) + self.assertFalse(self.gui.connection_type_control_port_radio.isChecked()) + self.assertFalse(self.gui.connection_type_socket_file_radio.isChecked()) + + # enable automatic mode + QtTest.QTest.mouseClick(self.gui.connection_type_automatic_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_automatic_radio.height()/2)) + self.assertTrue(self.gui.connection_type_automatic_radio.isChecked()) + # bundled is off + self.assertFalse(self.gui.connection_type_bundled_radio.isChecked()) + # bridges are hidden + self.assertFalse(self.gui.connection_type_bridges_radio_group.isVisible()) + + # auth type is hidden in bundled or automatic mode + self.assertFalse(self.gui.authenticate_no_auth_radio.isVisible()) + self.assertFalse(self.gui.authenticate_password_radio.isVisible()) + + # enable control port mode + QtTest.QTest.mouseClick(self.gui.connection_type_control_port_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_control_port_radio.height()/2)) + self.assertTrue(self.gui.connection_type_control_port_radio.isChecked()) + # automatic is off + self.assertFalse(self.gui.connection_type_automatic_radio.isChecked()) + # auth options appear + self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible()) + self.assertTrue(self.gui.authenticate_password_radio.isVisible()) + + # enable socket mode + QtTest.QTest.mouseClick(self.gui.connection_type_socket_file_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_socket_file_radio.height()/2)) + self.assertTrue(self.gui.connection_type_socket_file_radio.isChecked()) + # control port is off + self.assertFalse(self.gui.connection_type_control_port_radio.isChecked()) + # auth options are still present + self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible()) + self.assertTrue(self.gui.authenticate_password_radio.isVisible()) + + # re-enable bundled mode + QtTest.QTest.mouseClick(self.gui.connection_type_bundled_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_bundled_radio.height()/2)) + # go back to custom bridges + QtTest.QTest.mouseClick(self.gui.tor_bridges_use_custom_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_custom_radio.height()/2)) + self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked()) + self.assertTrue(self.gui.tor_bridges_use_custom_textbox.isVisible()) + self.assertFalse(self.gui.tor_bridges_use_obfs4_radio.isChecked()) + self.gui.tor_bridges_use_custom_textbox.setPlainText('94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\n148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\n93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3') + + # Test that the Settings Dialog can save the settings and close itself + QtTest.QTest.mouseClick(self.gui.save_button, QtCore.Qt.LeftButton) + self.assertFalse(self.gui.isVisible()) + + # Test our settings are reflected in the settings json + with open('/tmp/settings.json') as f: + data = json.load(f) + + self.assertTrue(data["public_mode"]) + self.assertTrue(data["shutdown_timeout"]) + + if self.gui.onion.is_authenticated(): + if self.gui.onion.supports_v3_onions: + self.assertTrue(data["use_legacy_v2_onions"]) + self.assertTrue(data["save_private_key"]) + self.assertTrue(data["use_stealth"]) + else: + self.assertFalse(data["use_legacy_v2_onions"]) + self.assertFalse(data["save_private_key"]) + self.assertFalse(data["use_stealth"]) + + self.assertEqual(data["downloads_dir"], "/tmp/OnionShareSettingsTest") + self.assertFalse(data["close_after_first_download"]) + self.assertEqual(data["connection_type"], "bundled") + self.assertFalse(data["tor_bridges_use_obfs4"]) + self.assertEqual(data["tor_bridges_use_custom_bridges"], "Bridge 94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\nBridge 148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\nBridge 93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3\n") diff --git a/tests/local_onionshare_settings_dialog_legacy_tor_test.py b/tests/local_onionshare_settings_dialog_legacy_tor_test.py new file mode 100644 index 00000000..ae6ce272 --- /dev/null +++ b/tests/local_onionshare_settings_dialog_legacy_tor_test.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import unittest + +from onionshare import strings +from .SettingsGuiBaseTest import SettingsGuiBaseTest, OnionStub + + +class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest): + @classmethod + def setUpClass(cls): + cls.gui = SettingsGuiBaseTest.set_up() + + @classmethod + def tearDownClass(cls): + SettingsGuiBaseTest.tear_down() + + def test_gui_legacy_tor(self): + self.gui.onion = OnionStub(True, False) + self.gui.reload_settings() + self.run_settings_gui_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/local_onionshare_settings_dialog_no_tor_test.py b/tests/local_onionshare_settings_dialog_no_tor_test.py new file mode 100644 index 00000000..f01e049d --- /dev/null +++ b/tests/local_onionshare_settings_dialog_no_tor_test.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import unittest + +from onionshare import strings +from .SettingsGuiBaseTest import SettingsGuiBaseTest, OnionStub + + +class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest): + @classmethod + def setUpClass(cls): + cls.gui = SettingsGuiBaseTest.set_up() + + @classmethod + def tearDownClass(cls): + SettingsGuiBaseTest.tear_down() + + def test_gui_no_tor(self): + self.gui.onion = OnionStub(False, False) + self.gui.reload_settings() + self.run_settings_gui_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/local_onionshare_settings_dialog_test.py b/tests/local_onionshare_settings_dialog_test.py deleted file mode 100644 index c1e48122..00000000 --- a/tests/local_onionshare_settings_dialog_test.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 -import json -import unittest -from PyQt5 import QtCore, QtTest - -from onionshare import strings -from .SettingsGuiBaseTest import SettingsGuiBaseTest - -class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest): - @classmethod - def setUpClass(cls): - test_settings = { - "no_bridges": False, - "tor_bridges_use_custom_bridges": "Bridge 1.2.3.4:56 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 5.6.7.8:910 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\nBridge 11.12.13.14:1516 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n", - } - cls.gui = SettingsGuiBaseTest.set_up(test_settings) - - @classmethod - def tearDownClass(cls): - SettingsGuiBaseTest.tear_down() - - def test_gui(self): - self.gui.show() - # Window is shown - self.assertTrue(self.gui.isVisible()) - self.assertEqual(self.gui.windowTitle(), strings._('gui_settings_window_title')) - # Check for updates button is hidden - self.assertFalse(self.gui.check_for_updates_button.isVisible()) - - # public mode is off - self.assertFalse(self.gui.public_mode_checkbox.isChecked()) - # enable public mode - QtTest.QTest.mouseClick(self.gui.public_mode_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.public_mode_checkbox.height()/2)) - self.assertTrue(self.gui.public_mode_checkbox.isChecked()) - - # shutdown timer is off - self.assertFalse(self.gui.shutdown_timeout_checkbox.isChecked()) - # enable shutdown timer - QtTest.QTest.mouseClick(self.gui.shutdown_timeout_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.shutdown_timeout_checkbox.height()/2)) - self.assertTrue(self.gui.shutdown_timeout_checkbox.isChecked()) - - # legacy mode checkbox and related widgets - # legacy mode is off - self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isChecked()) - # persistence, stealth is hidden and disabled - self.assertFalse(self.gui.save_private_key_widget.isVisible()) - self.assertFalse(self.gui.save_private_key_checkbox.isChecked()) - self.assertFalse(self.gui.use_stealth_widget.isVisible()) - self.assertFalse(self.gui.stealth_checkbox.isChecked()) - self.assertFalse(self.gui.hidservauth_copy_button.isVisible()) - - # enable legacy mode - QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2)) - self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isChecked()) - self.assertTrue(self.gui.save_private_key_checkbox.isVisible()) - self.assertTrue(self.gui.use_stealth_widget.isVisible()) - # enable persistent mode - QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2)) - self.assertTrue(self.gui.save_private_key_checkbox.isChecked()) - # enable stealth mode - QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2)) - self.assertTrue(self.gui.stealth_checkbox.isChecked()) - # now that stealth, persistence are enabled, we can't turn off legacy mode - self.assertFalse(self.gui.use_legacy_v2_onions_checkbox.isEnabled()) - # disable stealth, persistence - QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2)) - QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2)) - # legacy mode checkbox is enabled again - self.assertTrue(self.gui.use_legacy_v2_onions_checkbox.isEnabled()) - # uncheck legacy mode - QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2)) - # legacy options hidden again - self.assertFalse(self.gui.save_private_key_widget.isVisible()) - self.assertFalse(self.gui.use_stealth_widget.isVisible()) - # enable them all again so that we can see the setting stick in settings.json - QtTest.QTest.mouseClick(self.gui.use_legacy_v2_onions_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.use_legacy_v2_onions_checkbox.height()/2)) - QtTest.QTest.mouseClick(self.gui.save_private_key_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.save_private_key_checkbox.height()/2)) - QtTest.QTest.mouseClick(self.gui.stealth_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.stealth_checkbox.height()/2)) - - - # stay open toggled off, on - self.assertTrue(self.gui.close_after_first_download_checkbox.isChecked()) - QtTest.QTest.mouseClick(self.gui.close_after_first_download_checkbox, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.close_after_first_download_checkbox.height()/2)) - self.assertFalse(self.gui.close_after_first_download_checkbox.isChecked()) - - # receive mode - self.gui.downloads_dir_lineedit.setText('/tmp/OnionShareSettingsTest') - - - # bundled mode is enabled - self.assertTrue(self.gui.connection_type_bundled_radio.isEnabled()) - self.assertTrue(self.gui.connection_type_bundled_radio.isChecked()) - # bridge options are shown - self.assertTrue(self.gui.connection_type_bridges_radio_group.isVisible()) - # bridges are set to custom - self.assertFalse(self.gui.tor_bridges_no_bridges_radio.isChecked()) - self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked()) - - # switch to obfs4 - QtTest.QTest.mouseClick(self.gui.tor_bridges_use_obfs4_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_obfs4_radio.height()/2)) - self.assertTrue(self.gui.tor_bridges_use_obfs4_radio.isChecked()) - - # custom bridges are hidden - self.assertFalse(self.gui.tor_bridges_use_custom_textbox_options.isVisible()) - # other modes are unchecked but enabled - self.assertTrue(self.gui.connection_type_automatic_radio.isEnabled()) - self.assertTrue(self.gui.connection_type_control_port_radio.isEnabled()) - self.assertTrue(self.gui.connection_type_socket_file_radio.isEnabled()) - self.assertFalse(self.gui.connection_type_automatic_radio.isChecked()) - self.assertFalse(self.gui.connection_type_control_port_radio.isChecked()) - self.assertFalse(self.gui.connection_type_socket_file_radio.isChecked()) - - # enable automatic mode - QtTest.QTest.mouseClick(self.gui.connection_type_automatic_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_automatic_radio.height()/2)) - self.assertTrue(self.gui.connection_type_automatic_radio.isChecked()) - # bundled is off - self.assertFalse(self.gui.connection_type_bundled_radio.isChecked()) - # bridges are hidden - self.assertFalse(self.gui.connection_type_bridges_radio_group.isVisible()) - - # auth type is hidden in bundled or automatic mode - self.assertFalse(self.gui.authenticate_no_auth_radio.isVisible()) - self.assertFalse(self.gui.authenticate_password_radio.isVisible()) - - # enable control port mode - QtTest.QTest.mouseClick(self.gui.connection_type_control_port_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_control_port_radio.height()/2)) - self.assertTrue(self.gui.connection_type_control_port_radio.isChecked()) - # automatic is off - self.assertFalse(self.gui.connection_type_automatic_radio.isChecked()) - # auth options appear - self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible()) - self.assertTrue(self.gui.authenticate_password_radio.isVisible()) - - # enable socket mode - QtTest.QTest.mouseClick(self.gui.connection_type_socket_file_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_socket_file_radio.height()/2)) - self.assertTrue(self.gui.connection_type_socket_file_radio.isChecked()) - # control port is off - self.assertFalse(self.gui.connection_type_control_port_radio.isChecked()) - # auth options are still present - self.assertTrue(self.gui.authenticate_no_auth_radio.isVisible()) - self.assertTrue(self.gui.authenticate_password_radio.isVisible()) - - # re-enable bundled mode - QtTest.QTest.mouseClick(self.gui.connection_type_bundled_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.connection_type_bundled_radio.height()/2)) - # go back to custom bridges - QtTest.QTest.mouseClick(self.gui.tor_bridges_use_custom_radio, QtCore.Qt.LeftButton, pos=QtCore.QPoint(2,self.gui.tor_bridges_use_custom_radio.height()/2)) - self.assertTrue(self.gui.tor_bridges_use_custom_radio.isChecked()) - self.assertTrue(self.gui.tor_bridges_use_custom_textbox.isVisible()) - self.assertFalse(self.gui.tor_bridges_use_obfs4_radio.isChecked()) - self.gui.tor_bridges_use_custom_textbox.setPlainText('94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\n148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\n93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3') - - # Test that the Settings Dialog can save the settings and close itself - QtTest.QTest.mouseClick(self.gui.save_button, QtCore.Qt.LeftButton) - self.assertFalse(self.gui.isVisible()) - - # Test our settings are reflected in the settings json - with open('/tmp/settings.json') as f: - data = json.load(f) - - self.assertTrue(data["public_mode"]) - self.assertTrue(data["shutdown_timeout"]) - self.assertTrue(data["use_legacy_v2_onions"]) - self.assertTrue(data["save_private_key"]) - self.assertTrue(data["use_stealth"]) - self.assertEqual(data["downloads_dir"], "/tmp/OnionShareSettingsTest") - self.assertFalse(data["close_after_first_download"]) - self.assertEqual(data["connection_type"], "bundled") - self.assertFalse(data["tor_bridges_use_obfs4"]) - self.assertEqual(data["tor_bridges_use_custom_bridges"], "Bridge 94.242.249.2:83 E25A95F1DADB739F0A83EB0223A37C02FD519306\nBridge 148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC\nBridge 93.80.47.217:41727 A6A0D497D98097FCFE91D639548EE9E34C15CDD3\n") - -if __name__ == "__main__": - unittest.main() diff --git a/tests/local_onionshare_settings_dialog_v3_tor_test.py b/tests/local_onionshare_settings_dialog_v3_tor_test.py new file mode 100644 index 00000000..cb10f8c9 --- /dev/null +++ b/tests/local_onionshare_settings_dialog_v3_tor_test.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import unittest + +from onionshare import strings +from .SettingsGuiBaseTest import SettingsGuiBaseTest, OnionStub + + +class SettingsGuiTest(unittest.TestCase, SettingsGuiBaseTest): + @classmethod + def setUpClass(cls): + cls.gui = SettingsGuiBaseTest.set_up() + + @classmethod + def tearDownClass(cls): + SettingsGuiBaseTest.tear_down() + + def test_gui_v3_tor(self): + self.gui.onion = OnionStub(True, True) + self.gui.reload_settings() + self.run_settings_gui_tests() + + +if __name__ == "__main__": + unittest.main()