Merge pull request #1239 from micahflee/1214_onion_error_translations

Translate Tor-related error messages
This commit is contained in:
Micah Lee 2020-12-01 22:21:25 -08:00 committed by GitHub
commit 5f612a94a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 189 additions and 73 deletions

View File

@ -340,7 +340,7 @@ def main(cwd=None):
print("") print("")
sys.exit() sys.exit()
except Exception as e: except Exception as e:
sys.exit(e.args[0]) sys.exit()
# Start the onionshare app # Start the onionshare app
try: try:

View File

@ -34,8 +34,6 @@ import psutil
from distutils.version import LooseVersion as Version from distutils.version import LooseVersion as Version
from .settings import Settings from .settings import Settings
# TODO: Figure out how to localize this for the GUI
class TorErrorAutomatic(Exception): class TorErrorAutomatic(Exception):
""" """
@ -89,11 +87,15 @@ class TorErrorProtocolError(Exception):
""" """
class TorTooOld(Exception): class TorTooOldEphemeral(Exception):
""" """
This exception is raised if onionshare needs to use a feature of Tor or stem This exception is raised if the version of tor doesn't support ephemeral onion services
(like stealth ephemeral onion services) but the version you have installed """
is too old.
class TorTooOldStealth(Exception):
"""
This exception is raised if the version of tor doesn't support stealth onion services
""" """
@ -118,6 +120,12 @@ class BundledTorBroken(Exception):
""" """
class PortNotAvailable(Exception):
"""
There are no available ports for OnionShare to use, which really shouldn't ever happen
"""
class Onion(object): class Onion(object):
""" """
Onion is an abstraction layer for connecting to the Tor control port and Onion is an abstraction layer for connecting to the Tor control port and
@ -214,7 +222,8 @@ class Onion(object):
try: try:
self.tor_socks_port = self.common.get_available_port(1000, 65535) self.tor_socks_port = self.common.get_available_port(1000, 65535)
except: except:
raise OSError("OnionShare port not available") print("OnionShare port not available")
raise PortNotAvailable()
self.tor_torrc = os.path.join(self.tor_data_directory_name, "torrc") self.tor_torrc = os.path.join(self.tor_data_directory_name, "torrc")
# If there is an existing OnionShare tor process, kill it # If there is an existing OnionShare tor process, kill it
@ -246,7 +255,8 @@ class Onion(object):
try: try:
self.tor_control_port = self.common.get_available_port(1000, 65535) self.tor_control_port = self.common.get_available_port(1000, 65535)
except: except:
raise OSError("OnionShare port not available") print("OnionShare port not available")
raise PortNotAvailable()
self.tor_control_socket = None self.tor_control_socket = None
else: else:
# Linux and BSD can use unix sockets # Linux and BSD can use unix sockets
@ -315,10 +325,6 @@ class Onion(object):
f.write(self.settings.get("tor_bridges_use_custom_bridges")) f.write(self.settings.get("tor_bridges_use_custom_bridges"))
f.write("\nUseBridges 1") f.write("\nUseBridges 1")
# Make sure the tor path is accurate
if not os.path.exists(self.tor_path):
raise BundledTorNotSupported(f"Cannot find tor binary: {self.tor_path}")
# Execute a tor subprocess # Execute a tor subprocess
start_ts = time.time() start_ts = time.time()
if self.common.platform == "Windows": if self.common.platform == "Windows":
@ -353,10 +359,8 @@ class Onion(object):
self.c = Controller.from_socket_file(path=self.tor_control_socket) self.c = Controller.from_socket_file(path=self.tor_control_socket)
self.c.authenticate() self.c.authenticate()
except Exception as e: except Exception as e:
raise BundledTorBroken( print("OnionShare could not connect to Tor:\n{}".format(e.args[0]))
# strings._("settings_error_bundled_tor_broken").format(e.args[0]) raise BundledTorBroken(e.args[0])
"OnionShare could not connect to Tor:\n{}".format(e.args[0])
)
while True: while True:
try: try:
@ -403,15 +407,16 @@ class Onion(object):
print("") print("")
try: try:
self.tor_proc.terminate() self.tor_proc.terminate()
raise BundledTorTimeout( print(
# strings._("settings_error_bundled_tor_timeout")
"Taking too long to connect to Tor. Maybe you aren't connected to the Internet, or have an inaccurate system clock?" "Taking too long to connect to Tor. Maybe you aren't connected to the Internet, or have an inaccurate system clock?"
) )
raise BundledTorTimeout()
except FileNotFoundError: except FileNotFoundError:
pass pass
elif self.settings.get("connection_type") == "automatic": elif self.settings.get("connection_type") == "automatic":
# Automatically try to guess the right way to connect to Tor Browser # Automatically try to guess the right way to connect to Tor Browser
automatic_error = "Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?"
# Try connecting to control port # Try connecting to control port
found_tor = False found_tor = False
@ -463,30 +468,25 @@ class Onion(object):
) )
elif self.common.platform == "Windows": elif self.common.platform == "Windows":
# Windows doesn't support unix sockets # Windows doesn't support unix sockets
raise TorErrorAutomatic( print(automatic_error)
# strings._("settings_error_automatic") raise TorErrorAutomatic()
"Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?"
)
self.c = Controller.from_socket_file(path=socket_file_path) self.c = Controller.from_socket_file(path=socket_file_path)
except: except:
raise TorErrorAutomatic( print(automatic_error)
# strings._("settings_error_automatic") raise TorErrorAutomatic()
"Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?"
)
# Try authenticating # Try authenticating
try: try:
self.c.authenticate() self.c.authenticate()
except: except:
raise TorErrorAutomatic( print(automatic_error)
# strings._("settings_error_automatic") raise TorErrorAutomatic()
"Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?"
)
else: else:
# Use specific settings to connect to tor # Use specific settings to connect to tor
invalid_settings_error = "Can't connect to Tor controller because your settings don't make sense."
# Try connecting # Try connecting
try: try:
@ -500,27 +500,28 @@ class Onion(object):
path=self.settings.get("socket_file_path") path=self.settings.get("socket_file_path")
) )
else: else:
raise TorErrorInvalidSetting( print(invalid_settings_error)
# strings._("settings_error_unknown") raise TorErrorInvalidSetting()
"Can't connect to Tor controller because your settings don't make sense."
)
except: except:
if self.settings.get("connection_type") == "control_port": if self.settings.get("connection_type") == "control_port":
raise TorErrorSocketPort( print(
# strings._("settings_error_socket_port")
"Can't connect to the Tor controller at {}:{}.".format( "Can't connect to the Tor controller at {}:{}.".format(
self.settings.get("control_port_address"), self.settings.get("control_port_address"),
self.settings.get("control_port_port"), self.settings.get("control_port_port"),
) )
) )
raise TorErrorSocketPort(
self.settings.get("control_port_address"),
self.settings.get("control_port_port"),
)
else: else:
raise TorErrorSocketFile( print(
# strings._("settings_error_socket_file")
"Can't connect to the Tor controller using socket file {}.".format( "Can't connect to the Tor controller using socket file {}.".format(
self.settings.get("socket_file_path") self.settings.get("socket_file_path")
) )
) )
raise TorErrorSocketFile(self.settings.get("socket_file_path"))
# Try authenticating # Try authenticating
try: try:
@ -529,29 +530,30 @@ class Onion(object):
elif self.settings.get("auth_type") == "password": elif self.settings.get("auth_type") == "password":
self.c.authenticate(self.settings.get("auth_password")) self.c.authenticate(self.settings.get("auth_password"))
else: else:
raise TorErrorInvalidSetting( print(invalid_settings_error)
# strings._("settings_error_unknown") raise TorErrorInvalidSetting()
"Can't connect to Tor controller because your settings don't make sense."
)
except MissingPassword: except MissingPassword:
raise TorErrorMissingPassword( print(
# strings._("settings_error_missing_password")
"Connected to Tor controller, but it requires a password to authenticate." "Connected to Tor controller, but it requires a password to authenticate."
) )
raise TorErrorMissingPassword()
except UnreadableCookieFile: except UnreadableCookieFile:
raise TorErrorUnreadableCookieFile( print(
# strings._("settings_error_unreadable_cookie_file")
"Connected to the Tor controller, but password may be wrong, or your user is not permitted to read the cookie file." "Connected to the Tor controller, but password may be wrong, or your user is not permitted to read the cookie file."
) )
raise TorErrorUnreadableCookieFile()
except AuthenticationFailure: except AuthenticationFailure:
raise TorErrorAuthError( print(
# strings._("settings_error_auth")
"Connected to {}:{}, but can't authenticate. Maybe this isn't a Tor controller?".format( "Connected to {}:{}, but can't authenticate. Maybe this isn't a Tor controller?".format(
self.settings.get("control_port_address"), self.settings.get("control_port_address"),
self.settings.get("control_port_port"), self.settings.get("control_port_port"),
) )
) )
raise TorErrorAuthError(
self.settings.get("control_port_address"),
self.settings.get("control_port_port"),
)
# If we made it this far, we should be connected to Tor # If we made it this far, we should be connected to Tor
self.connected_to_tor = True self.connected_to_tor = True
@ -606,15 +608,15 @@ class Onion(object):
self.common.log("Onion", "start_onion_service", f"port={port}") self.common.log("Onion", "start_onion_service", f"port={port}")
if not self.supports_ephemeral: if not self.supports_ephemeral:
raise TorTooOld( print(
# strings._("error_ephemeral_not_supported")
"Your version of Tor is too old, ephemeral onion services are not supported" "Your version of Tor is too old, ephemeral onion services are not supported"
) )
raise TorTooOldEphemeral()
if mode_settings.get("general", "client_auth") and not self.supports_stealth: if mode_settings.get("general", "client_auth") and not self.supports_stealth:
raise TorTooOld( print(
# strings._("error_stealth_not_supported")
"Your version of Tor is too old, stealth onion services are not supported" "Your version of Tor is too old, stealth onion services are not supported"
) )
raise TorTooOldStealth()
auth_cookie = None auth_cookie = None
if mode_settings.get("general", "client_auth"): if mode_settings.get("general", "client_auth"):
@ -671,10 +673,8 @@ class Onion(object):
) )
except ProtocolError as e: except ProtocolError as e:
raise TorErrorProtocolError( print("Tor error: {}".format(e.args[0]))
# strings._("error_tor_protocol_error") raise TorErrorProtocolError(e.args[0])
"Tor error: {}".format(e.args[0])
)
onion_host = res.service_id + ".onion" onion_host = res.service_id + ".onion"

