summaryrefslogtreecommitdiff
path: root/onionshare/web/receive_mode.py
diff options
context:
space:
mode:
Diffstat (limited to 'onionshare/web/receive_mode.py')
-rw-r--r--onionshare/web/receive_mode.py365
1 files changed, 204 insertions, 161 deletions
diff --git a/onionshare/web/receive_mode.py b/onionshare/web/receive_mode.py
index bc805445..90f000b9 100644
--- a/onionshare/web/receive_mode.py
+++ b/onionshare/web/receive_mode.py
@@ -8,97 +8,107 @@ from werkzeug.utils import secure_filename
from .. import strings
-class ReceiveModeWeb(object):
+class ReceiveModeWeb:
"""
All of the web logic for receive mode
"""
+
def __init__(self, common, web):
self.common = common
- self.common.log('ReceiveModeWeb', '__init__')
+ self.common.log("ReceiveModeWeb", "__init__")
self.web = web
self.can_upload = True
- self.upload_count = 0
self.uploads_in_progress = []
+ # This tracks the history id
+ self.cur_history_id = 0
+
self.define_routes()
def define_routes(self):
"""
The web app routes for receiving files
"""
- def index_logic():
- self.web.add_request(self.web.REQUEST_LOAD, request.path)
-
- if self.common.settings.get('public_mode'):
- upload_action = '/upload'
- else:
- upload_action = '/{}/upload'.format(self.web.slug)
-
- r = make_response(render_template(
- 'receive.html',
- upload_action=upload_action))
- return self.web.add_security_headers(r)
-
- @self.web.app.route("/<slug_candidate>")
- def index(slug_candidate):
- if not self.can_upload:
- return self.web.error403()
- self.web.check_slug_candidate(slug_candidate)
- return index_logic()
@self.web.app.route("/")
- def index_public():
- if not self.can_upload:
- return self.web.error403()
- if not self.common.settings.get('public_mode'):
- return self.web.error404()
- return index_logic()
+ def index():
+ history_id = self.cur_history_id
+ self.cur_history_id += 1
+ self.web.add_request(
+ self.web.REQUEST_INDIVIDUAL_FILE_STARTED,
+ "{}".format(request.path),
+ {"id": history_id, "status_code": 200},
+ )
+ 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
+ )
+ )
+ return self.web.add_security_headers(r)
- def upload_logic(slug_candidate='', ajax=False):
+ @self.web.app.route("/upload", methods=["POST"])
+ def upload(ajax=False):
"""
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[]')
+ files = request.files.getlist("file[]")
filenames = []
for f in files:
- if f.filename != '':
+ 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.upload_id,
- 'filename': basename,
- 'dir': request.receive_mode_dir
- })
-
- self.common.log('ReceiveModeWeb', 'define_routes', '/upload, uploaded {}, saving to {}'.format(f.filename, local_path))
- print('\n' + "Received: {}".format(local_path))
+ 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",
+ "/upload, uploaded {}, saving to {}".format(
+ f.filename, local_path
+ ),
+ )
+ print("\n" + "Received: {}".format(local_path))
if request.upload_error:
- self.common.log('ReceiveModeWeb', 'define_routes', '/upload, there was an upload error')
-
- self.web.add_request(self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE, request.path, {
- "receive_mode_dir": request.receive_mode_dir
- })
- print("Could not create OnionShare data folder: {}".format(request.receive_mode_dir))
-
- msg = 'Error uploading, please inform the OnionShare user'
+ self.common.log(
+ "ReceiveModeWeb",
+ "define_routes",
+ "/upload, there was an upload error",
+ )
+
+ self.web.add_request(
+ self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE,
+ request.path,
+ {"receive_mode_dir": request.receive_mode_dir},
+ )
+ print(
+ "Could not create OnionShare data folder: {}".format(
+ request.receive_mode_dir
+ )
+ )
+
+ msg = "Error uploading, please inform the OnionShare user"
if ajax:
return json.dumps({"error_flashes": [msg]})
else:
- flash(msg, 'error')
-
- if self.common.settings.get('public_mode'):
- return redirect('/')
- else:
- return redirect('/{}'.format(slug_candidate))
+ flash(msg, "error")
+ return redirect("/")
# Note that flash strings are in English, and not translated, on purpose,
# to avoid leaking the locale of the OnionShare user
@@ -106,67 +116,49 @@ class ReceiveModeWeb(object):
info_flashes = []
if len(filenames) == 0:
- msg = 'No files uploaded'
+ msg = "No files uploaded"
if ajax:
info_flashes.append(msg)
else:
- flash(msg, 'info')
+ flash(msg, "info")
else:
- msg = 'Sent '
+ msg = "Sent "
for filename in filenames:
- msg += '{}, '.format(filename)
- msg = msg.rstrip(', ')
+ msg += "{}, ".format(filename)
+ msg = msg.rstrip(", ")
if ajax:
info_flashes.append(msg)
else:
- flash(msg, 'info')
+ flash(msg, "info")
if self.can_upload:
if ajax:
return json.dumps({"info_flashes": info_flashes})
else:
- if self.common.settings.get('public_mode'):
- path = '/'
- else:
- path = '/{}'.format(slug_candidate)
- return redirect('{}'.format(path))
+ return redirect("/")
else:
if ajax:
- return json.dumps({"new_body": render_template('thankyou.html')})
+ return json.dumps(
+ {
+ "new_body": render_template(
+ "thankyou.html",
+ static_url_path=self.web.static_url_path,
+ )
+ }
+ )
else:
# It was the last upload and the timer ran out
- r = make_response(render_template('thankyou.html'))
+ r = make_response(
+ render_template("thankyou.html"),
+ static_url_path=self.web.static_url_path,
+ )
return self.web.add_security_headers(r)
- @self.web.app.route("/<slug_candidate>/upload", methods=['POST'])
- def upload(slug_candidate):
- if not self.can_upload:
- return self.web.error403()
- self.web.check_slug_candidate(slug_candidate)
- return upload_logic(slug_candidate)
-
- @self.web.app.route("/upload", methods=['POST'])
- def upload_public():
- if not self.can_upload:
- return self.web.error403()
- if not self.common.settings.get('public_mode'):
- return self.web.error404()
- return upload_logic()
-
- @self.web.app.route("/<slug_candidate>/upload-ajax", methods=['POST'])
- def upload_ajax(slug_candidate):
- if not self.can_upload:
- return self.web.error403()
- self.web.check_slug_candidate(slug_candidate)
- return upload_logic(slug_candidate, ajax=True)
-
- @self.web.app.route("/upload-ajax", methods=['POST'])
+ @self.web.app.route("/upload-ajax", methods=["POST"])
def upload_ajax_public():
if not self.can_upload:
return self.web.error403()
- if not self.common.settings.get('public_mode'):
- return self.web.error404()
- return upload_logic(ajax=True)
+ return upload(ajax=True)
class ReceiveModeWSGIMiddleware(object):
@@ -174,13 +166,14 @@ class ReceiveModeWSGIMiddleware(object):
Custom WSGI middleware in order to attach the Web object to environ, so
ReceiveModeRequest can access it.
"""
+
def __init__(self, app, web):
self.app = app
self.web = web
def __call__(self, environ, start_response):
- environ['web'] = self.web
- environ['stop_q'] = self.web.stop_q
+ environ["web"] = self.web
+ environ["stop_q"] = self.web.stop_q
return self.app(environ, start_response)
@@ -190,6 +183,7 @@ class ReceiveModeFile(object):
written to it, in order to track the progress of uploads. It starts out with
a .part file extension, and when it's complete it removes that extension.
"""
+
def __init__(self, request, filename, write_func, close_func):
self.onionshare_request = request
self.onionshare_filename = filename
@@ -197,24 +191,44 @@ class ReceiveModeFile(object):
self.onionshare_close_func = close_func
self.filename = os.path.join(self.onionshare_request.receive_mode_dir, filename)
- self.filename_in_progress = '{}.part'.format(self.filename)
+ self.filename_in_progress = "{}.part".format(self.filename)
# Open the file
self.upload_error = False
try:
- self.f = open(self.filename_in_progress, 'wb+')
+ self.f = open(self.filename_in_progress, "wb+")
except:
# This will only happen if someone is messing with the data dir while
# OnionShare is running, but if it does make sure to throw an error
self.upload_error = True
- self.f = tempfile.TemporaryFile('wb+')
+ self.f = tempfile.TemporaryFile("wb+")
# Make all the file-like methods and attributes actually access the
# TemporaryFile, except for write
- attrs = ['closed', 'detach', 'fileno', 'flush', 'isatty', 'mode',
- 'name', 'peek', 'raw', 'read', 'read1', 'readable', 'readinto',
- 'readinto1', 'readline', 'readlines', 'seek', 'seekable', 'tell',
- 'truncate', 'writable', 'writelines']
+ attrs = [
+ "closed",
+ "detach",
+ "fileno",
+ "flush",
+ "isatty",
+ "mode",
+ "name",
+ "peek",
+ "raw",
+ "read",
+ "read1",
+ "readable",
+ "readinto",
+ "readinto1",
+ "readline",
+ "readlines",
+ "seek",
+ "seekable",
+ "tell",
+ "truncate",
+ "writable",
+ "writelines",
+ ]
for attr in attrs:
setattr(self, attr, getattr(self.f, attr))
@@ -256,25 +270,22 @@ class ReceiveModeRequest(Request):
A custom flask Request object that keeps track of how much data has been
uploaded for each file, for receive mode.
"""
+
def __init__(self, environ, populate_request=True, shallow=False):
super(ReceiveModeRequest, self).__init__(environ, populate_request, shallow)
- self.web = environ['web']
- self.stop_q = environ['stop_q']
+ self.web = environ["web"]
+ self.stop_q = environ["stop_q"]
- self.web.common.log('ReceiveModeRequest', '__init__')
+ self.web.common.log("ReceiveModeRequest", "__init__")
# Prevent running the close() method more than once
self.closed = False
# Is this a valid upload request?
self.upload_request = False
- if self.method == 'POST':
- if self.web.common.settings.get('public_mode'):
- if self.path == '/upload' or self.path == '/upload-ajax':
- self.upload_request = True
- else:
- if self.path == '/{}/upload'.format(self.web.slug) or self.path == '/{}/upload-ajax'.format(self.web.slug):
- self.upload_request = True
+ if self.method == "POST":
+ if self.path == "/upload" or self.path == "/upload-ajax":
+ self.upload_request = True
if self.upload_request:
# No errors yet
@@ -284,7 +295,9 @@ class ReceiveModeRequest(Request):
now = datetime.now()
date_dir = now.strftime("%Y-%m-%d")
time_dir = now.strftime("%H.%M.%S")
- self.receive_mode_dir = os.path.join(self.web.common.settings.get('data_dir'), date_dir, time_dir)
+ self.receive_mode_dir = os.path.join(
+ self.web.common.settings.get("data_dir"), date_dir, time_dir
+ )
# Create that directory, which shouldn't exist yet
try:
@@ -296,7 +309,7 @@ class ReceiveModeRequest(Request):
# Keep going until we find a directory name that's available
i = 1
while True:
- new_receive_mode_dir = '{}-{}'.format(self.receive_mode_dir, i)
+ new_receive_mode_dir = "{}-{}".format(self.receive_mode_dir, i)
try:
os.makedirs(new_receive_mode_dir, 0o700, exist_ok=False)
self.receive_mode_dir = new_receive_mode_dir
@@ -306,15 +319,29 @@ class ReceiveModeRequest(Request):
i += 1
# Failsafe
if i == 100:
- self.web.common.log('ReceiveModeRequest', '__init__', 'Error finding available receive mode directory')
+ self.web.common.log(
+ "ReceiveModeRequest",
+ "__init__",
+ "Error finding available receive mode directory",
+ )
self.upload_error = True
break
except PermissionError:
- self.web.add_request(self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE, request.path, {
- "receive_mode_dir": self.receive_mode_dir
- })
- print("Could not create OnionShare data folder: {}".format(self.receive_mode_dir))
- self.web.common.log('ReceiveModeRequest', '__init__', 'Permission denied creating receive mode directory')
+ self.web.add_request(
+ self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE,
+ request.path,
+ {"receive_mode_dir": self.receive_mode_dir},
+ )
+ print(
+ "Could not create OnionShare data folder: {}".format(
+ self.receive_mode_dir
+ )
+ )
+ self.web.common.log(
+ "ReceiveModeRequest",
+ "__init__",
+ "Permission denied creating receive mode directory",
+ )
self.upload_error = True
# If there's an error so far, finish early
@@ -327,28 +354,33 @@ class ReceiveModeRequest(Request):
# Prevent new uploads if we've said so (timer expired)
if self.web.receive_mode.can_upload:
- # Create an upload_id, attach it to the request
- self.upload_id = self.web.receive_mode.upload_count
-
- self.web.receive_mode.upload_count += 1
+ # Create an history_id, attach it to the request
+ self.history_id = self.web.receive_mode.cur_history_id
+ self.web.receive_mode.cur_history_id += 1
- # Figure out the content length
+ # Figure out the content length
try:
- self.content_length = int(self.headers['Content-Length'])
+ self.content_length = int(self.headers["Content-Length"])
except:
self.content_length = 0
- print("{}: {}".format(
- datetime.now().strftime("%b %d, %I:%M%p"),
- strings._("receive_mode_upload_starting").format(self.web.common.human_readable_filesize(self.content_length))
- ))
+ print(
+ "{}: {}".format(
+ datetime.now().strftime("%b %d, %I:%M%p"),
+ strings._("receive_mode_upload_starting").format(
+ self.web.common.human_readable_filesize(self.content_length)
+ ),
+ )
+ )
# Don't tell the GUI that a request has started until we start receiving files
self.told_gui_about_request = False
self.previous_file = None
- def _get_file_stream(self, total_content_length, content_type, filename=None, content_length=None):
+ def _get_file_stream(
+ self, total_content_length, content_type, filename=None, content_length=None
+ ):
"""
This gets called for each file that gets uploaded, and returns an file-like
writable stream.
@@ -356,24 +388,26 @@ class ReceiveModeRequest(Request):
if self.upload_request:
if not self.told_gui_about_request:
# Tell the GUI about the request
- self.web.add_request(self.web.REQUEST_STARTED, self.path, {
- 'id': self.upload_id,
- 'content_length': self.content_length
- })
- self.web.receive_mode.uploads_in_progress.append(self.upload_id)
+ self.web.add_request(
+ self.web.REQUEST_STARTED,
+ self.path,
+ {"id": self.history_id, "content_length": self.content_length},
+ )
+ self.web.receive_mode.uploads_in_progress.append(self.history_id)
self.told_gui_about_request = True
self.filename = secure_filename(filename)
- self.progress[self.filename] = {
- 'uploaded_bytes': 0,
- 'complete': False
- }
+ self.progress[self.filename] = {"uploaded_bytes": 0, "complete": False}
- f = ReceiveModeFile(self, self.filename, self.file_write_func, self.file_close_func)
+ f = ReceiveModeFile(
+ self, self.filename, self.file_write_func, self.file_close_func
+ )
if f.upload_error:
- self.web.common.log('ReceiveModeRequest', '_get_file_stream', 'Error creating file')
+ self.web.common.log(
+ "ReceiveModeRequest", "_get_file_stream", "Error creating file"
+ )
self.upload_error = True
return f
@@ -388,23 +422,26 @@ class ReceiveModeRequest(Request):
return
self.closed = True
- self.web.common.log('ReceiveModeRequest', 'close')
+ self.web.common.log("ReceiveModeRequest", "close")
try:
if self.told_gui_about_request:
- upload_id = self.upload_id
+ history_id = self.history_id
- if not self.web.stop_q.empty() or not self.progress[self.filename]['complete']:
+ if (
+ not self.web.stop_q.empty()
+ or not self.progress[self.filename]["complete"]
+ ):
# Inform the GUI that the upload has canceled
- self.web.add_request(self.web.REQUEST_UPLOAD_CANCELED, self.path, {
- 'id': upload_id
- })
+ self.web.add_request(
+ self.web.REQUEST_UPLOAD_CANCELED, self.path, {"id": history_id}
+ )
else:
# Inform the GUI that the upload has finished
- self.web.add_request(self.web.REQUEST_UPLOAD_FINISHED, self.path, {
- 'id': upload_id
- })
- self.web.receive_mode.uploads_in_progress.remove(upload_id)
+ self.web.add_request(
+ self.web.REQUEST_UPLOAD_FINISHED, self.path, {"id": history_id}
+ )
+ self.web.receive_mode.uploads_in_progress.remove(history_id)
except AttributeError:
pass
@@ -417,28 +454,34 @@ class ReceiveModeRequest(Request):
return
if self.upload_request:
- self.progress[filename]['uploaded_bytes'] += length
+ self.progress[filename]["uploaded_bytes"] += length
if self.previous_file != filename:
self.previous_file = filename
- print('\r=> {:15s} {}'.format(
- self.web.common.human_readable_filesize(self.progress[filename]['uploaded_bytes']),
- filename
- ), end='')
+ print(
+ "\r=> {:15s} {}".format(
+ self.web.common.human_readable_filesize(
+ self.progress[filename]["uploaded_bytes"]
+ ),
+ filename,
+ ),
+ end="",
+ )
# Update the GUI on the upload progress
if self.told_gui_about_request:
- self.web.add_request(self.web.REQUEST_PROGRESS, self.path, {
- 'id': self.upload_id,
- 'progress': self.progress
- })
+ self.web.add_request(
+ self.web.REQUEST_PROGRESS,
+ self.path,
+ {"id": self.history_id, "progress": self.progress},
+ )
def file_close_func(self, filename, upload_error=False):
"""
This function gets called when a specific file is closed.
"""
- self.progress[filename]['complete'] = True
+ self.progress[filename]["complete"] = True
# If the file tells us there was an upload error, let the request know as well
if upload_error: