diff --git a/onionshare/__init__.py b/onionshare/__init__.py index 29bc0ed4..006ed4c0 100644 --- a/onionshare/__init__.py +++ b/onionshare/__init__.py @@ -156,7 +156,7 @@ def main(cwd=None): app = OnionShare(debug, local_only, stay_open, transparent_torification, stealth) app.choose_port() app.start_onion_service() - except (TorTooOld, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported) as e: + except (TorTooOld, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported, BundledTorTimeout) as e: sys.exit(e.args[0]) except KeyboardInterrupt: print("") diff --git a/onionshare/onion.py b/onionshare/onion.py index 8ac03dd6..be10743f 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -93,6 +93,12 @@ class BundledTorNotSupported(Exception): but it's not supported on that platform, or in dev mode. """ +class BundledTorTimeout(Exception): + """ + This exception is raised if onionshare is set to use the bundled Tor binary, + but Tor doesn't finish connecting promptly. + """ + class Onion(object): """ Onion is an abstraction layer for connecting to the Tor control port and @@ -165,6 +171,7 @@ class Onion(object): open(self.tor_torrc, 'w').write(torrc_template) # Open tor in a subprocess, wait for the controller to start + start_ts = time.time() self.tor_proc = subprocess.Popen([self.tor_path, '-f', self.tor_torrc], stdout=subprocess.PIPE, stderr=subprocess.PIPE) time.sleep(0.2) @@ -190,6 +197,12 @@ class Onion(object): break time.sleep(0.2) + # Timeout after 10 seconds + if time.time() - start_ts > 10: + print("") + self.tor_proc.terminate() + raise BundledTorTimeout(strings._('settings_error_bundled_tor_timeout')) + elif self.settings.get('connection_type') == 'automatic': # Automatically try to guess the right way to connect to Tor Browser diff --git a/onionshare_gui/__init__.py b/onionshare_gui/__init__.py index 5e2617da..bc15f8c9 100644 --- a/onionshare_gui/__init__.py +++ b/onionshare_gui/__init__.py @@ -185,7 +185,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.app.start_onion_service(bundled_tor_func) self.starting_server_step2.emit() - except (onionshare.onion.TorTooOld, onionshare.onion.TorErrorInvalidSetting, onionshare.onion.TorErrorAutomatic, onionshare.onion.TorErrorSocketPort, onionshare.onion.TorErrorSocketFile, onionshare.onion.TorErrorMissingPassword, onionshare.onion.TorErrorUnreadableCookieFile, onionshare.onion.TorErrorAuthError, onionshare.onion.TorErrorProtocolError) as e: + except (onionshare.onion.TorTooOld, onionshare.onion.TorErrorInvalidSetting, onionshare.onion.TorErrorAutomatic, onionshare.onion.TorErrorSocketPort, onionshare.onion.TorErrorSocketFile, onionshare.onion.TorErrorMissingPassword, onionshare.onion.TorErrorUnreadableCookieFile, onionshare.onion.TorErrorAuthError, onionshare.onion.TorErrorProtocolError, onionshare.onion.BundledTorTimeout) as e: self.starting_server_error.emit(e.args[0]) return diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 74c70391..46c0b7e3 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -285,21 +285,27 @@ class SettingsDialog(QtWidgets.QDialog): """ settings = self.settings_from_fields() + def bundled_setup(): + self.tor_status.show() + self.test_button.setEnabled(False) + self.save_button.setEnabled(False) + self.cancel_button.setEnabled(False) + + def bundled_cleanup(): + self.tor_status.hide() + self.test_button.setEnabled(True) + self.save_button.setEnabled(True) + self.cancel_button.setEnabled(True) + try: # Show Tor connection status if connection type is bundled tor if settings.get('connection_type') == 'bundled': - self.tor_status.show() - self.test_button.setEnabled(False) - self.save_button.setEnabled(False) - self.cancel_button.setEnabled(False) + bundled_setup() def bundled_tor_func(message): self.tor_status.setText('{}
{}'.format(strings._('connecting_to_tor', True), message)) self.qtapp.processEvents() if 'Done' in message: - self.tor_status.hide() - self.test_button.setEnabled(True) - self.save_button.setEnabled(True) - self.cancel_button.setEnabled(True) + bundled_cleanup() else: bundled_tor_func = None @@ -308,8 +314,13 @@ class SettingsDialog(QtWidgets.QDialog): # If an exception hasn't been raised yet, the Tor settings work Alert(strings._('settings_test_success', True).format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth)) - except (TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported) as e: + # Clean up + onion.cleanup() + + except (TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported, BundledTorTimeout) as e: Alert(e.args[0], QtWidgets.QMessageBox.Warning) + if settings.get('connection_type') == 'bundled': + bundled_cleanup() def save_clicked(self): """ diff --git a/share/locale/en.json b/share/locale/en.json index d4743cf4..4eb6ec6e 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -86,6 +86,7 @@ "settings_error_missing_password": "Connected to Tor controller, but it requires a password to authenticate.", "settings_error_unreadable_cookie_file": "Connected to Tor controller, but can't authenticate because your password may be wrong, and your user doesn't have permission to read the cookie file.", "settings_error_bundled_tor_not_supported": "Bundled Tor is only supported in Windows and macOS. It's not supported when not using developer mode or the command line interface.", + "settings_error_bundled_tor_timeout": "Connecting to Tor is taking too long. Make sure you're online and your clock is accurate, then try again.", "settings_test_success": "Congratulations, OnionShare can connect to the Tor controller.\n\nTor version: {}\nSupports ephemeral onion services: {}\nSupports stealth onion services: {}", "error_tor_protocol_error": "Error talking to the Tor controller.\nIf you're using Whonix, check out https://www.whonix.org/wiki/onionshare to make OnionShare work.", "connecting_to_tor": "Connecting to the Tor network"