View File

@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, shutil import os, shutil
from . import common from . import common
from .onion import TorTooOld, TorErrorProtocolError
from .common import AutoStopTimer from .common import AutoStopTimer

View File

@ -24,7 +24,22 @@ import shutil
from pkg_resources import resource_filename from pkg_resources import resource_filename
from . import strings from . import strings
from onionshare_cli.onion import Onion from onionshare_cli.onion import (
Onion,
TorErrorInvalidSetting,
TorErrorAutomatic,
TorErrorSocketPort,
TorErrorSocketFile,
TorErrorMissingPassword,
TorErrorUnreadableCookieFile,
TorErrorAuthError,
TorErrorProtocolError,
BundledTorTimeout,
BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
)
class GuiCommon: class GuiCommon:
@ -377,3 +392,37 @@ class GuiCommon:
Returns the absolute path of a resource Returns the absolute path of a resource
""" """
return resource_filename("onionshare", os.path.join("resources", filename)) return resource_filename("onionshare", os.path.join("resources", filename))
@staticmethod
def get_translated_tor_error(e):
"""
Takes an exception defined in onion.py and returns a translated error message
"""
if type(e) is TorErrorInvalidSetting:
return strings._("settings_error_unknown")
elif type(e) is TorErrorAutomatic:
return strings._("settings_error_automatic")
elif type(e) is TorErrorSocketPort:
return strings._("settings_error_socket_port").format(e.args[0], e.args[1])
elif type(e) is TorErrorSocketFile:
return strings._("settings_error_socket_file").format(e.args[0])
elif type(e) is TorErrorMissingPassword:
return strings._("settings_error_missing_password")
elif type(e) is TorErrorUnreadableCookieFile:
return strings._("settings_error_unreadable_cookie_file")
elif type(e) is TorErrorAuthError:
return strings._("settings_error_auth").format(e.args[0], e.args[1])
elif type(e) is TorErrorProtocolError:
return strings._("error_tor_protocol_error").format(e.args[0])
elif type(e) is BundledTorTimeout:
return strings._("settings_error_bundled_tor_timeout")
elif type(e) is BundledTorBroken:
return strings._("settings_error_bundled_tor_broken").format(e.args[0])
elif type(e) is TorTooOldEphemeral:
return strings._("error_ephemeral_not_supported")
elif type(e) is TorTooOldStealth:
return strings._("error_stealth_not_supported")
elif type(e) is PortNotAvailable:
return strings._("error_port_not_available")
return None

