From 55d6ac4e3d02e02d718569ee0c718ad8dcbe3eab Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 20 Oct 2021 18:56:37 -0700 Subject: [PATCH 01/56] Refactor SettingsDialog into SettingsTab --- desktop/src/onionshare/main_window.py | 19 ++-- .../{settings_dialog.py => settings_tab.py} | 78 +++------------- desktop/src/onionshare/tab_widget.py | 91 ++++++++++++++----- ...settings_dialog.py => tor_settings_tab.py} | 48 +++++----- 4 files changed, 115 insertions(+), 121 deletions(-) rename desktop/src/onionshare/{settings_dialog.py => settings_tab.py} (84%) rename desktop/src/onionshare/{tor_settings_dialog.py => tor_settings_tab.py} (95%) diff --git a/desktop/src/onionshare/main_window.py b/desktop/src/onionshare/main_window.py index c125741c..0f11cf8e 100644 --- a/desktop/src/onionshare/main_window.py +++ b/desktop/src/onionshare/main_window.py @@ -24,8 +24,6 @@ from PySide2 import QtCore, QtWidgets, QtGui from . import strings from .tor_connection_dialog import TorConnectionDialog -from .tor_settings_dialog import TorSettingsDialog -from .settings_dialog import SettingsDialog from .widgets import Alert from .update_checker import UpdateThread from .tab_widget import TabWidget @@ -245,21 +243,22 @@ class MainWindow(QtWidgets.QMainWindow): def open_tor_settings(self): """ - Open the TorSettingsDialog. + Open the TorSettingsTab """ self.common.log("MainWindow", "open_tor_settings") - d = TorSettingsDialog(self.common) - d.settings_saved.connect(self.settings_have_changed) - d.exec_() + # d = TorSettingsDialog(self.common) + # d.settings_saved.connect(self.settings_have_changed) + # d.exec_() def open_settings(self): """ - Open the SettingsDialog. + Open the SettingsTab """ self.common.log("MainWindow", "open_settings") - d = SettingsDialog(self.common) - d.settings_saved.connect(self.settings_have_changed) - d.exec_() + self.tabs.open_settings_tab() + # d = SettingsDialog(self.common) + # d.settings_saved.connect(self.settings_have_changed) + # d.exec_() def settings_have_changed(self): self.common.log("OnionShareGui", "settings_have_changed") diff --git a/desktop/src/onionshare/settings_dialog.py b/desktop/src/onionshare/settings_tab.py similarity index 84% rename from desktop/src/onionshare/settings_dialog.py rename to desktop/src/onionshare/settings_tab.py index b1003386..251783aa 100644 --- a/desktop/src/onionshare/settings_dialog.py +++ b/desktop/src/onionshare/settings_tab.py @@ -19,57 +19,34 @@ along with this program. If not, see . """ from PySide2 import QtCore, QtWidgets, QtGui -from PySide2.QtCore import Slot, Qt -from PySide2.QtGui import QPalette, QColor import sys import platform import datetime import re import os from onionshare_cli.settings import Settings -from onionshare_cli.onion import ( - Onion, - TorErrorInvalidSetting, - TorErrorAutomatic, - TorErrorSocketPort, - TorErrorSocketFile, - TorErrorMissingPassword, - TorErrorUnreadableCookieFile, - TorErrorAuthError, - TorErrorProtocolError, - BundledTorTimeout, - BundledTorBroken, - TorTooOldEphemeral, - TorTooOldStealth, - PortNotAvailable, -) from . import strings from .widgets import Alert from .update_checker import UpdateThread -from .tor_connection_dialog import TorConnectionDialog from .gui_common import GuiCommon -class SettingsDialog(QtWidgets.QDialog): +class SettingsTab(QtWidgets.QWidget): """ Settings dialog. """ settings_saved = QtCore.Signal() - def __init__(self, common): - super(SettingsDialog, self).__init__() + def __init__(self, common, tab_id): + super(SettingsTab, self).__init__() self.common = common - - self.common.log("SettingsDialog", "__init__") - - self.setModal(True) - self.setWindowTitle(strings._("gui_settings_window_title")) - self.setWindowIcon(QtGui.QIcon(GuiCommon.get_resource_path("images/logo.png"))) + self.common.log("SettingsTab", "__init__") self.system = platform.system() + self.tab_id = tab_id # Automatic updates options @@ -146,31 +123,26 @@ class SettingsDialog(QtWidgets.QDialog): # Buttons self.save_button = QtWidgets.QPushButton(strings._("gui_settings_button_save")) self.save_button.clicked.connect(self.save_clicked) - self.cancel_button = QtWidgets.QPushButton( - strings._("gui_settings_button_cancel") - ) - self.cancel_button.clicked.connect(self.cancel_clicked) buttons_layout = QtWidgets.QHBoxLayout() buttons_layout.addStretch() buttons_layout.addWidget(self.save_button) - buttons_layout.addWidget(self.cancel_button) # Layout layout = QtWidgets.QVBoxLayout() + layout.addStretch() layout.addWidget(autoupdate_group) if autoupdate_group.isVisible(): layout.addSpacing(20) layout.addLayout(language_layout) layout.addLayout(theme_layout) layout.addSpacing(20) - layout.addStretch() layout.addWidget(version_label) layout.addWidget(help_label) layout.addSpacing(20) layout.addLayout(buttons_layout) + layout.addStretch() self.setLayout(layout) - self.cancel_button.setFocus() self.reload_settings() @@ -199,7 +171,7 @@ class SettingsDialog(QtWidgets.QDialog): """ Check for Updates button clicked. Manually force an update check. """ - self.common.log("SettingsDialog", "check_for_updates") + self.common.log("SettingsTab", "check_for_updates") # Disable buttons self._disable_buttons() self.common.gui.qtapp.processEvents() @@ -261,7 +233,7 @@ class SettingsDialog(QtWidgets.QDialog): """ Save button clicked. Save current settings to disk. """ - self.common.log("SettingsDialog", "save_clicked") + self.common.log("SettingsTab", "save_clicked") def changed(s1, s2, keys): """ @@ -301,30 +273,12 @@ class SettingsDialog(QtWidgets.QDialog): self.settings_saved.emit() self.close() - def cancel_clicked(self): - """ - Cancel button clicked. - """ - self.common.log("SettingsDialog", "cancel_clicked") - if ( - not self.common.gui.local_only - and not self.common.gui.onion.is_authenticated() - ): - Alert( - self.common, - strings._("gui_tor_connection_canceled"), - QtWidgets.QMessageBox.Warning, - ) - sys.exit() - else: - self.close() - def help_clicked(self): """ Help button clicked. """ - self.common.log("SettingsDialog", "help_clicked") - SettingsDialog.open_help() + self.common.log("SettingsTab", "help_clicked") + SettingsTab.open_help() @staticmethod def open_help(): @@ -335,7 +289,7 @@ class SettingsDialog(QtWidgets.QDialog): """ Return a Settings object that's full of values from the settings dialog. """ - self.common.log("SettingsDialog", "settings_from_fields") + self.common.log("SettingsTab", "settings_from_fields") settings = Settings(self.common) settings.load() # To get the last update timestamp @@ -351,7 +305,7 @@ class SettingsDialog(QtWidgets.QDialog): return settings def _update_autoupdate_timestamp(self, autoupdate_timestamp): - self.common.log("SettingsDialog", "_update_autoupdate_timestamp") + self.common.log("SettingsTab", "_update_autoupdate_timestamp") if autoupdate_timestamp: dt = datetime.datetime.fromtimestamp(autoupdate_timestamp) @@ -363,18 +317,16 @@ class SettingsDialog(QtWidgets.QDialog): ) def _disable_buttons(self): - self.common.log("SettingsDialog", "_disable_buttons") + self.common.log("SettingsTab", "_disable_buttons") self.check_for_updates_button.setEnabled(False) self.save_button.setEnabled(False) - self.cancel_button.setEnabled(False) def _enable_buttons(self): - self.common.log("SettingsDialog", "_enable_buttons") + self.common.log("SettingsTab", "_enable_buttons") # We can't check for updates if we're still not connected to Tor if not self.common.gui.onion.connected_to_tor: self.check_for_updates_button.setEnabled(False) else: self.check_for_updates_button.setEnabled(True) self.save_button.setEnabled(True) - self.cancel_button.setEnabled(True) diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index a955ea53..daf878d7 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -26,6 +26,8 @@ from . import strings from .tab import Tab from .threads import EventHandlerThread from .gui_common import GuiCommon +from .tor_settings_tab import TorSettingsTab +from .settings_tab import SettingsTab class TabWidget(QtWidgets.QTabWidget): @@ -116,6 +118,11 @@ class TabWidget(QtWidgets.QTabWidget): # Active tab was changed tab_id = self.currentIndex() self.common.log("TabWidget", "tab_changed", f"Tab was changed to {tab_id}") + + # If it's Settings or Tor Settings, ignore + if self.is_settings_tab(tab_id): + return + try: mode = self.tabs[tab_id].get_mode() if mode: @@ -160,20 +167,7 @@ class TabWidget(QtWidgets.QTabWidget): # In macOS, manually create a close button because tabs don't seem to have them otherwise if self.common.platform == "Darwin": - - def close_tab(): - self.tabBar().tabCloseRequested.emit(self.indexOf(tab)) - - tab.close_button = QtWidgets.QPushButton() - tab.close_button.setFlat(True) - tab.close_button.setFixedWidth(40) - tab.close_button.setIcon( - QtGui.QIcon(GuiCommon.get_resource_path("images/close_tab.png")) - ) - tab.close_button.clicked.connect(close_tab) - self.tabBar().setTabButton( - index, QtWidgets.QTabBar.RightSide, tab.close_button - ) + self.macos_create_close_button(tab, index) tab.init(mode_settings) @@ -187,6 +181,25 @@ class TabWidget(QtWidgets.QTabWidget): # Bring the window to front, in case this is being added by an event self.bring_to_front.emit() + def open_settings_tab(self): + self.common.log("TabWidget", "open_settings_tab") + + # See if a settings tab is already open, and if so switch to it + for index in range(self.count()): + if self.is_settings_tab(index): + self.setCurrentIndex(index) + return + + settings_tab = SettingsTab(self.common, self.current_tab_id) + self.tabs[self.current_tab_id] = settings_tab + self.current_tab_id += 1 + index = self.addTab(settings_tab, strings._("gui_settings_window_title")) + self.setCurrentIndex(index) + + # In macOS, manually create a close button because tabs don't seem to have them otherwise + if self.common.platform == "Darwin": + self.macos_create_close_button(settings_tab, index) + def change_title(self, tab_id, title): shortened_title = title if len(shortened_title) > 11: @@ -224,9 +237,10 @@ class TabWidget(QtWidgets.QTabWidget): # Figure out the order of persistent tabs to save in settings persistent_tabs = [] for index in range(self.count()): - tab = self.widget(index) - if tab.settings.get("persistent", "enabled"): - persistent_tabs.append(tab.settings.id) + if not self.is_settings_tab(index): + tab = self.widget(index) + if tab.settings.get("persistent", "enabled"): + persistent_tabs.append(tab.settings.id) # Only save if tabs have actually moved if persistent_tabs != self.common.settings.get("persistent_tabs"): self.common.settings.set("persistent_tabs", persistent_tabs) @@ -235,11 +249,8 @@ class TabWidget(QtWidgets.QTabWidget): def close_tab(self, index): self.common.log("TabWidget", "close_tab", f"{index}") tab = self.widget(index) - if tab.close_tab(): - # If the tab is persistent, delete the settings file from disk - if tab.settings.get("persistent", "enabled"): - tab.settings.delete() + if self.is_settings_tab(index): # Remove the tab self.removeTab(index) del self.tabs[tab.tab_id] @@ -248,7 +259,21 @@ class TabWidget(QtWidgets.QTabWidget): if self.count() == 0: self.new_tab_clicked() - self.save_persistent_tabs() + else: + if tab.close_tab(): + # If the tab is persistent, delete the settings file from disk + if tab.settings.get("persistent", "enabled"): + tab.settings.delete() + + self.save_persistent_tabs() + + # Remove the tab + self.removeTab(index) + del self.tabs[tab.tab_id] + + # If the last tab is closed, open a new one + if self.count() == 0: + self.new_tab_clicked() def are_tabs_active(self): """ @@ -273,6 +298,28 @@ class TabWidget(QtWidgets.QTabWidget): super(TabWidget, self).resizeEvent(event) self.move_new_tab_button() + def macos_create_close_button(self, tab, index): + def close_tab(): + self.tabBar().tabCloseRequested.emit(self.indexOf(tab)) + + close_button = QtWidgets.QPushButton() + close_button.setFlat(True) + close_button.setFixedWidth(40) + close_button.setIcon( + QtGui.QIcon(GuiCommon.get_resource_path("images/close_tab.png")) + ) + close_button.clicked.connect(close_tab) + self.tabBar().setTabButton(index, QtWidgets.QTabBar.RightSide, tab.close_button) + + def is_settings_tab(self, tab_id): + if tab_id not in self.tabs: + return True + + return ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ) + class TabBar(QtWidgets.QTabBar): """ diff --git a/desktop/src/onionshare/tor_settings_dialog.py b/desktop/src/onionshare/tor_settings_tab.py similarity index 95% rename from desktop/src/onionshare/tor_settings_dialog.py rename to desktop/src/onionshare/tor_settings_tab.py index 38ff512a..279469df 100644 --- a/desktop/src/onionshare/tor_settings_dialog.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -34,25 +34,21 @@ from .moat_dialog import MoatDialog from .gui_common import GuiCommon -class TorSettingsDialog(QtWidgets.QDialog): +class TorSettingsTab(QtWidgets.QWidget): """ Settings dialog. """ settings_saved = QtCore.Signal() - def __init__(self, common): - super(TorSettingsDialog, self).__init__() + def __init__(self, common, tab_id): + super(TorSettingsTab, self).__init__() self.common = common - - self.common.log("TorSettingsDialog", "__init__") - - self.setModal(True) - self.setWindowTitle(strings._("gui_tor_settings_window_title")) - self.setWindowIcon(QtGui.QIcon(GuiCommon.get_resource_path("images/logo.png"))) + self.common.log("TorSettingsTab", "__init__") self.system = platform.system() + self.tab_id = tab_id # Connection type: either automatic, control port, or socket file @@ -443,7 +439,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Connection type bundled was toggled """ - self.common.log("TorSettingsDialog", "connection_type_bundled_toggled") + self.common.log("TorSettingsTab", "connection_type_bundled_toggled") if checked: self.tor_settings_group.hide() self.connection_type_socks.hide() @@ -495,7 +491,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Request new bridge button clicked """ - self.common.log("TorSettingsDialog", "bridge_moat_button_clicked") + self.common.log("TorSettingsTab", "bridge_moat_button_clicked") moat_dialog = MoatDialog(self.common) moat_dialog.got_bridges.connect(self.bridge_moat_got_bridges) @@ -505,7 +501,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Got new bridges from moat """ - self.common.log("TorSettingsDialog", "bridge_moat_got_bridges") + self.common.log("TorSettingsTab", "bridge_moat_got_bridges") self.bridge_moat_textbox.document().setPlainText(bridges) self.bridge_moat_textbox.show() @@ -522,7 +518,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Connection type automatic was toggled. If checked, hide authentication fields. """ - self.common.log("TorSettingsDialog", "connection_type_automatic_toggled") + self.common.log("TorSettingsTab", "connection_type_automatic_toggled") if checked: self.tor_settings_group.hide() self.connection_type_socks.hide() @@ -533,7 +529,7 @@ class TorSettingsDialog(QtWidgets.QDialog): Connection type control port was toggled. If checked, show extra fields for Tor control address and port. If unchecked, hide those extra fields. """ - self.common.log("TorSettingsDialog", "connection_type_control_port_toggled") + self.common.log("TorSettingsTab", "connection_type_control_port_toggled") if checked: self.tor_settings_group.show() self.connection_type_control_port_extras.show() @@ -547,7 +543,7 @@ class TorSettingsDialog(QtWidgets.QDialog): Connection type socket file was toggled. If checked, show extra fields for socket file. If unchecked, hide those extra fields. """ - self.common.log("TorSettingsDialog", "connection_type_socket_file_toggled") + self.common.log("TorSettingsTab", "connection_type_socket_file_toggled") if checked: self.tor_settings_group.show() self.connection_type_socket_file_extras.show() @@ -560,7 +556,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Authentication option no authentication was toggled. """ - self.common.log("TorSettingsDialog", "authenticate_no_auth_toggled") + self.common.log("TorSettingsTab", "authenticate_no_auth_toggled") if checked: self.authenticate_password_extras.hide() else: @@ -571,7 +567,7 @@ class TorSettingsDialog(QtWidgets.QDialog): Test Tor Settings button clicked. With the given settings, see if we can successfully connect and authenticate to Tor. """ - self.common.log("TorSettingsDialog", "test_tor_clicked") + self.common.log("TorSettingsTab", "test_tor_clicked") settings = self.settings_from_fields() if not settings: return @@ -605,7 +601,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Save button clicked. Save current settings to disk. """ - self.common.log("TorSettingsDialog", "save_clicked") + self.common.log("TorSettingsTab", "save_clicked") def changed(s1, s2, keys): """ @@ -628,7 +624,7 @@ class TorSettingsDialog(QtWidgets.QDialog): if not self.common.gui.local_only: if self.common.gui.onion.is_authenticated(): self.common.log( - "TorSettingsDialog", "save_clicked", "Connected to Tor" + "TorSettingsTab", "save_clicked", "Connected to Tor" ) if changed( @@ -654,7 +650,7 @@ class TorSettingsDialog(QtWidgets.QDialog): else: self.common.log( - "TorSettingsDialog", "save_clicked", "Not connected to Tor" + "TorSettingsTab", "save_clicked", "Not connected to Tor" ) # Tor isn't connected, so try connecting reboot_onion = True @@ -663,7 +659,7 @@ class TorSettingsDialog(QtWidgets.QDialog): if reboot_onion: # Reinitialize the Onion object self.common.log( - "TorSettingsDialog", "save_clicked", "rebooting the Onion" + "TorSettingsTab", "save_clicked", "rebooting the Onion" ) self.common.gui.onion.cleanup() @@ -671,7 +667,7 @@ class TorSettingsDialog(QtWidgets.QDialog): tor_con.start() self.common.log( - "TorSettingsDialog", + "TorSettingsTab", "save_clicked", f"Onion done rebooting, connected to Tor: {self.common.gui.onion.connected_to_tor}", ) @@ -694,7 +690,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Cancel button clicked. """ - self.common.log("TorSettingsDialog", "cancel_clicked") + self.common.log("TorSettingsTab", "cancel_clicked") if ( not self.common.gui.local_only and not self.common.gui.onion.is_authenticated() @@ -712,7 +708,7 @@ class TorSettingsDialog(QtWidgets.QDialog): """ Return a Settings object that's full of values from the settings dialog. """ - self.common.log("TorSettingsDialog", "settings_from_fields") + self.common.log("TorSettingsTab", "settings_from_fields") settings = Settings(self.common) settings.load() # To get the last update timestamp @@ -833,13 +829,13 @@ class TorSettingsDialog(QtWidgets.QDialog): return settings def closeEvent(self, e): - self.common.log("TorSettingsDialog", "closeEvent") + self.common.log("TorSettingsTab", "closeEvent") # On close, if Tor isn't connected, then quit OnionShare altogether if not self.common.gui.local_only: if not self.common.gui.onion.is_authenticated(): self.common.log( - "TorSettingsDialog", + "TorSettingsTab", "closeEvent", "Closing while not connected to Tor", ) From 0fb7d7d761397d6240b76faaaafaa8bf6661280c Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 20 Oct 2021 19:03:24 -0700 Subject: [PATCH 02/56] Refactor TorSettingsDialog into TorSettingsTab --- desktop/src/onionshare/main_window.py | 7 +--- desktop/src/onionshare/tab_widget.py | 37 +++++++++++++++++---- desktop/src/onionshare/tor_settings_tab.py | 38 ++++++++++------------ 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/desktop/src/onionshare/main_window.py b/desktop/src/onionshare/main_window.py index 0f11cf8e..4a9d0c7e 100644 --- a/desktop/src/onionshare/main_window.py +++ b/desktop/src/onionshare/main_window.py @@ -246,9 +246,7 @@ class MainWindow(QtWidgets.QMainWindow): Open the TorSettingsTab """ self.common.log("MainWindow", "open_tor_settings") - # d = TorSettingsDialog(self.common) - # d.settings_saved.connect(self.settings_have_changed) - # d.exec_() + self.tabs.open_tor_settings_tab() def open_settings(self): """ @@ -256,9 +254,6 @@ class MainWindow(QtWidgets.QMainWindow): """ self.common.log("MainWindow", "open_settings") self.tabs.open_settings_tab() - # d = SettingsDialog(self.common) - # d.settings_saved.connect(self.settings_have_changed) - # d.exec_() def settings_have_changed(self): self.common.log("OnionShareGui", "settings_have_changed") diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index daf878d7..fe6d08dc 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -92,8 +92,9 @@ class TabWidget(QtWidgets.QTabWidget): # Clean up each tab for index in range(self.count()): - tab = self.widget(index) - tab.cleanup() + if not self.is_settings_tab(index): + tab = self.widget(index) + tab.cleanup() def move_new_tab_button(self): # Find the width of all tabs @@ -186,7 +187,7 @@ class TabWidget(QtWidgets.QTabWidget): # See if a settings tab is already open, and if so switch to it for index in range(self.count()): - if self.is_settings_tab(index): + if type(self.tabs[index]) is SettingsTab: self.setCurrentIndex(index) return @@ -200,6 +201,27 @@ class TabWidget(QtWidgets.QTabWidget): if self.common.platform == "Darwin": self.macos_create_close_button(settings_tab, index) + def open_tor_settings_tab(self): + self.common.log("TabWidget", "open_tor_settings_tab") + + # See if a settings tab is already open, and if so switch to it + for index in range(self.count()): + if type(self.tabs[index]) is TorSettingsTab: + self.setCurrentIndex(index) + return + + tor_settings_tab = TorSettingsTab(self.common, self.current_tab_id) + self.tabs[self.current_tab_id] = tor_settings_tab + self.current_tab_id += 1 + index = self.addTab( + tor_settings_tab, strings._("gui_tor_settings_window_title") + ) + self.setCurrentIndex(index) + + # In macOS, manually create a close button because tabs don't seem to have them otherwise + if self.common.platform == "Darwin": + self.macos_create_close_button(tor_settings_tab, index) + def change_title(self, tab_id, title): shortened_title = title if len(shortened_title) > 11: @@ -280,10 +302,11 @@ class TabWidget(QtWidgets.QTabWidget): See if there are active servers in any open tabs """ for tab_id in self.tabs: - mode = self.tabs[tab_id].get_mode() - if mode: - if mode.server_status.status != mode.server_status.STATUS_STOPPED: - return True + if not self.is_settings_tab(tab_id): + mode = self.tabs[tab_id].get_mode() + if mode: + if mode.server_status.status != mode.server_status.STATUS_STOPPED: + return True return False def paintEvent(self, event): diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 279469df..e46fa729 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -319,15 +319,10 @@ class TorSettingsTab(QtWidgets.QWidget): self.test_tor_button.clicked.connect(self.test_tor_clicked) self.save_button = QtWidgets.QPushButton(strings._("gui_settings_button_save")) self.save_button.clicked.connect(self.save_clicked) - self.cancel_button = QtWidgets.QPushButton( - strings._("gui_settings_button_cancel") - ) - self.cancel_button.clicked.connect(self.cancel_clicked) buttons_layout = QtWidgets.QHBoxLayout() buttons_layout.addWidget(self.test_tor_button) buttons_layout.addStretch() buttons_layout.addWidget(self.save_button) - buttons_layout.addWidget(self.cancel_button) # Layout layout = QtWidgets.QVBoxLayout() @@ -337,7 +332,6 @@ class TorSettingsTab(QtWidgets.QWidget): layout.addLayout(buttons_layout) self.setLayout(layout) - self.cancel_button.setFocus() self.reload_settings() @@ -686,23 +680,27 @@ class TorSettingsTab(QtWidgets.QWidget): self.settings_saved.emit() self.close() - def cancel_clicked(self): + def close_tab(self): """ - Cancel button clicked. + Tab is closed """ self.common.log("TorSettingsTab", "cancel_clicked") - if ( - not self.common.gui.local_only - and not self.common.gui.onion.is_authenticated() - ): - Alert( - self.common, - strings._("gui_tor_connection_canceled"), - QtWidgets.QMessageBox.Warning, - ) - sys.exit() - else: - self.close() + return True + + # TODO: Figure out flow for first connecting, when closing settings when not connected + + # if ( + # not self.common.gui.local_only + # and not self.common.gui.onion.is_authenticated() + # ): + # Alert( + # self.common, + # strings._("gui_tor_connection_canceled"), + # QtWidgets.QMessageBox.Warning, + # ) + # sys.exit() + # else: + # self.close() def settings_from_fields(self): """ From 3b9cc80160bf0658e6b7f908cac325f7c99b9041 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 20 Oct 2021 20:33:16 -0700 Subject: [PATCH 03/56] 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. From 556aedf08d296d7fcb83c4f1933689f250561bd2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 20 Oct 2021 21:06:38 -0700 Subject: [PATCH 04/56] Fix mixup with tab_ids and their indicies, so tabs open and close smoothly --- desktop/src/onionshare/settings_tab.py | 10 +++++--- desktop/src/onionshare/tab_widget.py | 30 +++++++++++++++++----- desktop/src/onionshare/tor_settings_tab.py | 14 +++++----- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/desktop/src/onionshare/settings_tab.py b/desktop/src/onionshare/settings_tab.py index 251783aa..c01d2662 100644 --- a/desktop/src/onionshare/settings_tab.py +++ b/desktop/src/onionshare/settings_tab.py @@ -37,7 +37,7 @@ class SettingsTab(QtWidgets.QWidget): Settings dialog. """ - settings_saved = QtCore.Signal() + close_this_tab = QtCore.Signal() def __init__(self, common, tab_id): super(SettingsTab, self).__init__() @@ -94,6 +94,7 @@ class SettingsTab(QtWidgets.QWidget): locale = language_names_to_locales[language_name] self.language_combobox.addItem(language_name, locale) language_layout = QtWidgets.QHBoxLayout() + language_layout.addStretch() language_layout.addWidget(language_label) language_layout.addWidget(self.language_combobox) language_layout.addStretch() @@ -108,6 +109,7 @@ class SettingsTab(QtWidgets.QWidget): ] self.theme_combobox.addItems(theme_choices) theme_layout = QtWidgets.QHBoxLayout() + theme_layout.addStretch() theme_layout.addWidget(theme_label) theme_layout.addWidget(self.theme_combobox) theme_layout.addStretch() @@ -116,7 +118,9 @@ class SettingsTab(QtWidgets.QWidget): version_label = QtWidgets.QLabel( strings._("gui_settings_version_label").format(self.common.version) ) + version_label.setAlignment(QtCore.Qt.AlignHCenter) help_label = QtWidgets.QLabel(strings._("gui_settings_help_label")) + help_label.setAlignment(QtCore.Qt.AlignHCenter) help_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) help_label.setOpenExternalLinks(True) @@ -126,6 +130,7 @@ class SettingsTab(QtWidgets.QWidget): buttons_layout = QtWidgets.QHBoxLayout() buttons_layout.addStretch() buttons_layout.addWidget(self.save_button) + buttons_layout.addStretch() # Layout layout = QtWidgets.QVBoxLayout() @@ -270,8 +275,7 @@ class SettingsTab(QtWidgets.QWidget): # Save the new settings settings.save() - self.settings_saved.emit() - self.close() + self.close_this_tab.emit() def help_clicked(self): """ diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index fe6d08dc..36a6c22f 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -186,12 +186,13 @@ class TabWidget(QtWidgets.QTabWidget): self.common.log("TabWidget", "open_settings_tab") # See if a settings tab is already open, and if so switch to it - for index in range(self.count()): - if type(self.tabs[index]) is SettingsTab: - self.setCurrentIndex(index) + for tab_id in self.tabs: + if type(self.tabs[tab_id]) is SettingsTab: + self.setCurrentIndex(self.indexOf(self.tabs[tab_id])) return settings_tab = SettingsTab(self.common, self.current_tab_id) + settings_tab.close_this_tab.connect(self.close_settings_tab) self.tabs[self.current_tab_id] = settings_tab self.current_tab_id += 1 index = self.addTab(settings_tab, strings._("gui_settings_window_title")) @@ -205,12 +206,13 @@ class TabWidget(QtWidgets.QTabWidget): self.common.log("TabWidget", "open_tor_settings_tab") # See if a settings tab is already open, and if so switch to it - for index in range(self.count()): - if type(self.tabs[index]) is TorSettingsTab: - self.setCurrentIndex(index) + for tab_id in self.tabs: + if type(self.tabs[tab_id]) is TorSettingsTab: + self.setCurrentIndex(self.indexOf(self.tabs[tab_id])) return tor_settings_tab = TorSettingsTab(self.common, self.current_tab_id) + tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab) self.tabs[self.current_tab_id] = tor_settings_tab self.current_tab_id += 1 index = self.addTab( @@ -297,6 +299,22 @@ class TabWidget(QtWidgets.QTabWidget): if self.count() == 0: self.new_tab_clicked() + def close_settings_tab(self): + self.common.log("TabWidget", "close_settings_tab") + for tab_id in self.tabs: + if type(self.tabs[tab_id]) is SettingsTab: + index = self.indexOf(self.tabs[tab_id]) + self.close_tab(index) + return + + def close_tor_settings_tab(self): + self.common.log("TabWidget", "close_tor_settings_tab") + for tab_id in self.tabs: + if type(self.tabs[tab_id]) is TorSettingsTab: + index = self.indexOf(self.tabs[tab_id]) + self.close_tab(index) + return + def are_tabs_active(self): """ See if there are active servers in any open tabs diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 4b84e923..e2fcead4 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -39,7 +39,7 @@ class TorSettingsTab(QtWidgets.QWidget): Settings dialog. """ - settings_saved = QtCore.Signal() + close_this_tab = QtCore.Signal() def __init__(self, common, tab_id): super(TorSettingsTab, self).__init__() @@ -339,7 +339,6 @@ class TorSettingsTab(QtWidgets.QWidget): # Layout layout = QtWidgets.QVBoxLayout() - layout.addStretch() layout.addLayout(columns_layout) layout.addWidget(self.tor_con) layout.addStretch() @@ -582,6 +581,7 @@ class TorSettingsTab(QtWidgets.QWidget): return self.test_tor_button.hide() + self.save_button.hide() onion = Onion( self.common, @@ -616,6 +616,7 @@ class TorSettingsTab(QtWidgets.QWidget): """ self.tor_con.hide() self.test_tor_button.show() + self.save_button.show() def save_clicked(self): """ @@ -696,15 +697,12 @@ class TorSettingsTab(QtWidgets.QWidget): self.common.gui.onion.is_authenticated() and not tor_con.wasCanceled() ): - self.settings_saved.emit() - self.close() + self.close_this_tab.emit() else: - self.settings_saved.emit() - self.close() + self.close_this_tab.emit() else: - self.settings_saved.emit() - self.close() + self.close_this_tab.emit() def close_tab(self): """ From f3f25166d487b2a7326ae0f1743ae42ec78f143b Mon Sep 17 00:00:00 2001 From: Benjamin Erhart Date: Fri, 22 Oct 2021 12:21:22 +0200 Subject: [PATCH 05/56] Fixed send.js table sorting. Looks like it stopped working a long time ago. --- .../resources/static/js/send.js | 24 +++++++++++-------- .../resources/templates/send.html | 14 +++++------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/cli/onionshare_cli/resources/static/js/send.js b/cli/onionshare_cli/resources/static/js/send.js index 43e9892d..22844ab9 100644 --- a/cli/onionshare_cli/resources/static/js/send.js +++ b/cli/onionshare_cli/resources/static/js/send.js @@ -11,7 +11,7 @@ function unhumanize(text) { } } function sortTable(n) { - var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; + var table, rows, switching, i, x, y, valX, valY, shouldSwitch, dir, switchcount = 0; table = document.getElementById("file-list"); switching = true; // Set the sorting direction to ascending: @@ -21,7 +21,7 @@ function sortTable(n) { while (switching) { // Start by saying: no switching is done: switching = false; - rows = table.getElementsByTagName("TR"); + rows = table.getElementsByClassName("row"); /* Loop through all table rows (except the first, which contains table headers): */ for (i = 1; i < (rows.length - 1); i++) { @@ -29,18 +29,22 @@ function sortTable(n) { shouldSwitch = false; /* Get the two elements you want to compare, one from current row and one from the next: */ - x = rows[i].getElementsByTagName("TD")[n]; - y = rows[i + 1].getElementsByTagName("TD")[n]; + x = rows[i].getElementsByClassName("cell-data")[n]; + y = rows[i + 1].getElementsByClassName("cell-data")[n]; + + valX = x.classList.contains("size") ? unhumanize(x.innerHTML.toLowerCase()) : x.innerHTML; + valY = y.classList.contains("size") ? unhumanize(y.innerHTML.toLowerCase()) : y.innerHTML; + /* Check if the two rows should switch place, based on the direction, asc or desc: */ if (dir == "asc") { - if (unhumanize(x.innerHTML.toLowerCase()) > unhumanize(y.innerHTML.toLowerCase())) { - // If so, mark as a switch and break the loop: - shouldSwitch= true; - break; - } + if (valX > valY) { + // If so, mark as a switch and break the loop: + shouldSwitch= true; + break; + } } else if (dir == "desc") { - if (unhumanize(x.innerHTML.toLowerCase()) < unhumanize(y.innerHTML.toLowerCase())) { + if (valX < valY) { // If so, mark as a switch and break the loop: shouldSwitch= true; break; diff --git a/cli/onionshare_cli/resources/templates/send.html b/cli/onionshare_cli/resources/templates/send.html index 5fc1ba1f..b1532cec 100644 --- a/cli/onionshare_cli/resources/templates/send.html +++ b/cli/onionshare_cli/resources/templates/send.html @@ -32,7 +32,7 @@ {% endif %}
-
+
Filename
Size
@@ -41,26 +41,26 @@ -
+
{% endfor %} {% for info in files %} -
+
{% if download_individual_files %} - {{ info.basename }} + {{ info.basename }} {% else %} - {{ info.basename }} + {{ info.basename }} {% endif %}
-
{{ info.size_human }}
+
{{ info.size_human }}
{% endfor %}
From c3eeaefb9f1f67afc81c75068e6e7216d9fe8bfb Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 17:35:24 -0700 Subject: [PATCH 06/56] In CLI get_tor_path, stop trying to look in resources first --- cli/onionshare_cli/common.py | 58 ++++++++++-------------------------- 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index 945a75bb..07e0aa0a 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -309,30 +309,14 @@ class Common: def get_tor_paths(self): if self.platform == "Linux": - # Look in resources first - base_path = self.get_resource_path("tor") - if os.path.exists(base_path): - self.log( - "Common", "get_tor_paths", f"using tor binaries in {base_path}" - ) - tor_path = os.path.join(base_path, "tor") - tor_geo_ip_file_path = os.path.join(base_path, "geoip") - tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6") - obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy") - snowflake_file_path = os.path.join(base_path, "snowflake-client") - else: - # Fallback to looking in the path - self.log( - "Common", "get_tor_paths", f"using tor binaries in system path" - ) - tor_path = shutil.which("tor") - if not tor_path: - raise CannotFindTor() - obfs4proxy_file_path = shutil.which("obfs4proxy") - snowflake_file_path = shutil.which("snowflake-client") - prefix = os.path.dirname(os.path.dirname(tor_path)) - tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") - tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6") + tor_path = shutil.which("tor") + if not tor_path: + raise CannotFindTor() + obfs4proxy_file_path = shutil.which("obfs4proxy") + snowflake_file_path = shutil.which("snowflake-client") + prefix = os.path.dirname(os.path.dirname(tor_path)) + tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") + tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6") elif self.platform == "Windows": base_path = self.get_resource_path("tor") tor_path = os.path.join(base_path, "Tor", "tor.exe") @@ -341,24 +325,14 @@ class Common: tor_geo_ip_file_path = os.path.join(base_path, "Data", "Tor", "geoip") tor_geo_ipv6_file_path = os.path.join(base_path, "Data", "Tor", "geoip6") elif self.platform == "Darwin": - # Look in resources first - base_path = self.get_resource_path("tor") - if os.path.exists(base_path): - tor_path = os.path.join(base_path, "tor") - tor_geo_ip_file_path = os.path.join(base_path, "geoip") - tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6") - obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy") - snowflake_file_path = os.path.join(base_path, "snowflake-client") - else: - # Fallback to looking in the path - tor_path = shutil.which("tor") - if not tor_path: - raise CannotFindTor() - obfs4proxy_file_path = shutil.which("obfs4proxy") - snowflake_file_path = shutil.which("snowflake-client") - prefix = os.path.dirname(os.path.dirname(tor_path)) - tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") - tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6") + tor_path = shutil.which("tor") + if not tor_path: + raise CannotFindTor() + obfs4proxy_file_path = shutil.which("obfs4proxy") + snowflake_file_path = shutil.which("snowflake-client") + prefix = os.path.dirname(os.path.dirname(tor_path)) + tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") + tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6") elif self.platform == "BSD": tor_path = "/usr/local/bin/tor" tor_geo_ip_file_path = "/usr/local/share/tor/geoip" From 1420b28d2332cb1f16a7f0ca5ae11c167e4a1eb0 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 18:55:25 -0700 Subject: [PATCH 07/56] Saving tor settings connects to tor in the widget, not the dialog. And erros are displayed in a label, not an alert --- desktop/src/onionshare/gui_common.py | 6 +- desktop/src/onionshare/moat_dialog.py | 2 +- .../src/onionshare/resources/locale/en.json | 2 +- desktop/src/onionshare/settings_tab.py | 4 - .../src/onionshare/tor_connection_dialog.py | 26 +--- desktop/src/onionshare/tor_settings_tab.py | 130 +++++++++++------- 6 files changed, 88 insertions(+), 82 deletions(-) diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index 0f1dd46e..f2fd6ef0 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -392,10 +392,10 @@ class GuiCommon: QPushButton { padding: 5px 10px; }""", - # Moat dialog - "moat_error": """ + # Tor Settings dialogs + "tor_settings_error": """ QLabel { - color: #990000; + color: #FF0000; } """, } diff --git a/desktop/src/onionshare/moat_dialog.py b/desktop/src/onionshare/moat_dialog.py index 28193c25..56e872b5 100644 --- a/desktop/src/onionshare/moat_dialog.py +++ b/desktop/src/onionshare/moat_dialog.py @@ -67,7 +67,7 @@ class MoatDialog(QtWidgets.QDialog): # Error label self.error_label = QtWidgets.QLabel() - self.error_label.setStyleSheet(self.common.gui.css["moat_error"]) + self.error_label.setStyleSheet(self.common.gui.css["tor_settings_error"]) self.error_label.hide() # Buttons diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index a9fb562a..3f380466 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -71,7 +71,7 @@ "gui_settings_bridge_custom_radio_option": "Provide a bridge you learned about from a trusted source", "gui_settings_bridge_custom_placeholder": "type address:port (one per line)", "gui_settings_moat_bridges_invalid": "You have not requested a bridge from torproject.org yet.", - "gui_settings_tor_bridges_invalid": "None of the bridges you added work.\nDouble-check them or add others.", + "gui_settings_tor_bridges_invalid": "None of the bridges you added work. Double-check them or add others.", "gui_settings_button_save": "Save", "gui_settings_button_cancel": "Cancel", "gui_settings_button_help": "Help", diff --git a/desktop/src/onionshare/settings_tab.py b/desktop/src/onionshare/settings_tab.py index c01d2662..c792d94e 100644 --- a/desktop/src/onionshare/settings_tab.py +++ b/desktop/src/onionshare/settings_tab.py @@ -19,17 +19,13 @@ along with this program. If not, see . """ from PySide2 import QtCore, QtWidgets, QtGui -import sys import platform import datetime -import re -import os from onionshare_cli.settings import Settings from . import strings from .widgets import Alert from .update_checker import UpdateThread -from .gui_common import GuiCommon class SettingsTab(QtWidgets.QWidget): diff --git a/desktop/src/onionshare/tor_connection_dialog.py b/desktop/src/onionshare/tor_connection_dialog.py index 7ba7c800..3eb38876 100644 --- a/desktop/src/onionshare/tor_connection_dialog.py +++ b/desktop/src/onionshare/tor_connection_dialog.py @@ -164,7 +164,7 @@ class TorConnectionWidget(QtWidgets.QWidget): open_tor_settings = QtCore.Signal() success = QtCore.Signal() - fail = QtCore.Signal() + fail = QtCore.Signal(str) def __init__(self, common): super(TorConnectionWidget, self).__init__(None) @@ -231,7 +231,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 @@ -262,27 +262,7 @@ class TorConnectionWidget(QtWidgets.QWidget): 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() + self.fail.emit(msg) class TorConnectionThread(QtCore.QThread): diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index e2fcead4..be9dac37 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -29,9 +29,8 @@ from onionshare_cli.onion import Onion from . import strings from .widgets import Alert -from .tor_connection_dialog import TorConnectionDialog, TorConnectionWidget +from .tor_connection_dialog import TorConnectionWidget from .moat_dialog import MoatDialog -from .gui_common import GuiCommon class TorSettingsTab(QtWidgets.QWidget): @@ -319,10 +318,21 @@ class TorSettingsTab(QtWidgets.QWidget): columns_layout.addWidget(connection_type_radio_group) columns_layout.addSpacing(20) columns_layout.addLayout(connection_type_layout, stretch=1) + columns_wrapper = QtWidgets.QWidget() + columns_wrapper.setFixedHeight(400) + columns_wrapper.setLayout(columns_layout) # Tor connection widget self.tor_con = TorConnectionWidget(self.common) + self.tor_con.success.connect(self.tor_con_success) + self.tor_con.fail.connect(self.tor_con_fail) self.tor_con.hide() + self.tor_con_type = None + + # Error label + self.error_label = QtWidgets.QLabel() + self.error_label.setStyleSheet(self.common.gui.css["tor_settings_error"]) + self.error_label.setWordWrap(True) # Buttons self.test_tor_button = QtWidgets.QPushButton( @@ -332,18 +342,18 @@ 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.addStretch() + buttons_layout.addWidget(self.error_label, stretch=1) + buttons_layout.addSpacing(20) buttons_layout.addWidget(self.test_tor_button) buttons_layout.addWidget(self.save_button) - buttons_layout.addStretch() # Layout layout = QtWidgets.QVBoxLayout() - layout.addLayout(columns_layout) + layout.addWidget(columns_wrapper) + layout.addStretch() layout.addWidget(self.tor_con) layout.addStretch() layout.addLayout(buttons_layout) - layout.addStretch() self.setLayout(layout) @@ -576,6 +586,9 @@ class TorSettingsTab(QtWidgets.QWidget): successfully connect and authenticate to Tor. """ self.common.log("TorSettingsTab", "test_tor_clicked") + + self.error_label.setText("") + settings = self.settings_from_fields() if not settings: return @@ -583,40 +596,15 @@ class TorSettingsTab(QtWidgets.QWidget): self.test_tor_button.hide() self.save_button.hide() - onion = Onion( + self.test_onion = Onion( self.common, use_tmp_dir=True, get_tor_paths=self.common.gui.get_tor_paths, ) + self.tor_con_type = "test" 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: - Alert( - self.common, - strings._("settings_test_success").format( - onion.tor_version, - onion.supports_ephemeral, - onion.supports_stealth, - onion.supports_v3_onions, - ), - title=strings._("gui_settings_connection_type_test_button"), - ) - - # Clean up - onion.cleanup() - - def test_tor_button_finished(self): - """ - Finished testing tor connection. - """ - self.tor_con.hide() - self.test_tor_button.show() - self.save_button.show() + self.tor_con.start(settings, True, self.test_onion) def save_clicked(self): """ @@ -624,6 +612,8 @@ class TorSettingsTab(QtWidgets.QWidget): """ self.common.log("TorSettingsTab", "save_clicked") + self.error_label.setText("") + def changed(s1, s2, keys): """ Compare the Settings objects s1 and s2 and return true if any values @@ -684,26 +674,62 @@ class TorSettingsTab(QtWidgets.QWidget): ) self.common.gui.onion.cleanup() - tor_con = TorConnectionDialog(self.common, settings) - tor_con.start() - - self.common.log( - "TorSettingsTab", - "save_clicked", - f"Onion done rebooting, connected to Tor: {self.common.gui.onion.connected_to_tor}", - ) - - if ( - self.common.gui.onion.is_authenticated() - and not tor_con.wasCanceled() - ): - self.close_this_tab.emit() + self.test_tor_button.hide() + self.save_button.hide() + self.tor_con_type = "save" + self.tor_con.show() + self.tor_con.start(settings) else: self.close_this_tab.emit() else: self.close_this_tab.emit() + def tor_con_success(self): + """ + Finished testing tor connection. + """ + self.tor_con.hide() + self.test_tor_button.show() + self.save_button.show() + + if self.tor_con_type == "test": + Alert( + self.common, + strings._("settings_test_success").format( + self.test_onion.tor_version, + self.test_onion.supports_ephemeral, + self.test_onion.supports_stealth, + self.test_onion.supports_v3_onions, + ), + title=strings._("gui_settings_connection_type_test_button"), + ) + self.test_onion.cleanup() + + elif self.tor_con_type == "save": + if ( + self.common.gui.onion.is_authenticated() + and not self.tor_con.wasCanceled() + ): + self.close_this_tab.emit() + + self.tor_con_type = None + + def tor_con_fail(self, msg): + """ + Finished testing tor connection. + """ + self.tor_con.hide() + self.test_tor_button.show() + self.save_button.show() + + self.error_label.setText(msg) + + if self.tor_con_type == "test": + self.test_onion.cleanup() + + self.tor_con_type = None + def close_tab(self): """ Tab is closed @@ -796,7 +822,9 @@ class TorSettingsTab(QtWidgets.QWidget): moat_bridges = self.bridge_moat_textbox.toPlainText() if moat_bridges.strip() == "": - Alert(self.common, strings._("gui_settings_moat_bridges_invalid")) + self.error_label.setText( + strings._("gui_settings_moat_bridges_invalid") + ) return False settings.set( @@ -843,7 +871,9 @@ class TorSettingsTab(QtWidgets.QWidget): new_bridges = "\n".join(new_bridges) + "\n" settings.set("tor_bridges_use_custom_bridges", new_bridges) else: - Alert(self.common, strings._("gui_settings_tor_bridges_invalid")) + self.error_label.setText( + strings._("gui_settings_tor_bridges_invalid") + ) return False else: settings.set("no_bridges", True) From 4897015ad7f6b98bcf27a50d20ebe5de339b6924 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 18:57:14 -0700 Subject: [PATCH 08/56] Rename tor_connection_dialog.py to tor_connection.py --- desktop/src/onionshare/main_window.py | 2 +- .../onionshare/{tor_connection_dialog.py => tor_connection.py} | 0 desktop/src/onionshare/tor_settings_tab.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename desktop/src/onionshare/{tor_connection_dialog.py => tor_connection.py} (100%) diff --git a/desktop/src/onionshare/main_window.py b/desktop/src/onionshare/main_window.py index 4a9d0c7e..546592a1 100644 --- a/desktop/src/onionshare/main_window.py +++ b/desktop/src/onionshare/main_window.py @@ -23,7 +23,7 @@ import time from PySide2 import QtCore, QtWidgets, QtGui from . import strings -from .tor_connection_dialog import TorConnectionDialog +from .tor_connection import TorConnectionDialog from .widgets import Alert from .update_checker import UpdateThread from .tab_widget import TabWidget diff --git a/desktop/src/onionshare/tor_connection_dialog.py b/desktop/src/onionshare/tor_connection.py similarity index 100% rename from desktop/src/onionshare/tor_connection_dialog.py rename to desktop/src/onionshare/tor_connection.py diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index be9dac37..5905b44d 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 TorConnectionWidget +from .tor_connection import TorConnectionWidget from .moat_dialog import MoatDialog From 20a0d7f25bb066a7a67f47b11de69bb98c50ab63 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 19:31:53 -0700 Subject: [PATCH 09/56] Fix TabWidget to stop confusing tab_id and index --- desktop/src/onionshare/tab_widget.py | 69 +++++++++++++++------- desktop/src/onionshare/tor_settings_tab.py | 22 ------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index 36a6c22f..0ab19279 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -45,7 +45,9 @@ class TabWidget(QtWidgets.QTabWidget): self.system_tray = system_tray self.status_bar = status_bar - # Keep track of tabs in a dictionary + # Keep track of tabs in a dictionary that maps tab_id to tab. + # Each tab has a unique, auto-incremented id (tab_id). This is different than the + # tab's index, which changes as tabs are re-arranged. self.tabs = {} self.current_tab_id = 0 # Each tab has a unique id @@ -91,10 +93,12 @@ class TabWidget(QtWidgets.QTabWidget): self.event_handler_t.wait(50) # Clean up each tab - for index in range(self.count()): - if not self.is_settings_tab(index): - tab = self.widget(index) - tab.cleanup() + for tab_id in self.tabs: + if not ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): + self.tabs[tab_id].cleanup() def move_new_tab_button(self): # Find the width of all tabs @@ -117,11 +121,26 @@ class TabWidget(QtWidgets.QTabWidget): def tab_changed(self): # Active tab was changed - tab_id = self.currentIndex() + tab = self.widget(self.currentIndex()) + if not tab: + self.common.log( + "TabWidget", + "tab_changed", + f"tab at index {self.currentIndex()} does not exist", + ) + return + + tab_id = tab.tab_id self.common.log("TabWidget", "tab_changed", f"Tab was changed to {tab_id}") # If it's Settings or Tor Settings, ignore - if self.is_settings_tab(tab_id): + if ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): + # Blank the server status indicator + self.status_bar.server_status_image_label.clear() + self.status_bar.server_status_label.clear() return try: @@ -260,9 +279,12 @@ class TabWidget(QtWidgets.QTabWidget): def save_persistent_tabs(self): # Figure out the order of persistent tabs to save in settings persistent_tabs = [] - for index in range(self.count()): - if not self.is_settings_tab(index): - tab = self.widget(index) + for tab_id in self.tabs: + if not ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): + tab = self.widget(self.indexOf(self.tabs[tab_id])) if tab.settings.get("persistent", "enabled"): persistent_tabs.append(tab.settings.id) # Only save if tabs have actually moved @@ -273,8 +295,14 @@ class TabWidget(QtWidgets.QTabWidget): def close_tab(self, index): self.common.log("TabWidget", "close_tab", f"{index}") tab = self.widget(index) + tab_id = tab.tab_id + + if ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): + self.common.log("TabWidget", "closing a settings tab") - if self.is_settings_tab(index): # Remove the tab self.removeTab(index) del self.tabs[tab.tab_id] @@ -284,7 +312,10 @@ class TabWidget(QtWidgets.QTabWidget): self.new_tab_clicked() else: + self.common.log("TabWidget", "closing a service tab") if tab.close_tab(): + self.common.log("TabWidget", "user is okay with closing the tab") + # If the tab is persistent, delete the settings file from disk if tab.settings.get("persistent", "enabled"): tab.settings.delete() @@ -298,6 +329,8 @@ class TabWidget(QtWidgets.QTabWidget): # If the last tab is closed, open a new one if self.count() == 0: self.new_tab_clicked() + else: + self.common.log("TabWidget", "user does not want to close the tab") def close_settings_tab(self): self.common.log("TabWidget", "close_settings_tab") @@ -320,7 +353,10 @@ class TabWidget(QtWidgets.QTabWidget): See if there are active servers in any open tabs """ for tab_id in self.tabs: - if not self.is_settings_tab(tab_id): + if not ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): mode = self.tabs[tab_id].get_mode() if mode: if mode.server_status.status != mode.server_status.STATUS_STOPPED: @@ -352,15 +388,6 @@ class TabWidget(QtWidgets.QTabWidget): close_button.clicked.connect(close_tab) self.tabBar().setTabButton(index, QtWidgets.QTabBar.RightSide, tab.close_button) - def is_settings_tab(self, tab_id): - if tab_id not in self.tabs: - return True - - return ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab - ) - class TabBar(QtWidgets.QTabBar): """ diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 5905b44d..21941268 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -730,28 +730,6 @@ class TorSettingsTab(QtWidgets.QWidget): self.tor_con_type = None - def close_tab(self): - """ - Tab is closed - """ - self.common.log("TorSettingsTab", "cancel_clicked") - return True - - # TODO: Figure out flow for first connecting, when closing settings when not connected - - # if ( - # not self.common.gui.local_only - # and not self.common.gui.onion.is_authenticated() - # ): - # Alert( - # self.common, - # strings._("gui_tor_connection_canceled"), - # QtWidgets.QMessageBox.Warning, - # ) - # sys.exit() - # else: - # self.close() - def settings_from_fields(self): """ Return a Settings object that's full of values from the settings dialog. From f784870c76bb89a41529c5e5282cc54e47148d9e Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 19:53:37 -0700 Subject: [PATCH 10/56] Implement blank settings_have_changed in SettingsTab and TorSettingsTab --- desktop/src/onionshare/settings_tab.py | 4 ++++ desktop/src/onionshare/tor_connection.py | 10 +++++----- desktop/src/onionshare/tor_settings_tab.py | 4 ++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/desktop/src/onionshare/settings_tab.py b/desktop/src/onionshare/settings_tab.py index c792d94e..8f41b2aa 100644 --- a/desktop/src/onionshare/settings_tab.py +++ b/desktop/src/onionshare/settings_tab.py @@ -304,6 +304,10 @@ class SettingsTab(QtWidgets.QWidget): return settings + def settings_have_changed(self): + # Global settings have changed + self.common.log("SettingsTab", "settings_have_changed") + def _update_autoupdate_timestamp(self, autoupdate_timestamp): self.common.log("SettingsTab", "_update_autoupdate_timestamp") diff --git a/desktop/src/onionshare/tor_connection.py b/desktop/src/onionshare/tor_connection.py index 3eb38876..1cfed2a8 100644 --- a/desktop/src/onionshare/tor_connection.py +++ b/desktop/src/onionshare/tor_connection.py @@ -271,20 +271,20 @@ class TorConnectionThread(QtCore.QThread): canceled_connecting_to_tor = QtCore.Signal() error_connecting_to_tor = QtCore.Signal(str) - def __init__(self, common, settings, dialog): + def __init__(self, common, settings, parent): super(TorConnectionThread, self).__init__() self.common = common self.common.log("TorConnectionThread", "__init__") self.settings = settings - self.dialog = dialog + self.parent = parent def run(self): self.common.log("TorConnectionThread", "run") # Connect to the Onion try: - self.dialog.onion.connect(self.settings, False, self._tor_status_update) - if self.dialog.onion.connected_to_tor: + self.parent.onion.connect(self.settings, False, self._tor_status_update) + if self.parent.onion.connected_to_tor: self.connected_to_tor.emit() else: self.canceled_connecting_to_tor.emit() @@ -320,4 +320,4 @@ class TorConnectionThread(QtCore.QThread): self.tor_status_update.emit(progress, summary) # Return False if the dialog was canceled - return not self.dialog.wasCanceled() + return not self.parent.wasCanceled() diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 21941268..df7cf3be 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -872,3 +872,7 @@ class TorSettingsTab(QtWidgets.QWidget): # Wait 1ms for the event loop to finish, then quit QtCore.QTimer.singleShot(1, self.common.gui.qtapp.quit) + + def settings_have_changed(self): + # Global settings have changed + self.common.log("TorSettingsTab", "settings_have_changed") From e6c7cc989f78a8de531a3cc7420eb9193abd9a06 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 20:03:19 -0700 Subject: [PATCH 11/56] Only show bridge error if connection type is bundled --- cli/onionshare_cli/onion.py | 8 ++++++-- desktop/src/onionshare/tor_settings_tab.py | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index 0d205b97..aa2344db 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -199,8 +199,6 @@ class Onion(object): ) return - self.common.log("Onion", "connect") - # Either use settings that are passed in, or use them from common if custom_settings: self.settings = custom_settings @@ -211,6 +209,12 @@ class Onion(object): self.common.load_settings() self.settings = self.common.settings + self.common.log( + "Onion", + "connect", + f"connection_type={self.settings.get('connection_type')}", + ) + # The Tor controller self.c = None diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index df7cf3be..a56d360b 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -799,7 +799,10 @@ class TorSettingsTab(QtWidgets.QWidget): settings.set("tor_bridges_use_moat", True) moat_bridges = self.bridge_moat_textbox.toPlainText() - if moat_bridges.strip() == "": + if ( + self.connection_type_bundled_radio.isChecked() + and moat_bridges.strip() == "" + ): self.error_label.setText( strings._("gui_settings_moat_bridges_invalid") ) From 44f4053603eef5ea7dedd000416256cbdf6350c9 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 20:23:55 -0700 Subject: [PATCH 12/56] Make meek debug log show host:port on one line --- cli/onionshare_cli/meek.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cli/onionshare_cli/meek.py b/cli/onionshare_cli/meek.py index 6b31a584..b2e70dc1 100644 --- a/cli/onionshare_cli/meek.py +++ b/cli/onionshare_cli/meek.py @@ -136,8 +136,11 @@ class Meek(object): if "CMETHOD meek socks5" in line: self.meek_host = line.split(" ")[3].split(":")[0] self.meek_port = line.split(" ")[3].split(":")[1] - self.common.log("Meek", "start", f"Meek host is {self.meek_host}") - self.common.log("Meek", "start", f"Meek port is {self.meek_port}") + self.common.log( + "Meek", + "start", + f"Meek running on {self.meek_host}:{self.meek_port}", + ) break if self.meek_port: From 54f4f2a53fe541f2a17dd453051d32dc0e43a75b Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 24 Oct 2021 20:26:36 -0700 Subject: [PATCH 13/56] Oops, fix meek-client path --- cli/onionshare_cli/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index b76e72b2..bab3fd86 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -315,7 +315,7 @@ class Common: raise CannotFindTor() obfs4proxy_file_path = shutil.which("obfs4proxy") snowflake_file_path = shutil.which("snowflake-client") - meek_client_file_path = os.path.join(base_path, "meek-client") + meek_client_file_path = shutil.which("meek-client") prefix = os.path.dirname(os.path.dirname(tor_path)) tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6") From 577b2a7f67ae8e1d5557756f0303342858819492 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 25 Oct 2021 15:01:12 +1100 Subject: [PATCH 14/56] Correctly load a persistent Chat tab --- desktop/src/onionshare/tab/tab.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desktop/src/onionshare/tab/tab.py b/desktop/src/onionshare/tab/tab.py index 5d9bb077..7276b8da 100644 --- a/desktop/src/onionshare/tab/tab.py +++ b/desktop/src/onionshare/tab/tab.py @@ -243,6 +243,8 @@ class Tab(QtWidgets.QWidget): elif mode == "website": self.filenames = self.settings.get("website", "filenames") self.website_mode_clicked() + elif mode == "chat": + self.chat_mode_clicked() else: # This is a new tab self.settings = ModeSettings(self.common) From 2b3b6d7635b935741646281c7a206a71993c0f9e Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 26 Oct 2021 21:06:05 -0700 Subject: [PATCH 15/56] Update bridge related settings in Settings, and use those new settings in Onion --- cli/onionshare_cli/onion.py | 63 +++++++++++++++++----------------- cli/onionshare_cli/settings.py | 12 +++---- cli/tests/test_cli_settings.py | 21 ++++++------ 3 files changed, 46 insertions(+), 50 deletions(-) diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index e8fcc12a..5ce83261 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -319,40 +319,39 @@ class Onion(object): f.write(torrc_template) # Bridge support - if self.settings.get("tor_bridges_use_obfs4"): - with open( - self.common.get_resource_path("torrc_template-obfs4") - ) as o: - for line in o: - f.write(line) - elif self.settings.get("tor_bridges_use_meek_lite_azure"): - with open( - self.common.get_resource_path("torrc_template-meek_lite_azure") - ) as o: - for line in o: - f.write(line) - elif self.settings.get("tor_bridges_use_snowflake"): - with open( - self.common.get_resource_path("torrc_template-snowflake") - ) as o: - for line in o: - f.write(line) + if self.settings.get("bridges_enabled"): + if self.settings.get("bridges_type") == "built-in": + if self.settings.get("bridges_builtin_pt") == "obfs4": + with open( + self.common.get_resource_path("torrc_template-obfs4") + ) as o: + f.write(o.read()) + elif self.settings.get("bridges_builtin_pt") == "meek-azure": + with open( + self.common.get_resource_path( + "torrc_template-meek_lite_azure" + ) + ) as o: + f.write(o.read()) + elif self.settings.get("bridges_builtin_pt") == "snowflake": + with open( + self.common.get_resource_path( + "torrc_template-snowflake" + ) + ) as o: + f.write(o.read()) - elif self.settings.get("tor_bridges_use_moat"): - for line in self.settings.get("tor_bridges_use_moat_bridges").split( - "\n" - ): - if line.strip() != "": - f.write(f"Bridge {line}\n") - f.write("\nUseBridges 1\n") + elif self.settings.get("bridges_type") == "moat": + for line in self.settings.get("bridges_moat").split("\n"): + if line.strip() != "": + f.write(f"Bridge {line}\n") + f.write("\nUseBridges 1\n") - elif self.settings.get("tor_bridges_use_custom_bridges"): - for line in self.settings.get( - "tor_bridges_use_custom_bridges" - ).split("\n"): - if line.strip() != "": - f.write(f"Bridge {line}\n") - f.write("\nUseBridges 1\n") + elif self.settings.get("bridges_type") == "custom": + for line in self.settings.get("bridges_custom").split("\n"): + if line.strip() != "": + f.write(f"Bridge {line}\n") + f.write("\nUseBridges 1\n") # Execute a tor subprocess start_ts = time.time() diff --git a/cli/onionshare_cli/settings.py b/cli/onionshare_cli/settings.py index 29b59c80..c7d74a70 100644 --- a/cli/onionshare_cli/settings.py +++ b/cli/onionshare_cli/settings.py @@ -105,13 +105,11 @@ class Settings(object): "auth_password": "", "use_autoupdate": True, "autoupdate_timestamp": None, - "no_bridges": True, - "tor_bridges_use_obfs4": False, - "tor_bridges_use_meek_lite_azure": False, - "tor_bridges_use_snowflake": False, - "tor_bridges_use_moat": False, - "tor_bridges_use_moat_bridges": "", - "tor_bridges_use_custom_bridges": "", + "bridges_enabled": False, + "bridges_type": "built-in", # "built-in", "moat", or "custom" + "bridges_builtin_pt": "obfs4", # "obfs4", "meek-azure", or "snowflake" + "bridges_moat": "", + "bridges_custom": "", "persistent_tabs": [], "locale": None, # this gets defined in fill_in_defaults() "theme": 0, diff --git a/cli/tests/test_cli_settings.py b/cli/tests/test_cli_settings.py index b44ddbec..c7140e70 100644 --- a/cli/tests/test_cli_settings.py +++ b/cli/tests/test_cli_settings.py @@ -29,13 +29,11 @@ class TestSettings: "auth_password": "", "use_autoupdate": True, "autoupdate_timestamp": None, - "no_bridges": True, - "tor_bridges_use_obfs4": False, - "tor_bridges_use_meek_lite_azure": False, - "tor_bridges_use_snowflake": False, - "tor_bridges_use_moat": False, - "tor_bridges_use_moat_bridges": "", - "tor_bridges_use_custom_bridges": "", + "bridges_enabled": False, + "bridges_type": "built-in", + "bridges_builtin_pt": "obfs4", + "bridges_moat": "", + "bridges_custom": "", "persistent_tabs": [], "theme": 0, } @@ -96,10 +94,11 @@ class TestSettings: assert settings_obj.get("use_autoupdate") is True assert settings_obj.get("autoupdate_timestamp") is None assert settings_obj.get("autoupdate_timestamp") is None - assert settings_obj.get("no_bridges") is True - assert settings_obj.get("tor_bridges_use_obfs4") is False - assert settings_obj.get("tor_bridges_use_meek_lite_azure") is False - assert settings_obj.get("tor_bridges_use_custom_bridges") == "" + assert settings_obj.get("bridges_enabled") is False + assert settings_obj.get("bridges_type") == "built-in" + assert settings_obj.get("bridges_builtin_pt") == "obfs4" + assert settings_obj.get("bridges_moat") == "" + assert settings_obj.get("bridges_custom") == "" def test_set_version(self, settings_obj): settings_obj.set("version", "CUSTOM_VERSION") From 9515fe6aaf387868c1406c972435591edb70f785 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 26 Oct 2021 21:07:38 -0700 Subject: [PATCH 16/56] Remove all references to old settings --- cli/onionshare_cli/onion.py | 6 +----- cli/tests/test_cli_settings.py | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index 5ce83261..536b9ba0 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -424,11 +424,7 @@ class Onion(object): time.sleep(0.2) # If using bridges, it might take a bit longer to connect to Tor - if ( - self.settings.get("tor_bridges_use_custom_bridges") - or self.settings.get("tor_bridges_use_obfs4") - or self.settings.get("tor_bridges_use_meek_lite_azure") - ): + if self.settings.get("bridges_enabled"): # Only override timeout if a custom timeout has not been passed in if connect_timeout == 120: connect_timeout = 150 diff --git a/cli/tests/test_cli_settings.py b/cli/tests/test_cli_settings.py index c7140e70..9513b013 100644 --- a/cli/tests/test_cli_settings.py +++ b/cli/tests/test_cli_settings.py @@ -141,10 +141,10 @@ class TestSettings: def test_set_custom_bridge(self, settings_obj): settings_obj.set( - "tor_bridges_use_custom_bridges", + "bridges_custom", "Bridge 45.3.20.65:9050 21300AD88890A49C429A6CB9959CFD44490A8F6E", ) assert ( - settings_obj._settings["tor_bridges_use_custom_bridges"] + settings_obj._settings["bridges_custom"] == "Bridge 45.3.20.65:9050 21300AD88890A49C429A6CB9959CFD44490A8F6E" ) From 53c192665bfcc4942d42cfdd3b356524943950a9 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 26 Oct 2021 21:33:58 -0700 Subject: [PATCH 17/56] Refactor Tor Settings tab to use the new settings --- desktop/src/onionshare/tor_settings_tab.py | 119 +++++++++------------ 1 file changed, 48 insertions(+), 71 deletions(-) diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 4f73ca66..e3dc9bee 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -405,57 +405,54 @@ class TorSettingsTab(QtWidgets.QWidget): self.old_settings.get("auth_password") ) - if self.old_settings.get("no_bridges"): - self.bridge_use_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.bridge_settings.hide() - - else: + if self.old_settings.get("bridges_enabled"): self.bridge_use_checkbox.setCheckState(QtCore.Qt.Checked) self.bridge_settings.show() - builtin_obfs4 = self.old_settings.get("tor_bridges_use_obfs4") - builtin_meek_azure = self.old_settings.get( - "tor_bridges_use_meek_lite_azure" - ) - builtin_snowflake = self.old_settings.get("tor_bridges_use_snowflake") - - if builtin_obfs4 or builtin_meek_azure or builtin_snowflake: + bridges_type = self.old_settings.get("bridges_type") + if bridges_type == "built-in": self.bridge_builtin_radio.setChecked(True) self.bridge_builtin_dropdown.show() - if builtin_obfs4: + self.bridge_moat_radio.setChecked(False) + self.bridge_moat_textbox_options.hide() + self.bridge_custom_radio.setChecked(False) + self.bridge_custom_textbox_options.hide() + + bridges_builtin_pt = self.old_settings.get("bridges_builtin_pt") + if bridges_builtin_pt == "obfs4": self.bridge_builtin_dropdown.setCurrentText("obfs4") - elif builtin_meek_azure: + elif bridges_builtin_pt == "meek-azure": self.bridge_builtin_dropdown.setCurrentText("meek-azure") - elif builtin_snowflake: + else: self.bridge_builtin_dropdown.setCurrentText("snowflake") self.bridge_moat_textbox_options.hide() self.bridge_custom_textbox_options.hide() + + elif bridges_type == "moat": + self.bridge_builtin_radio.setChecked(False) + self.bridge_builtin_dropdown.hide() + self.bridge_moat_radio.setChecked(True) + self.bridge_moat_textbox_options.show() + self.bridge_custom_radio.setChecked(False) + self.bridge_custom_textbox_options.hide() + else: self.bridge_builtin_radio.setChecked(False) self.bridge_builtin_dropdown.hide() + self.bridge_moat_radio.setChecked(False) + self.bridge_moat_textbox_options.hide() + self.bridge_custom_radio.setChecked(True) + self.bridge_custom_textbox_options.show() - use_moat = self.old_settings.get("tor_bridges_use_moat") - self.bridge_moat_radio.setChecked(use_moat) - if use_moat: - self.bridge_builtin_dropdown.hide() - self.bridge_custom_textbox_options.hide() + bridges_moat = self.old_settings.get("bridges_moat") + self.bridge_moat_textbox.document().setPlainText(bridges_moat) + bridges_custom = self.old_settings.get("bridges_custom") + self.bridge_custom_textbox.document().setPlainText(bridges_custom) - moat_bridges = self.old_settings.get("tor_bridges_use_moat_bridges") - self.bridge_moat_textbox.document().setPlainText(moat_bridges) - if len(moat_bridges.strip()) > 0: - self.bridge_moat_textbox_options.show() - else: - self.bridge_moat_textbox_options.hide() - - custom_bridges = self.old_settings.get("tor_bridges_use_custom_bridges") - if len(custom_bridges.strip()) != 0: - self.bridge_custom_radio.setChecked(True) - self.bridge_custom_textbox.setPlainText(custom_bridges) - - self.bridge_builtin_dropdown.hide() - self.bridge_moat_textbox_options.hide() - self.bridge_custom_textbox_options.show() + else: + self.bridge_use_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.bridge_settings.hide() def connection_type_bundled_toggled(self, checked): """ @@ -493,7 +490,7 @@ class TorSettingsTab(QtWidgets.QWidget): """ if selection == "meek-azure": # Alert the user about meek's costliness if it looks like they're turning it on - if not self.old_settings.get("tor_bridges_use_meek_lite_azure"): + if not self.old_settings.get("bridges_builtin_pt") == "meek-azure": Alert( self.common, strings._("gui_settings_meek_lite_expensive_warning"), @@ -654,10 +651,11 @@ class TorSettingsTab(QtWidgets.QWidget): "socket_file_path", "auth_type", "auth_password", - "no_bridges", - "tor_bridges_use_obfs4", - "tor_bridges_use_meek_lite_azure", - "tor_bridges_use_custom_bridges", + "bridges_enabled", + "bridges_type", + "bridges_builtin_pt", + "bridges_moat", + "bridges_custom", ], ): @@ -775,33 +773,16 @@ class TorSettingsTab(QtWidgets.QWidget): # Whether we use bridges if self.bridge_use_checkbox.checkState() == QtCore.Qt.Checked: - settings.set("no_bridges", False) + settings.set("bridges_enabled", True) if self.bridge_builtin_radio.isChecked(): - selection = self.bridge_builtin_dropdown.currentText() - if selection == "obfs4": - settings.set("tor_bridges_use_obfs4", True) - settings.set("tor_bridges_use_meek_lite_azure", False) - settings.set("tor_bridges_use_snowflake", False) - elif selection == "meek-azure": - settings.set("tor_bridges_use_obfs4", False) - settings.set("tor_bridges_use_meek_lite_azure", True) - settings.set("tor_bridges_use_snowflake", False) - elif selection == "snowflake": - settings.set("tor_bridges_use_obfs4", False) - settings.set("tor_bridges_use_meek_lite_azure", False) - settings.set("tor_bridges_use_snowflake", True) + settings.set("bridges_type", "built-in") - settings.set("tor_bridges_use_moat", False) - settings.set("tor_bridges_use_custom_bridges", "") + selection = self.bridge_builtin_dropdown.currentText() + settings.set("bridges_builtin_pt", selection) if self.bridge_moat_radio.isChecked(): - settings.set("tor_bridges_use_obfs4", False) - settings.set("tor_bridges_use_meek_lite_azure", False) - settings.set("tor_bridges_use_snowflake", False) - - settings.set("tor_bridges_use_moat", True) - + settings.set("bridges_type", "moat") moat_bridges = self.bridge_moat_textbox.toPlainText() if ( self.connection_type_bundled_radio.isChecked() @@ -812,15 +793,11 @@ class TorSettingsTab(QtWidgets.QWidget): ) return False - settings.set("tor_bridges_use_moat_bridges", moat_bridges) - - settings.set("tor_bridges_use_custom_bridges", "") + settings.set("bridges_moat", moat_bridges) + settings.set("bridges_custom", "") if self.bridge_custom_radio.isChecked(): - settings.set("tor_bridges_use_obfs4", False) - settings.set("tor_bridges_use_meek_lite_azure", False) - settings.set("tor_bridges_use_snowflake", False) - settings.set("tor_bridges_use_moat", False) + settings.set("bridges_type", "custom") new_bridges = [] bridges = self.bridge_custom_textbox.toPlainText().split("\n") @@ -851,14 +828,14 @@ class TorSettingsTab(QtWidgets.QWidget): if bridges_valid: new_bridges = "\n".join(new_bridges) + "\n" - settings.set("tor_bridges_use_custom_bridges", new_bridges) + settings.set("bridges_custom", new_bridges) else: self.error_label.setText( strings._("gui_settings_tor_bridges_invalid") ) return False else: - settings.set("no_bridges", True) + settings.set("bridges_enabled", False) return settings From 706a04242ff7a70b2fc50b2dbda78f435db9d9e6 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 26 Oct 2021 22:00:39 -0700 Subject: [PATCH 18/56] Show message in Tor Settings tab if any tabs have active services, to prevent the user from changing settings without stopping them --- .../src/onionshare/resources/locale/en.json | 1 + desktop/src/onionshare/tab_widget.py | 21 ++++++--- desktop/src/onionshare/tor_settings_tab.py | 44 +++++++++++++++---- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index 3f380466..398782af 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -72,6 +72,7 @@ "gui_settings_bridge_custom_placeholder": "type address:port (one per line)", "gui_settings_moat_bridges_invalid": "You have not requested a bridge from torproject.org yet.", "gui_settings_tor_bridges_invalid": "None of the bridges you added work. Double-check them or add others.", + "gui_settings_stop_active_tabs_label": "There are services running in some of your tabs.\nYou must stop all services to change your Tor settings.", "gui_settings_button_save": "Save", "gui_settings_button_cancel": "Cancel", "gui_settings_button_help": "Help", diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index 0ab19279..ead4d960 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -50,6 +50,7 @@ class TabWidget(QtWidgets.QTabWidget): # tab's index, which changes as tabs are re-arranged. self.tabs = {} self.current_tab_id = 0 # Each tab has a unique id + self.tor_settings_tab = None # Define the new tab button self.new_tab_button = QtWidgets.QPushButton("+", parent=self) @@ -230,18 +231,20 @@ class TabWidget(QtWidgets.QTabWidget): self.setCurrentIndex(self.indexOf(self.tabs[tab_id])) return - tor_settings_tab = TorSettingsTab(self.common, self.current_tab_id) - tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab) - self.tabs[self.current_tab_id] = tor_settings_tab + self.tor_settings_tab = TorSettingsTab( + self.common, self.current_tab_id, self.are_tabs_active() + ) + self.tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab) + self.tabs[self.current_tab_id] = self.tor_settings_tab self.current_tab_id += 1 index = self.addTab( - tor_settings_tab, strings._("gui_tor_settings_window_title") + self.tor_settings_tab, strings._("gui_tor_settings_window_title") ) self.setCurrentIndex(index) # In macOS, manually create a close button because tabs don't seem to have them otherwise if self.common.platform == "Darwin": - self.macos_create_close_button(tor_settings_tab, index) + self.macos_create_close_button(self.tor_settings_tab, index) def change_title(self, tab_id, title): shortened_title = title @@ -256,6 +259,11 @@ class TabWidget(QtWidgets.QTabWidget): index = self.indexOf(self.tabs[tab_id]) self.setTabIcon(index, QtGui.QIcon(GuiCommon.get_resource_path(icon_path))) + # The icon changes when the server status changes, so if we have an open + # Tor Settings tab, tell it to update + if self.tor_settings_tab: + self.tor_settings_tab.active_tabs_changed(self.are_tabs_active()) + def change_persistent(self, tab_id, is_persistent): self.common.log( "TabWidget", @@ -307,6 +315,9 @@ class TabWidget(QtWidgets.QTabWidget): self.removeTab(index) del self.tabs[tab.tab_id] + if type(self.tabs[tab_id]) is TorSettingsTab: + self.tor_settings_tab = None + # If the last tab is closed, open a new one if self.count() == 0: self.new_tab_clicked() diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index e3dc9bee..c8990901 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -41,7 +41,7 @@ class TorSettingsTab(QtWidgets.QWidget): close_this_tab = QtCore.Signal() - def __init__(self, common, tab_id): + def __init__(self, common, tab_id, are_tabs_active): super(TorSettingsTab, self).__init__() self.common = common @@ -351,16 +351,36 @@ class TorSettingsTab(QtWidgets.QWidget): buttons_layout.addWidget(self.test_tor_button) buttons_layout.addWidget(self.save_button) - # Layout - layout = QtWidgets.QVBoxLayout() - layout.addWidget(columns_wrapper) - layout.addStretch() - layout.addWidget(self.tor_con) - layout.addStretch() - layout.addLayout(buttons_layout) + # Main layout + main_layout = QtWidgets.QVBoxLayout() + main_layout.addWidget(columns_wrapper) + main_layout.addStretch() + main_layout.addWidget(self.tor_con) + main_layout.addStretch() + main_layout.addLayout(buttons_layout) + self.main_widget = QtWidgets.QWidget() + self.main_widget.setLayout(main_layout) + # Tabs are active label + active_tabs_label = QtWidgets.QLabel( + strings._("gui_settings_stop_active_tabs_label") + ) + active_tabs_label.setAlignment(QtCore.Qt.AlignHCenter) + + # Active tabs layout + active_tabs_layout = QtWidgets.QVBoxLayout() + active_tabs_layout.addStretch() + active_tabs_layout.addWidget(active_tabs_label) + active_tabs_layout.addStretch() + self.active_tabs_widget = QtWidgets.QWidget() + self.active_tabs_widget.setLayout(active_tabs_layout) + + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.main_widget) + layout.addWidget(self.active_tabs_widget) self.setLayout(layout) + self.active_tabs_changed(are_tabs_active) self.reload_settings() def reload_settings(self): @@ -454,6 +474,14 @@ class TorSettingsTab(QtWidgets.QWidget): self.bridge_use_checkbox.setCheckState(QtCore.Qt.Unchecked) self.bridge_settings.hide() + def active_tabs_changed(self, are_tabs_active): + if are_tabs_active: + self.main_widget.hide() + self.active_tabs_widget.show() + else: + self.main_widget.show() + self.active_tabs_widget.hide() + def connection_type_bundled_toggled(self, checked): """ Connection type bundled was toggled From bde94d37fc1cdf16767437a624bbdfe2150bf598 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 26 Oct 2021 22:09:24 -0700 Subject: [PATCH 19/56] Don't delete any custom bridges that are set --- desktop/src/onionshare/tor_settings_tab.py | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index c8990901..7ef8d850 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -822,7 +822,6 @@ class TorSettingsTab(QtWidgets.QWidget): return False settings.set("bridges_moat", moat_bridges) - settings.set("bridges_custom", "") if self.bridge_custom_radio.isChecked(): settings.set("bridges_type", "custom") From de3c95cc507968b406871f46620b84991df2f056 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 26 Oct 2021 22:12:22 -0700 Subject: [PATCH 20/56] Set self.torr_settings_tab to None _before_ deleting the tab --- desktop/src/onionshare/tab_widget.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index ead4d960..da7d50bf 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -311,13 +311,13 @@ class TabWidget(QtWidgets.QTabWidget): ): self.common.log("TabWidget", "closing a settings tab") + if type(self.tabs[tab_id]) is TorSettingsTab: + self.tor_settings_tab = None + # Remove the tab self.removeTab(index) del self.tabs[tab.tab_id] - if type(self.tabs[tab_id]) is TorSettingsTab: - self.tor_settings_tab = None - # If the last tab is closed, open a new one if self.count() == 0: self.new_tab_clicked() From e9ef087c8fcb3839eb77aa090de0a66f38e8e581 Mon Sep 17 00:00:00 2001 From: Max1Truc Date: Wed, 27 Oct 2021 11:47:17 +0200 Subject: [PATCH 21/56] Fix "KeyError: 'fr'" in desktop strings.py --- desktop/src/onionshare/strings.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/desktop/src/onionshare/strings.py b/desktop/src/onionshare/strings.py index c4192a33..55f56788 100644 --- a/desktop/src/onionshare/strings.py +++ b/desktop/src/onionshare/strings.py @@ -43,7 +43,11 @@ def load_strings(common, locale_dir): current_locale = common.settings.get("locale") strings = {} for s in translations[default_locale]: - if s in translations[current_locale] and translations[current_locale][s] != "": + if ( + current_locale in translations + and s in translations[current_locale] + and translations[current_locale][s] != "" + ): strings[s] = translations[current_locale][s] else: strings[s] = translations[default_locale][s] From 7da1ac187b7991a4bdd0f8b0e2828bc31f0adcb7 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 19:20:36 -0700 Subject: [PATCH 22/56] Remove sticky "Disconnected from Tor" message (patch thanks to @mig5) --- desktop/src/onionshare/tab_widget.py | 2 +- desktop/src/onionshare/tor_connection.py | 5 +++-- desktop/src/onionshare/tor_settings_tab.py | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index da7d50bf..3579d21b 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -232,7 +232,7 @@ class TabWidget(QtWidgets.QTabWidget): return self.tor_settings_tab = TorSettingsTab( - self.common, self.current_tab_id, self.are_tabs_active() + self.common, self.current_tab_id, self.are_tabs_active(), self.status_bar ) self.tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab) self.tabs[self.current_tab_id] = self.tor_settings_tab diff --git a/desktop/src/onionshare/tor_connection.py b/desktop/src/onionshare/tor_connection.py index 1cfed2a8..2cc599c4 100644 --- a/desktop/src/onionshare/tor_connection.py +++ b/desktop/src/onionshare/tor_connection.py @@ -117,7 +117,6 @@ class TorConnectionDialog(QtWidgets.QProgressDialog): def _connected_to_tor(self): self.common.log("TorConnectionDialog", "_connected_to_tor") self.active = False - # Close the dialog after connecting self.setValue(self.maximum()) @@ -166,11 +165,12 @@ class TorConnectionWidget(QtWidgets.QWidget): success = QtCore.Signal() fail = QtCore.Signal(str) - def __init__(self, common): + def __init__(self, common, status_bar): super(TorConnectionWidget, self).__init__(None) self.common = common self.common.log("TorConnectionWidget", "__init__") + self.status_bar = status_bar self.label = QtWidgets.QLabel(strings._("connecting_to_tor")) self.label.setAlignment(QtCore.Qt.AlignHCenter) @@ -245,6 +245,7 @@ class TorConnectionWidget(QtWidgets.QWidget): def _connected_to_tor(self): self.common.log("TorConnectionWidget", "_connected_to_tor") self.active = False + self.status_bar.clearMessage() # Close the dialog after connecting self.progress.setValue(self.progress.maximum()) diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 7ef8d850..85645ca0 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -41,12 +41,13 @@ class TorSettingsTab(QtWidgets.QWidget): close_this_tab = QtCore.Signal() - def __init__(self, common, tab_id, are_tabs_active): + def __init__(self, common, tab_id, are_tabs_active, status_bar): super(TorSettingsTab, self).__init__() self.common = common self.common.log("TorSettingsTab", "__init__") + self.status_bar = status_bar self.meek = Meek(common, get_tor_paths=self.common.gui.get_tor_paths) self.system = platform.system() @@ -327,7 +328,7 @@ class TorSettingsTab(QtWidgets.QWidget): columns_wrapper.setLayout(columns_layout) # Tor connection widget - self.tor_con = TorConnectionWidget(self.common) + self.tor_con = TorConnectionWidget(self.common, self.status_bar) self.tor_con.success.connect(self.tor_con_success) self.tor_con.fail.connect(self.tor_con_fail) self.tor_con.hide() From 7b162b4bc7475ab124a91f95dc8e58986f3ab476 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 20:05:20 -0700 Subject: [PATCH 23/56] In all modes, if Tor isn't connected display a message instead of showing the mode content --- desktop/src/onionshare/gui_common.py | 5 ++ .../src/onionshare/resources/locale/en.json | 3 +- desktop/src/onionshare/tab/mode/__init__.py | 54 ++++++++++++++++++- .../onionshare/tab/mode/chat_mode/__init__.py | 6 +-- .../tab/mode/receive_mode/__init__.py | 6 +-- .../tab/mode/share_mode/__init__.py | 6 +-- .../tab/mode/website_mode/__init__.py | 6 +-- desktop/src/onionshare/tab/tab.py | 1 - desktop/src/onionshare/tab_widget.py | 22 ++++++++ desktop/src/onionshare/tor_settings_tab.py | 8 +++ 10 files changed, 98 insertions(+), 19 deletions(-) diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index b081774e..39f6d46f 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -281,6 +281,11 @@ class GuiCommon: QLabel { color: #cc0000; }""", + "tor_not_connected_label": """ + QLabel { + font-size: 16px; + font-style: italic; + }""", # New tab "new_tab_button_image": """ QLabel { diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index 398782af..868a6fa9 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -229,5 +229,6 @@ "moat_captcha_reload": "Reload", "moat_bridgedb_error": "Error contacting BridgeDB.", "moat_captcha_error": "The solution is not correct. Please try again.", - "moat_solution_empty_error": "You must enter the characters from the image" + "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/mode/__init__.py b/desktop/src/onionshare/tab/mode/__init__.py index d4f2c23a..936c91cb 100644 --- a/desktop/src/onionshare/tab/mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/__init__.py @@ -28,7 +28,7 @@ from .mode_settings_widget import ModeSettingsWidget from ..server_status import ServerStatus from ... import strings from ...threads import OnionThread, AutoStartTimer -from ...widgets import Alert +from ...widgets import Alert, MinimumSizeWidget class Mode(QtWidgets.QWidget): @@ -101,6 +101,35 @@ class Mode(QtWidgets.QWidget): self.primary_action = QtWidgets.QWidget() self.primary_action.setLayout(self.primary_action_layout) + # It's up to the downstream Mode to add stuff to self.content_layout + # self.content_layout shows the actual content of the mode + # self.tor_not_connected_layout is displayed when Tor isn't connected + self.content_layout = QtWidgets.QVBoxLayout() + self.content_widget = QtWidgets.QWidget() + self.content_widget.setLayout(self.content_layout) + + tor_not_connected_label = QtWidgets.QLabel( + strings._("mode_tor_not_connected_label") + ) + tor_not_connected_label.setAlignment(QtCore.Qt.AlignHCenter) + tor_not_connected_label.setStyleSheet( + self.common.gui.css["tor_not_connected_label"] + ) + self.tor_not_connected_layout = QtWidgets.QVBoxLayout() + self.tor_not_connected_layout.addStretch() + self.tor_not_connected_layout.addWidget(tor_not_connected_label) + self.tor_not_connected_layout.addWidget(MinimumSizeWidget(700, 0)) + self.tor_not_connected_layout.addStretch() + self.tor_not_connected_widget = QtWidgets.QWidget() + self.tor_not_connected_widget.setLayout(self.tor_not_connected_layout) + + self.wrapper_layout = QtWidgets.QVBoxLayout() + self.wrapper_layout.addWidget(self.content_widget) + self.wrapper_layout.addWidget(self.tor_not_connected_widget) + self.setLayout(self.wrapper_layout) + + self.tor_connection_init() + def init(self): """ Add custom initialization here. @@ -524,3 +553,26 @@ class Mode(QtWidgets.QWidget): Used in both Share and Website modes, so implemented here. """ self.history.cancel(event["data"]["id"]) + + def tor_connection_init(self): + """ + Figure out if Tor is connected and display the right widget + """ + if self.common.gui.onion.is_authenticated(): + self.tor_connection_started() + else: + self.tor_connection_stopped() + + def tor_connection_started(self): + """ + This is called on every Mode when Tor is connected + """ + self.content_widget.show() + self.tor_not_connected_widget.hide() + + def tor_connection_stopped(self): + """ + This is called on every Mode when Tor is disconnected + """ + self.content_widget.hide() + self.tor_not_connected_widget.show() diff --git a/desktop/src/onionshare/tab/mode/chat_mode/__init__.py b/desktop/src/onionshare/tab/mode/chat_mode/__init__.py index e7a17ce7..1081fe9d 100644 --- a/desktop/src/onionshare/tab/mode/chat_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/chat_mode/__init__.py @@ -98,10 +98,8 @@ class ChatMode(Mode): self.column_layout.addWidget(self.image) self.column_layout.addLayout(self.main_layout) - # Wrapper layout - self.wrapper_layout = QtWidgets.QVBoxLayout() - self.wrapper_layout.addLayout(self.column_layout) - self.setLayout(self.wrapper_layout) + # Content layout + self.content_layout.addLayout(self.column_layout) def get_type(self): """ diff --git a/desktop/src/onionshare/tab/mode/receive_mode/__init__.py b/desktop/src/onionshare/tab/mode/receive_mode/__init__.py index d5036d1d..b2b2fc5a 100644 --- a/desktop/src/onionshare/tab/mode/receive_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/receive_mode/__init__.py @@ -198,10 +198,8 @@ class ReceiveMode(Mode): self.column_layout.addLayout(row_layout) self.column_layout.addWidget(self.history, stretch=1) - # Wrapper layout - self.wrapper_layout = QtWidgets.QVBoxLayout() - self.wrapper_layout.addLayout(self.column_layout) - self.setLayout(self.wrapper_layout) + # Content layout + self.content_layout.addLayout(self.column_layout) def get_type(self): """ diff --git a/desktop/src/onionshare/tab/mode/share_mode/__init__.py b/desktop/src/onionshare/tab/mode/share_mode/__init__.py index 5d3e3c35..7be93f1d 100644 --- a/desktop/src/onionshare/tab/mode/share_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/share_mode/__init__.py @@ -169,10 +169,8 @@ class ShareMode(Mode): self.column_layout.addLayout(self.main_layout) self.column_layout.addWidget(self.history, stretch=1) - # Wrapper layout - self.wrapper_layout = QtWidgets.QVBoxLayout() - self.wrapper_layout.addLayout(self.column_layout) - self.setLayout(self.wrapper_layout) + # Content layout + self.content_layout.addLayout(self.column_layout) # Always start with focus on file selection self.file_selection.setFocus() diff --git a/desktop/src/onionshare/tab/mode/website_mode/__init__.py b/desktop/src/onionshare/tab/mode/website_mode/__init__.py index a50d15b9..73c4bad2 100644 --- a/desktop/src/onionshare/tab/mode/website_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/website_mode/__init__.py @@ -167,10 +167,8 @@ class WebsiteMode(Mode): self.column_layout.addLayout(self.main_layout) self.column_layout.addWidget(self.history, stretch=1) - # Wrapper layout - self.wrapper_layout = QtWidgets.QVBoxLayout() - self.wrapper_layout.addLayout(self.column_layout) - self.setLayout(self.wrapper_layout) + # Content layout + self.content_layout.addLayout(self.column_layout) # Always start with focus on file selection self.file_selection.setFocus() diff --git a/desktop/src/onionshare/tab/tab.py b/desktop/src/onionshare/tab/tab.py index 5d9bb077..fb7f1836 100644 --- a/desktop/src/onionshare/tab/tab.py +++ b/desktop/src/onionshare/tab/tab.py @@ -96,7 +96,6 @@ class Tab(QtWidgets.QWidget): tab_id, system_tray, status_bar, - mode_settings=None, filenames=None, ): super(Tab, self).__init__() diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index 3579d21b..c7a3552a 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -235,6 +235,8 @@ class TabWidget(QtWidgets.QTabWidget): self.common, self.current_tab_id, self.are_tabs_active(), self.status_bar ) self.tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab) + self.tor_settings_tab.tor_is_connected.connect(self.tor_is_connected) + self.tor_settings_tab.tor_is_disconnected.connect(self.tor_is_disconnected) self.tabs[self.current_tab_id] = self.tor_settings_tab self.current_tab_id += 1 index = self.addTab( @@ -399,6 +401,26 @@ class TabWidget(QtWidgets.QTabWidget): close_button.clicked.connect(close_tab) self.tabBar().setTabButton(index, QtWidgets.QTabBar.RightSide, tab.close_button) + def tor_is_connected(self): + for tab_id in self.tabs: + if not ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): + mode = self.tabs[tab_id].get_mode() + if mode: + mode.tor_connection_started() + + def tor_is_disconnected(self): + for tab_id in self.tabs: + if not ( + type(self.tabs[tab_id]) is SettingsTab + or type(self.tabs[tab_id]) is TorSettingsTab + ): + mode = self.tabs[tab_id].get_mode() + if mode: + mode.tor_connection_stopped() + class TabBar(QtWidgets.QTabBar): """ diff --git a/desktop/src/onionshare/tor_settings_tab.py b/desktop/src/onionshare/tor_settings_tab.py index 85645ca0..e28e5260 100644 --- a/desktop/src/onionshare/tor_settings_tab.py +++ b/desktop/src/onionshare/tor_settings_tab.py @@ -40,6 +40,8 @@ class TorSettingsTab(QtWidgets.QWidget): """ close_this_tab = QtCore.Signal() + tor_is_connected = QtCore.Signal() + tor_is_disconnected = QtCore.Signal() def __init__(self, common, tab_id, are_tabs_active, status_bar): super(TorSettingsTab, self).__init__() @@ -699,6 +701,9 @@ class TorSettingsTab(QtWidgets.QWidget): # Do we need to reinitialize Tor? if reboot_onion: + # Tell the tabs that Tor is disconnected + self.tor_is_disconnected.emit() + # Reinitialize the Onion object self.common.log( "TorSettingsTab", "save_clicked", "rebooting the Onion" @@ -742,6 +747,9 @@ class TorSettingsTab(QtWidgets.QWidget): self.common.gui.onion.is_authenticated() and not self.tor_con.wasCanceled() ): + # Tell the tabs that Tor is connected + self.tor_is_connected.emit() + # Close the tab self.close_this_tab.emit() self.tor_con_type = None From 7448f76f2e54899e932ecc2a116c04cf25ed061d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 20:17:02 -0700 Subject: [PATCH 24/56] Respect --local-only --- desktop/src/onionshare/tab/mode/__init__.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/desktop/src/onionshare/tab/mode/__init__.py b/desktop/src/onionshare/tab/mode/__init__.py index 936c91cb..6be97995 100644 --- a/desktop/src/onionshare/tab/mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/__init__.py @@ -128,7 +128,10 @@ class Mode(QtWidgets.QWidget): self.wrapper_layout.addWidget(self.tor_not_connected_widget) self.setLayout(self.wrapper_layout) - self.tor_connection_init() + if self.common.gui.onion.is_authenticated(): + self.tor_connection_started() + else: + self.tor_connection_stopped() def init(self): """ @@ -554,15 +557,6 @@ class Mode(QtWidgets.QWidget): """ self.history.cancel(event["data"]["id"]) - def tor_connection_init(self): - """ - Figure out if Tor is connected and display the right widget - """ - if self.common.gui.onion.is_authenticated(): - self.tor_connection_started() - else: - self.tor_connection_stopped() - def tor_connection_started(self): """ This is called on every Mode when Tor is connected @@ -574,5 +568,9 @@ class Mode(QtWidgets.QWidget): """ This is called on every Mode when Tor is disconnected """ + if self.common.gui.local_only: + self.tor_connection_started() + return + self.content_widget.hide() self.tor_not_connected_widget.show() From 4e62b8831a867c47cfa880c7d0841ba1483da5c2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 20:42:51 -0700 Subject: [PATCH 25/56] Get tor from Tor Browser 11.0a10 on all platforms --- desktop/scripts/get-tor-linux.py | 6 +++--- desktop/scripts/get-tor-osx.py | 6 +++--- desktop/scripts/get-tor-windows.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/desktop/scripts/get-tor-linux.py b/desktop/scripts/get-tor-linux.py index b8f83c92..51beb475 100755 --- a/desktop/scripts/get-tor-linux.py +++ b/desktop/scripts/get-tor-linux.py @@ -34,10 +34,10 @@ import requests def main(): - tarball_url = "https://dist.torproject.org/torbrowser/11.0a9/tor-browser-linux64-11.0a9_en-US.tar.xz" - tarball_filename = "tor-browser-linux64-11.0a9_en-US.tar.xz" + tarball_url = "https://dist.torproject.org/torbrowser/11.0a10/tor-browser-linux64-11.0a10_en-US.tar.xz" + tarball_filename = "tor-browser-linux64-11.0a10_en-US.tar.xz" expected_tarball_sha256 = ( - "cba4a2120b4f847d1ade637e41e69bd01b2e70b4a13e41fe8e69d0424fcf7ca7" + "5d3e2ebc4fb6a10f44624359bc2a5a151a57e8402cbd8563d15f9b2524374f1f" ) # Build paths diff --git a/desktop/scripts/get-tor-osx.py b/desktop/scripts/get-tor-osx.py index be5f7a56..410a8157 100755 --- a/desktop/scripts/get-tor-osx.py +++ b/desktop/scripts/get-tor-osx.py @@ -34,10 +34,10 @@ import requests def main(): - dmg_url = "https://dist.torproject.org/torbrowser/11.0a7/TorBrowser-11.0a7-osx64_en-US.dmg" - dmg_filename = "TorBrowser-11.0a7-osx64_en-US.dmg" + dmg_url = "https://dist.torproject.org/torbrowser/11.0a10/TorBrowser-11.0a10-osx64_en-US.dmg" + dmg_filename = "TorBrowser-11.0a10-osx64_en-US.dmg" expected_dmg_sha256 = ( - "46594cefa29493150d1c0e1933dd656aafcb6b51ef310d44ac059eed2fd1388e" + "c6823a28fd28205437564815f93011ff93b7972da2a8ce16919adfc65909e7b9" ) # Build paths diff --git a/desktop/scripts/get-tor-windows.py b/desktop/scripts/get-tor-windows.py index 751faecc..8ca2e79f 100644 --- a/desktop/scripts/get-tor-windows.py +++ b/desktop/scripts/get-tor-windows.py @@ -33,10 +33,10 @@ import requests def main(): - exe_url = "https://dist.torproject.org/torbrowser/11.0a7/torbrowser-install-11.0a7_en-US.exe" - exe_filename = "torbrowser-install-11.0a7_en-US.exe" + exe_url = "https://dist.torproject.org/torbrowser/11.0a10/torbrowser-install-11.0a10_en-US.exe" + exe_filename = "torbrowser-install-11.0a10_en-US.exe" expected_exe_sha256 = ( - "8b2013669d88e3ae8fa9bc17a3495eaac9475f79a849354e826e5132811a860b" + "f567dd8368dea0a8d7bbf7c19ece7840f93d493e70662939b92f5058c8dc8d2d" ) # Build paths root_path = os.path.dirname( From fa70368a8d4891461ae503ba40594e456e1b4dae Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 20:46:52 -0700 Subject: [PATCH 26/56] Copy snowflake-client from macOS Tor Browser --- desktop/scripts/get-tor-osx.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/desktop/scripts/get-tor-osx.py b/desktop/scripts/get-tor-osx.py index 410a8157..80d7aee8 100755 --- a/desktop/scripts/get-tor-osx.py +++ b/desktop/scripts/get-tor-osx.py @@ -101,6 +101,14 @@ def main(): os.path.join(dist_path, "obfs4proxy"), ) os.chmod(os.path.join(dist_path, "obfs4proxy"), 0o755) + # snowflake-client binary + shutil.copyfile( + os.path.join( + dmg_tor_path, "MacOS", "Tor", "PluggableTransports", "snowflake-client" + ), + os.path.join(dist_path, "snowflake-client"), + ) + os.chmod(os.path.join(dist_path, "snowflake-client"), 0o755) # Eject dmg subprocess.call(["diskutil", "eject", "/Volumes/Tor Browser"]) From 0e2d2a1e46f87df6fa76bf180f6bda2df1efa5e9 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 20:52:05 -0700 Subject: [PATCH 27/56] macOS seems to have close buttons that work on their own now --- desktop/src/onionshare/tab_widget.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index c7a3552a..7f42632d 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -186,10 +186,6 @@ class TabWidget(QtWidgets.QTabWidget): index = self.addTab(tab, strings._("gui_new_tab")) self.setCurrentIndex(index) - # In macOS, manually create a close button because tabs don't seem to have them otherwise - if self.common.platform == "Darwin": - self.macos_create_close_button(tab, index) - tab.init(mode_settings) # Make sure the title is set @@ -218,10 +214,6 @@ class TabWidget(QtWidgets.QTabWidget): index = self.addTab(settings_tab, strings._("gui_settings_window_title")) self.setCurrentIndex(index) - # In macOS, manually create a close button because tabs don't seem to have them otherwise - if self.common.platform == "Darwin": - self.macos_create_close_button(settings_tab, index) - def open_tor_settings_tab(self): self.common.log("TabWidget", "open_tor_settings_tab") @@ -244,10 +236,6 @@ class TabWidget(QtWidgets.QTabWidget): ) self.setCurrentIndex(index) - # In macOS, manually create a close button because tabs don't seem to have them otherwise - if self.common.platform == "Darwin": - self.macos_create_close_button(self.tor_settings_tab, index) - def change_title(self, tab_id, title): shortened_title = title if len(shortened_title) > 11: @@ -388,19 +376,6 @@ class TabWidget(QtWidgets.QTabWidget): super(TabWidget, self).resizeEvent(event) self.move_new_tab_button() - def macos_create_close_button(self, tab, index): - def close_tab(): - self.tabBar().tabCloseRequested.emit(self.indexOf(tab)) - - close_button = QtWidgets.QPushButton() - close_button.setFlat(True) - close_button.setFixedWidth(40) - close_button.setIcon( - QtGui.QIcon(GuiCommon.get_resource_path("images/close_tab.png")) - ) - close_button.clicked.connect(close_tab) - self.tabBar().setTabButton(index, QtWidgets.QTabBar.RightSide, tab.close_button) - def tor_is_connected(self): for tab_id in self.tabs: if not ( From f915f911c6fc7dd4dea500538a993b7382fee85b Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 20:55:50 -0700 Subject: [PATCH 28/56] Make autoupdate group in Settings Tab centered --- desktop/src/onionshare/settings_tab.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/desktop/src/onionshare/settings_tab.py b/desktop/src/onionshare/settings_tab.py index 8f41b2aa..75b7a326 100644 --- a/desktop/src/onionshare/settings_tab.py +++ b/desktop/src/onionshare/settings_tab.py @@ -73,9 +73,16 @@ class SettingsTab(QtWidgets.QWidget): ) autoupdate_group.setLayout(autoupdate_group_layout) + autoupdate_layout = QtWidgets.QHBoxLayout() + autoupdate_layout.addStretch() + autoupdate_layout.addWidget(autoupdate_group) + autoupdate_layout.addStretch() + autoupdate_widget = QtWidgets.QWidget() + autoupdate_widget.setLayout(autoupdate_layout) + # Autoupdate is only available for Windows and Mac (Linux updates using package manager) if self.system != "Windows" and self.system != "Darwin": - autoupdate_group.hide() + autoupdate_widget.hide() # Language settings language_label = QtWidgets.QLabel(strings._("gui_settings_language_label")) @@ -131,8 +138,8 @@ class SettingsTab(QtWidgets.QWidget): # Layout layout = QtWidgets.QVBoxLayout() layout.addStretch() - layout.addWidget(autoupdate_group) - if autoupdate_group.isVisible(): + layout.addWidget(autoupdate_widget) + if autoupdate_widget.isVisible(): layout.addSpacing(20) layout.addLayout(language_layout) layout.addLayout(theme_layout) From 472e383b7d0f4040e4963ffff95da1e86fe85cca Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 6 Nov 2021 21:02:24 -0700 Subject: [PATCH 29/56] Fix settings error color in dark mode --- desktop/src/onionshare/gui_common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index 39f6d46f..0db0f051 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -93,6 +93,7 @@ class GuiCommon: share_zip_progess_bar_chunk_color = "#4E064F" history_background_color = "#ffffff" history_label_color = "#000000" + settings_error_color = "#FF0000" if color_mode == "dark": header_color = "#F2F2F2" title_color = "#F2F2F2" @@ -103,6 +104,7 @@ class GuiCommon: share_zip_progess_bar_border_color = "#F2F2F2" history_background_color = "#191919" history_label_color = "#ffffff" + settings_error_color = "#FF9999" return { # OnionShareGui styles @@ -400,7 +402,9 @@ class GuiCommon: # Tor Settings dialogs "tor_settings_error": """ QLabel { - color: #FF0000; + color: """ + + settings_error_color + + """; } """, } From 9430439b5f02829e3677ac187e1db7a52076ddad Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 7 Nov 2021 12:12:12 -0800 Subject: [PATCH 30/56] Fix meek-client in Windows --- cli/onionshare_cli/meek.py | 12 +++++++++++- desktop/README.md | 2 +- desktop/scripts/build-meek-client.py | 16 +++++++++++----- desktop/src/onionshare/moat_dialog.py | 10 ++++++++-- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/cli/onionshare_cli/meek.py b/cli/onionshare_cli/meek.py index b2e70dc1..c5df7b7f 100644 --- a/cli/onionshare_cli/meek.py +++ b/cli/onionshare_cli/meek.py @@ -85,6 +85,10 @@ class Meek(object): self.common.log("Meek", "start", "Starting meek client") if self.common.platform == "Windows": + env = os.environ.copy() + for key in self.meek_env: + env[key] = self.meek_env[key] + # In Windows, hide console window when opening meek-client.exe subprocess startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW @@ -100,7 +104,7 @@ class Meek(object): stderr=subprocess.PIPE, startupinfo=startupinfo, bufsize=1, - env=self.meek_env, + env=env, text=True, ) else: @@ -129,6 +133,7 @@ class Meek(object): # read stdout without blocking try: line = q.get_nowait() + self.common.log("Meek", "start", line.strip()) except Empty: # no stdout yet? pass @@ -143,6 +148,10 @@ class Meek(object): ) break + if "CMETHOD-ERROR" in line: + self.cleanup() + raise MeekNotRunning() + if self.meek_port: self.meek_proxies = { "http": f"socks5h://{self.meek_host}:{self.meek_port}", @@ -150,6 +159,7 @@ class Meek(object): } else: self.common.log("Meek", "start", "Could not obtain the meek port") + self.cleanup() raise MeekNotRunning() def cleanup(self): diff --git a/desktop/README.md b/desktop/README.md index 408b6852..7f13ad70 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -65,7 +65,7 @@ python scripts\get-tor-windows.py ### Compile dependencies -Install Go. The simplest way to make sure everything works is to install Go by following [these instructions](https://golang.org/doc/install). +Install Go. The simplest way to make sure everything works is to install Go by following [these instructions](https://golang.org/doc/install). (In Windows, make sure to install the 32-bit version of Go, such as `go1.17.3.windows-386.msi`.) Download and compile `meek-client`: diff --git a/desktop/scripts/build-meek-client.py b/desktop/scripts/build-meek-client.py index d043754c..af58173a 100755 --- a/desktop/scripts/build-meek-client.py +++ b/desktop/scripts/build-meek-client.py @@ -28,6 +28,7 @@ import shutil import os import subprocess import inspect +import platform def main(): @@ -46,16 +47,21 @@ def main(): root_path = os.path.dirname( os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) ) - dist_path = os.path.join(root_path, "src", "onionshare", "resources", "tor") + if platform.system() == "Windows": + dist_path = os.path.join(root_path, "src", "onionshare", "resources", "tor", "Tor") + bin_filename = "meek-client.exe" + else: + dist_path = os.path.join(root_path, "src", "onionshare", "resources", "tor") + bin_filename = "meek-client" - bin_path = os.path.expanduser("~/go/bin/meek-client") + bin_path = os.path.join(os.path.expanduser("~"), "go", "bin", bin_filename) shutil.copyfile( os.path.join(bin_path), - os.path.join(dist_path, "meek-client"), + os.path.join(dist_path, bin_filename), ) - os.chmod(os.path.join(dist_path, "meek-client"), 0o755) + os.chmod(os.path.join(dist_path, bin_filename), 0o755) - print(f"Installed meek-client in {dist_path}") + print(f"Installed {bin_filename} in {dist_path}") if __name__ == "__main__": diff --git a/desktop/src/onionshare/moat_dialog.py b/desktop/src/onionshare/moat_dialog.py index 85b5e888..84a52390 100644 --- a/desktop/src/onionshare/moat_dialog.py +++ b/desktop/src/onionshare/moat_dialog.py @@ -26,7 +26,7 @@ import json from . import strings from .gui_common import GuiCommon -from onionshare_cli.meek import MeekNotFound +from onionshare_cli.meek import MeekNotFound, MeekNotRunning class MoatDialog(QtWidgets.QDialog): @@ -237,7 +237,13 @@ class MoatThread(QtCore.QThread): try: self.meek.start() except MeekNotFound: - self.common.log("MoatThread", "run", f"Could not find the Meek Client") + self.common.log("MoatThread", "run", f"Could not find meek-client") + self.bridgedb_error.emit() + return + except MeekNotRunning: + self.common.log( + "MoatThread", "run", f"Ran meek-client, but there was an error" + ) self.bridgedb_error.emit() return From 73b570f4b44da8790d7e7d22b6c160b047fc86c7 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 9 Nov 2021 18:49:23 -0800 Subject: [PATCH 31/56] Check if Tor is connected instead of if the Tor controller is authenticated --- desktop/src/onionshare/tab/mode/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/src/onionshare/tab/mode/__init__.py b/desktop/src/onionshare/tab/mode/__init__.py index 6be97995..c9b5cad1 100644 --- a/desktop/src/onionshare/tab/mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/__init__.py @@ -128,7 +128,7 @@ class Mode(QtWidgets.QWidget): self.wrapper_layout.addWidget(self.tor_not_connected_widget) self.setLayout(self.wrapper_layout) - if self.common.gui.onion.is_authenticated(): + if self.common.gui.onion.connected_to_tor: self.tor_connection_started() else: self.tor_connection_stopped() @@ -571,6 +571,6 @@ class Mode(QtWidgets.QWidget): if self.common.gui.local_only: self.tor_connection_started() return - + self.content_widget.hide() self.tor_not_connected_widget.show() From d88005d550ab65024e298f05e0c269eccf98f9e3 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 9 Nov 2021 18:57:07 -0800 Subject: [PATCH 32/56] When Tor is disconnected, hide the Check for Updates button in the Settings tab --- desktop/src/onionshare/settings_tab.py | 11 ++++++++++ desktop/src/onionshare/tab_widget.py | 28 +++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/desktop/src/onionshare/settings_tab.py b/desktop/src/onionshare/settings_tab.py index 75b7a326..cfa3261e 100644 --- a/desktop/src/onionshare/settings_tab.py +++ b/desktop/src/onionshare/settings_tab.py @@ -154,6 +154,11 @@ class SettingsTab(QtWidgets.QWidget): self.reload_settings() + if self.common.gui.onion.connected_to_tor: + self.tor_is_connected() + else: + self.tor_is_disconnected() + def reload_settings(self): # Load settings, and fill them in self.old_settings = Settings(self.common) @@ -341,3 +346,9 @@ class SettingsTab(QtWidgets.QWidget): else: self.check_for_updates_button.setEnabled(True) self.save_button.setEnabled(True) + + def tor_is_connected(self): + self.check_for_updates_button.show() + + def tor_is_disconnected(self): + self.check_for_updates_button.hide() diff --git a/desktop/src/onionshare/tab_widget.py b/desktop/src/onionshare/tab_widget.py index 7f42632d..7162fcc4 100644 --- a/desktop/src/onionshare/tab_widget.py +++ b/desktop/src/onionshare/tab_widget.py @@ -378,23 +378,23 @@ class TabWidget(QtWidgets.QTabWidget): def tor_is_connected(self): for tab_id in self.tabs: - if not ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab - ): - mode = self.tabs[tab_id].get_mode() - if mode: - mode.tor_connection_started() + if type(self.tabs[tab_id]) is SettingsTab: + self.tabs[tab_id].tor_is_connected() + else: + if not type(self.tabs[tab_id]) is TorSettingsTab: + mode = self.tabs[tab_id].get_mode() + if mode: + mode.tor_connection_started() def tor_is_disconnected(self): for tab_id in self.tabs: - if not ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab - ): - mode = self.tabs[tab_id].get_mode() - if mode: - mode.tor_connection_stopped() + if type(self.tabs[tab_id]) is SettingsTab: + self.tabs[tab_id].tor_is_disconnected() + else: + if not type(self.tabs[tab_id]) is TorSettingsTab: + mode = self.tabs[tab_id].get_mode() + if mode: + mode.tor_connection_stopped() class TabBar(QtWidgets.QTabBar): From f5e4d70731274be3f4cc3c8ad4c9e1b65ffa0ccc Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 12 Nov 2021 11:43:09 +1100 Subject: [PATCH 33/56] Remove unnecessary censorship class invocation, which breaks CLI mode right now. Fix Website and Chat modes with auto-stop timer in CLI mode. Add 'poetry run onionshare-cli' tests to CircleCI to catch CLI runtime bugs. --- .circleci/config.yml | 4 ++++ cli/onionshare_cli/__init__.py | 28 +++++++++++----------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 175595f3..b9938c28 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,6 +30,10 @@ jobs: command: | cd ~/repo/cli poetry run pytest -v ./tests + poetry run onionshare-cli --local-only ./tests --auto-stop-timer 2 + poetry run onionshare-cli --local-only --receive --auto-stop-timer 2 + poetry run onionshare-cli --local-only --website ../docs --auto-stop-timer 2 + poetry run onionshare-cli --local-only --chat --auto-stop-timer 2 test-gui: docker: diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index 4e34a508..1c26b050 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -27,8 +27,6 @@ from datetime import datetime from datetime import timedelta from .common import Common, CannotFindTor -from .censorship import CensorshipCircumvention -from .meek import Meek, MeekNotRunning from .web import Web from .onion import TorErrorProtocolError, TorTooOldEphemeral, TorTooOldStealth, Onion from .onionshare import OnionShare @@ -285,20 +283,6 @@ def main(cwd=None): # Create the Web object web = Web(common, False, mode_settings, mode) - # Create the Meek object and start the meek client - # meek = Meek(common) - # meek.start() - - # Create the CensorshipCircumvention object to make - # API calls to Tor over Meek - censorship = CensorshipCircumvention(common, meek) - # Example: request recommended bridges, pretending to be from China, using - # domain fronting. - # censorship_recommended_settings = censorship.request_settings(country="cn") - # print(censorship_recommended_settings) - # Clean up the meek subprocess once we're done working with the censorship circumvention API - # meek.cleanup() - # Start the Onion object try: onion = Onion(common, use_tmp_dir=True) @@ -474,12 +458,18 @@ def main(cwd=None): if app.autostop_timer > 0: # if the auto-stop timer was set and has run out, stop the server if not app.autostop_timer_thread.is_alive(): - if mode == "share" or (mode == "website"): + if mode == "share": # If there were no attempts to download the share, or all downloads are done, we can stop if web.share_mode.cur_history_id == 0 or web.done: print("Stopped because auto-stop timer ran out") web.stop(app.port) break + if mode == "website": + # If there were no attempts to visit the website, or all downloads are done, we can stop + if web.website_mode.cur_history_id == 0 or web.done: + print("Stopped because auto-stop timer ran out") + web.stop(app.port) + break if mode == "receive": if ( web.receive_mode.cur_history_id == 0 @@ -489,6 +479,10 @@ def main(cwd=None): web.stop(app.port) break web.receive_mode.can_upload = False + if mode == "chat": + print("Stopped because auto-stop timer ran out") + web.stop(app.port) + break # Allow KeyboardInterrupt exception to be handled with threads # https://stackoverflow.com/questions/3788208/python-threading-ignores-keyboardinterrupt-exception time.sleep(0.2) From 80e73162498f06985f7892caca06888ec1849338 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 12 Nov 2021 11:58:48 +1100 Subject: [PATCH 34/56] Allow website mode to stop even if people have viewed it --- cli/onionshare_cli/__init__.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index 1c26b050..060c5628 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -464,13 +464,7 @@ def main(cwd=None): print("Stopped because auto-stop timer ran out") web.stop(app.port) break - if mode == "website": - # If there were no attempts to visit the website, or all downloads are done, we can stop - if web.website_mode.cur_history_id == 0 or web.done: - print("Stopped because auto-stop timer ran out") - web.stop(app.port) - break - if mode == "receive": + elif mode == "receive": if ( web.receive_mode.cur_history_id == 0 or not web.receive_mode.uploads_in_progress @@ -479,7 +473,8 @@ def main(cwd=None): web.stop(app.port) break web.receive_mode.can_upload = False - if mode == "chat": + else: + # website or chat mode print("Stopped because auto-stop timer ran out") web.stop(app.port) break From 58dd3c73f96d82fe5760d6f0c48f66089beb3e4a Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Thu, 11 Nov 2021 17:38:38 -0800 Subject: [PATCH 35/56] Delete flatpak from here since it is managed in a separate repo --- flatpak/org.onionshare.OnionShare.yaml | 309 ------------------------- 1 file changed, 309 deletions(-) delete mode 100644 flatpak/org.onionshare.OnionShare.yaml diff --git a/flatpak/org.onionshare.OnionShare.yaml b/flatpak/org.onionshare.OnionShare.yaml deleted file mode 100644 index f2ca9dcf..00000000 --- a/flatpak/org.onionshare.OnionShare.yaml +++ /dev/null @@ -1,309 +0,0 @@ ---- -app-id: org.onionshare.OnionShare -command: onionshare -runtime: org.kde.Platform -runtime-version: "5.15" -sdk: org.kde.Sdk -sdk-extensions: - - org.freedesktop.Sdk.Extension.golang -separate-locales: false -finish-args: - - "--device=dri" - - "--share=ipc" - - "--share=network" - - "--socket=wayland" - - "--socket=x11" - - "--talk-name=org.freedesktop.Flatpak" - - "--talk-name=org.freedesktop.Notifications" - - "--talk-name=org.freedesktop.secrets" - - "--filesystem=home:ro" - - "--filesystem=~/OnionShare:create" - - "--filesystem=xdg-config/onionshare:create" -cleanup: - - "/go" - - "/bin/scripts" -modules: - - name: pyside2 - buildsystem: cmake-ninja - builddir: true - config-opts: - - -DCMAKE_BUILD_TYPE=Release - - -DBUILD_TESTS=OFF - cleanup: - - /bin - sources: - - type: archive - sha256: f175c1d8813257904cf0efeb58e44f68d53b9916f73adaf9ce19514c0271c3fa - url: https://download.qt.io/official_releases/QtForPython/pyside2/PySide2-5.15.1-src/pyside-setup-opensource-src-5.15.1.tar.xz - - type: shell - commands: - - mkdir -p /app/include/qt5tmp && cp -R /usr/include/Qt* /app/include/qt5tmp # https://bugreports.qt.io/browse/PYSIDE-787 - - sed -i 's|\(--include-paths=\)|\1/app/include/qt5tmp:|' sources/pyside2/cmake/Macros/PySideModules.cmake - - name: tor - buildsystem: simple - build-commands: - - "./configure --prefix=${FLATPAK_DEST}" - - make - - make install - sources: - - type: archive - sha256: 22cba3794fedd5fa87afc1e512c6ce2c21bc20b4e1c6f8079d832dc1e545e733 - url: https://dist.torproject.org/tor-0.4.5.6.tar.gz - modules: - - name: libevent - buildsystem: simple - build-commands: - - "./configure --prefix=${FLATPAK_DEST}" - - make - - make install - sources: - - type: archive - url: https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz - sha256: 92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb - - name: obfs4proxy - buildsystem: simple - build-options: - env: - GOBIN: "/app/bin/" - GO111MODULE: "off" - build-commands: - - ". /usr/lib/sdk/golang/enable.sh; GOPATH=$PWD go install gitlab.com/yawning/obfs4.git/obfs4proxy" - sources: - - type: git - url: https://go.googlesource.com/net - commit: 5f55cee0dc0dc168ce29222f077fe7fcd4be72c5 - dest: src/golang.org/x/net - - type: git - url: https://go.googlesource.com/crypto - commit: 5ea612d1eb830b38bc4e914e37f55311eb58adce - dest: src/golang.org/x/crypto - - type: git - url: https://go.googlesource.com/sys - commit: 9a76102bfb4322425a1228caa377974426e82c84 - dest: src/golang.org/x/sys - - type: git - url: https://go.googlesource.com/text - commit: 8f690f22cf1c026c950adddf3d45258bfd0912f0 - dest: src/golang.org/x/text - - type: git - url: https://gitlab.com/yawning/utls - commit: 2dd4f38ff9e07464eb2748cc017eac1355e42251 - dest: src/gitlab.com/yawning/utls.git - - type: git - url: https://gitlab.com/yawning/obfs4 - commit: f638c33f6c6f697498150d5f0dfbf26453759262 - dest: src/gitlab.com/yawning/obfs4.git - - type: git - url: https://gitlab.com/yawning/bsaes - commit: 0a714cd429ec754482b4001e918db30cd2094405 - dest: src/gitlab.com/yawning/bsaes.git - - type: git - url: https://github.com/dchest/siphash - commit: a21c2e7914a8fe0db087fa007cbe804967665dfc - dest: src/github.com/dchest/siphash - - type: git - url: https://github.com/dsnet/compress - commit: da652975a8eea9fa0735aba8056747a751db0bd3 - dest: src/github.com/dsnet/compress - - type: git - url: https://git.torproject.org/pluggable-transports/goptlib - commit: 781a46c66d2ddbc3509354ae7f1fccab74cb9927 - dest: src/git.torproject.org/pluggable-transports/goptlib.git - - name: onionshare - buildsystem: simple - ensure-writable: - - easy-install.pth - build-commands: - - python3 setup.py install --prefix=${FLATPAK_DEST} - - install -D -m0644 org.onionshare.OnionShare.appdata.xml ${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.appdata.xml - - install -D -m0644 org.onionshare.OnionShare.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/org.onionshare.OnionShare.svg - - install -D -m0644 org.onionshare.OnionShare.desktop ${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop - sources: - - type: dir - path: ../desktop/src - modules: - - name: python3-qrcode - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "qrcode" - sources: - - type: file - url: https://files.pythonhosted.org/packages/19/d5/6c7d4e103d94364d067636417a77a6024219c58cd6e9f428ece9b5061ef9/qrcode-6.1.tar.gz - sha256: 505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369 - - name: onionshare-cli - buildsystem: simple - build-commands: - - python3 setup.py install --prefix=${FLATPAK_DEST} - sources: - - type: dir - path: ../cli - modules: - - name: python3-click - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "click" - sources: - - type: file - url: https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz - sha256: d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a - - name: python3-flask - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "flask" - sources: - - type: file - url: https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz - sha256: a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 - - type: file - url: https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz - sha256: d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a - - type: file - url: https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz - sha256: 29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b - - type: file - url: https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz - sha256: 321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 - - type: file - url: https://files.pythonhosted.org/packages/4e/0b/cb02268c90e67545a0e3a37ea1ca3d45de3aca43ceb7dbf1712fb5127d5d/Flask-1.1.2.tar.gz - sha256: 4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060 - - type: file - url: https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz - sha256: 6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c - - name: python3-flask-socketio - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "flask-socketio" - sources: - - type: file - url: https://files.pythonhosted.org/packages/06/7f/7496b6684e2b8eadb150555fa979497303459a31cb7dc592a5da51900090/Flask-SocketIO-5.0.1.tar.gz - sha256: 5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227 - - type: file - url: https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz - sha256: a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 - - type: file - url: https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz - sha256: d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a - - type: file - url: https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz - sha256: 29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b - - type: file - url: https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz - sha256: 321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 - - type: file - url: https://files.pythonhosted.org/packages/4e/0b/cb02268c90e67545a0e3a37ea1ca3d45de3aca43ceb7dbf1712fb5127d5d/Flask-1.1.2.tar.gz - sha256: 4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060 - - type: file - url: https://files.pythonhosted.org/packages/bd/7c/83fbbc8568be511bc48704b97ef58f67ff2ab85ec4fcd1dad12cd2323c32/bidict-0.21.2.tar.gz - sha256: 4fa46f7ff96dc244abfc437383d987404ae861df797e2fd5b190e233c302be09 - - type: file - url: https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz - sha256: 6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c - - type: file - url: https://files.pythonhosted.org/packages/37/91/7713854e0741f807c38ef084169b489ff6e87b24d1d9ba1e943bb9e10b8b/python-socketio-5.0.4.tar.gz - sha256: f53fd0d5bd9f75a70492062f4ae6195ab5d34d67a29024d740f25e468392893e - - type: file - url: https://files.pythonhosted.org/packages/92/e8/2dd4bd782b593adcc0bdce0675fe92016c3ffca061202142fcf1e55cbf6a/python-engineio-4.0.0.tar.gz - sha256: 9f34afa4170f5ba6e3d9ff158752ccf8fbb2145f16554b2f0fc84646675be99a - - type: file - url: https://files.pythonhosted.org/packages/12/68/95515eaff788370246dac534830ea9ccb0758e921ac9e9041996026ecaf2/setuptools-53.0.0.tar.gz - sha256: 1b18ef17d74ba97ac9c0e4b4265f123f07a8ae85d9cd093949fa056d3eeeead5 - - type: file - url: https://files.pythonhosted.org/packages/ed/46/e298a50dde405e1c202e316fa6a3015ff9288423661d7ea5e8f22f589071/wheel-0.36.2.tar.gz - sha256: e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e - - type: file - url: https://files.pythonhosted.org/packages/af/df/f8aa8a78d4d29e0cffa4512e9bc223ed02f24893fe1837c6cee2749ebd67/setuptools_scm-5.0.1.tar.gz - sha256: c85b6b46d0edd40d2301038cdea96bb6adc14d62ef943e75afb08b3e7bcf142a - - name: python3-psutil - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "psutil" - sources: - - type: file - url: https://files.pythonhosted.org/packages/e1/b0/7276de53321c12981717490516b7e612364f2cb372ee8901bd4a66a000d7/psutil-5.8.0.tar.gz - sha256: 0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6 - - name: python3-pycryptodome - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "pycryptodome" - sources: - - type: file - url: https://files.pythonhosted.org/packages/88/7f/740b99ffb8173ba9d20eb890cc05187677df90219649645aca7e44eb8ff4/pycryptodome-3.10.1.tar.gz - sha256: 3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673 - - name: python3-pysocks - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "pysocks" - sources: - - type: file - url: https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz - sha256: 3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0 - - name: python3-requests - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "requests" - sources: - - type: file - url: https://files.pythonhosted.org/packages/d7/8d/7ee68c6b48e1ec8d41198f694ecdc15f7596356f2ff8e6b1420300cf5db3/urllib3-1.26.3.tar.gz - sha256: de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73 - - type: file - url: https://files.pythonhosted.org/packages/ee/2d/9cdc2b527e127b4c9db64b86647d567985940ac3698eeabc7ffaccb4ea61/chardet-4.0.0.tar.gz - sha256: 0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa - - type: file - url: https://files.pythonhosted.org/packages/06/a9/cd1fd8ee13f73a4d4f491ee219deeeae20afefa914dfb4c130cfc9dc397a/certifi-2020.12.5.tar.gz - sha256: 1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c - - type: file - url: https://files.pythonhosted.org/packages/6b/47/c14abc08432ab22dc18b9892252efaf005ab44066de871e72a38d6af464b/requests-2.25.1.tar.gz - sha256: 27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 - - type: file - url: https://files.pythonhosted.org/packages/ea/b7/e0e3c1c467636186c39925827be42f16fee389dc404ac29e930e9136be70/idna-2.10.tar.gz - sha256: b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 - - name: python3-stem - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "stem" - sources: - - type: file - url: https://files.pythonhosted.org/packages/71/bd/ab05ffcbfe74dca704e860312e00c53ef690b1ddcb23be7a4d9ea4f40260/stem-1.8.0.tar.gz - sha256: a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2 - - name: python3-unidecode - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "unidecode" - sources: - - type: file - url: https://files.pythonhosted.org/packages/cd/31/245d8a384939aa0ee152c76fc62890f79f35fc41cd12839f5df268d9081d/Unidecode-1.2.0.tar.gz - sha256: 8d73a97d387a956922344f6b74243c2c6771594659778744b2dbdaad8f6b727d - - name: python3-urllib3 - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "urllib3" - sources: - - type: file - url: https://files.pythonhosted.org/packages/d7/8d/7ee68c6b48e1ec8d41198f694ecdc15f7596356f2ff8e6b1420300cf5db3/urllib3-1.26.3.tar.gz - sha256: de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73 - - name: python3-eventlet - buildsystem: simple - build-commands: - - pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} - "eventlet" - sources: - - type: file - url: https://files.pythonhosted.org/packages/40/9c/bd7bc0202a84012a4b6b653b54a389ef48bc7f13ce628865357ffdf37160/eventlet-0.30.1.tar.gz - sha256: d00649a7e17de0bcddff1a96311ed3baf1b295b3223d4b71aceafe7b45e6d6f8 - - type: file - url: https://files.pythonhosted.org/packages/ec/c5/14bcd63cb6d06092a004793399ec395405edf97c2301dfdc146dfbd5beed/dnspython-1.16.0.zip - sha256: 36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01 - - type: file - url: https://files.pythonhosted.org/packages/92/be/878cc5314fa5aadce33e68738c1a24debe317605196bdfc2049e66bc9c30/greenlet-1.0.0.tar.gz - sha256: 719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8 From f00d2cc0b0029e1b21fbba28dc7476f94f6c92f8 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 12 Nov 2021 15:47:33 -0800 Subject: [PATCH 36/56] Switch stem with cepa, and update CLI packages --- cli/poetry.lock | 180 ++++++++++++++++++++++----------------------- cli/pyproject.toml | 2 +- 2 files changed, 91 insertions(+), 91 deletions(-) diff --git a/cli/poetry.lock b/cli/poetry.lock index 9314b096..5a3708d7 100644 --- a/cli/poetry.lock +++ b/cli/poetry.lock @@ -22,12 +22,20 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "bidict" -version = "0.21.3" +version = "0.21.4" description = "The bidirectional mapping library for Python." category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "cepa" +version = "1.8.2" +description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "certifi" version = "2021.10.8" @@ -38,7 +46,7 @@ python-versions = "*" [[package]] name = "cffi" -version = "1.14.6" +version = "1.15.0" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -146,7 +154,7 @@ docs = ["sphinx"] [[package]] name = "idna" -version = "3.2" +version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false @@ -154,7 +162,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.8.1" +version = "4.8.2" description = "Read metadata from Python packages" category = "dev" optional = false @@ -167,7 +175,7 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] perf = ["ipython"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -209,14 +217,14 @@ python-versions = ">=3.6" [[package]] name = "packaging" -version = "21.0" +version = "21.2" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -pyparsing = ">=2.0.2" +pyparsing = ">=2.0.2,<3" [[package]] name = "pluggy" @@ -246,15 +254,15 @@ test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] [[package]] name = "py" -version = "1.10.0" +version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycparser" -version = "2.20" +version = "2.21" description = "C parser in Python" category = "main" optional = false @@ -316,7 +324,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "python-engineio" -version = "4.2.1" +version = "4.3.0" description = "Engine.IO server and client for Python" category = "main" optional = false @@ -328,7 +336,7 @@ client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] [[package]] name = "python-socketio" -version = "5.4.0" +version = "5.4.1" description = "Socket.IO server and client for Python" category = "main" optional = false @@ -369,21 +377,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "stem" -version = "1.8.1" -description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)." -category = "main" -optional = false -python-versions = "*" -develop = false - -[package.source] -type = "git" -url = "https://github.com/onionshare/stem.git" -reference = "1.8.1" -resolved_reference = "de3d03a03c7ee57c74c80e9c63cb88072d833717" - [[package]] name = "toml" version = "0.10.2" @@ -448,7 +441,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "181891640e59dac730905019444d42ef8e99da0c34c96fb8a616781661bae537" +content-hash = "77b8c0bae7732109de895b3e8dc729eb434c6c24915cb555659bd71c0048f776" [metadata.files] atomicwrites = [ @@ -460,59 +453,67 @@ attrs = [ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] bidict = [ - {file = "bidict-0.21.3-py3-none-any.whl", hash = "sha256:2cce0d01eb3db9b3fa85db501c00aaa3389ee4cab7ef82178604552dfa943a1b"}, - {file = "bidict-0.21.3.tar.gz", hash = "sha256:d50bd81fae75e34198ffc94979a0eb0939ff9adb3ef32bcc93a913d8b3e3ed1d"}, + {file = "bidict-0.21.4-py3-none-any.whl", hash = "sha256:3ac67daa353ecf853a1df9d3e924f005e729227a60a8dbada31a4c31aba7f654"}, + {file = "bidict-0.21.4.tar.gz", hash = "sha256:42c84ffbe6f8de898af6073b4be9ea7ccedcd78d3474aa844c54e49d5a079f6f"}, +] +cepa = [ + {file = "cepa-1.8.2.tar.gz", hash = "sha256:6cc192b76c32fe5ae45005b2d904cfa704d47c565c22af291e9f9e715045bb65"}, ] certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] cffi = [ - {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"}, - {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"}, - {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"}, - {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"}, - {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, - {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, - {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, - {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, - {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, - {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, - {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, - {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, - {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"}, - {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"}, - {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"}, - {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"}, - {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"}, - {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"}, - {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"}, - {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"}, - {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"}, - {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"}, - {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"}, - {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"}, - {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"}, + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, ] charset-normalizer = [ {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, @@ -595,12 +596,12 @@ greenlet = [ {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, ] idna = [ - {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, - {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, - {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, + {file = "importlib_metadata-4.8.2-py3-none-any.whl", hash = "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100"}, + {file = "importlib_metadata-4.8.2.tar.gz", hash = "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -671,8 +672,8 @@ markupsafe = [ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, ] packaging = [ - {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, - {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, + {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, + {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, @@ -709,12 +710,12 @@ psutil = [ {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, ] py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pycparser = [ - {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, - {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] pynacl = [ {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, @@ -750,12 +751,12 @@ pytest = [ {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] python-engineio = [ - {file = "python-engineio-4.2.1.tar.gz", hash = "sha256:d510329b6d8ed5662547862f58bc73659ae62defa66b66d745ba021de112fa62"}, - {file = "python_engineio-4.2.1-py3-none-any.whl", hash = "sha256:f3ef9a2c048d08990f294c5f8991f6f162c3b12ecbd368baa0d90441de907d1c"}, + {file = "python-engineio-4.3.0.tar.gz", hash = "sha256:fed35eeacfa21f53f1fc05ef0cadd65a50780364da3a2be7650eb92f928fdb11"}, + {file = "python_engineio-4.3.0-py3-none-any.whl", hash = "sha256:ad06a975f7e14cb3bb7137cbf70fd883804484d29acd58004d1db1e2a7fc0ad3"}, ] python-socketio = [ - {file = "python-socketio-5.4.0.tar.gz", hash = "sha256:ca807c9e1f168e96dea412d64dd834fb47c470d27fd83da0504aa4b248ba2544"}, - {file = "python_socketio-5.4.0-py3-none-any.whl", hash = "sha256:7ed57f6c024abdfeb9b25c74c0c00ffc18da47d903e8d72deecb87584370d1fc"}, + {file = "python-socketio-5.4.1.tar.gz", hash = "sha256:ef4e273ddfebb421144a228cbab1e7e27ffe8d372514fa561e57d590ea6627b0"}, + {file = "python_socketio-5.4.1-py3-none-any.whl", hash = "sha256:d84fa319e943aa18328280c8fbc4e2ba03cf9e96ff905b294b8b482af64532c9"}, ] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, @@ -765,7 +766,6 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -stem = [] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/cli/pyproject.toml b/cli/pyproject.toml index 9994240c..0ad36f4d 100644 --- a/cli/pyproject.toml +++ b/cli/pyproject.toml @@ -29,7 +29,7 @@ eventlet = "*" setuptools = "*" pynacl = "^1.4.0" colorama = "*" -stem = {git = "https://github.com/onionshare/stem.git", rev = "1.8.1"} +cepa = "1.8.2" [tool.poetry.dev-dependencies] pytest = "*" From 3464a24e698d61d5a4cbf429abd7920aa3bcdde2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 12 Nov 2021 16:21:17 -0800 Subject: [PATCH 37/56] Add logging --- desktop/src/onionshare/gui_common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index 0db0f051..0d145f11 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -413,6 +413,9 @@ class GuiCommon: if self.common.platform == "Linux": base_path = self.get_resource_path("tor") if os.path.exists(base_path): + self.common.log( + "GuiCommon", "get_tor_paths", "using paths in resources" + ) tor_path = os.path.join(base_path, "tor") tor_geo_ip_file_path = os.path.join(base_path, "geoip") tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6") @@ -421,6 +424,7 @@ class GuiCommon: meek_client_file_path = os.path.join(base_path, "meek-client") else: # Fallback to looking in the path + self.common.log("GuiCommon", "get_tor_paths", "using paths from PATH") tor_path = shutil.which("tor") obfs4proxy_file_path = shutil.which("obfs4proxy") snowflake_file_path = shutil.which("snowflake-client") From be1772b2a4d1b3b73cf88c554f88cea5981127be Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 12 Nov 2021 17:18:28 -0800 Subject: [PATCH 38/56] Fix tor paths for flatpak --- desktop/src/onionshare/gui_common.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index 0d145f11..af755401 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -412,7 +412,7 @@ class GuiCommon: def get_tor_paths(self): if self.common.platform == "Linux": base_path = self.get_resource_path("tor") - if os.path.exists(base_path): + if base_path: self.common.log( "GuiCommon", "get_tor_paths", "using paths in resources" ) @@ -471,7 +471,10 @@ class GuiCommon: """ Returns the absolute path of a resource """ - return resource_filename("onionshare", os.path.join("resources", filename)) + try: + return resource_filename("onionshare", os.path.join("resources", filename)) + except KeyError: + return None @staticmethod def get_translated_tor_error(e): From 27510a9f26cb51e7f2c5641a259106408cbc95e0 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 11:08:00 -0800 Subject: [PATCH 39/56] Start adding new pluggable transports to snap --- snap/snapcraft.yaml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 9bbc58ae..3f2c68f7 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -8,7 +8,7 @@ description: | web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service. -grade: stable # stable or devel +grade: devel # stable or devel confinement: strict apps: @@ -125,7 +125,7 @@ parts: - setuptools - pynacl - colorama - - git+https://github.com/onionshare/stem.git@1.8.1 + - cepa == 1.8.2 stage-packages: - build-essential - libssl-dev @@ -135,11 +135,11 @@ parts: stage: - -usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 - -usr/share/doc/libssl1.1/changelog.Debian.gz - after: [tor, obfs4] + after: [tor, obfs4, snowflake-client, meek-client] tor: - source: https://dist.torproject.org/tor-0.4.6.7.tar.gz - source-checksum: sha256/ff665ce121b2952110bd98b9c8741b5593bf6c01ac09033ad848ed92c2510f9a + source: https://dist.torproject.org/tor-0.4.6.8.tar.gz + source-checksum: sha256/15ce1a37b4cc175b07761e00acdcfa2c08f0d23d6c3ab9c97c464bd38cc5476a source-type: tar plugin: autotools build-packages: @@ -158,3 +158,18 @@ parts: go-importpath: gitlab.com/yawning/obfs4 source: https://gitlab.com/yawning/obfs4 source-type: git + + snowflake-client: + plugin: go + go-importpath: git.torproject.org/pluggable-transports/snowflake.git/client + source: https://git.torproject.org/pluggable-transports/snowflake.git + source-type: git + source-tag: v2.0.1 + + meek-client: + plugin: go + go-importpath: git.torproject.org/pluggable-transports/meek.git/meek-client + go-packages: ["google.golang.org/appengine"] + source: https://git.torproject.org/pluggable-transports/meek.git + source-type: git + source-tag: v0.37.0 From 5d7293db4449851be86e17d234ff8802275f6f51 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 13:28:33 -0800 Subject: [PATCH 40/56] Keep using stem for now, and comment out meek-client for now --- snap/snapcraft.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 3f2c68f7..7413bf72 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -125,7 +125,7 @@ parts: - setuptools - pynacl - colorama - - cepa == 1.8.2 + - git+https://github.com/onionshare/stem.git@1.8.1 stage-packages: - build-essential - libssl-dev @@ -135,7 +135,7 @@ parts: stage: - -usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 - -usr/share/doc/libssl1.1/changelog.Debian.gz - after: [tor, obfs4, snowflake-client, meek-client] + after: [tor, obfs4, snowflake-client] tor: source: https://dist.torproject.org/tor-0.4.6.8.tar.gz @@ -166,10 +166,10 @@ parts: source-type: git source-tag: v2.0.1 - meek-client: - plugin: go - go-importpath: git.torproject.org/pluggable-transports/meek.git/meek-client - go-packages: ["google.golang.org/appengine"] - source: https://git.torproject.org/pluggable-transports/meek.git - source-type: git - source-tag: v0.37.0 + # meek-client: + # plugin: go + # go-importpath: git.torproject.org/pluggable-transports/meek.git/meek-client + # go-packages: ["google.golang.org/appengine"] + # source: https://git.torproject.org/pluggable-transports/meek.git + # source-type: git + # source-tag: v0.37.0 From 94c588d0ce6ce76ffc7714621a04b207e0522c14 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 14:24:02 -0800 Subject: [PATCH 41/56] Make the FileSelection widget use native file browser widgets under Flatpak in order to enable the sandbox --- .../src/onionshare/tab/mode/file_selection.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index 302f07b9..6d1c2d65 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -342,8 +342,20 @@ class FileSelection(QtWidgets.QVBoxLayout): self.file_list.files_dropped.connect(self.update) self.file_list.files_updated.connect(self.update) - # Buttons + # Sandboxes (for masOS, Flatpak, etc.) need separate add files and folders buttons, in + # order to use native file selection dialogs + + # macOS if self.common.platform == "Darwin": + self.sandbox = True + # Flatpack + elif self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + self.sandbox = True + else: + self.sandbox = False + + # Buttons + if self.sandbox: # The macOS sandbox makes it so the Mac version needs separate add files # and folders buttons, in order to use native file selection dialogs self.add_files_button = QtWidgets.QPushButton(strings._("gui_add_files")) @@ -357,7 +369,7 @@ class FileSelection(QtWidgets.QVBoxLayout): self.remove_button.clicked.connect(self.delete) button_layout = QtWidgets.QHBoxLayout() button_layout.addStretch() - if self.common.platform == "Darwin": + if self.sandbox: button_layout.addWidget(self.add_files_button) button_layout.addWidget(self.add_folder_button) else: @@ -376,14 +388,14 @@ class FileSelection(QtWidgets.QVBoxLayout): """ # All buttons should be hidden if the server is on if self.server_on: - if self.common.platform == "Darwin": + if self.sandbox: self.add_files_button.hide() self.add_folder_button.hide() else: self.add_button.hide() self.remove_button.hide() else: - if self.common.platform == "Darwin": + if self.sandbox: self.add_files_button.show() self.add_folder_button.show() else: From bfce4b005c34baa92ec8f34fd7f6476173f0bda0 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 15:08:06 -0800 Subject: [PATCH 42/56] Disable drag and drop in flatpak --- desktop/src/onionshare/resources/locale/en.json | 1 + desktop/src/onionshare/tab/mode/file_selection.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index 868a6fa9..d405c702 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -10,6 +10,7 @@ "gui_add_files": "Add Files", "gui_add_folder": "Add Folder", "gui_remove": "Remove", + "gui_dragdrop_sandbox_flatpak": "To make the Flatpak sandbox more secure, drag and drop is not supported. Use the Add Files and Add Folder buttons to browse for files instead.", "gui_file_selection_remove_all": "Remove All", "gui_choose_items": "Choose", "gui_share_start_server": "Start sharing", diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index 6d1c2d65..daf347c0 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -243,6 +243,13 @@ class FileList(QtWidgets.QListWidget): """ Add a file or directory to this widget. """ + # Drag and drop doesn't work in Flatpak, because of the sandbox + if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + Alert( + self.common, strings._("gui_dragdrop_sandbox_flatpak").format(filename) + ) + return + filenames = [] for index in range(self.count()): filenames.append(self.item(index).filename) From 3eb912bf5ac8f405c8c9838fb8ca0bba73bc4a9b Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 15:46:20 -0800 Subject: [PATCH 43/56] Move drag and drop logic into dropEvent --- desktop/src/onionshare/tab/mode/file_selection.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index daf347c0..7ab110fc 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -225,6 +225,14 @@ class FileList(QtWidgets.QListWidget): """ dropEvent for dragging files and directories into the widget. """ + # Drag and drop doesn't work in Flatpak, because of the sandbox + if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + Alert( + self.common, strings._("gui_dragdrop_sandbox_flatpak").format(filename) + ) + event.ignore() + return + if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() @@ -243,13 +251,6 @@ class FileList(QtWidgets.QListWidget): """ Add a file or directory to this widget. """ - # Drag and drop doesn't work in Flatpak, because of the sandbox - if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): - Alert( - self.common, strings._("gui_dragdrop_sandbox_flatpak").format(filename) - ) - return - filenames = [] for index in range(self.count()): filenames.append(self.item(index).filename) From ad9cacd44afcdcfd9f648cec154590d9714306f6 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 15:49:30 -0800 Subject: [PATCH 44/56] Drag and drop string does not use filename --- desktop/src/onionshare/tab/mode/file_selection.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index 7ab110fc..5cf58c70 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -227,9 +227,7 @@ class FileList(QtWidgets.QListWidget): """ # Drag and drop doesn't work in Flatpak, because of the sandbox if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): - Alert( - self.common, strings._("gui_dragdrop_sandbox_flatpak").format(filename) - ) + Alert(self.common, strings._("gui_dragdrop_sandbox_flatpak").format()) event.ignore() return From ab800c577a37db53c2b2566c7b850eea90938ee2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 16:00:00 -0800 Subject: [PATCH 45/56] More thoroughly remove drag and drop to prevent the drop label from getting created --- desktop/src/onionshare/tab/mode/file_selection.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index 5cf58c70..ea80de53 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -185,6 +185,11 @@ class FileList(QtWidgets.QListWidget): """ dragEnterEvent for dragging files and directories into the widget. """ + # Drag and drop doesn't work in Flatpak, because of the sandbox + if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + event.ignore() + return + if event.mimeData().hasUrls: self.setStyleSheet(self.common.gui.css["share_file_list_drag_enter"]) count = len(event.mimeData().urls()) @@ -206,6 +211,11 @@ class FileList(QtWidgets.QListWidget): """ dragLeaveEvent for dragging files and directories into the widget. """ + # Drag and drop doesn't work in Flatpak, because of the sandbox + if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + event.ignore() + return + self.setStyleSheet(self.common.gui.css["share_file_list_drag_leave"]) self.drop_count.hide() event.accept() @@ -215,6 +225,11 @@ class FileList(QtWidgets.QListWidget): """ dragMoveEvent for dragging files and directories into the widget. """ + # Drag and drop doesn't work in Flatpak, because of the sandbox + if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + event.ignore() + return + if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() From 6b521c152ebcef881247181426310a576ea10413 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 16:11:39 -0800 Subject: [PATCH 46/56] Show the alert on the the dragEnterEvent --- desktop/src/onionshare/tab/mode/file_selection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index ea80de53..40ca1685 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -187,6 +187,7 @@ class FileList(QtWidgets.QListWidget): """ # Drag and drop doesn't work in Flatpak, because of the sandbox if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + Alert(self.common, strings._("gui_dragdrop_sandbox_flatpak").format()) event.ignore() return @@ -242,7 +243,6 @@ class FileList(QtWidgets.QListWidget): """ # Drag and drop doesn't work in Flatpak, because of the sandbox if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): - Alert(self.common, strings._("gui_dragdrop_sandbox_flatpak").format()) event.ignore() return From 70248ed4a5ee12ce5d8526db5a1916e202e784e3 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 16:24:36 -0800 Subject: [PATCH 47/56] Upgrade to cepa 1.8.3 --- cli/poetry.lock | 24 +++++++++++------------- cli/pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/cli/poetry.lock b/cli/poetry.lock index 5a3708d7..966aea84 100644 --- a/cli/poetry.lock +++ b/cli/poetry.lock @@ -30,7 +30,7 @@ python-versions = ">=3.6" [[package]] name = "cepa" -version = "1.8.2" +version = "1.8.3" description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)." category = "main" optional = false @@ -336,7 +336,7 @@ client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] [[package]] name = "python-socketio" -version = "5.4.1" +version = "5.5.0" description = "Socket.IO server and client for Python" category = "main" optional = false @@ -344,7 +344,7 @@ python-versions = ">=3.6" [package.dependencies] bidict = ">=0.21.0" -python-engineio = ">=4.1.0" +python-engineio = ">=4.3.0" [package.extras] asyncio_client = ["aiohttp (>=3.4)"] @@ -387,11 +387,11 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "typing-extensions" -version = "3.10.0.2" -description = "Backported and Experimental Type Hints for Python 3.5+" +version = "4.0.0" +description = "Backported and Experimental Type Hints for Python 3.6+" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "unidecode" @@ -441,7 +441,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "77b8c0bae7732109de895b3e8dc729eb434c6c24915cb555659bd71c0048f776" +content-hash = "b6700c9652a3292f2ab3153544c46ed78c75fc9b65c15fcf4e3c243f841565dd" [metadata.files] atomicwrites = [ @@ -457,7 +457,7 @@ bidict = [ {file = "bidict-0.21.4.tar.gz", hash = "sha256:42c84ffbe6f8de898af6073b4be9ea7ccedcd78d3474aa844c54e49d5a079f6f"}, ] cepa = [ - {file = "cepa-1.8.2.tar.gz", hash = "sha256:6cc192b76c32fe5ae45005b2d904cfa704d47c565c22af291e9f9e715045bb65"}, + {file = "cepa-1.8.3.tar.gz", hash = "sha256:1dc6f0b324d37a2ed2ca274648ece8fd2c96a1d2f440f58c0ca17afd4b5ede7a"}, ] certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, @@ -755,8 +755,8 @@ python-engineio = [ {file = "python_engineio-4.3.0-py3-none-any.whl", hash = "sha256:ad06a975f7e14cb3bb7137cbf70fd883804484d29acd58004d1db1e2a7fc0ad3"}, ] python-socketio = [ - {file = "python-socketio-5.4.1.tar.gz", hash = "sha256:ef4e273ddfebb421144a228cbab1e7e27ffe8d372514fa561e57d590ea6627b0"}, - {file = "python_socketio-5.4.1-py3-none-any.whl", hash = "sha256:d84fa319e943aa18328280c8fbc4e2ba03cf9e96ff905b294b8b482af64532c9"}, + {file = "python-socketio-5.5.0.tar.gz", hash = "sha256:ce972ea1b82aa1811fa10d30cf0d5c251b9a1558c3d66829b6fe70854bcccf0b"}, + {file = "python_socketio-5.5.0-py3-none-any.whl", hash = "sha256:ca28a0ff0ca5dd05ec5ba4ee2572fe06b96d6f0bc7df384d8b50fbbc06986134"}, ] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, @@ -771,9 +771,7 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] typing-extensions = [ - {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, - {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, - {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, + {file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"}, ] unidecode = [ {file = "Unidecode-1.3.2-py3-none-any.whl", hash = "sha256:215fe33c9d1c889fa823ccb66df91b02524eb8cc8c9c80f9c5b8129754d27829"}, diff --git a/cli/pyproject.toml b/cli/pyproject.toml index 0ad36f4d..372d9aa4 100644 --- a/cli/pyproject.toml +++ b/cli/pyproject.toml @@ -29,7 +29,7 @@ eventlet = "*" setuptools = "*" pynacl = "^1.4.0" colorama = "*" -cepa = "1.8.2" +cepa = "1.8.3" [tool.poetry.dev-dependencies] pytest = "*" From 0f63c89a5929182e9ea120075fe47d58536ce2fc Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 17:16:21 -0800 Subject: [PATCH 48/56] Detect Flatpak via environment variable --- cli/onionshare_cli/common.py | 6 ++++++ desktop/src/onionshare/tab/mode/file_selection.py | 15 +++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index bab3fd86..ca88deef 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -432,6 +432,12 @@ class Common: r = random.SystemRandom() return "-".join(r.choice(wordlist) for _ in range(word_count)) + def is_flatpak(self): + """ + Returns True if OnionShare is running in a Flatpak sandbox + """ + return os.environ.get("FLATPAK_ID") == "org.onionshare.OnionShare" + @staticmethod def random_string(num_bytes, output_len=None): """ diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index 40ca1685..c75e9db6 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -186,7 +186,7 @@ class FileList(QtWidgets.QListWidget): dragEnterEvent for dragging files and directories into the widget. """ # Drag and drop doesn't work in Flatpak, because of the sandbox - if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + if self.common.is_flatpak(): Alert(self.common, strings._("gui_dragdrop_sandbox_flatpak").format()) event.ignore() return @@ -213,7 +213,7 @@ class FileList(QtWidgets.QListWidget): dragLeaveEvent for dragging files and directories into the widget. """ # Drag and drop doesn't work in Flatpak, because of the sandbox - if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + if self.common.is_flatpak(): event.ignore() return @@ -227,7 +227,7 @@ class FileList(QtWidgets.QListWidget): dragMoveEvent for dragging files and directories into the widget. """ # Drag and drop doesn't work in Flatpak, because of the sandbox - if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + if self.common.is_flatpak(): event.ignore() return @@ -242,7 +242,7 @@ class FileList(QtWidgets.QListWidget): dropEvent for dragging files and directories into the widget. """ # Drag and drop doesn't work in Flatpak, because of the sandbox - if self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + if self.common.is_flatpak(): event.ignore() return @@ -365,12 +365,7 @@ class FileSelection(QtWidgets.QVBoxLayout): # Sandboxes (for masOS, Flatpak, etc.) need separate add files and folders buttons, in # order to use native file selection dialogs - - # macOS - if self.common.platform == "Darwin": - self.sandbox = True - # Flatpack - elif self.common.platform == "Linux" and os.path.exists("/app/manifest.json"): + if self.common.platform == "Darwin" or self.common.is_flatpak(): self.sandbox = True else: self.sandbox = False From f09bb66425ea61973a46750e8f37e8292bce286f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 17:52:40 -0800 Subject: [PATCH 49/56] Snap successfully builds meek-client, and WIP getting tor to connect --- cli/onionshare_cli/onion.py | 38 ++++++++++++++++++---------- desktop/src/onionshare/gui_common.py | 2 +- snap/snapcraft.yaml | 25 +++++++++++------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index 536b9ba0..39b1dc9a 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -354,6 +354,11 @@ class Onion(object): f.write("\nUseBridges 1\n") # Execute a tor subprocess + self.common.log( + "Onion", + "connect", + f"starting {self.tor_path} subprocess", + ) start_ts = time.time() if self.common.platform == "Windows": # In Windows, hide console window when opening tor.exe subprocess @@ -374,22 +379,29 @@ class Onion(object): ) # Wait for the tor controller to start + self.common.log( + "Onion", + "connect", + f"tor pid: {self.tor_proc.pid}", + ) time.sleep(2) # Connect to the controller - try: - if ( - self.common.platform == "Windows" - or self.common.platform == "Darwin" - ): - self.c = Controller.from_port(port=self.tor_control_port) - self.c.authenticate() - else: - self.c = Controller.from_socket_file(path=self.tor_control_socket) - self.c.authenticate() - except Exception as e: - print("OnionShare could not connect to Tor:\n{}".format(e.args[0])) - raise BundledTorBroken(e.args[0]) + self.common.log( + "Onion", + "connect", + "authenticating to tor controller", + ) + # try: + if self.common.platform == "Windows" or self.common.platform == "Darwin": + self.c = Controller.from_port(port=self.tor_control_port) + self.c.authenticate() + else: + self.c = Controller.from_socket_file(path=self.tor_control_socket) + self.c.authenticate() + # except Exception as e: + # print("OnionShare could not connect to Tor:\n{}".format(e)) + # raise BundledTorBroken(e.args[0]) while True: try: diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index af755401..d0ad249b 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -412,7 +412,7 @@ class GuiCommon: def get_tor_paths(self): if self.common.platform == "Linux": base_path = self.get_resource_path("tor") - if base_path: + if base_path and os.path.isdir(base_path): self.common.log( "GuiCommon", "get_tor_paths", "using paths in resources" ) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 7413bf72..e197c207 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -15,6 +15,7 @@ apps: onionshare: common-id: org.onionshare.OnionShare command: onionshare + extensions: [ gnome-3-34 ] plugs: - desktop - home @@ -125,7 +126,7 @@ parts: - setuptools - pynacl - colorama - - git+https://github.com/onionshare/stem.git@1.8.1 + - cepa == 1.8.3 stage-packages: - build-essential - libssl-dev @@ -135,7 +136,7 @@ parts: stage: - -usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 - -usr/share/doc/libssl1.1/changelog.Debian.gz - after: [tor, obfs4, snowflake-client] + after: [tor, obfs4, snowflake-client, meek-client] tor: source: https://dist.torproject.org/tor-0.4.6.8.tar.gz @@ -165,11 +166,17 @@ parts: source: https://git.torproject.org/pluggable-transports/snowflake.git source-type: git source-tag: v2.0.1 + organize: + bin/client: bin/snowflake-client - # meek-client: - # plugin: go - # go-importpath: git.torproject.org/pluggable-transports/meek.git/meek-client - # go-packages: ["google.golang.org/appengine"] - # source: https://git.torproject.org/pluggable-transports/meek.git - # source-type: git - # source-tag: v0.37.0 + meek-client: + plugin: go + go-channel: stable + go-importpath: git.torproject.org/pluggable-transports/meek.git/meek-client + # Not sure why I have to do this, but it works + override-build: | + cd meek-client + go build -o /root/parts/meek-client/install/bin ./... + source: https://git.torproject.org/pluggable-transports/meek.git + source-type: git + source-tag: v0.37.0 From 99c85f55e27e777e64e4de12c74e3dedc88feec1 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 14 Nov 2021 20:53:22 -0800 Subject: [PATCH 50/56] Fix bug that was preventing tor from connecting in snapcraft --- cli/onionshare_cli/common.py | 6 ++++++ cli/onionshare_cli/onion.py | 32 +++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index ca88deef..4333d2d5 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -437,6 +437,12 @@ class Common: Returns True if OnionShare is running in a Flatpak sandbox """ return os.environ.get("FLATPAK_ID") == "org.onionshare.OnionShare" + + def is_snapcraft(self): + """ + Returns True if OnionShare is running in a Flatpak sandbox + """ + return os.environ.get("SNAP_INSTANCE_NAME") == "onionshare" @staticmethod def random_string(num_bytes, output_len=None): diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index 39b1dc9a..5ac669b8 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -29,6 +29,7 @@ import subprocess import time import shlex import psutil +import traceback from distutils.version import LooseVersion as Version @@ -371,11 +372,16 @@ class Onion(object): startupinfo=startupinfo, ) else: + if self.common.is_snapcraft(): + env = None + else: + env = {"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)} + self.tor_proc = subprocess.Popen( [self.tor_path, "-f", self.tor_torrc], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env={"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)}, + env=env, ) # Wait for the tor controller to start @@ -392,16 +398,20 @@ class Onion(object): "connect", "authenticating to tor controller", ) - # try: - if self.common.platform == "Windows" or self.common.platform == "Darwin": - self.c = Controller.from_port(port=self.tor_control_port) - self.c.authenticate() - else: - self.c = Controller.from_socket_file(path=self.tor_control_socket) - self.c.authenticate() - # except Exception as e: - # print("OnionShare could not connect to Tor:\n{}".format(e)) - # raise BundledTorBroken(e.args[0]) + try: + if ( + self.common.platform == "Windows" + or self.common.platform == "Darwin" + ): + self.c = Controller.from_port(port=self.tor_control_port) + self.c.authenticate() + else: + self.c = Controller.from_socket_file(path=self.tor_control_socket) + self.c.authenticate() + except Exception as e: + print("OnionShare could not connect to Tor:\n{}".format(e.args[0])) + print(traceback.format_exc()) + raise BundledTorBroken(e.args[0]) while True: try: From a4db0d26c96bd66be8f3017ddd44a5c7d7792fc2 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 15 Nov 2021 18:07:57 -0800 Subject: [PATCH 51/56] Fix for meek to working in snapcraft --- cli/onionshare_cli/meek.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/onionshare_cli/meek.py b/cli/onionshare_cli/meek.py index c5df7b7f..dffbad83 100644 --- a/cli/onionshare_cli/meek.py +++ b/cli/onionshare_cli/meek.py @@ -120,7 +120,8 @@ class Meek(object): stderr=subprocess.PIPE, bufsize=1, env=self.meek_env, - text=True, + # Using universal_newlines instead of text because the snap package using python < 3.7 + universal_newlines=True, ) # Queue up the stdout from the subprocess for polling later From 267e9eb1e43bb188e8925caecb71914130fb266f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 15 Nov 2021 18:16:10 -0800 Subject: [PATCH 52/56] Exception handling on printing the banner --- cli/onionshare_cli/common.py | 393 ++++++++++++++++++----------------- 1 file changed, 201 insertions(+), 192 deletions(-) diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index 4333d2d5..a705809b 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -87,197 +87,206 @@ class Common: ╰───────────────────────────────────────────╯ """ - if self.platform == "Windows": - pass - else: - pass - - print( - Back.MAGENTA + Fore.WHITE + "╭───────────────────────────────────────────╮" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "▄▄█████▄▄" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▄████▀▀▀████▄" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▀▀█▀ ▀██▄ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "▄█▄ ▀██▄ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▄█████▄ ███" - + Fore.LIGHTMAGENTA_EX - + " -+- " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ███ ▀█████▀ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▀██▄ ▀█▀ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "▀██▄ ▄█▄▄" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.LIGHTMAGENTA_EX - + " * " - + Fore.WHITE - + "▀████▄▄▄████▀ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▀▀█████▀▀ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.LIGHTMAGENTA_EX - + " -+- * " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▄▀▄ ▄▀▀ █ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " █ █ ▀ ▀▄ █ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA + Fore.WHITE + "│ │" - ) - left_spaces = (43 - len(self.version) - 1) // 2 - right_spaces = left_spaces - if left_spaces + len(self.version) + 1 + right_spaces < 43: - right_spaces += 1 - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + f"{' '*left_spaces}v{self.version}{' '*right_spaces}" - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA + Fore.WHITE + "│ │" - ) - print( - Back.MAGENTA - + Fore.WHITE - + "│" - + Fore.WHITE - + " https://onionshare.org/ " - + Fore.WHITE - + "│" - ) - print( - Back.MAGENTA + Fore.WHITE + "╰───────────────────────────────────────────╯" - ) - print() + try: + print( + Back.MAGENTA + + Fore.WHITE + + "╭───────────────────────────────────────────╮" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "▄▄█████▄▄" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▄████▀▀▀████▄" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▀▀█▀ ▀██▄ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "▄█▄ ▀██▄ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▄█████▄ ███" + + Fore.LIGHTMAGENTA_EX + + " -+- " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ███ ▀█████▀ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▀██▄ ▀█▀ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "▀██▄ ▄█▄▄" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.LIGHTMAGENTA_EX + + " * " + + Fore.WHITE + + "▀████▄▄▄████▀ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▀▀█████▀▀ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.LIGHTMAGENTA_EX + + " -+- * " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▄▀▄ ▄▀▀ █ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " █ █ ▀ ▀▄ █ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│ │" + ) + left_spaces = (43 - len(self.version) - 1) // 2 + right_spaces = left_spaces + if left_spaces + len(self.version) + 1 + right_spaces < 43: + right_spaces += 1 + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + f"{' '*left_spaces}v{self.version}{' '*right_spaces}" + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│ │" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "│" + + Fore.WHITE + + " https://onionshare.org/ " + + Fore.WHITE + + "│" + ) + print( + Back.MAGENTA + + Fore.WHITE + + "╰───────────────────────────────────────────╯" + ) + print() + except: + # If anything fails, print a boring banner + print(f"OnionShare v{self.version}") + print("https://onionshare.org/") + print() def load_settings(self, config=None): """ @@ -437,7 +446,7 @@ class Common: Returns True if OnionShare is running in a Flatpak sandbox """ return os.environ.get("FLATPAK_ID") == "org.onionshare.OnionShare" - + def is_snapcraft(self): """ Returns True if OnionShare is running in a Flatpak sandbox From f40e5012e0b57ce6b66d764e493ea18b98485b04 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 15 Nov 2021 18:30:10 -0800 Subject: [PATCH 53/56] Change snapcraft grade to stable, and version bump to 2.4.1 in snapcraft --- snap/snapcraft.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index e197c207..2ee174df 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: onionshare base: core18 -version: '2.4' +version: '2.4.1' summary: Securely and anonymously share files, host websites, and chat using Tor description: | OnionShare lets you securely and anonymously send and receive files. It works by starting @@ -8,7 +8,7 @@ description: | web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service. -grade: devel # stable or devel +grade: stable # stable or devel confinement: strict apps: From ec45a0805421a1f680b198902c48e5d2f51ce9d4 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 15 Nov 2021 18:41:42 -0800 Subject: [PATCH 54/56] Update snapcraft docs --- RELEASE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 948b5713..4db4f28a 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -74,11 +74,11 @@ git checkout v$VERSION You must have `snap` and `snapcraft` (`snap install snapcraft --classic`) installed. -Build and test the snap before publishing: +Build and test the snap before publishing (note that `--dangerous` lets you install the snap before it's codesigned): ```sh snapcraft -snap install --devmode ./onionshare_$VERSION_amd64.snap +snap install --dangerous ./onionshare_$VERSION_amd64.snap ``` This will create `onionshare_$VERSION_amd64.snap`. From 221eb1cbac03417c0234d38c47b6dd3d9cad33be Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 16 Nov 2021 18:10:25 -0800 Subject: [PATCH 55/56] Fix comment --- cli/onionshare_cli/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index a705809b..a8e32411 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -449,7 +449,7 @@ class Common: def is_snapcraft(self): """ - Returns True if OnionShare is running in a Flatpak sandbox + Returns True if OnionShare is running in a Snapcraft sandbox """ return os.environ.get("SNAP_INSTANCE_NAME") == "onionshare" From 236bc33bc850f3209934de003b732705a4b984f4 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Tue, 16 Nov 2021 18:15:07 -0800 Subject: [PATCH 56/56] Don't try adding a folder if the user clicks cancel, and add additional logging --- .../src/onionshare/tab/mode/file_selection.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index c75e9db6..1addba22 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -435,6 +435,7 @@ class FileSelection(QtWidgets.QVBoxLayout): """ file_dialog = AddFileDialog(self.common, caption=strings._("gui_choose_items")) if file_dialog.exec_() == QtWidgets.QDialog.Accepted: + self.common.log("FileSelection", "add", file_dialog.selectedFiles()) for filename in file_dialog.selectedFiles(): self.file_list.add_file(filename) @@ -443,25 +444,34 @@ class FileSelection(QtWidgets.QVBoxLayout): def add_files(self): """ - Add files button clicked. + Add Files button clicked. """ files = QtWidgets.QFileDialog.getOpenFileNames( self.parent, caption=strings._("gui_choose_items") ) + self.common.log("FileSelection", "add_files", files) + filenames = files[0] for filename in filenames: self.file_list.add_file(filename) + self.file_list.setCurrentItem(None) + self.update() + def add_folder(self): """ - Add folder button clicked. + Add Folder button clicked. """ filename = QtWidgets.QFileDialog.getExistingDirectory( self.parent, caption=strings._("gui_choose_items"), options=QtWidgets.QFileDialog.ShowDirsOnly, ) - self.file_list.add_file(filename) + self.common.log("FileSelection", "add_folder", filename) + if filename: + self.file_list.add_file(filename) + self.file_list.setCurrentItem(None) + self.update() def delete(self): """