diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index c40bf6a0..1bd0582c 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -193,6 +193,12 @@ def main(cwd=None): default=None, help="Receive files: Save files received to this directory", ) + parser.add_argument( + "--webhook-url", + metavar="webhook_url", + default=None, + help="Receive files: URL to receive webhook notifications", + ) # Website args parser.add_argument( "--disable_csp", @@ -235,6 +241,7 @@ def main(cwd=None): client_auth = bool(args.client_auth) autostop_sharing = not bool(args.no_autostop_sharing) data_dir = args.data_dir + webhook_url = args.webhook_url disable_csp = bool(args.disable_csp) verbose = bool(args.verbose) @@ -283,6 +290,8 @@ def main(cwd=None): if mode == "receive": if data_dir: mode_settings.set("receive", "data_dir", data_dir) + if webhook_url: + mode_settings.set("receive", "webhook_url", webhook_url) if mode == "website": mode_settings.set("website", "disable_csp", disable_csp) else: @@ -354,10 +363,23 @@ def main(cwd=None): # Start the onionshare app try: common.settings.load() - if not mode_settings.get("general", "public"): - web.generate_password(mode_settings.get("onion", "password")) - else: + + if mode_settings.get("general", "public"): web.password = None + else: + web.generate_password(mode_settings.get("onion", "password")) + + # Receive mode needs to know the tor proxy details for webhooks + if mode == "receive": + if local_only: + web.proxies = None + else: + (socks_address, socks_port) = onion.get_tor_socks_port() + web.proxies = { + "http": f"socks5://{socks_address}:{socks_port}", + "https": f"socks5://{socks_address}:{socks_port}", + } + app = OnionShare(common, onion, local_only, autostop_timer) app.choose_port() diff --git a/cli/onionshare_cli/mode_settings.py b/cli/onionshare_cli/mode_settings.py index 4f105653..c2e5e6cd 100644 --- a/cli/onionshare_cli/mode_settings.py +++ b/cli/onionshare_cli/mode_settings.py @@ -50,7 +50,10 @@ class ModeSettings: "service_id": None, }, "share": {"autostop_sharing": True, "filenames": []}, - "receive": {"data_dir": self.build_default_receive_data_dir()}, + "receive": { + "data_dir": self.build_default_receive_data_dir(), + "webhook_url": None, + }, "website": {"disable_csp": False, "filenames": []}, "chat": {"room": "default"}, } diff --git a/cli/onionshare_cli/web/receive_mode.py b/cli/onionshare_cli/web/receive_mode.py index 62415578..d3bd3c2b 100644 --- a/cli/onionshare_cli/web/receive_mode.py +++ b/cli/onionshare_cli/web/receive_mode.py @@ -21,6 +21,7 @@ along with this program. If not, see . import os import tempfile import json +import requests from datetime import datetime from flask import Request, request, render_template, make_response, flash, redirect from werkzeug.utils import secure_filename @@ -101,6 +102,14 @@ class ReceiveModeWeb: ) print(f"\nReceived: {local_path}") + # Send webhook if configured + if ( + self.web.settings.get("receive", "webhook_url") + and not request.upload_error + and len(files) > 0 + ): + self.send_websocket_notification(f"{len(files)} files uploaded") + if request.upload_error: self.common.log( "ReceiveModeWeb", @@ -172,6 +181,22 @@ class ReceiveModeWeb: return self.web.error403() return upload(ajax=True) + def send_websocket_notification(self, data): + self.common.log("ReceiveModeWeb", "send_websocket_notification", data) + try: + requests.post( + self.web.settings.get("receive", "webhook_url"), + data=data, + timeout=5, + proxies=self.web.proxies, + ) + except Exception as e: + self.common.log( + "ReceiveModeWeb", + "send_websocket_notification", + f"sending failed: {e}", + ) + class ReceiveModeWSGIMiddleware(object): """