View File

@ -187,5 +187,6 @@
"settings_error_unreadable_cookie_file": "Connected to the Tor controller, but password may be wrong, or your user is not permitted to read the cookie file.", "settings_error_unreadable_cookie_file": "Connected to the Tor controller, but password may be wrong, or your user is not permitted to read the cookie file.",
"settings_error_bundled_tor_not_supported": "Using the Tor version that comes with OnionShare does not work in developer mode on Windows or macOS.", "settings_error_bundled_tor_not_supported": "Using the Tor version that comes with OnionShare does not work in developer mode on Windows or macOS.",
"settings_error_bundled_tor_timeout": "Taking too long to connect to Tor. Maybe you aren't connected to the Internet, or have an inaccurate system clock?", "settings_error_bundled_tor_timeout": "Taking too long to connect to Tor. Maybe you aren't connected to the Internet, or have an inaccurate system clock?",
"settings_error_bundled_tor_broken": "OnionShare could not connect to Tor:\n{}" "settings_error_bundled_tor_broken": "OnionShare could not connect to Tor:\n{}",
"error_port_not_available": "OnionShare port not available"
} }

View File

@ -27,11 +27,31 @@ import os
from onionshare_cli import common from onionshare_cli import common
from onionshare_cli.settings import Settings from onionshare_cli.settings import Settings
from onionshare_cli.onion import * from onionshare_cli.onion import (
Onion,
TorErrorInvalidSetting,
TorErrorAutomatic,
TorErrorSocketPort,
TorErrorSocketFile,
TorErrorMissingPassword,
TorErrorUnreadableCookieFile,
TorErrorAuthError,
TorErrorProtocolError,
BundledTorTimeout,
BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
)
from . import strings from . import strings
from .widgets import Alert from .widgets import Alert
from .update_checker import * from .update_checker import (
UpdateCheckerCheckError,
UpdateCheckerInvalidLatestVersion,
UpdateChecker,
UpdateThread,
)
from .tor_connection_dialog import TorConnectionDialog from .tor_connection_dialog import TorConnectionDialog
from .gui_common import GuiCommon from .gui_common import GuiCommon
@ -698,10 +718,18 @@ class SettingsDialog(QtWidgets.QDialog):
TorErrorUnreadableCookieFile, TorErrorUnreadableCookieFile,
TorErrorAuthError, TorErrorAuthError,
TorErrorProtocolError, TorErrorProtocolError,
BundledTorNotSupported,
BundledTorTimeout, BundledTorTimeout,
BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
) as e: ) as e:
Alert(self.common, e.args[0], QtWidgets.QMessageBox.Warning) message = self.common.gui.get_translated_tor_error(e)
Alert(
self.common,
message,
QtWidgets.QMessageBox.Warning,
)
if settings.get("connection_type") == "bundled": if settings.get("connection_type") == "bundled":
self.tor_status.hide() self.tor_status.hide()
self._enable_buttons() self._enable_buttons()

