From ee5b950f7c07c22f3acbcdd81b3d776f1535a70f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 29 Nov 2020 11:08:46 -0800 Subject: [PATCH 1/4] Make CLI throw an error if it cannot find tor binary --- cli/onionshare_cli/__init__.py | 12 ++++++++++-- cli/onionshare_cli/common.py | 10 ++++++++++ cli/onionshare_cli/onion.py | 19 ------------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index 7361ac9e..34627deb 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -22,7 +22,7 @@ import os, sys, time, argparse, threading from datetime import datetime from datetime import timedelta -from .common import Common +from .common import Common, CannotFindTor from .web import Web from .onion import * from .onionshare import OnionShare @@ -320,7 +320,15 @@ def main(cwd=None): web = Web(common, False, mode_settings, mode) # 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: onion.connect( custom_settings=False, diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index a1213387..e8b174b6 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -34,6 +34,12 @@ from pkg_resources import resource_filename from .settings import Settings +class CannotFindTor(Exception): + """ + OnionShare can't find a tor binary + """ + + class Common: """ The Common object is shared amongst all parts of OnionShare. @@ -82,6 +88,8 @@ class Common: def get_tor_paths(self): if self.platform == "Linux": tor_path = shutil.which("tor") + if not tor_path: + raise CannotFindTor() obfs4proxy_file_path = shutil.which("obfs4proxy") prefix = os.path.dirname(os.path.dirname(tor_path)) 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") elif self.platform == "Darwin": tor_path = shutil.which("tor") + if not tor_path: + raise CannotFindTor() obfs4proxy_file_path = shutil.which("obfs4proxy") prefix = os.path.dirname(os.path.dirname(tor_path)) tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index 1b025bf8..d6f59a59 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -32,7 +32,6 @@ import getpass import psutil from distutils.version import LooseVersion as Version -from . import common from .settings import Settings # TODO: Figure out how to localize this for the GUI @@ -44,40 +43,30 @@ class TorErrorAutomatic(Exception): using automatic settings that should work with Tor Browser. """ - pass - class TorErrorInvalidSetting(Exception): """ This exception is raised if the settings just don't make sense. """ - pass - class TorErrorSocketPort(Exception): """ OnionShare can't connect to the Tor controller using the supplied address and port. """ - pass - class TorErrorSocketFile(Exception): """ OnionShare can't connect to the Tor controller using the supplied socket file. """ - pass - class TorErrorMissingPassword(Exception): """ OnionShare connected to the Tor controller, but it requires a password. """ - pass - class TorErrorUnreadableCookieFile(Exception): """ @@ -85,8 +74,6 @@ class TorErrorUnreadableCookieFile(Exception): to access the cookie file. """ - pass - class TorErrorAuthError(Exception): """ @@ -94,8 +81,6 @@ class TorErrorAuthError(Exception): that a Tor controller isn't listening on this port. """ - pass - class TorErrorProtocolError(Exception): """ @@ -103,8 +88,6 @@ class TorErrorProtocolError(Exception): isn't acting like a Tor controller (such as in Whonix). """ - pass - class TorTooOld(Exception): """ @@ -113,8 +96,6 @@ class TorTooOld(Exception): is too old. """ - pass - class BundledTorTimeout(Exception): """ From d416abdf9531586816e7d7a7e39e73e5c7f7b89f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 29 Nov 2020 11:18:03 -0800 Subject: [PATCH 2/4] Make settings dialog use the correct get_tor_paths method --- desktop/src/onionshare/settings_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/src/onionshare/settings_dialog.py b/desktop/src/onionshare/settings_dialog.py index 5f37bda1..40de8648 100644 --- a/desktop/src/onionshare/settings_dialog.py +++ b/desktop/src/onionshare/settings_dialog.py @@ -142,7 +142,7 @@ class SettingsDialog(QtWidgets.QDialog): self.tor_geo_ip_file_path, self.tor_geo_ipv6_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( self.obfs4proxy_file_path ): @@ -165,7 +165,7 @@ class SettingsDialog(QtWidgets.QDialog): self.tor_geo_ip_file_path, self.tor_geo_ipv6_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( self.obfs4proxy_file_path ): From bbd5d6147bd24207d189a9f0bc485b06b6af8df7 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 29 Nov 2020 11:23:23 -0800 Subject: [PATCH 3/4] Update desktop readme, and add a helper script to use CLI args --- desktop/README.md | 14 ++++++++++++-- desktop/scripts/dev.sh | 9 +++++++++ desktop/scripts/rebuild-cli.sh | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100755 desktop/scripts/dev.sh diff --git a/desktop/README.md b/desktop/README.md index 053bf4ea..5e1c11ea 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -51,7 +51,7 @@ Download Tor Browser and extract the binaries: python scripts\get-tor-windows.py ``` -### Prepare the code +### Prepare the virtual environment OnionShare uses [Briefcase](https://briefcase.readthedocs.io/en/latest/). @@ -82,12 +82,22 @@ In order to work with the desktop app, you'll need to build a wheel of the CLI p ./scripts/rebuild-cli.sh ``` -Run OnionShare from the source tree like this: +### Running OnionShare from the source code tree + +Inside the virtual environment, run OnionShare like this to install all of the dependencies: ``` briefcase dev -d ``` +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`: + +``` +./scripts/dev.sh --help +./scripts/dev.sh -v +./scripts/dev.sh -v --local-only +``` + ## Running tests Install these packages inside your virtual environment: diff --git a/desktop/scripts/dev.sh b/desktop/scripts/dev.sh new file mode 100755 index 00000000..6ce5e796 --- /dev/null +++ b/desktop/scripts/dev.sh @@ -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()" $@ \ No newline at end of file diff --git a/desktop/scripts/rebuild-cli.sh b/desktop/scripts/rebuild-cli.sh index f8191955..9d6a1338 100755 --- a/desktop/scripts/rebuild-cli.sh +++ b/desktop/scripts/rebuild-cli.sh @@ -5,5 +5,6 @@ SCRIPTS_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" cd $SCRIPTS_DIR cd ../../cli +poetry install poetry build cp dist/*.whl ../desktop \ No newline at end of file From c2a8c7d75a996920c23545fbc4e7fe3fbecdb370 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 29 Nov 2020 13:57:53 -0800 Subject: [PATCH 4/4] Write rebuild-cli in python instead of bash to work in Windows, and make the dev script available in Windows --- desktop/README.md | 4 ++- desktop/scripts/dev.bat | 3 +++ desktop/scripts/rebuild-cli.py | 45 ++++++++++++++++++++++++++++++++++ desktop/scripts/rebuild-cli.sh | 10 -------- 4 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 desktop/scripts/dev.bat create mode 100755 desktop/scripts/rebuild-cli.py delete mode 100755 desktop/scripts/rebuild-cli.sh diff --git a/desktop/README.md b/desktop/README.md index 5e1c11ea..97d0fd30 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -79,7 +79,7 @@ pip install briefcase 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 -./scripts/rebuild-cli.sh +python scripts/rebuild-cli.py ``` ### Running OnionShare from the source code tree @@ -98,6 +98,8 @@ Once you have the dependencies installed, you can run it using the `dev.sh` scri ./scripts/dev.sh -v --local-only ``` +Windows uses `scripts\dev.bat` instead. + ## Running tests Install these packages inside your virtual environment: diff --git a/desktop/scripts/dev.bat b/desktop/scripts/dev.bat new file mode 100644 index 00000000..9b537a90 --- /dev/null +++ b/desktop/scripts/dev.bat @@ -0,0 +1,3 @@ +cd src +python -c "import onionshare; onionshare.main()" %* +cd .. \ No newline at end of file diff --git a/desktop/scripts/rebuild-cli.py b/desktop/scripts/rebuild-cli.py new file mode 100755 index 00000000..c13461bc --- /dev/null +++ b/desktop/scripts/rebuild-cli.py @@ -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() diff --git a/desktop/scripts/rebuild-cli.sh b/desktop/scripts/rebuild-cli.sh deleted file mode 100755 index 9d6a1338..00000000 --- a/desktop/scripts/rebuild-cli.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Build the CLI python wheel and copy it to the desktop folder - -SCRIPTS_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" -cd $SCRIPTS_DIR -cd ../../cli -poetry install -poetry build -cp dist/*.whl ../desktop \ No newline at end of file