diff options
author | Micah Lee <micah@micahflee.com> | 2019-01-28 17:22:02 -0800 |
---|---|---|
committer | Micah Lee <micah@micahflee.com> | 2019-01-28 17:22:02 -0800 |
commit | 58844ba7c823b3e7c95a15574e55e665fb888140 (patch) | |
tree | e87feddd85cd3f19ff479542deb973bf3a0f326e /onionshare | |
parent | b1d5b29cf69371caf9936e4e4943bf6d9b909e07 (diff) | |
parent | 4c11900a8193efb8b463259feef4549c9d6d01a5 (diff) | |
download | onionshare-58844ba7c823b3e7c95a15574e55e665fb888140.tar.gz onionshare-58844ba7c823b3e7c95a15574e55e665fb888140.zip |
Merge branch 'develop' into 812_v3_tor_version
Diffstat (limited to 'onionshare')
-rw-r--r-- | onionshare/__init__.py | 2 | ||||
-rw-r--r-- | onionshare/settings.py | 8 | ||||
-rw-r--r-- | onionshare/web/receive_mode.py | 104 | ||||
-rw-r--r-- | onionshare/web/share_mode.py | 10 | ||||
-rw-r--r-- | onionshare/web/web.py | 29 |
5 files changed, 106 insertions, 47 deletions
diff --git a/onionshare/__init__.py b/onionshare/__init__.py index 0d064639..2f44c846 100644 --- a/onionshare/__init__.py +++ b/onionshare/__init__.py @@ -175,7 +175,7 @@ def main(cwd=None): print('') if mode == 'receive': - print(strings._('receive_mode_downloads_dir').format(common.settings.get('downloads_dir'))) + print(strings._('receive_mode_data_dir').format(common.settings.get('data_dir'))) print('') print(strings._('receive_mode_warning')) print('') diff --git a/onionshare/settings.py b/onionshare/settings.py index 06235198..1570a150 100644 --- a/onionshare/settings.py +++ b/onionshare/settings.py @@ -100,7 +100,7 @@ class Settings(object): 'public_mode': False, 'slug': '', 'hidservauth_string': '', - 'downloads_dir': self.build_default_downloads_dir(), + 'data_dir': self.build_default_data_dir(), 'locale': None # this gets defined in fill_in_defaults() } self._settings = {} @@ -140,7 +140,7 @@ class Settings(object): """ return os.path.join(self.common.build_data_dir(), 'onionshare.json') - def build_default_downloads_dir(self): + def build_default_data_dir(self): """ Returns the path of the default Downloads directory for receive mode. """ @@ -174,9 +174,9 @@ class Settings(object): except: pass - # Make sure downloads_dir exists + # Make sure data_dir exists try: - os.makedirs(self.get('downloads_dir'), exist_ok=True) + os.makedirs(self.get('data_dir'), exist_ok=True) except: pass diff --git a/onionshare/web/receive_mode.py b/onionshare/web/receive_mode.py index 6985f38a..f035271a 100644 --- a/onionshare/web/receive_mode.py +++ b/onionshare/web/receive_mode.py @@ -60,19 +60,19 @@ class ReceiveModeWeb(object): """ Upload files. """ - # Make sure the receive mode dir exists + # Figure out what the receive mode dir should be now = datetime.now() date_dir = now.strftime("%Y-%m-%d") time_dir = now.strftime("%H.%M.%S") - receive_mode_dir = os.path.join(self.common.settings.get('downloads_dir'), date_dir, time_dir) + receive_mode_dir = os.path.join(self.common.settings.get('data_dir'), date_dir, time_dir) valid = True try: - os.makedirs(receive_mode_dir, 0o700) + os.makedirs(receive_mode_dir, 0o700, exist_ok=True) except PermissionError: - self.web.add_request(self.web.REQUEST_ERROR_DOWNLOADS_DIR_CANNOT_CREATE, request.path, { + self.web.add_request(self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE, request.path, { "receive_mode_dir": receive_mode_dir }) - print(strings._('error_cannot_create_downloads_dir').format(receive_mode_dir)) + print(strings._('error_cannot_create_data_dir').format(receive_mode_dir)) valid = False if not valid: flash('Error uploading, please inform the OnionShare user', 'error') @@ -134,6 +134,23 @@ class ReceiveModeWeb(object): 'dir': receive_mode_dir }) + # Make sure receive mode dir exists before writing file + valid = True + try: + os.makedirs(receive_mode_dir, 0o700, exist_ok=True) + except PermissionError: + self.web.add_request(self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE, request.path, { + "receive_mode_dir": receive_mode_dir + }) + print(strings._('error_cannot_create_data_dir').format(receive_mode_dir)) + valid = False + if not valid: + flash('Error uploading, please inform the OnionShare user', 'error') + if self.common.settings.get('public_mode'): + return redirect('/') + else: + return redirect('/{}'.format(slug_candidate)) + self.common.log('ReceiveModeWeb', 'define_routes', '/upload, uploaded {}, saving to {}'.format(f.filename, local_path)) print(strings._('receive_mode_received_file').format(local_path)) f.save(local_path) @@ -193,6 +210,7 @@ class ReceiveModeWSGIMiddleware(object): def __call__(self, environ, start_response): environ['web'] = self.web + environ['stop_q'] = self.web.stop_q return self.app(environ, start_response) @@ -201,7 +219,8 @@ class ReceiveModeTemporaryFile(object): A custom TemporaryFile that tells ReceiveModeRequest every time data gets written to it, in order to track the progress of uploads. """ - def __init__(self, filename, write_func, close_func): + def __init__(self, request, filename, write_func, close_func): + self.onionshare_request = request self.onionshare_filename = filename self.onionshare_write_func = write_func self.onionshare_close_func = close_func @@ -222,6 +241,11 @@ class ReceiveModeTemporaryFile(object): """ Custom write method that calls out to onionshare_write_func """ + if not self.onionshare_request.stop_q.empty(): + self.close() + self.onionshare_request.close() + return + bytes_written = self.f.write(b) self.onionshare_write_func(self.onionshare_filename, bytes_written) @@ -241,6 +265,12 @@ class ReceiveModeRequest(Request): 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.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 @@ -275,13 +305,8 @@ class ReceiveModeRequest(Request): strings._("receive_mode_upload_starting").format(self.web.common.human_readable_filesize(self.content_length)) )) - # Tell the GUI - 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) + # 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 @@ -291,25 +316,52 @@ class ReceiveModeRequest(Request): writable stream. """ 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.told_gui_about_request = True + self.progress[filename] = { 'uploaded_bytes': 0, 'complete': False } - return ReceiveModeTemporaryFile(filename, self.file_write_func, self.file_close_func) + return ReceiveModeTemporaryFile(self, filename, self.file_write_func, self.file_close_func) def close(self): """ Closing the request. """ super(ReceiveModeRequest, self).close() + + # Prevent calling this method more than once per request + if self.closed: + return + self.closed = True + + self.web.common.log('ReceiveModeRequest', 'close') + try: - upload_id = self.upload_id - # 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) + if self.told_gui_about_request: + upload_id = self.upload_id + + if not self.web.stop_q.empty(): + # Inform the GUI that the upload has canceled + self.web.add_request(self.web.REQUEST_UPLOAD_CANCELED, self.path, { + 'id': upload_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) + except AttributeError: pass @@ -317,6 +369,9 @@ class ReceiveModeRequest(Request): """ This function gets called when a specific file is written to. """ + if self.closed: + return + if self.upload_request: self.progress[filename]['uploaded_bytes'] += length @@ -331,10 +386,11 @@ class ReceiveModeRequest(Request): ), end='') # Update the GUI on the upload progress - self.web.add_request(self.web.REQUEST_PROGRESS, self.path, { - 'id': self.upload_id, - 'progress': self.progress - }) + if self.told_gui_about_request: + self.web.add_request(self.web.REQUEST_PROGRESS, self.path, { + 'id': self.upload_id, + 'progress': self.progress + }) def file_close_func(self, filename): """ diff --git a/onionshare/web/share_mode.py b/onionshare/web/share_mode.py index a57d0a39..eb487c42 100644 --- a/onionshare/web/share_mode.py +++ b/onionshare/web/share_mode.py @@ -34,11 +34,6 @@ class ShareModeWeb(object): # one download at a time. self.download_in_progress = False - # If the client closes the OnionShare window while a download is in progress, - # it should immediately stop serving the file. The client_cancel global is - # used to tell the download function that the client is canceling the download. - self.client_cancel = False - self.define_routes() def define_routes(self): @@ -146,9 +141,6 @@ class ShareModeWeb(object): basename = os.path.basename(self.download_filename) def generate(): - # The user hasn't canceled the download - self.client_cancel = False - # Starting a new download if not self.web.stay_open: self.download_in_progress = True @@ -160,7 +152,7 @@ class ShareModeWeb(object): canceled = False while not self.web.done: # The user has canceled the download, so stop serving the file - if self.client_cancel: + if not self.web.stop_q.empty(): self.web.add_request(self.web.REQUEST_CANCELED, path, { 'id': download_id }) diff --git a/onionshare/web/web.py b/onionshare/web/web.py index 0f156941..e2b22c4d 100644 --- a/onionshare/web/web.py +++ b/onionshare/web/web.py @@ -35,11 +35,11 @@ class Web(object): REQUEST_OTHER = 3 REQUEST_CANCELED = 4 REQUEST_RATE_LIMIT = 5 - REQUEST_CLOSE_SERVER = 6 - REQUEST_UPLOAD_FILE_RENAMED = 7 - REQUEST_UPLOAD_SET_DIR = 8 - REQUEST_UPLOAD_FINISHED = 9 - REQUEST_ERROR_DOWNLOADS_DIR_CANNOT_CREATE = 10 + REQUEST_UPLOAD_FILE_RENAMED = 6 + REQUEST_UPLOAD_SET_DIR = 7 + REQUEST_UPLOAD_FINISHED = 8 + REQUEST_UPLOAD_CANCELED = 9 + REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 10 def __init__(self, common, is_gui, mode='share'): self.common = common @@ -58,6 +58,11 @@ class Web(object): # Are we running in GUI mode? self.is_gui = is_gui + # If the user stops the server while a transfer is in progress, it should + # immediately stop the transfer. In order to make it thread-safe, stop_q + # is a queue. If anything is in it, then the user stopped the server + self.stop_q = queue.Queue() + # Are we using receive mode? self.mode = mode if self.mode == 'receive': @@ -225,6 +230,13 @@ class Web(object): self.stay_open = stay_open + # Make sure the stop_q is empty when starting a new server + while not self.stop_q.empty(): + try: + self.stop_q.get(block=False) + except queue.Empty: + pass + # In Whonix, listen on 0.0.0.0 instead of 127.0.0.1 (#220) if os.path.exists('/usr/share/anon-ws-base-files/workstation'): host = '0.0.0.0' @@ -238,11 +250,10 @@ class Web(object): """ Stop the flask web server by loading /shutdown. """ + self.common.log('Web', 'stop', 'stopping server') - if self.mode == 'share': - # If the user cancels the download, let the download function know to stop - # serving the file - self.share_mode.client_cancel = True + # Let the mode know that the user stopped the server + self.stop_q.put(True) # To stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown if self.running: |