View File

@ -24,7 +24,6 @@ import os
from PySide2 import QtCore from PySide2 import QtCore
from onionshare_cli.onion import ( from onionshare_cli.onion import (
TorTooOld,
TorErrorInvalidSetting, TorErrorInvalidSetting,
TorErrorAutomatic, TorErrorAutomatic,
TorErrorSocketPort, TorErrorSocketPort,
@ -34,6 +33,10 @@ from onionshare_cli.onion import (
TorErrorAuthError, TorErrorAuthError,
TorErrorProtocolError, TorErrorProtocolError,
BundledTorTimeout, BundledTorTimeout,
BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
) )
from . import strings from . import strings
@ -93,7 +96,6 @@ class OnionThread(QtCore.QThread):
self.success.emit() self.success.emit()
except ( except (
TorTooOld,
TorErrorInvalidSetting, TorErrorInvalidSetting,
TorErrorAutomatic, TorErrorAutomatic,
TorErrorSocketPort, TorErrorSocketPort,
@ -103,9 +105,13 @@ class OnionThread(QtCore.QThread):
TorErrorAuthError, TorErrorAuthError,
TorErrorProtocolError, TorErrorProtocolError,
BundledTorTimeout, BundledTorTimeout,
OSError, BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
) as e: ) as e:
self.error.emit(e.args[0]) message = self.mode.common.gui.get_translated_tor_error(e)
self.error.emit(message)
return return
@ -174,7 +180,7 @@ class AutoStartTimer(QtCore.QThread):
class EventHandlerThread(QtCore.QThread): class EventHandlerThread(QtCore.QThread):
""" """
To trigger an event, write a JSON line to the events file. When that file changes, To trigger an event, write a JSON line to the events file. When that file changes,
each line will be handled as an event. Valid events are: each line will be handled as an event. Valid events are:
{"type": "new_tab"} {"type": "new_tab"}
{"type": "new_share_tab", "filenames": ["file1", "file2"]} {"type": "new_share_tab", "filenames": ["file1", "file2"]}

View File

@ -18,9 +18,25 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
import time
from PySide2 import QtCore, QtWidgets, QtGui from PySide2 import QtCore, QtWidgets, QtGui
from onionshare_cli.onion import * from onionshare_cli.onion import (
BundledTorCanceled,
TorErrorInvalidSetting,
TorErrorAutomatic,
TorErrorSocketPort,
TorErrorSocketFile,
TorErrorMissingPassword,
TorErrorUnreadableCookieFile,
TorErrorAuthError,
TorErrorProtocolError,
BundledTorTimeout,
BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
)
from . import strings from . import strings
from .gui_common import GuiCommon from .gui_common import GuiCommon
@ -156,9 +172,26 @@ class TorConnectionThread(QtCore.QThread):
) )
self.canceled_connecting_to_tor.emit() self.canceled_connecting_to_tor.emit()
except Exception as e: except (
self.common.log("TorConnectionThread", "run", f"caught exception: {e}") TorErrorInvalidSetting,
self.error_connecting_to_tor.emit(str(e)) TorErrorAutomatic,
TorErrorSocketPort,
TorErrorSocketFile,
TorErrorMissingPassword,
TorErrorUnreadableCookieFile,
TorErrorAuthError,
TorErrorProtocolError,
BundledTorTimeout,
BundledTorBroken,
TorTooOldEphemeral,
TorTooOldStealth,
PortNotAvailable,
) as e:
message = self.common.gui.get_translated_tor_error(e)
self.common.log(
"TorConnectionThread", "run", f"caught exception: {message}"
)
self.error_connecting_to_tor.emit(message)
def _tor_status_update(self, progress, summary): def _tor_status_update(self, progress, summary):
self.tor_status_update.emit(progress, summary) self.tor_status_update.emit(progress, summary)