diff options
author | Micah Lee <micah@micahflee.com> | 2021-04-25 20:46:03 -0400 |
---|---|---|
committer | Micah Lee <micah@micahflee.com> | 2021-04-25 20:46:06 -0400 |
commit | 2acdea52298695549dfe25260ed59df88b6c3a2c (patch) | |
tree | 6f8342bfb8d6af2a1b2a912beae78ff74857cf34 | |
parent | 470fb2bda3a04c856256191ceee267ce94515eef (diff) | |
download | onionshare-2acdea52298695549dfe25260ed59df88b6c3a2c.tar.gz onionshare-2acdea52298695549dfe25260ed59df88b6c3a2c.zip |
Allow sending messages as well as uploading files in receive mode
-rw-r--r-- | cli/onionshare_cli/__init__.py | 21 | ||||
-rw-r--r-- | cli/onionshare_cli/resources/static/css/style.css | 7 | ||||
-rw-r--r-- | cli/onionshare_cli/resources/static/js/receive.js | 65 | ||||
-rw-r--r-- | cli/onionshare_cli/resources/templates/receive.html | 102 | ||||
-rw-r--r-- | cli/onionshare_cli/web/receive_mode.py | 145 |
5 files changed, 225 insertions, 115 deletions
diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index 42ec9673..0a7a1d3c 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -199,6 +199,18 @@ def main(cwd=None): default=None, help="Receive files: URL to receive webhook notifications", ) + parser.add_argument( + "--disable-text", + action="store_true", + dest="disable_text", + help="Receive files: Disable receiving text messages", + ) + parser.add_argument( + "--disable-files", + action="store_true", + dest="disable_files", + help="Receive files: Disable receiving files", + ) # Website args parser.add_argument( "--disable_csp", @@ -242,6 +254,8 @@ def main(cwd=None): autostop_sharing = not bool(args.no_autostop_sharing) data_dir = args.data_dir webhook_url = args.webhook_url + disable_text = args.disable_text + disable_files = args.disable_files disable_csp = bool(args.disable_csp) verbose = bool(args.verbose) @@ -292,6 +306,8 @@ def main(cwd=None): mode_settings.set("receive", "data_dir", data_dir) if webhook_url: mode_settings.set("receive", "webhook_url", webhook_url) + mode_settings.set("receive", "disable_text", disable_text) + mode_settings.set("receive", "disable_files", disable_files) if mode == "website": mode_settings.set("website", "disable_csp", disable_csp) else: @@ -334,6 +350,11 @@ def main(cwd=None): if persistent_filename: mode_settings.set(mode, "filenames", filenames) + # In receive mode, you must allows either text, files, or both + if mode == "receive" and disable_text and disable_files: + print(f"You cannot disable both text and files") + sys.exit() + # Create the Web object web = Web(common, False, mode_settings, mode) diff --git a/cli/onionshare_cli/resources/static/css/style.css b/cli/onionshare_cli/resources/static/css/style.css index 88d9cb79..57b23fdb 100644 --- a/cli/onionshare_cli/resources/static/css/style.css +++ b/cli/onionshare_cli/resources/static/css/style.css @@ -285,6 +285,13 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited { margin: 0 0 20px 0; } +.upload-wrapper textarea { + max-width: 95%; + width: 600px; + height: 150px; + padding: 10px; +} + div#uploads { width: 800px; max-width: 90%; diff --git a/cli/onionshare_cli/resources/static/js/receive.js b/cli/onionshare_cli/resources/static/js/receive.js index eac67412..f4bdedec 100644 --- a/cli/onionshare_cli/resources/static/js/receive.js +++ b/cli/onionshare_cli/resources/static/js/receive.js @@ -1,24 +1,35 @@ -$(function(){ +$(function () { // Add a flash message - var flash = function(category, message) { + var flash = function (category, message) { $('#flashes').append($('<li>').addClass(category).text(message)); }; var scriptSrc = document.getElementById('receive-script').src; - var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf( '/' )+1).replace('js', 'img'); + var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf('/') + 1).replace('js', 'img'); // Intercept submitting the form - $('#send').submit(function(event){ + $('#send').submit(function (event) { event.preventDefault(); - // Create form data, and list of filenames - var files = $('#file-select').get(0).files; - var filenames = []; + // Build the form data var formData = new FormData(); - for(var i = 0; i < files.length; i++) { - var file = files[i]; - filenames.push(file.name); - formData.append('file[]', file, file.name); + + // Files + var $fileSelect = $('#file-select'); + if ($fileSelect.length > 0) { + var files = $fileSelect.get(0).files; + var filenames = []; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + filenames.push(file.name); + formData.append('file[]', file, file.name); + } + } + + // Text message + var $text = $('#text'); + if ($text.length > 0) { + formData.append("text", $text.val()) } // Reset the upload form @@ -28,9 +39,9 @@ $(function(){ // have access to the the XMLHttpRequest object var ajax = new XMLHttpRequest(); - ajax.upload.addEventListener('progress', function(event){ + ajax.upload.addEventListener('progress', function (event) { // Update progress bar for this specific upload - if(event.lengthComputable) { + if (event.lengthComputable) { $('progress', ajax.$upload_div).attr({ value: event.loaded, max: event.total, @@ -39,13 +50,13 @@ $(function(){ // If it's finished sending all data to the first Tor node, remove cancel button // and update the status - if(event.loaded == event.total) { + if (event.loaded == event.total) { $('.cancel', ajax.$upload_div).remove(); $('.upload-status', ajax.$upload_div).html('<img src="' + staticImgPath + '/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...'); } }, false); - ajax.addEventListener('load', function(event){ + ajax.addEventListener('load', function (event) { // Remove the upload div ajax.$upload_div.remove(); @@ -54,38 +65,38 @@ $(function(){ var response = JSON.parse(ajax.response); // The 'new_body' response replaces the whole HTML document and ends - if('new_body' in response) { + if ('new_body' in response) { $('body').html(response['new_body']); return; } // Show error flashes - if('error_flashes' in response) { - for(var i=0; i<response['error_flashes'].length; i++) { + if ('error_flashes' in response) { + for (var i = 0; i < response['error_flashes'].length; i++) { flash('error', response['error_flashes'][i]); } } // Show info flashes - if('info_flashes' in response) { - for(var i=0; i<response['info_flashes'].length; i++) { + if ('info_flashes' in response) { + for (var i = 0; i < response['info_flashes'].length; i++) { flash('info', response['info_flashes'][i]); } } - } catch(e) { - flash('error', 'Invalid response from server: '+data); + } catch (e) { + flash('error', 'Invalid response from server: ' + data); } }, false); - ajax.addEventListener('error', function(event){ - flash('error', 'Error uploading: '+filenames.join(', ')); + ajax.addEventListener('error', function (event) { + flash('error', 'Error uploading: ' + filenames.join(', ')); // Remove the upload div ajax.$upload_div.remove() }, false); - ajax.addEventListener('abort', function(event){ - flash('error', 'Upload aborted: '+filenames.join(', ')); + ajax.addEventListener('abort', function (event) { + flash('error', 'Upload aborted: ' + filenames.join(', ')); }, false); // Make the upload div @@ -114,7 +125,7 @@ $(function(){ ) .append($progress); - $cancel_button.click(function(){ + $cancel_button.click(function () { // Abort the upload, and remove the upload div ajax.abort(); $upload_div.remove() diff --git a/cli/onionshare_cli/resources/templates/receive.html b/cli/onionshare_cli/resources/templates/receive.html index 20f4bb7e..507ebea6 100644 --- a/cli/onionshare_cli/resources/templates/receive.html +++ b/cli/onionshare_cli/resources/templates/receive.html @@ -1,46 +1,64 @@ <!DOCTYPE html> <html> - <head> - <title>OnionShare</title> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link href="{{ static_url_path }}/img/favicon.ico" rel="icon" type="image/x-icon"> - <link rel="stylesheet" rel="subresource" type="text/css" href="{{ static_url_path }}/css/style.css" media="all"> - </head> - <body> - - <header class="clearfix"> - <img class="logo" src="{{ static_url_path }}/img/logo.png" title="OnionShare"> - <h1>OnionShare</h1> - </header> - - <div class="upload-wrapper"> - <p><img class="logo" src="{{ static_url_path }}/img/logo_large.png" title="OnionShare"></p> - - <p class="upload-header">Send Files</p> - <p class="upload-description">Select the files you want to send, then click "Send Files"...</p> - - <div id="uploads"></div> - - <div> - <ul id="flashes" class="flashes"> - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} - <li class="{{ category }}">{{ message }}</li> - {% endfor %} - {% endif %} - {% endwith %} - </ul> - </div> - - <form id="send" method="post" enctype="multipart/form-data" action="/upload"> - <p><input type="file" id="file-select" name="file[]" multiple /></p> - <p><button type="submit" id="send-button" class="button">Send Files</button></p> - </form> +<head> + <title>OnionShare</title> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link href="{{ static_url_path }}/img/favicon.ico" rel="icon" type="image/x-icon"> + <link rel="stylesheet" rel="subresource" type="text/css" href="{{ static_url_path }}/css/style.css" media="all"> +</head> + +<body> + + <header class="clearfix"> + <img class="logo" src="{{ static_url_path }}/img/logo.png" title="OnionShare"> + <h1>OnionShare</h1> + </header> + + <div class="upload-wrapper"> + <p><img class="logo" src="{{ static_url_path }}/img/logo_large.png" title="OnionShare"></p> + + {% if not disable_text and not disable_files %} + <p class="upload-header">Submit Files or Messages</p> + <p class="upload-description">You can submit files, a message, or both</p> + {% endif %} + {% if not disable_text and disable_files %} + <p class="upload-header">Submit Messages</p> + <p class="upload-description">You can submit a message</p> + {% endif %} + {% if disable_text and not disable_files %} + <p class="upload-header">Submit Files</p> + <p class="upload-description">You can submit files</p> + {% endif %} + + <div id="uploads"></div> + + <div> + <ul id="flashes" class="flashes"> + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + <li class="{{ category }}">{{ message }}</li> + {% endfor %} + {% endif %} + {% endwith %} + </ul> </div> - <script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script> - <script async src="{{ static_url_path }}/js/receive.js" id="receive-script"></script> - </body> -</html> + + <form id="send" method="post" enctype="multipart/form-data" action="/upload"> + {% if not disable_files %} + <p><input type="file" id="file-select" name="file[]" multiple /></p> + {% endif %} + {% if not disable_text %} + <p><textarea id="text" name="text" placeholder="Write a message"></textarea></p> + {% endif %} + <p><button type="submit" id="send-button" class="button">Submit</button></p> + </form> + + </div> + <script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script> + <script async src="{{ static_url_path }}/js/receive.js" id="receive-script"></script> +</body> + +</html>
\ No newline at end of file diff --git a/cli/onionshare_cli/web/receive_mode.py b/cli/onionshare_cli/web/receive_mode.py index 46f471a8..f52e691e 100644 --- a/cli/onionshare_cli/web/receive_mode.py +++ b/cli/onionshare_cli/web/receive_mode.py @@ -64,7 +64,10 @@ class ReceiveModeWeb: self.web.add_request(self.web.REQUEST_LOAD, request.path) r = make_response( render_template( - "receive.html", static_url_path=self.web.static_url_path + "receive.html", + static_url_path=self.web.static_url_path, + disable_text=self.web.settings.get("receive", "disable_text"), + disable_files=self.web.settings.get("receive", "disable_files"), ) ) return self.web.add_security_headers(r) @@ -75,44 +78,88 @@ class ReceiveModeWeb: Handle the upload files POST request, though at this point, the files have already been uploaded and saved to their correct locations. """ - files = request.files.getlist("file[]") - filenames = [] - for f in files: - if f.filename != "": - filename = secure_filename(f.filename) - filenames.append(filename) - local_path = os.path.join(request.receive_mode_dir, filename) - basename = os.path.basename(local_path) - - # Tell the GUI the receive mode directory for this file - self.web.add_request( - self.web.REQUEST_UPLOAD_SET_DIR, - request.path, - { - "id": request.history_id, - "filename": basename, - "dir": request.receive_mode_dir, - }, - ) - - self.common.log( - "ReceiveModeWeb", - "define_routes", - f"/upload, uploaded {f.filename}, saving to {local_path}", - ) - print(f"\nReceived: {local_path}") + text_received = False + if not self.web.settings.get("receive", "disable_text"): + text_message = request.form.get("text") + if text_message: + if text_message.strip() != "": + text_received = True + filename = "message.txt" + local_path = os.path.join(request.receive_mode_dir, filename) + + with open(local_path, "w") as f: + f.write(text_message) + + basename = os.path.basename(local_path) + + # TODO: possibly change this + self.web.add_request( + self.web.REQUEST_UPLOAD_SET_DIR, + request.path, + { + "id": request.history_id, + "filename": basename, + "dir": request.receive_mode_dir, + }, + ) + + self.common.log( + "ReceiveModeWeb", + "define_routes", + f"/upload, sent text message, saving to {local_path}", + ) + print(f"\nReceived: {local_path}") + + files_received = 0 + if not self.web.settings.get("receive", "disable_files"): + files = request.files.getlist("file[]") + + filenames = [] + for f in files: + if f.filename != "": + filename = secure_filename(f.filename) + filenames.append(filename) + local_path = os.path.join(request.receive_mode_dir, filename) + basename = os.path.basename(local_path) + + # Tell the GUI the receive mode directory for this file + self.web.add_request( + self.web.REQUEST_UPLOAD_SET_DIR, + request.path, + { + "id": request.history_id, + "filename": basename, + "dir": request.receive_mode_dir, + }, + ) + + self.common.log( + "ReceiveModeWeb", + "define_routes", + f"/upload, uploaded {f.filename}, saving to {local_path}", + ) + print(f"\nReceived: {local_path}") + + files_received = len(filenames) # Send webhook if configured if ( - self.web.settings.get("receive", "webhook_url") + self.web.settings.get("receive", "webhook_url") is not None and not request.upload_error - and len(files) > 0 + and (text_received or files_received) ): - if len(files) == 1: - file_msg = "1 file" - else: - file_msg = f"{len(files)} files" - self.send_webhook_notification(f"{file_msg} uploaded to OnionShare") + msg = "" + if files_received > 0: + if files_received == 1: + msg += "1 file" + else: + msg += f"{files_received} files" + if text_received: + if msg == "": + msg = "A text message" + else: + msg += " and a text message" + self.send_webhook_notification(f"{msg} submitted to OnionShare") if request.upload_error: self.common.log( @@ -140,21 +187,27 @@ class ReceiveModeWeb: if ajax: info_flashes = [] - if len(filenames) == 0: - msg = "No files uploaded" - if ajax: - info_flashes.append(msg) + if files_received > 0: + files_msg = "" + for filename in filenames: + files_msg += f"{filename}, " + files_msg = files_msg.rstrip(", ") + + if text_received: + if files_received > 0: + msg = f"Message submitted, uploaded {files_msg}" else: - flash(msg, "info") + msg = "Message submitted" else: - msg = "Sent " - for filename in filenames: - msg += f"{filename}, " - msg = msg.rstrip(", ") - if ajax: - info_flashes.append(msg) + if files_received > 0: + msg = f"Uploaded {files_msg}" else: - flash(msg, "info") + msg = "Nothing submitted" + + if ajax: + info_flashes.append(msg) + else: + flash(msg, "info") if self.can_upload: if ajax: |