mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-26 11:13:00 -03:00
Merge branch 'develop' into 929_download_errors
This commit is contained in:
commit
b96b83905b
15 changed files with 294 additions and 116 deletions
|
@ -22,7 +22,7 @@ import os, sys, time, argparse, threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from .common import Common
|
from .common import Common, CannotFindTor
|
||||||
from .web import Web
|
from .web import Web
|
||||||
from .onion import *
|
from .onion import *
|
||||||
from .onionshare import OnionShare
|
from .onionshare import OnionShare
|
||||||
|
@ -320,7 +320,15 @@ def main(cwd=None):
|
||||||
web = Web(common, False, mode_settings, mode)
|
web = Web(common, False, mode_settings, mode)
|
||||||
|
|
||||||
# Start the Onion object
|
# Start the Onion object
|
||||||
onion = Onion(common, use_tmp_dir=True)
|
try:
|
||||||
|
onion = Onion(common, use_tmp_dir=True)
|
||||||
|
except CannotFindTor:
|
||||||
|
print("You must install tor to use OnionShare from the command line")
|
||||||
|
if common.platform == "Darwin":
|
||||||
|
print("In macOS, you can do this with Homebrew (https://brew.sh):")
|
||||||
|
print(" brew install tor")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
onion.connect(
|
onion.connect(
|
||||||
custom_settings=False,
|
custom_settings=False,
|
||||||
|
@ -332,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:
|
||||||
|
|
|
@ -34,6 +34,12 @@ from pkg_resources import resource_filename
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
|
|
||||||
|
|
||||||
|
class CannotFindTor(Exception):
|
||||||
|
"""
|
||||||
|
OnionShare can't find a tor binary
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Common:
|
class Common:
|
||||||
"""
|
"""
|
||||||
The Common object is shared amongst all parts of OnionShare.
|
The Common object is shared amongst all parts of OnionShare.
|
||||||
|
@ -82,6 +88,8 @@ class Common:
|
||||||
def get_tor_paths(self):
|
def get_tor_paths(self):
|
||||||
if self.platform == "Linux":
|
if self.platform == "Linux":
|
||||||
tor_path = shutil.which("tor")
|
tor_path = shutil.which("tor")
|
||||||
|
if not tor_path:
|
||||||
|
raise CannotFindTor()
|
||||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||||
prefix = os.path.dirname(os.path.dirname(tor_path))
|
prefix = os.path.dirname(os.path.dirname(tor_path))
|
||||||
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
||||||
|
@ -94,6 +102,8 @@ class Common:
|
||||||
tor_geo_ipv6_file_path = os.path.join(base_path, "Data", "Tor", "geoip6")
|
tor_geo_ipv6_file_path = os.path.join(base_path, "Data", "Tor", "geoip6")
|
||||||
elif self.platform == "Darwin":
|
elif self.platform == "Darwin":
|
||||||
tor_path = shutil.which("tor")
|
tor_path = shutil.which("tor")
|
||||||
|
if not tor_path:
|
||||||
|
raise CannotFindTor()
|
||||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||||
prefix = os.path.dirname(os.path.dirname(tor_path))
|
prefix = os.path.dirname(os.path.dirname(tor_path))
|
||||||
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
||||||
|
|
|
@ -32,11 +32,8 @@ import getpass
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from distutils.version import LooseVersion as Version
|
from distutils.version import LooseVersion as Version
|
||||||
from . import common
|
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
|
|
||||||
# TODO: Figure out how to localize this for the GUI
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorAutomatic(Exception):
|
class TorErrorAutomatic(Exception):
|
||||||
"""
|
"""
|
||||||
|
@ -44,40 +41,30 @@ class TorErrorAutomatic(Exception):
|
||||||
using automatic settings that should work with Tor Browser.
|
using automatic settings that should work with Tor Browser.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorInvalidSetting(Exception):
|
class TorErrorInvalidSetting(Exception):
|
||||||
"""
|
"""
|
||||||
This exception is raised if the settings just don't make sense.
|
This exception is raised if the settings just don't make sense.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorSocketPort(Exception):
|
class TorErrorSocketPort(Exception):
|
||||||
"""
|
"""
|
||||||
OnionShare can't connect to the Tor controller using the supplied address and port.
|
OnionShare can't connect to the Tor controller using the supplied address and port.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorSocketFile(Exception):
|
class TorErrorSocketFile(Exception):
|
||||||
"""
|
"""
|
||||||
OnionShare can't connect to the Tor controller using the supplied socket file.
|
OnionShare can't connect to the Tor controller using the supplied socket file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorMissingPassword(Exception):
|
class TorErrorMissingPassword(Exception):
|
||||||
"""
|
"""
|
||||||
OnionShare connected to the Tor controller, but it requires a password.
|
OnionShare connected to the Tor controller, but it requires a password.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorUnreadableCookieFile(Exception):
|
class TorErrorUnreadableCookieFile(Exception):
|
||||||
"""
|
"""
|
||||||
|
@ -85,8 +72,6 @@ class TorErrorUnreadableCookieFile(Exception):
|
||||||
to access the cookie file.
|
to access the cookie file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorAuthError(Exception):
|
class TorErrorAuthError(Exception):
|
||||||
"""
|
"""
|
||||||
|
@ -94,8 +79,6 @@ class TorErrorAuthError(Exception):
|
||||||
that a Tor controller isn't listening on this port.
|
that a Tor controller isn't listening on this port.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TorErrorProtocolError(Exception):
|
class TorErrorProtocolError(Exception):
|
||||||
"""
|
"""
|
||||||
|
@ -103,17 +86,17 @@ class TorErrorProtocolError(Exception):
|
||||||
isn't acting like a Tor controller (such as in Whonix).
|
isn't acting like a Tor controller (such as in Whonix).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
class TorTooOldEphemeral(Exception):
|
||||||
class TorTooOld(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.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
class TorTooOldStealth(Exception):
|
||||||
|
"""
|
||||||
|
This exception is raised if the version of tor doesn't support stealth onion services
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class BundledTorTimeout(Exception):
|
class BundledTorTimeout(Exception):
|
||||||
|
@ -137,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
|
||||||
|
@ -236,7 +225,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
|
||||||
|
@ -268,7 +258,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
|
||||||
|
@ -337,10 +328,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":
|
||||||
|
@ -375,10 +362,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:
|
||||||
|
@ -425,15 +410,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
|
||||||
|
@ -485,30 +471,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:
|
||||||
|
@ -522,27 +503,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:
|
||||||
|
@ -551,29 +533,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
|
||||||
|
@ -628,15 +611,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"):
|
||||||
|
@ -693,10 +676,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"
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,17 +51,7 @@ Download Tor Browser and extract the binaries:
|
||||||
python scripts\get-tor-windows.py
|
python scripts\get-tor-windows.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Prepare the code
|
### Prepare the virtual environment
|
||||||
|
|
||||||
In order to work with the desktop app, you'll need to build a wheel of the CLI package first, and copy it into the `desktop` folder:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd ../cli
|
|
||||||
poetry install
|
|
||||||
poetry build
|
|
||||||
cp dist/onionshare_cli-*.whl ../desktop
|
|
||||||
cd ../desktop
|
|
||||||
```
|
|
||||||
|
|
||||||
OnionShare uses [Briefcase](https://briefcase.readthedocs.io/en/latest/).
|
OnionShare uses [Briefcase](https://briefcase.readthedocs.io/en/latest/).
|
||||||
|
|
||||||
|
@ -86,19 +76,30 @@ While your virtual environment is active, install briefcase from pip.
|
||||||
pip install briefcase
|
pip install briefcase
|
||||||
```
|
```
|
||||||
|
|
||||||
Run OnionShare from the source tree like this (`-d` re-installs dependencies, which you'll have to do each time you update the `onionshare-cli` wheel):
|
In order to work with the desktop app, you'll need to build a wheel of the CLI package first, and copy it into the `desktop` folder. You'll need to re-run this script each time you change the CLI code.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python scripts/rebuild-cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running OnionShare from the source code tree
|
||||||
|
|
||||||
|
Inside the virtual environment, run OnionShare like this to install all of the dependencies:
|
||||||
|
|
||||||
```
|
```
|
||||||
briefcase dev -d
|
briefcase dev -d
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to pass arguments into `onionshare`, such as to use verbose mode:
|
Once you have the dependencies installed, you can run it using the `dev.sh` script, which lets you use command line arguments, such as to `--verbose` or `--local-only`:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd src
|
./scripts/dev.sh --help
|
||||||
python -c "import onionshare; onionshare.main()" --help
|
./scripts/dev.sh -v
|
||||||
|
./scripts/dev.sh -v --local-only
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Windows uses `scripts\dev.bat` instead.
|
||||||
|
|
||||||
## Running tests
|
## Running tests
|
||||||
|
|
||||||
Install these packages inside your virtual environment:
|
Install these packages inside your virtual environment:
|
||||||
|
|
3
desktop/scripts/dev.bat
Normal file
3
desktop/scripts/dev.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
cd src
|
||||||
|
python -c "import onionshare; onionshare.main()" %*
|
||||||
|
cd ..
|
9
desktop/scripts/dev.sh
Executable file
9
desktop/scripts/dev.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Run OnionShare desktop, allowing you to use command-line arguments
|
||||||
|
|
||||||
|
SCRIPTS_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
|
||||||
|
cd $SCRIPTS_DIR
|
||||||
|
|
||||||
|
cd ../src
|
||||||
|
python -c "import onionshare; onionshare.main()" $@
|
45
desktop/scripts/rebuild-cli.py
Executable file
45
desktop/scripts/rebuild-cli.py
Executable file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This script builds the CLI python wheel, copies it to the desktop folder,
|
||||||
|
and installs it in the virtual environment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Build paths
|
||||||
|
root_path = os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cli_path = os.path.join(root_path, "cli")
|
||||||
|
desktop_path = os.path.join(root_path, "desktop")
|
||||||
|
|
||||||
|
# Delete old wheels
|
||||||
|
for filename in glob.glob(os.path.join(cli_path, "dist", "*.whl")):
|
||||||
|
os.remove(filename)
|
||||||
|
|
||||||
|
# Build new wheel
|
||||||
|
subprocess.call(["poetry", "install"], cwd=cli_path)
|
||||||
|
subprocess.call(["poetry", "build"], cwd=cli_path)
|
||||||
|
wheel_filename = glob.glob(os.path.join(cli_path, "dist", "*.whl"))[0]
|
||||||
|
wheel_basename = os.path.basename(wheel_filename)
|
||||||
|
shutil.copyfile(
|
||||||
|
wheel_filename,
|
||||||
|
os.path.join(desktop_path, wheel_basename),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Reinstall the new wheel
|
||||||
|
subprocess.call(["pip", "uninstall", "onionshare-cli", "-y"])
|
||||||
|
subprocess.call(["pip", "install", os.path.join(desktop_path, wheel_basename)])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -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:
|
||||||
|
@ -245,7 +260,7 @@ class GuiCommon:
|
||||||
QLabel {
|
QLabel {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
font-size: 28px;
|
font-size: 25px;
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
# Share mode and child widget styles
|
# Share mode and child widget styles
|
||||||
|
@ -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
|
||||||
|
|
|
@ -189,5 +189,6 @@
|
||||||
"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{}",
|
||||||
"gui_rendezvous_cleanup": "Waiting for Tor circuits to close to be sure your files have successfully transferred.\n\nThis might take a few minutes.",
|
"gui_rendezvous_cleanup": "Waiting for Tor circuits to close to be sure your files have successfully transferred.\n\nThis might take a few minutes.",
|
||||||
"gui_rendezvous_cleanup_quit_early": "Quit Early"
|
"gui_rendezvous_cleanup_quit_early": "Quit Early",
|
||||||
|
"error_port_not_available": "OnionShare port not available"
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
@ -142,7 +162,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
||||||
self.tor_geo_ip_file_path,
|
self.tor_geo_ip_file_path,
|
||||||
self.tor_geo_ipv6_file_path,
|
self.tor_geo_ipv6_file_path,
|
||||||
self.obfs4proxy_file_path,
|
self.obfs4proxy_file_path,
|
||||||
) = self.common.get_tor_paths()
|
) = self.common.gui.get_tor_paths()
|
||||||
if not self.obfs4proxy_file_path or not os.path.isfile(
|
if not self.obfs4proxy_file_path or not os.path.isfile(
|
||||||
self.obfs4proxy_file_path
|
self.obfs4proxy_file_path
|
||||||
):
|
):
|
||||||
|
@ -165,7 +185,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
||||||
self.tor_geo_ip_file_path,
|
self.tor_geo_ip_file_path,
|
||||||
self.tor_geo_ipv6_file_path,
|
self.tor_geo_ipv6_file_path,
|
||||||
self.obfs4proxy_file_path,
|
self.obfs4proxy_file_path,
|
||||||
) = self.common.get_tor_paths()
|
) = self.common.gui.get_tor_paths()
|
||||||
if not self.obfs4proxy_file_path or not os.path.isfile(
|
if not self.obfs4proxy_file_path or not os.path.isfile(
|
||||||
self.obfs4proxy_file_path
|
self.obfs4proxy_file_path
|
||||||
):
|
):
|
||||||
|
@ -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()
|
||||||
|
|
|
@ -183,8 +183,7 @@ class Mode(QtWidgets.QWidget):
|
||||||
|
|
||||||
self.status_bar.clearMessage()
|
self.status_bar.clearMessage()
|
||||||
if not self.app.autostop_timer_thread.is_alive():
|
if not self.app.autostop_timer_thread.is_alive():
|
||||||
if self.autostop_timer_finished_should_stop_server():
|
self.autostop_timer_finished_should_stop_server()
|
||||||
self.server_status.stop_server()
|
|
||||||
|
|
||||||
def timer_callback_custom(self):
|
def timer_callback_custom(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -53,16 +53,22 @@ class NewTabButton(QtWidgets.QPushButton):
|
||||||
)
|
)
|
||||||
self.image_label.setAlignment(QtCore.Qt.AlignCenter)
|
self.image_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
self.image_label.setStyleSheet(self.common.gui.css["new_tab_button_image"])
|
self.image_label.setStyleSheet(self.common.gui.css["new_tab_button_image"])
|
||||||
self.image_label.setGeometry(0, 0, self.width(), 200)
|
self.image_label.setGeometry(0, 0, self.width(), 190)
|
||||||
self.image_label.show()
|
self.image_label.show()
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
self.title_label = QtWidgets.QLabel(title, parent=self)
|
self.title_label = QtWidgets.QLabel(title, parent=self)
|
||||||
|
self.title_label.setWordWrap(True)
|
||||||
self.title_label.setAlignment(QtCore.Qt.AlignCenter)
|
self.title_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
self.title_label.setStyleSheet(self.common.gui.css["new_tab_title_text"])
|
self.title_label.setStyleSheet(self.common.gui.css["new_tab_title_text"])
|
||||||
self.title_label.setGeometry(
|
if self.title_label.sizeHint().width() >= 250:
|
||||||
(self.width() - 250) / 2, self.height() - 100, 250, 30
|
self.title_label.setGeometry(
|
||||||
)
|
(self.width() - 250) / 2, self.height() - 120, 250, 60
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.title_label.setGeometry(
|
||||||
|
(self.width() - 250) / 2, self.height() - 100, 250, 30
|
||||||
|
)
|
||||||
self.title_label.show()
|
self.title_label.show()
|
||||||
|
|
||||||
# Text
|
# Text
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue